]> ToastFreeware Gitweb - philipp/winterrodeln/mediawiki_extensions/wrmap.git/blob - openlayers/OpenLayers.js
Made extension compatible with MobileFrontend extension.
[philipp/winterrodeln/mediawiki_extensions/wrmap.git] / openlayers / OpenLayers.js
1 /*
2
3   OpenLayers.js -- OpenLayers Map Viewer Library
4
5   Copyright (c) 2006-2013 by OpenLayers Contributors
6   Published under the 2-clause BSD license.
7   See http://openlayers.org/dev/license.txt for the full text of the license, and http://openlayers.org/dev/authors.txt for full list of contributors.
8
9   Includes compressed code under the following licenses:
10
11   (For uncompressed versions of the code used, please see the
12   OpenLayers Github repository: <https://github.com/openlayers/openlayers>)
13
14 */
15
16 /**
17  * Contains XMLHttpRequest.js <http://code.google.com/p/xmlhttprequest/>
18  * Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com)
19  *
20  * Licensed under the Apache License, Version 2.0 (the "License");
21  * you may not use this file except in compliance with the License.
22  * You may obtain a copy of the License at
23  * http://www.apache.org/licenses/LICENSE-2.0
24  */
25
26 /**
27  * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is
28  * Copyright (c) 2006, Yahoo! Inc.
29  * All rights reserved.
30  * 
31  * Redistribution and use of this software in source and binary forms, with or
32  * without modification, are permitted provided that the following conditions
33  * are met:
34  * 
35  * * Redistributions of source code must retain the above copyright notice,
36  *   this list of conditions and the following disclaimer.
37  * 
38  * * Redistributions in binary form must reproduce the above copyright notice,
39  *   this list of conditions and the following disclaimer in the documentation
40  *   and/or other materials provided with the distribution.
41  * 
42  * * Neither the name of Yahoo! Inc. nor the names of its contributors may be
43  *   used to endorse or promote products derived from this software without
44  *   specific prior written permission of Yahoo! Inc.
45  * 
46  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
47  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
50  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
51  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
52  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
53  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
54  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
55  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
56  * POSSIBILITY OF SUCH DAMAGE.
57  */
58 /* ======================================================================
59     OpenLayers/SingleFile.js
60    ====================================================================== */
61
62 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
63  * full list of contributors). Published under the 2-clause BSD license.
64  * See license.txt in the OpenLayers distribution or repository for the
65  * full text of the license. */
66
67 var OpenLayers = {
68     /**
69      * Constant: VERSION_NUMBER
70      */
71     VERSION_NUMBER: "Release 2.13.1",
72
73     /**
74      * Constant: singleFile
75      * TODO: remove this in 3.0 when we stop supporting build profiles that
76      * include OpenLayers.js
77      */
78     singleFile: true,
79
80     /**
81      * Method: _getScriptLocation
82      * Return the path to this script. This is also implemented in
83      * OpenLayers.js
84      *
85      * Returns:
86      * {String} Path to this script
87      */
88     _getScriptLocation: (function() {
89         var r = new RegExp("(^|(.*?\\/))(OpenLayers[^\\/]*?\\.js)(\\?|$)"),
90             s = document.getElementsByTagName('script'),
91             src, m, l = "";
92         for(var i=0, len=s.length; i<len; i++) {
93             src = s[i].getAttribute('src');
94             if(src) {
95                 m = src.match(r);
96                 if(m) {
97                     l = m[1];
98                     break;
99                 }
100             }
101         }
102         return (function() { return l; });
103     })(),
104     
105     /**
106      * Property: ImgPath
107      * {String} Set this to the path where control images are stored, a path  
108      * given here must end with a slash. If set to '' (which is the default) 
109      * OpenLayers will use its script location + "img/".
110      * 
111      * You will need to set this property when you have a singlefile build of 
112      * OpenLayers that either is not named "OpenLayers.js" or if you move
113      * the file in a way such that the image directory cannot be derived from 
114      * the script location.
115      * 
116      * If your custom OpenLayers build is named "my-custom-ol.js" and the images
117      * of OpenLayers are in a folder "/resources/external/images/ol" a correct
118      * way of including OpenLayers in your HTML would be:
119      * 
120      * (code)
121      *   <script src="/path/to/my-custom-ol.js" type="text/javascript"></script>
122      *   <script type="text/javascript">
123      *      // tell OpenLayers where the control images are
124      *      // remember the trailing slash
125      *      OpenLayers.ImgPath = "/resources/external/images/ol/";
126      *   </script>
127      * (end code)
128      * 
129      * Please remember that when your OpenLayers script is not named 
130      * "OpenLayers.js" you will have to make sure that the default theme is 
131      * loaded into the page by including an appropriate <link>-tag, 
132      * e.g.:
133      * 
134      * (code)
135      *   <link rel="stylesheet" href="/path/to/default/style.css"  type="text/css">
136      * (end code)
137      */
138     ImgPath : ''
139 };
140 /* ======================================================================
141     OpenLayers/BaseTypes.js
142    ====================================================================== */
143
144 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
145  * full list of contributors). Published under the 2-clause BSD license.
146  * See license.txt in the OpenLayers distribution or repository for the
147  * full text of the license. */
148
149 /**
150  * @requires OpenLayers/SingleFile.js
151  */
152
153 /** 
154  * Header: OpenLayers Base Types
155  * OpenLayers custom string, number and function functions are described here.
156  */
157
158 /**
159  * Namespace: OpenLayers.String
160  * Contains convenience functions for string manipulation.
161  */
162 OpenLayers.String = {
163
164     /**
165      * APIFunction: startsWith
166      * Test whether a string starts with another string. 
167      * 
168      * Parameters:
169      * str - {String} The string to test.
170      * sub - {String} The substring to look for.
171      *  
172      * Returns:
173      * {Boolean} The first string starts with the second.
174      */
175     startsWith: function(str, sub) {
176         return (str.indexOf(sub) == 0);
177     },
178
179     /**
180      * APIFunction: contains
181      * Test whether a string contains another string.
182      * 
183      * Parameters:
184      * str - {String} The string to test.
185      * sub - {String} The substring to look for.
186      * 
187      * Returns:
188      * {Boolean} The first string contains the second.
189      */
190     contains: function(str, sub) {
191         return (str.indexOf(sub) != -1);
192     },
193     
194     /**
195      * APIFunction: trim
196      * Removes leading and trailing whitespace characters from a string.
197      * 
198      * Parameters:
199      * str - {String} The (potentially) space padded string.  This string is not
200      *     modified.
201      * 
202      * Returns:
203      * {String} A trimmed version of the string with all leading and 
204      *     trailing spaces removed.
205      */
206     trim: function(str) {
207         return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
208     },
209     
210     /**
211      * APIFunction: camelize
212      * Camel-case a hyphenated string. 
213      *     Ex. "chicken-head" becomes "chickenHead", and
214      *     "-chicken-head" becomes "ChickenHead".
215      *
216      * Parameters:
217      * str - {String} The string to be camelized.  The original is not modified.
218      * 
219      * Returns:
220      * {String} The string, camelized
221      */
222     camelize: function(str) {
223         var oStringList = str.split('-');
224         var camelizedString = oStringList[0];
225         for (var i=1, len=oStringList.length; i<len; i++) {
226             var s = oStringList[i];
227             camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
228         }
229         return camelizedString;
230     },
231     
232     /**
233      * APIFunction: format
234      * Given a string with tokens in the form ${token}, return a string
235      *     with tokens replaced with properties from the given context
236      *     object.  Represent a literal "${" by doubling it, e.g. "${${".
237      *
238      * Parameters:
239      * template - {String} A string with tokens to be replaced.  A template
240      *     has the form "literal ${token}" where the token will be replaced
241      *     by the value of context["token"].
242      * context - {Object} An optional object with properties corresponding
243      *     to the tokens in the format string.  If no context is sent, the
244      *     window object will be used.
245      * args - {Array} Optional arguments to pass to any functions found in
246      *     the context.  If a context property is a function, the token
247      *     will be replaced by the return from the function called with
248      *     these arguments.
249      *
250      * Returns:
251      * {String} A string with tokens replaced from the context object.
252      */
253     format: function(template, context, args) {
254         if(!context) {
255             context = window;
256         }
257
258         // Example matching: 
259         // str   = ${foo.bar}
260         // match = foo.bar
261         var replacer = function(str, match) {
262             var replacement;
263
264             // Loop through all subs. Example: ${a.b.c}
265             // 0 -> replacement = context[a];
266             // 1 -> replacement = context[a][b];
267             // 2 -> replacement = context[a][b][c];
268             var subs = match.split(/\.+/);
269             for (var i=0; i< subs.length; i++) {
270                 if (i == 0) {
271                     replacement = context;
272                 }
273                 if (replacement === undefined) {
274                     break;
275                 }
276                 replacement = replacement[subs[i]];
277             }
278
279             if(typeof replacement == "function") {
280                 replacement = args ?
281                     replacement.apply(null, args) :
282                     replacement();
283             }
284
285             // If replacement is undefined, return the string 'undefined'.
286             // This is a workaround for a bugs in browsers not properly 
287             // dealing with non-participating groups in regular expressions:
288             // http://blog.stevenlevithan.com/archives/npcg-javascript
289             if (typeof replacement == 'undefined') {
290                 return 'undefined';
291             } else {
292                 return replacement; 
293             }
294         };
295
296         return template.replace(OpenLayers.String.tokenRegEx, replacer);
297     },
298
299     /**
300      * Property: tokenRegEx
301      * Used to find tokens in a string.
302      * Examples: ${a}, ${a.b.c}, ${a-b}, ${5}
303      */
304     tokenRegEx:  /\$\{([\w.]+?)\}/g,
305     
306     /**
307      * Property: numberRegEx
308      * Used to test strings as numbers.
309      */
310     numberRegEx: /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/,
311     
312     /**
313      * APIFunction: isNumeric
314      * Determine whether a string contains only a numeric value.
315      *
316      * Examples:
317      * (code)
318      * OpenLayers.String.isNumeric("6.02e23") // true
319      * OpenLayers.String.isNumeric("12 dozen") // false
320      * OpenLayers.String.isNumeric("4") // true
321      * OpenLayers.String.isNumeric(" 4 ") // false
322      * (end)
323      *
324      * Returns:
325      * {Boolean} String contains only a number.
326      */
327     isNumeric: function(value) {
328         return OpenLayers.String.numberRegEx.test(value);
329     },
330     
331     /**
332      * APIFunction: numericIf
333      * Converts a string that appears to be a numeric value into a number.
334      * 
335      * Parameters:
336      * value - {String}
337      * trimWhitespace - {Boolean}
338      *
339      * Returns:
340      * {Number|String} a Number if the passed value is a number, a String
341      *     otherwise. 
342      */
343     numericIf: function(value, trimWhitespace) {
344         var originalValue = value;
345         if (trimWhitespace === true && value != null && value.replace) {
346             value = value.replace(/^\s*|\s*$/g, "");
347         }
348         return OpenLayers.String.isNumeric(value) ? parseFloat(value) : originalValue;
349     }
350
351 };
352
353 /**
354  * Namespace: OpenLayers.Number
355  * Contains convenience functions for manipulating numbers.
356  */
357 OpenLayers.Number = {
358
359     /**
360      * Property: decimalSeparator
361      * Decimal separator to use when formatting numbers.
362      */
363     decimalSeparator: ".",
364     
365     /**
366      * Property: thousandsSeparator
367      * Thousands separator to use when formatting numbers.
368      */
369     thousandsSeparator: ",",
370     
371     /**
372      * APIFunction: limitSigDigs
373      * Limit the number of significant digits on a float.
374      * 
375      * Parameters:
376      * num - {Float}
377      * sig - {Integer}
378      * 
379      * Returns:
380      * {Float} The number, rounded to the specified number of significant
381      *     digits.
382      */
383     limitSigDigs: function(num, sig) {
384         var fig = 0;
385         if (sig > 0) {
386             fig = parseFloat(num.toPrecision(sig));
387         }
388         return fig;
389     },
390     
391     /**
392      * APIFunction: format
393      * Formats a number for output.
394      * 
395      * Parameters:
396      * num  - {Float}
397      * dec  - {Integer} Number of decimal places to round to.
398      *        Defaults to 0. Set to null to leave decimal places unchanged.
399      * tsep - {String} Thousands separator.
400      *        Default is ",".
401      * dsep - {String} Decimal separator.
402      *        Default is ".".
403      *
404      * Returns:
405      * {String} A string representing the formatted number.
406      */
407     format: function(num, dec, tsep, dsep) {
408         dec = (typeof dec != "undefined") ? dec : 0; 
409         tsep = (typeof tsep != "undefined") ? tsep :
410             OpenLayers.Number.thousandsSeparator; 
411         dsep = (typeof dsep != "undefined") ? dsep :
412             OpenLayers.Number.decimalSeparator;
413
414         if (dec != null) {
415             num = parseFloat(num.toFixed(dec));
416         }
417
418         var parts = num.toString().split(".");
419         if (parts.length == 1 && dec == null) {
420             // integer where we do not want to touch the decimals
421             dec = 0;
422         }
423         
424         var integer = parts[0];
425         if (tsep) {
426             var thousands = /(-?[0-9]+)([0-9]{3})/; 
427             while(thousands.test(integer)) { 
428                 integer = integer.replace(thousands, "$1" + tsep + "$2"); 
429             }
430         }
431         
432         var str;
433         if (dec == 0) {
434             str = integer;
435         } else {
436             var rem = parts.length > 1 ? parts[1] : "0";
437             if (dec != null) {
438                 rem = rem + new Array(dec - rem.length + 1).join("0");
439             }
440             str = integer + dsep + rem;
441         }
442         return str;
443     },
444
445     /**
446      * Method: zeroPad
447      * Create a zero padded string optionally with a radix for casting numbers.
448      *
449      * Parameters:
450      * num - {Number} The number to be zero padded.
451      * len - {Number} The length of the string to be returned.
452      * radix - {Number} An integer between 2 and 36 specifying the base to use
453      *     for representing numeric values.
454      */
455     zeroPad: function(num, len, radix) {
456         var str = num.toString(radix || 10);
457         while (str.length < len) {
458             str = "0" + str;
459         }
460         return str;
461     }    
462 };
463
464 /**
465  * Namespace: OpenLayers.Function
466  * Contains convenience functions for function manipulation.
467  */
468 OpenLayers.Function = {
469     /**
470      * APIFunction: bind
471      * Bind a function to an object.  Method to easily create closures with
472      *     'this' altered.
473      * 
474      * Parameters:
475      * func - {Function} Input function.
476      * object - {Object} The object to bind to the input function (as this).
477      * 
478      * Returns:
479      * {Function} A closure with 'this' set to the passed in object.
480      */
481     bind: function(func, object) {
482         // create a reference to all arguments past the second one
483         var args = Array.prototype.slice.apply(arguments, [2]);
484         return function() {
485             // Push on any additional arguments from the actual function call.
486             // These will come after those sent to the bind call.
487             var newArgs = args.concat(
488                 Array.prototype.slice.apply(arguments, [0])
489             );
490             return func.apply(object, newArgs);
491         };
492     },
493     
494     /**
495      * APIFunction: bindAsEventListener
496      * Bind a function to an object, and configure it to receive the event
497      *     object as first parameter when called. 
498      * 
499      * Parameters:
500      * func - {Function} Input function to serve as an event listener.
501      * object - {Object} A reference to this.
502      * 
503      * Returns:
504      * {Function}
505      */
506     bindAsEventListener: function(func, object) {
507         return function(event) {
508             return func.call(object, event || window.event);
509         };
510     },
511     
512     /**
513      * APIFunction: False
514      * A simple function to that just does "return false". We use this to 
515      * avoid attaching anonymous functions to DOM event handlers, which 
516      * causes "issues" on IE<8.
517      * 
518      * Usage:
519      * document.onclick = OpenLayers.Function.False;
520      * 
521      * Returns:
522      * {Boolean}
523      */
524     False : function() {
525         return false;
526     },
527
528     /**
529      * APIFunction: True
530      * A simple function to that just does "return true". We use this to 
531      * avoid attaching anonymous functions to DOM event handlers, which 
532      * causes "issues" on IE<8.
533      * 
534      * Usage:
535      * document.onclick = OpenLayers.Function.True;
536      * 
537      * Returns:
538      * {Boolean}
539      */
540     True : function() {
541         return true;
542     },
543     
544     /**
545      * APIFunction: Void
546      * A reusable function that returns ``undefined``.
547      *
548      * Returns:
549      * {undefined}
550      */
551     Void: function() {}
552
553 };
554
555 /**
556  * Namespace: OpenLayers.Array
557  * Contains convenience functions for array manipulation.
558  */
559 OpenLayers.Array = {
560
561     /**
562      * APIMethod: filter
563      * Filter an array.  Provides the functionality of the
564      *     Array.prototype.filter extension to the ECMA-262 standard.  Where
565      *     available, Array.prototype.filter will be used.
566      *
567      * Based on well known example from http://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Array/filter
568      *
569      * Parameters:
570      * array - {Array} The array to be filtered.  This array is not mutated.
571      *     Elements added to this array by the callback will not be visited.
572      * callback - {Function} A function that is called for each element in
573      *     the array.  If this function returns true, the element will be
574      *     included in the return.  The function will be called with three
575      *     arguments: the element in the array, the index of that element, and
576      *     the array itself.  If the optional caller parameter is specified
577      *     the callback will be called with this set to caller.
578      * caller - {Object} Optional object to be set as this when the callback
579      *     is called.
580      *
581      * Returns:
582      * {Array} An array of elements from the passed in array for which the
583      *     callback returns true.
584      */
585     filter: function(array, callback, caller) {
586         var selected = [];
587         if (Array.prototype.filter) {
588             selected = array.filter(callback, caller);
589         } else {
590             var len = array.length;
591             if (typeof callback != "function") {
592                 throw new TypeError();
593             }
594             for(var i=0; i<len; i++) {
595                 if (i in array) {
596                     var val = array[i];
597                     if (callback.call(caller, val, i, array)) {
598                         selected.push(val);
599                     }
600                 }
601             }        
602         }
603         return selected;
604     }
605     
606 };
607 /* ======================================================================
608     OpenLayers/BaseTypes/Class.js
609    ====================================================================== */
610
611 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
612  * full list of contributors). Published under the 2-clause BSD license.
613  * See license.txt in the OpenLayers distribution or repository for the
614  * full text of the license. */
615
616 /**
617  * @requires OpenLayers/SingleFile.js
618  */
619
620 /**
621  * Constructor: OpenLayers.Class
622  * Base class used to construct all other classes. Includes support for 
623  *     multiple inheritance. 
624  *     
625  * This constructor is new in OpenLayers 2.5.  At OpenLayers 3.0, the old 
626  *     syntax for creating classes and dealing with inheritance 
627  *     will be removed.
628  * 
629  * To create a new OpenLayers-style class, use the following syntax:
630  * (code)
631  *     var MyClass = OpenLayers.Class(prototype);
632  * (end)
633  *
634  * To create a new OpenLayers-style class with multiple inheritance, use the
635  *     following syntax:
636  * (code)
637  *     var MyClass = OpenLayers.Class(Class1, Class2, prototype);
638  * (end)
639  * 
640  * Note that instanceof reflection will only reveal Class1 as superclass.
641  *
642  */
643 OpenLayers.Class = function() {
644     var len = arguments.length;
645     var P = arguments[0];
646     var F = arguments[len-1];
647
648     var C = typeof F.initialize == "function" ?
649         F.initialize :
650         function(){ P.prototype.initialize.apply(this, arguments); };
651
652     if (len > 1) {
653         var newArgs = [C, P].concat(
654                 Array.prototype.slice.call(arguments).slice(1, len-1), F);
655         OpenLayers.inherit.apply(null, newArgs);
656     } else {
657         C.prototype = F;
658     }
659     return C;
660 };
661
662 /**
663  * Function: OpenLayers.inherit
664  *
665  * Parameters:
666  * C - {Object} the class that inherits
667  * P - {Object} the superclass to inherit from
668  *
669  * In addition to the mandatory C and P parameters, an arbitrary number of
670  * objects can be passed, which will extend C.
671  */
672 OpenLayers.inherit = function(C, P) {
673    var F = function() {};
674    F.prototype = P.prototype;
675    C.prototype = new F;
676    var i, l, o;
677    for(i=2, l=arguments.length; i<l; i++) {
678        o = arguments[i];
679        if(typeof o === "function") {
680            o = o.prototype;
681        }
682        OpenLayers.Util.extend(C.prototype, o);
683    }
684 };
685
686 /**
687  * APIFunction: extend
688  * Copy all properties of a source object to a destination object.  Modifies
689  *     the passed in destination object.  Any properties on the source object
690  *     that are set to undefined will not be (re)set on the destination object.
691  *
692  * Parameters:
693  * destination - {Object} The object that will be modified
694  * source - {Object} The object with properties to be set on the destination
695  *
696  * Returns:
697  * {Object} The destination object.
698  */
699 OpenLayers.Util = OpenLayers.Util || {};
700 OpenLayers.Util.extend = function(destination, source) {
701     destination = destination || {};
702     if (source) {
703         for (var property in source) {
704             var value = source[property];
705             if (value !== undefined) {
706                 destination[property] = value;
707             }
708         }
709
710         /**
711          * IE doesn't include the toString property when iterating over an object's
712          * properties with the for(property in object) syntax.  Explicitly check if
713          * the source has its own toString property.
714          */
715
716         /*
717          * FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
718          * prototype object" when calling hawOwnProperty if the source object
719          * is an instance of window.Event.
720          */
721
722         var sourceIsEvt = typeof window.Event == "function"
723                           && source instanceof window.Event;
724
725         if (!sourceIsEvt
726            && source.hasOwnProperty && source.hasOwnProperty("toString")) {
727             destination.toString = source.toString;
728         }
729     }
730     return destination;
731 };
732 /* ======================================================================
733     OpenLayers/BaseTypes/Bounds.js
734    ====================================================================== */
735
736 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
737  * full list of contributors). Published under the 2-clause BSD license.
738  * See license.txt in the OpenLayers distribution or repository for the
739  * full text of the license. */
740
741 /**
742  * @requires OpenLayers/BaseTypes/Class.js
743  */
744
745 /**
746  * Class: OpenLayers.Bounds
747  * Instances of this class represent bounding boxes.  Data stored as left,
748  * bottom, right, top floats. All values are initialized to null, however,
749  * you should make sure you set them before using the bounds for anything.
750  * 
751  * Possible use case:
752  * (code)
753  *     bounds = new OpenLayers.Bounds();
754  *     bounds.extend(new OpenLayers.LonLat(4,5));
755  *     bounds.extend(new OpenLayers.LonLat(5,6));
756  *     bounds.toBBOX(); // returns 4,5,5,6
757  * (end)
758  */
759 OpenLayers.Bounds = OpenLayers.Class({
760
761     /**
762      * Property: left
763      * {Number} Minimum horizontal coordinate.
764      */
765     left: null,
766
767     /**
768      * Property: bottom
769      * {Number} Minimum vertical coordinate.
770      */
771     bottom: null,
772
773     /**
774      * Property: right
775      * {Number} Maximum horizontal coordinate.
776      */
777     right: null,
778
779     /**
780      * Property: top
781      * {Number} Maximum vertical coordinate.
782      */
783     top: null,
784     
785     /**
786      * Property: centerLonLat
787      * {<OpenLayers.LonLat>} A cached center location.  This should not be
788      *     accessed directly.  Use <getCenterLonLat> instead.
789      */
790     centerLonLat: null,
791
792     /**
793      * Constructor: OpenLayers.Bounds
794      * Construct a new bounds object. Coordinates can either be passed as four
795      * arguments, or as a single argument.
796      *
797      * Parameters (four arguments):
798      * left - {Number} The left bounds of the box.  Note that for width
799      *        calculations, this is assumed to be less than the right value.
800      * bottom - {Number} The bottom bounds of the box.  Note that for height
801      *          calculations, this is assumed to be less than the top value.
802      * right - {Number} The right bounds.
803      * top - {Number} The top bounds.
804      *
805      * Parameters (single argument):
806      * bounds - {Array(Number)} [left, bottom, right, top]
807      */
808     initialize: function(left, bottom, right, top) {
809         if (OpenLayers.Util.isArray(left)) {
810             top = left[3];
811             right = left[2];
812             bottom = left[1];
813             left = left[0];
814         }
815         if (left != null) {
816             this.left = OpenLayers.Util.toFloat(left);
817         }
818         if (bottom != null) {
819             this.bottom = OpenLayers.Util.toFloat(bottom);
820         }
821         if (right != null) {
822             this.right = OpenLayers.Util.toFloat(right);
823         }
824         if (top != null) {
825             this.top = OpenLayers.Util.toFloat(top);
826         }
827     },
828
829     /**
830      * Method: clone
831      * Create a cloned instance of this bounds.
832      *
833      * Returns:
834      * {<OpenLayers.Bounds>} A fresh copy of the bounds
835      */
836     clone:function() {
837         return new OpenLayers.Bounds(this.left, this.bottom, 
838                                      this.right, this.top);
839     },
840
841     /**
842      * Method: equals
843      * Test a two bounds for equivalence.
844      *
845      * Parameters:
846      * bounds - {<OpenLayers.Bounds>}
847      *
848      * Returns:
849      * {Boolean} The passed-in bounds object has the same left,
850      *           right, top, bottom components as this.  Note that if bounds 
851      *           passed in is null, returns false.
852      */
853     equals:function(bounds) {
854         var equals = false;
855         if (bounds != null) {
856             equals = ((this.left == bounds.left) && 
857                       (this.right == bounds.right) &&
858                       (this.top == bounds.top) && 
859                       (this.bottom == bounds.bottom));
860         }
861         return equals;
862     },
863
864     /** 
865      * APIMethod: toString
866      * Returns a string representation of the bounds object.
867      * 
868      * Returns:
869      * {String} String representation of bounds object. 
870      */
871     toString:function() {
872         return [this.left, this.bottom, this.right, this.top].join(",");
873     },
874
875     /**
876      * APIMethod: toArray
877      * Returns an array representation of the bounds object.
878      *
879      * Returns an array of left, bottom, right, top properties, or -- when the
880      *     optional parameter is true -- an array of the  bottom, left, top,
881      *     right properties.
882      *
883      * Parameters:
884      * reverseAxisOrder - {Boolean} Should we reverse the axis order?
885      *
886      * Returns:
887      * {Array} array of left, bottom, right, top
888      */
889     toArray: function(reverseAxisOrder) {
890         if (reverseAxisOrder === true) {
891             return [this.bottom, this.left, this.top, this.right];
892         } else {
893             return [this.left, this.bottom, this.right, this.top];
894         }
895     },    
896
897     /** 
898      * APIMethod: toBBOX
899      * Returns a boundingbox-string representation of the bounds object.
900      * 
901      * Parameters:
902      * decimal - {Integer} How many significant digits in the bbox coords?
903      *                     Default is 6
904      * reverseAxisOrder - {Boolean} Should we reverse the axis order?
905      * 
906      * Returns:
907      * {String} Simple String representation of bounds object.
908      *          (e.g. "5,42,10,45")
909      */
910     toBBOX:function(decimal, reverseAxisOrder) {
911         if (decimal== null) {
912             decimal = 6; 
913         }
914         var mult = Math.pow(10, decimal);
915         var xmin = Math.round(this.left * mult) / mult;
916         var ymin = Math.round(this.bottom * mult) / mult;
917         var xmax = Math.round(this.right * mult) / mult;
918         var ymax = Math.round(this.top * mult) / mult;
919         if (reverseAxisOrder === true) {
920             return ymin + "," + xmin + "," + ymax + "," + xmax;
921         } else {
922             return xmin + "," + ymin + "," + xmax + "," + ymax;
923         }
924     },
925  
926     /**
927      * APIMethod: toGeometry
928      * Create a new polygon geometry based on this bounds.
929      *
930      * Returns:
931      * {<OpenLayers.Geometry.Polygon>} A new polygon with the coordinates
932      *     of this bounds.
933      */
934     toGeometry: function() {
935         return new OpenLayers.Geometry.Polygon([
936             new OpenLayers.Geometry.LinearRing([
937                 new OpenLayers.Geometry.Point(this.left, this.bottom),
938                 new OpenLayers.Geometry.Point(this.right, this.bottom),
939                 new OpenLayers.Geometry.Point(this.right, this.top),
940                 new OpenLayers.Geometry.Point(this.left, this.top)
941             ])
942         ]);
943     },
944     
945     /**
946      * APIMethod: getWidth
947      * Returns the width of the bounds.
948      * 
949      * Returns:
950      * {Float} The width of the bounds (right minus left).
951      */
952     getWidth:function() {
953         return (this.right - this.left);
954     },
955
956     /**
957      * APIMethod: getHeight
958      * Returns the height of the bounds.
959      * 
960      * Returns:
961      * {Float} The height of the bounds (top minus bottom).
962      */
963     getHeight:function() {
964         return (this.top - this.bottom);
965     },
966
967     /**
968      * APIMethod: getSize
969      * Returns an <OpenLayers.Size> object of the bounds.
970      * 
971      * Returns:
972      * {<OpenLayers.Size>} The size of the bounds.
973      */
974     getSize:function() {
975         return new OpenLayers.Size(this.getWidth(), this.getHeight());
976     },
977
978     /**
979      * APIMethod: getCenterPixel
980      * Returns the <OpenLayers.Pixel> object which represents the center of the
981      *     bounds.
982      * 
983      * Returns:
984      * {<OpenLayers.Pixel>} The center of the bounds in pixel space.
985      */
986     getCenterPixel:function() {
987         return new OpenLayers.Pixel( (this.left + this.right) / 2,
988                                      (this.bottom + this.top) / 2);
989     },
990
991     /**
992      * APIMethod: getCenterLonLat
993      * Returns the <OpenLayers.LonLat> object which represents the center of the
994      *     bounds.
995      *
996      * Returns:
997      * {<OpenLayers.LonLat>} The center of the bounds in map space.
998      */
999     getCenterLonLat:function() {
1000         if(!this.centerLonLat) {
1001             this.centerLonLat = new OpenLayers.LonLat(
1002                 (this.left + this.right) / 2, (this.bottom + this.top) / 2
1003             );
1004         }
1005         return this.centerLonLat;
1006     },
1007
1008     /**
1009      * APIMethod: scale
1010      * Scales the bounds around a pixel or lonlat. Note that the new 
1011      *     bounds may return non-integer properties, even if a pixel
1012      *     is passed. 
1013      * 
1014      * Parameters:
1015      * ratio - {Float} 
1016      * origin - {<OpenLayers.Pixel> or <OpenLayers.LonLat>}
1017      *          Default is center.
1018      *
1019      * Returns:
1020      * {<OpenLayers.Bounds>} A new bounds that is scaled by ratio
1021      *                      from origin.
1022      */
1023     scale: function(ratio, origin){
1024         if(origin == null){
1025             origin = this.getCenterLonLat();
1026         }
1027         
1028         var origx,origy;
1029
1030         // get origin coordinates
1031         if(origin.CLASS_NAME == "OpenLayers.LonLat"){
1032             origx = origin.lon;
1033             origy = origin.lat;
1034         } else {
1035             origx = origin.x;
1036             origy = origin.y;
1037         }
1038
1039         var left = (this.left - origx) * ratio + origx;
1040         var bottom = (this.bottom - origy) * ratio + origy;
1041         var right = (this.right - origx) * ratio + origx;
1042         var top = (this.top - origy) * ratio + origy;
1043         
1044         return new OpenLayers.Bounds(left, bottom, right, top);
1045     },
1046
1047     /**
1048      * APIMethod: add
1049      * Shifts the coordinates of the bound by the given horizontal and vertical
1050      *     deltas.
1051      *
1052      * (start code)
1053      * var bounds = new OpenLayers.Bounds(0, 0, 10, 10);
1054      * bounds.toString();
1055      * // => "0,0,10,10"
1056      *
1057      * bounds.add(-1.5, 4).toString();
1058      * // => "-1.5,4,8.5,14"
1059      * (end)
1060      *
1061      * This method will throw a TypeError if it is passed null as an argument.
1062      *
1063      * Parameters:
1064      * x - {Float} horizontal delta
1065      * y - {Float} vertical delta
1066      *
1067      * Returns:
1068      * {<OpenLayers.Bounds>} A new bounds whose coordinates are the same as
1069      *     this, but shifted by the passed-in x and y values.
1070      */
1071     add:function(x, y) {
1072         if ( (x == null) || (y == null) ) {
1073             throw new TypeError('Bounds.add cannot receive null values');
1074         }
1075         return new OpenLayers.Bounds(this.left + x, this.bottom + y,
1076                                      this.right + x, this.top + y);
1077     },
1078     
1079     /**
1080      * APIMethod: extend
1081      * Extend the bounds to include the <OpenLayers.LonLat>,
1082      *     <OpenLayers.Geometry.Point> or <OpenLayers.Bounds> specified.
1083      *
1084      * Please note that this function assumes that left < right and
1085      *     bottom < top.
1086      *
1087      * Parameters:
1088      * object - {<OpenLayers.LonLat>, <OpenLayers.Geometry.Point> or
1089      *     <OpenLayers.Bounds>} The object to be included in the new bounds
1090      *     object.
1091      */
1092     extend:function(object) {
1093         if (object) {
1094             switch(object.CLASS_NAME) {
1095                 case "OpenLayers.LonLat":
1096                     this.extendXY(object.lon, object.lat);
1097                     break;
1098                 case "OpenLayers.Geometry.Point":
1099                     this.extendXY(object.x, object.y);
1100                     break;
1101
1102                 case "OpenLayers.Bounds":
1103                     // clear cached center location
1104                     this.centerLonLat = null;
1105
1106                     if ( (this.left == null) || (object.left < this.left)) {
1107                         this.left = object.left;
1108                     }
1109                     if ( (this.bottom == null) || (object.bottom < this.bottom) ) {
1110                         this.bottom = object.bottom;
1111                     }
1112                     if ( (this.right == null) || (object.right > this.right) ) {
1113                         this.right = object.right;
1114                     }
1115                     if ( (this.top == null) || (object.top > this.top) ) {
1116                         this.top = object.top;
1117                     }
1118                     break;
1119             }
1120         }
1121     },
1122
1123     /**
1124      * APIMethod: extendXY
1125      * Extend the bounds to include the XY coordinate specified.
1126      *
1127      * Parameters:
1128      * x - {number} The X part of the the coordinate.
1129      * y - {number} The Y part of the the coordinate.
1130      */
1131     extendXY:function(x, y) {
1132         // clear cached center location
1133         this.centerLonLat = null;
1134
1135         if ((this.left == null) || (x < this.left)) {
1136             this.left = x;
1137         }
1138         if ((this.bottom == null) || (y < this.bottom)) {
1139             this.bottom = y;
1140         }
1141         if ((this.right == null) || (x > this.right)) {
1142             this.right = x;
1143         }
1144         if ((this.top == null) || (y > this.top)) {
1145             this.top = y;
1146         }
1147     },
1148
1149     /**
1150      * APIMethod: containsLonLat
1151      * Returns whether the bounds object contains the given <OpenLayers.LonLat>.
1152      * 
1153      * Parameters:
1154      * ll - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
1155      *     object with a 'lon' and 'lat' properties.
1156      * options - {Object} Optional parameters
1157      *
1158      * Acceptable options:
1159      * inclusive - {Boolean} Whether or not to include the border.
1160      *     Default is true.
1161      * worldBounds - {<OpenLayers.Bounds>} If a worldBounds is provided, the
1162      *     ll will be considered as contained if it exceeds the world bounds,
1163      *     but can be wrapped around the dateline so it is contained by this
1164      *     bounds.
1165      *
1166      * Returns:
1167      * {Boolean} The passed-in lonlat is within this bounds.
1168      */
1169     containsLonLat: function(ll, options) {
1170         if (typeof options === "boolean") {
1171             options =  {inclusive: options};
1172         }
1173         options = options || {};
1174         var contains = this.contains(ll.lon, ll.lat, options.inclusive),
1175             worldBounds = options.worldBounds;
1176         if (worldBounds && !contains) {
1177             var worldWidth = worldBounds.getWidth();
1178             var worldCenterX = (worldBounds.left + worldBounds.right) / 2;
1179             var worldsAway = Math.round((ll.lon - worldCenterX) / worldWidth);
1180             contains = this.containsLonLat({
1181                 lon: ll.lon - worldsAway * worldWidth,
1182                 lat: ll.lat
1183             }, {inclusive: options.inclusive});
1184         }
1185         return contains;
1186     },
1187
1188     /**
1189      * APIMethod: containsPixel
1190      * Returns whether the bounds object contains the given <OpenLayers.Pixel>.
1191      * 
1192      * Parameters:
1193      * px - {<OpenLayers.Pixel>}
1194      * inclusive - {Boolean} Whether or not to include the border. Default is
1195      *     true.
1196      *
1197      * Returns:
1198      * {Boolean} The passed-in pixel is within this bounds.
1199      */
1200     containsPixel:function(px, inclusive) {
1201         return this.contains(px.x, px.y, inclusive);
1202     },
1203     
1204     /**
1205      * APIMethod: contains
1206      * Returns whether the bounds object contains the given x and y.
1207      * 
1208      * Parameters:
1209      * x - {Float}
1210      * y - {Float}
1211      * inclusive - {Boolean} Whether or not to include the border. Default is
1212      *     true.
1213      *
1214      * Returns:
1215      * {Boolean} Whether or not the passed-in coordinates are within this
1216      *     bounds.
1217      */
1218     contains:function(x, y, inclusive) {
1219         //set default
1220         if (inclusive == null) {
1221             inclusive = true;
1222         }
1223
1224         if (x == null || y == null) {
1225             return false;
1226         }
1227
1228         x = OpenLayers.Util.toFloat(x);
1229         y = OpenLayers.Util.toFloat(y);
1230
1231         var contains = false;
1232         if (inclusive) {
1233             contains = ((x >= this.left) && (x <= this.right) && 
1234                         (y >= this.bottom) && (y <= this.top));
1235         } else {
1236             contains = ((x > this.left) && (x < this.right) && 
1237                         (y > this.bottom) && (y < this.top));
1238         }              
1239         return contains;
1240     },
1241
1242     /**
1243      * APIMethod: intersectsBounds
1244      * Determine whether the target bounds intersects this bounds.  Bounds are
1245      *     considered intersecting if any of their edges intersect or if one
1246      *     bounds contains the other.
1247      * 
1248      * Parameters:
1249      * bounds - {<OpenLayers.Bounds>} The target bounds.
1250      * options - {Object} Optional parameters.
1251      * 
1252      * Acceptable options:
1253      * inclusive - {Boolean} Treat coincident borders as intersecting.  Default
1254      *     is true.  If false, bounds that do not overlap but only touch at the
1255      *     border will not be considered as intersecting.
1256      * worldBounds - {<OpenLayers.Bounds>} If a worldBounds is provided, two
1257      *     bounds will be considered as intersecting if they intersect when 
1258      *     shifted to within the world bounds.  This applies only to bounds that
1259      *     cross or are completely outside the world bounds.
1260      *
1261      * Returns:
1262      * {Boolean} The passed-in bounds object intersects this bounds.
1263      */
1264     intersectsBounds:function(bounds, options) {
1265         if (typeof options === "boolean") {
1266             options =  {inclusive: options};
1267         }
1268         options = options || {};
1269         if (options.worldBounds) {
1270             var self = this.wrapDateLine(options.worldBounds);
1271             bounds = bounds.wrapDateLine(options.worldBounds);
1272         } else {
1273             self = this;
1274         }
1275         if (options.inclusive == null) {
1276             options.inclusive = true;
1277         }
1278         var intersects = false;
1279         var mightTouch = (
1280             self.left == bounds.right ||
1281             self.right == bounds.left ||
1282             self.top == bounds.bottom ||
1283             self.bottom == bounds.top
1284         );
1285         
1286         // if the two bounds only touch at an edge, and inclusive is false,
1287         // then the bounds don't *really* intersect.
1288         if (options.inclusive || !mightTouch) {
1289             // otherwise, if one of the boundaries even partially contains another,
1290             // inclusive of the edges, then they do intersect.
1291             var inBottom = (
1292                 ((bounds.bottom >= self.bottom) && (bounds.bottom <= self.top)) ||
1293                 ((self.bottom >= bounds.bottom) && (self.bottom <= bounds.top))
1294             );
1295             var inTop = (
1296                 ((bounds.top >= self.bottom) && (bounds.top <= self.top)) ||
1297                 ((self.top > bounds.bottom) && (self.top < bounds.top))
1298             );
1299             var inLeft = (
1300                 ((bounds.left >= self.left) && (bounds.left <= self.right)) ||
1301                 ((self.left >= bounds.left) && (self.left <= bounds.right))
1302             );
1303             var inRight = (
1304                 ((bounds.right >= self.left) && (bounds.right <= self.right)) ||
1305                 ((self.right >= bounds.left) && (self.right <= bounds.right))
1306             );
1307             intersects = ((inBottom || inTop) && (inLeft || inRight));
1308         }
1309         // document me
1310         if (options.worldBounds && !intersects) {
1311             var world = options.worldBounds;
1312             var width = world.getWidth();
1313             var selfCrosses = !world.containsBounds(self);
1314             var boundsCrosses = !world.containsBounds(bounds);
1315             if (selfCrosses && !boundsCrosses) {
1316                 bounds = bounds.add(-width, 0);
1317                 intersects = self.intersectsBounds(bounds, {inclusive: options.inclusive});
1318             } else if (boundsCrosses && !selfCrosses) {
1319                 self = self.add(-width, 0);
1320                 intersects = bounds.intersectsBounds(self, {inclusive: options.inclusive});                
1321             }
1322         }
1323         return intersects;
1324     },
1325     
1326     /**
1327      * APIMethod: containsBounds
1328      * Returns whether the bounds object contains the given <OpenLayers.Bounds>.
1329      * 
1330      * bounds - {<OpenLayers.Bounds>} The target bounds.
1331      * partial - {Boolean} If any of the target corners is within this bounds
1332      *     consider the bounds contained.  Default is false.  If false, the
1333      *     entire target bounds must be contained within this bounds.
1334      * inclusive - {Boolean} Treat shared edges as contained.  Default is
1335      *     true.
1336      *
1337      * Returns:
1338      * {Boolean} The passed-in bounds object is contained within this bounds. 
1339      */
1340     containsBounds:function(bounds, partial, inclusive) {
1341         if (partial == null) {
1342             partial = false;
1343         }
1344         if (inclusive == null) {
1345             inclusive = true;
1346         }
1347         var bottomLeft  = this.contains(bounds.left, bounds.bottom, inclusive);
1348         var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive);
1349         var topLeft  = this.contains(bounds.left, bounds.top, inclusive);
1350         var topRight = this.contains(bounds.right, bounds.top, inclusive);
1351         
1352         return (partial) ? (bottomLeft || bottomRight || topLeft || topRight)
1353                          : (bottomLeft && bottomRight && topLeft && topRight);
1354     },
1355
1356     /** 
1357      * APIMethod: determineQuadrant
1358      * Returns the the quadrant ("br", "tr", "tl", "bl") in which the given
1359      *     <OpenLayers.LonLat> lies.
1360      *
1361      * Parameters:
1362      * lonlat - {<OpenLayers.LonLat>}
1363      *
1364      * Returns:
1365      * {String} The quadrant ("br" "tr" "tl" "bl") of the bounds in which the
1366      *     coordinate lies.
1367      */
1368     determineQuadrant: function(lonlat) {
1369     
1370         var quadrant = "";
1371         var center = this.getCenterLonLat();
1372         
1373         quadrant += (lonlat.lat < center.lat) ? "b" : "t";
1374         quadrant += (lonlat.lon < center.lon) ? "l" : "r";
1375     
1376         return quadrant; 
1377     },
1378     
1379     /**
1380      * APIMethod: transform
1381      * Transform the Bounds object from source to dest. 
1382      *
1383      * Parameters: 
1384      * source - {<OpenLayers.Projection>} Source projection. 
1385      * dest   - {<OpenLayers.Projection>} Destination projection. 
1386      *
1387      * Returns:
1388      * {<OpenLayers.Bounds>} Itself, for use in chaining operations.
1389      */
1390     transform: function(source, dest) {
1391         // clear cached center location
1392         this.centerLonLat = null;
1393         var ll = OpenLayers.Projection.transform(
1394             {'x': this.left, 'y': this.bottom}, source, dest);
1395         var lr = OpenLayers.Projection.transform(
1396             {'x': this.right, 'y': this.bottom}, source, dest);
1397         var ul = OpenLayers.Projection.transform(
1398             {'x': this.left, 'y': this.top}, source, dest);
1399         var ur = OpenLayers.Projection.transform(
1400             {'x': this.right, 'y': this.top}, source, dest);
1401         this.left   = Math.min(ll.x, ul.x);
1402         this.bottom = Math.min(ll.y, lr.y);
1403         this.right  = Math.max(lr.x, ur.x);
1404         this.top    = Math.max(ul.y, ur.y);
1405         return this;
1406     },
1407
1408     /**
1409      * APIMethod: wrapDateLine
1410      * Wraps the bounds object around the dateline.
1411      *  
1412      * Parameters:
1413      * maxExtent - {<OpenLayers.Bounds>}
1414      * options - {Object} Some possible options are:
1415      *
1416      * Allowed Options:
1417      *                    leftTolerance - {float} Allow for a margin of error 
1418      *                                            with the 'left' value of this 
1419      *                                            bound.
1420      *                                            Default is 0.
1421      *                    rightTolerance - {float} Allow for a margin of error 
1422      *                                             with the 'right' value of 
1423      *                                             this bound.
1424      *                                             Default is 0.
1425      * 
1426      * Returns:
1427      * {<OpenLayers.Bounds>} A copy of this bounds, but wrapped around the 
1428      *                       "dateline" (as specified by the borders of 
1429      *                       maxExtent). Note that this function only returns 
1430      *                       a different bounds value if this bounds is 
1431      *                       *entirely* outside of the maxExtent. If this 
1432      *                       bounds straddles the dateline (is part in/part 
1433      *                       out of maxExtent), the returned bounds will always 
1434      *                       cross the left edge of the given maxExtent.
1435      *.
1436      */
1437     wrapDateLine: function(maxExtent, options) {    
1438         options = options || {};
1439         
1440         var leftTolerance = options.leftTolerance || 0;
1441         var rightTolerance = options.rightTolerance || 0;
1442
1443         var newBounds = this.clone();
1444     
1445         if (maxExtent) {
1446             var width = maxExtent.getWidth();
1447
1448             //shift right?
1449             while (newBounds.left < maxExtent.left && 
1450                    newBounds.right - rightTolerance <= maxExtent.left ) { 
1451                 newBounds = newBounds.add(width, 0);
1452             }
1453
1454             //shift left?
1455             while (newBounds.left + leftTolerance >= maxExtent.right && 
1456                    newBounds.right > maxExtent.right ) { 
1457                 newBounds = newBounds.add(-width, 0);
1458             }
1459            
1460             // crosses right only? force left
1461             var newLeft = newBounds.left + leftTolerance;
1462             if (newLeft < maxExtent.right && newLeft > maxExtent.left && 
1463                    newBounds.right - rightTolerance > maxExtent.right) {
1464                 newBounds = newBounds.add(-width, 0);
1465             }
1466         }
1467                 
1468         return newBounds;
1469     },
1470
1471     CLASS_NAME: "OpenLayers.Bounds"
1472 });
1473
1474 /** 
1475  * APIFunction: fromString
1476  * Alternative constructor that builds a new OpenLayers.Bounds from a 
1477  *     parameter string.
1478  *
1479  * (begin code)
1480  * OpenLayers.Bounds.fromString("5,42,10,45");
1481  * // => equivalent to ...
1482  * new OpenLayers.Bounds(5, 42, 10, 45);
1483  * (end)
1484  *
1485  * Parameters: 
1486  * str - {String} Comma-separated bounds string. (e.g. "5,42,10,45")
1487  * reverseAxisOrder - {Boolean} Does the string use reverse axis order?
1488  *
1489  * Returns:
1490  * {<OpenLayers.Bounds>} New bounds object built from the 
1491  *                       passed-in String.
1492  */
1493 OpenLayers.Bounds.fromString = function(str, reverseAxisOrder) {
1494     var bounds = str.split(",");
1495     return OpenLayers.Bounds.fromArray(bounds, reverseAxisOrder);
1496 };
1497
1498 /** 
1499  * APIFunction: fromArray
1500  * Alternative constructor that builds a new OpenLayers.Bounds from an array.
1501  *
1502  * (begin code)
1503  * OpenLayers.Bounds.fromArray( [5, 42, 10, 45] );
1504  * // => equivalent to ...
1505  * new OpenLayers.Bounds(5, 42, 10, 45);
1506  * (end)
1507  *
1508  * Parameters:
1509  * bbox - {Array(Float)} Array of bounds values (e.g. [5,42,10,45])
1510  * reverseAxisOrder - {Boolean} Does the array use reverse axis order?
1511  *
1512  * Returns:
1513  * {<OpenLayers.Bounds>} New bounds object built from the passed-in Array.
1514  */
1515 OpenLayers.Bounds.fromArray = function(bbox, reverseAxisOrder) {
1516     return reverseAxisOrder === true ?
1517            new OpenLayers.Bounds(bbox[1], bbox[0], bbox[3], bbox[2]) :
1518            new OpenLayers.Bounds(bbox[0], bbox[1], bbox[2], bbox[3]);
1519 };
1520
1521 /** 
1522  * APIFunction: fromSize
1523  * Alternative constructor that builds a new OpenLayers.Bounds from a size.
1524  *
1525  * (begin code)
1526  * OpenLayers.Bounds.fromSize( new OpenLayers.Size(10, 20) );
1527  * // => equivalent to ...
1528  * new OpenLayers.Bounds(0, 20, 10, 0);
1529  * (end)
1530  *
1531  * Parameters:
1532  * size - {<OpenLayers.Size> or Object} <OpenLayers.Size> or an object with
1533  *     both 'w' and 'h' properties.
1534  *
1535  * Returns:
1536  * {<OpenLayers.Bounds>} New bounds object built from the passed-in size.
1537  */
1538 OpenLayers.Bounds.fromSize = function(size) {
1539     return new OpenLayers.Bounds(0,
1540                                  size.h,
1541                                  size.w,
1542                                  0);
1543 };
1544
1545 /**
1546  * Function: oppositeQuadrant
1547  * Get the opposite quadrant for a given quadrant string.
1548  *
1549  * (begin code)
1550  * OpenLayers.Bounds.oppositeQuadrant( "tl" );
1551  * // => "br"
1552  *
1553  * OpenLayers.Bounds.oppositeQuadrant( "tr" );
1554  * // => "bl"
1555  * (end)
1556  *
1557  * Parameters:
1558  * quadrant - {String} two character quadrant shortstring
1559  *
1560  * Returns:
1561  * {String} The opposing quadrant ("br" "tr" "tl" "bl"). For Example, if 
1562  *          you pass in "bl" it returns "tr", if you pass in "br" it 
1563  *          returns "tl", etc.
1564  */
1565 OpenLayers.Bounds.oppositeQuadrant = function(quadrant) {
1566     var opp = "";
1567     
1568     opp += (quadrant.charAt(0) == 't') ? 'b' : 't';
1569     opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l';
1570     
1571     return opp;
1572 };
1573 /* ======================================================================
1574     OpenLayers/BaseTypes/Element.js
1575    ====================================================================== */
1576
1577 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
1578  * full list of contributors). Published under the 2-clause BSD license.
1579  * See license.txt in the OpenLayers distribution or repository for the
1580  * full text of the license. */
1581
1582 /**
1583  * @requires OpenLayers/Util.js
1584  * @requires OpenLayers/BaseTypes.js
1585  */
1586
1587 /**
1588  * Namespace: OpenLayers.Element
1589  */
1590 OpenLayers.Element = {
1591
1592     /**
1593      * APIFunction: visible
1594      * 
1595      * Parameters: 
1596      * element - {DOMElement}
1597      * 
1598      * Returns:
1599      * {Boolean} Is the element visible?
1600      */
1601     visible: function(element) {
1602         return OpenLayers.Util.getElement(element).style.display != 'none';
1603     },
1604
1605     /**
1606      * APIFunction: toggle
1607      * Toggle the visibility of element(s) passed in
1608      * 
1609      * Parameters:
1610      * element - {DOMElement} Actually user can pass any number of elements
1611      */
1612     toggle: function() {
1613         for (var i=0, len=arguments.length; i<len; i++) {
1614             var element = OpenLayers.Util.getElement(arguments[i]);
1615             var display = OpenLayers.Element.visible(element) ? 'none' 
1616                                                               : '';
1617             element.style.display = display;
1618         }
1619     },
1620
1621     /**
1622      * APIFunction: remove
1623      * Remove the specified element from the DOM.
1624      * 
1625      * Parameters:
1626      * element - {DOMElement}
1627      */
1628     remove: function(element) {
1629         element = OpenLayers.Util.getElement(element);
1630         element.parentNode.removeChild(element);
1631     },
1632
1633     /**
1634      * APIFunction: getHeight
1635      *  
1636      * Parameters:
1637      * element - {DOMElement}
1638      * 
1639      * Returns:
1640      * {Integer} The offset height of the element passed in
1641      */
1642     getHeight: function(element) {
1643         element = OpenLayers.Util.getElement(element);
1644         return element.offsetHeight;
1645     },
1646
1647     /**
1648      * Function: hasClass
1649      * Tests if an element has the given CSS class name.
1650      *
1651      * Parameters:
1652      * element - {DOMElement} A DOM element node.
1653      * name - {String} The CSS class name to search for.
1654      *
1655      * Returns:
1656      * {Boolean} The element has the given class name.
1657      */
1658     hasClass: function(element, name) {
1659         var names = element.className;
1660         return (!!names && new RegExp("(^|\\s)" + name + "(\\s|$)").test(names));
1661     },
1662     
1663     /**
1664      * Function: addClass
1665      * Add a CSS class name to an element.  Safe where element already has
1666      *     the class name.
1667      *
1668      * Parameters:
1669      * element - {DOMElement} A DOM element node.
1670      * name - {String} The CSS class name to add.
1671      *
1672      * Returns:
1673      * {DOMElement} The element.
1674      */
1675     addClass: function(element, name) {
1676         if(!OpenLayers.Element.hasClass(element, name)) {
1677             element.className += (element.className ? " " : "") + name;
1678         }
1679         return element;
1680     },
1681
1682     /**
1683      * Function: removeClass
1684      * Remove a CSS class name from an element.  Safe where element does not
1685      *     have the class name.
1686      *
1687      * Parameters:
1688      * element - {DOMElement} A DOM element node.
1689      * name - {String} The CSS class name to remove.
1690      *
1691      * Returns:
1692      * {DOMElement} The element.
1693      */
1694     removeClass: function(element, name) {
1695         var names = element.className;
1696         if(names) {
1697             element.className = OpenLayers.String.trim(
1698                 names.replace(
1699                     new RegExp("(^|\\s+)" + name + "(\\s+|$)"), " "
1700                 )
1701             );
1702         }
1703         return element;
1704     },
1705
1706     /**
1707      * Function: toggleClass
1708      * Remove a CSS class name from an element if it exists.  Add the class name
1709      *     if it doesn't exist.
1710      *
1711      * Parameters:
1712      * element - {DOMElement} A DOM element node.
1713      * name - {String} The CSS class name to toggle.
1714      *
1715      * Returns:
1716      * {DOMElement} The element.
1717      */
1718     toggleClass: function(element, name) {
1719         if(OpenLayers.Element.hasClass(element, name)) {
1720             OpenLayers.Element.removeClass(element, name);
1721         } else {
1722             OpenLayers.Element.addClass(element, name);
1723         }
1724         return element;
1725     },
1726
1727     /**
1728      * APIFunction: getStyle
1729      * 
1730      * Parameters:
1731      * element - {DOMElement}
1732      * style - {?}
1733      * 
1734      * Returns:
1735      * {?}
1736      */
1737     getStyle: function(element, style) {
1738         element = OpenLayers.Util.getElement(element);
1739
1740         var value = null;
1741         if (element && element.style) {
1742             value = element.style[OpenLayers.String.camelize(style)];
1743             if (!value) {
1744                 if (document.defaultView && 
1745                     document.defaultView.getComputedStyle) {
1746                     
1747                     var css = document.defaultView.getComputedStyle(element, null);
1748                     value = css ? css.getPropertyValue(style) : null;
1749                 } else if (element.currentStyle) {
1750                     value = element.currentStyle[OpenLayers.String.camelize(style)];
1751                 }
1752             }
1753         
1754             var positions = ['left', 'top', 'right', 'bottom'];
1755             if (window.opera &&
1756                 (OpenLayers.Util.indexOf(positions,style) != -1) &&
1757                 (OpenLayers.Element.getStyle(element, 'position') == 'static')) { 
1758                 value = 'auto';
1759             }
1760         }
1761     
1762         return value == 'auto' ? null : value;
1763     }
1764
1765 };
1766 /* ======================================================================
1767     OpenLayers/BaseTypes/LonLat.js
1768    ====================================================================== */
1769
1770 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
1771  * full list of contributors). Published under the 2-clause BSD license.
1772  * See license.txt in the OpenLayers distribution or repository for the
1773  * full text of the license. */
1774
1775 /**
1776  * @requires OpenLayers/BaseTypes/Class.js
1777  */
1778
1779 /**
1780  * Class: OpenLayers.LonLat
1781  * This class represents a longitude and latitude pair
1782  */
1783 OpenLayers.LonLat = OpenLayers.Class({
1784
1785     /** 
1786      * APIProperty: lon
1787      * {Float} The x-axis coodinate in map units
1788      */
1789     lon: 0.0,
1790     
1791     /** 
1792      * APIProperty: lat
1793      * {Float} The y-axis coordinate in map units
1794      */
1795     lat: 0.0,
1796
1797     /**
1798      * Constructor: OpenLayers.LonLat
1799      * Create a new map location. Coordinates can be passed either as two
1800      * arguments, or as a single argument.
1801      *
1802      * Parameters (two arguments):
1803      * lon - {Number} The x-axis coordinate in map units.  If your map is in
1804      *     a geographic projection, this will be the Longitude.  Otherwise,
1805      *     it will be the x coordinate of the map location in your map units.
1806      * lat - {Number} The y-axis coordinate in map units.  If your map is in
1807      *     a geographic projection, this will be the Latitude.  Otherwise,
1808      *     it will be the y coordinate of the map location in your map units.
1809      *
1810      * Parameters (single argument):
1811      * location - {Array(Float)} [lon, lat]
1812      */
1813     initialize: function(lon, lat) {
1814         if (OpenLayers.Util.isArray(lon)) {
1815             lat = lon[1];
1816             lon = lon[0];
1817         }
1818         this.lon = OpenLayers.Util.toFloat(lon);
1819         this.lat = OpenLayers.Util.toFloat(lat);
1820     },
1821     
1822     /**
1823      * Method: toString
1824      * Return a readable string version of the lonlat
1825      *
1826      * Returns:
1827      * {String} String representation of OpenLayers.LonLat object. 
1828      *           (e.g. <i>"lon=5,lat=42"</i>)
1829      */
1830     toString:function() {
1831         return ("lon=" + this.lon + ",lat=" + this.lat);
1832     },
1833
1834     /** 
1835      * APIMethod: toShortString
1836      * 
1837      * Returns:
1838      * {String} Shortened String representation of OpenLayers.LonLat object. 
1839      *         (e.g. <i>"5, 42"</i>)
1840      */
1841     toShortString:function() {
1842         return (this.lon + ", " + this.lat);
1843     },
1844
1845     /** 
1846      * APIMethod: clone
1847      * 
1848      * Returns:
1849      * {<OpenLayers.LonLat>} New OpenLayers.LonLat object with the same lon 
1850      *                       and lat values
1851      */
1852     clone:function() {
1853         return new OpenLayers.LonLat(this.lon, this.lat);
1854     },
1855
1856     /** 
1857      * APIMethod: add
1858      * 
1859      * Parameters:
1860      * lon - {Float}
1861      * lat - {Float}
1862      * 
1863      * Returns:
1864      * {<OpenLayers.LonLat>} A new OpenLayers.LonLat object with the lon and 
1865      *                       lat passed-in added to this's. 
1866      */
1867     add:function(lon, lat) {
1868         if ( (lon == null) || (lat == null) ) {
1869             throw new TypeError('LonLat.add cannot receive null values');
1870         }
1871         return new OpenLayers.LonLat(this.lon + OpenLayers.Util.toFloat(lon), 
1872                                      this.lat + OpenLayers.Util.toFloat(lat));
1873     },
1874
1875     /** 
1876      * APIMethod: equals
1877      * 
1878      * Parameters:
1879      * ll - {<OpenLayers.LonLat>}
1880      * 
1881      * Returns:
1882      * {Boolean} Boolean value indicating whether the passed-in 
1883      *           <OpenLayers.LonLat> object has the same lon and lat 
1884      *           components as this.
1885      *           Note: if ll passed in is null, returns false
1886      */
1887     equals:function(ll) {
1888         var equals = false;
1889         if (ll != null) {
1890             equals = ((this.lon == ll.lon && this.lat == ll.lat) ||
1891                       (isNaN(this.lon) && isNaN(this.lat) && isNaN(ll.lon) && isNaN(ll.lat)));
1892         }
1893         return equals;
1894     },
1895
1896     /**
1897      * APIMethod: transform
1898      * Transform the LonLat object from source to dest. This transformation is
1899      *    *in place*: if you want a *new* lonlat, use .clone() first.
1900      *
1901      * Parameters: 
1902      * source - {<OpenLayers.Projection>} Source projection. 
1903      * dest   - {<OpenLayers.Projection>} Destination projection. 
1904      *
1905      * Returns:
1906      * {<OpenLayers.LonLat>} Itself, for use in chaining operations.
1907      */
1908     transform: function(source, dest) {
1909         var point = OpenLayers.Projection.transform(
1910             {'x': this.lon, 'y': this.lat}, source, dest);
1911         this.lon = point.x;
1912         this.lat = point.y;
1913         return this;
1914     },
1915     
1916     /**
1917      * APIMethod: wrapDateLine
1918      * 
1919      * Parameters:
1920      * maxExtent - {<OpenLayers.Bounds>}
1921      * 
1922      * Returns:
1923      * {<OpenLayers.LonLat>} A copy of this lonlat, but wrapped around the 
1924      *                       "dateline" (as specified by the borders of 
1925      *                       maxExtent)
1926      */
1927     wrapDateLine: function(maxExtent) {    
1928
1929         var newLonLat = this.clone();
1930     
1931         if (maxExtent) {
1932             //shift right?
1933             while (newLonLat.lon < maxExtent.left) {
1934                 newLonLat.lon +=  maxExtent.getWidth();
1935             }    
1936            
1937             //shift left?
1938             while (newLonLat.lon > maxExtent.right) {
1939                 newLonLat.lon -= maxExtent.getWidth();
1940             }    
1941         }
1942                 
1943         return newLonLat;
1944     },
1945
1946     CLASS_NAME: "OpenLayers.LonLat"
1947 });
1948
1949 /** 
1950  * Function: fromString
1951  * Alternative constructor that builds a new <OpenLayers.LonLat> from a 
1952  *     parameter string
1953  * 
1954  * Parameters:
1955  * str - {String} Comma-separated Lon,Lat coordinate string. 
1956  *                 (e.g. <i>"5,40"</i>)
1957  * 
1958  * Returns:
1959  * {<OpenLayers.LonLat>} New <OpenLayers.LonLat> object built from the 
1960  *                       passed-in String.
1961  */
1962 OpenLayers.LonLat.fromString = function(str) {
1963     var pair = str.split(",");
1964     return new OpenLayers.LonLat(pair[0], pair[1]);
1965 };
1966
1967 /** 
1968  * Function: fromArray
1969  * Alternative constructor that builds a new <OpenLayers.LonLat> from an 
1970  *     array of two numbers that represent lon- and lat-values.
1971  * 
1972  * Parameters:
1973  * arr - {Array(Float)} Array of lon/lat values (e.g. [5,-42])
1974  * 
1975  * Returns:
1976  * {<OpenLayers.LonLat>} New <OpenLayers.LonLat> object built from the 
1977  *                       passed-in array.
1978  */
1979 OpenLayers.LonLat.fromArray = function(arr) {
1980     var gotArr = OpenLayers.Util.isArray(arr),
1981         lon = gotArr && arr[0],
1982         lat = gotArr && arr[1];
1983     return new OpenLayers.LonLat(lon, lat);
1984 };
1985 /* ======================================================================
1986     OpenLayers/BaseTypes/Pixel.js
1987    ====================================================================== */
1988
1989 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
1990  * full list of contributors). Published under the 2-clause BSD license.
1991  * See license.txt in the OpenLayers distribution or repository for the
1992  * full text of the license. */
1993
1994 /**
1995  * @requires OpenLayers/BaseTypes/Class.js
1996  */
1997
1998 /**
1999  * Class: OpenLayers.Pixel
2000  * This class represents a screen coordinate, in x and y coordinates
2001  */
2002 OpenLayers.Pixel = OpenLayers.Class({
2003     
2004     /**
2005      * APIProperty: x
2006      * {Number} The x coordinate
2007      */
2008     x: 0.0,
2009
2010     /**
2011      * APIProperty: y
2012      * {Number} The y coordinate
2013      */
2014     y: 0.0,
2015     
2016     /**
2017      * Constructor: OpenLayers.Pixel
2018      * Create a new OpenLayers.Pixel instance
2019      *
2020      * Parameters:
2021      * x - {Number} The x coordinate
2022      * y - {Number} The y coordinate
2023      *
2024      * Returns:
2025      * An instance of OpenLayers.Pixel
2026      */
2027     initialize: function(x, y) {
2028         this.x = parseFloat(x);
2029         this.y = parseFloat(y);
2030     },
2031     
2032     /**
2033      * Method: toString
2034      * Cast this object into a string
2035      *
2036      * Returns:
2037      * {String} The string representation of Pixel. ex: "x=200.4,y=242.2"
2038      */
2039     toString:function() {
2040         return ("x=" + this.x + ",y=" + this.y);
2041     },
2042
2043     /**
2044      * APIMethod: clone
2045      * Return a clone of this pixel object
2046      *
2047      * Returns:
2048      * {<OpenLayers.Pixel>} A clone pixel
2049      */
2050     clone:function() {
2051         return new OpenLayers.Pixel(this.x, this.y); 
2052     },
2053     
2054     /**
2055      * APIMethod: equals
2056      * Determine whether one pixel is equivalent to another
2057      *
2058      * Parameters:
2059      * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with
2060      *                                  a 'x' and 'y' properties.
2061      *
2062      * Returns:
2063      * {Boolean} The point passed in as parameter is equal to this. Note that
2064      * if px passed in is null, returns false.
2065      */
2066     equals:function(px) {
2067         var equals = false;
2068         if (px != null) {
2069             equals = ((this.x == px.x && this.y == px.y) ||
2070                       (isNaN(this.x) && isNaN(this.y) && isNaN(px.x) && isNaN(px.y)));
2071         }
2072         return equals;
2073     },
2074
2075     /**
2076      * APIMethod: distanceTo
2077      * Returns the distance to the pixel point passed in as a parameter.
2078      *
2079      * Parameters:
2080      * px - {<OpenLayers.Pixel>}
2081      *
2082      * Returns:
2083      * {Float} The pixel point passed in as parameter to calculate the
2084      *     distance to.
2085      */
2086     distanceTo:function(px) {
2087         return Math.sqrt(
2088             Math.pow(this.x - px.x, 2) +
2089             Math.pow(this.y - px.y, 2)
2090         );
2091     },
2092
2093     /**
2094      * APIMethod: add
2095      *
2096      * Parameters:
2097      * x - {Integer}
2098      * y - {Integer}
2099      *
2100      * Returns:
2101      * {<OpenLayers.Pixel>} A new Pixel with this pixel's x&y augmented by the 
2102      * values passed in.
2103      */
2104     add:function(x, y) {
2105         if ( (x == null) || (y == null) ) {
2106             throw new TypeError('Pixel.add cannot receive null values');
2107         }
2108         return new OpenLayers.Pixel(this.x + x, this.y + y);
2109     },
2110
2111     /**
2112     * APIMethod: offset
2113     * 
2114     * Parameters
2115     * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with
2116     *                                  a 'x' and 'y' properties.
2117     * 
2118     * Returns:
2119     * {<OpenLayers.Pixel>} A new Pixel with this pixel's x&y augmented by the 
2120     *                      x&y values of the pixel passed in.
2121     */
2122     offset:function(px) {
2123         var newPx = this.clone();
2124         if (px) {
2125             newPx = this.add(px.x, px.y);
2126         }
2127         return newPx;
2128     },
2129
2130     CLASS_NAME: "OpenLayers.Pixel"
2131 });
2132 /* ======================================================================
2133     OpenLayers/BaseTypes/Size.js
2134    ====================================================================== */
2135
2136 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
2137  * full list of contributors). Published under the 2-clause BSD license.
2138  * See license.txt in the OpenLayers distribution or repository for the
2139  * full text of the license. */
2140
2141 /**
2142  * @requires OpenLayers/BaseTypes/Class.js
2143  */
2144
2145 /**
2146  * Class: OpenLayers.Size
2147  * Instances of this class represent a width/height pair
2148  */
2149 OpenLayers.Size = OpenLayers.Class({
2150
2151     /**
2152      * APIProperty: w
2153      * {Number} width
2154      */
2155     w: 0.0,
2156     
2157     /**
2158      * APIProperty: h
2159      * {Number} height
2160      */
2161     h: 0.0,
2162
2163
2164     /**
2165      * Constructor: OpenLayers.Size
2166      * Create an instance of OpenLayers.Size
2167      *
2168      * Parameters:
2169      * w - {Number} width
2170      * h - {Number} height
2171      */
2172     initialize: function(w, h) {
2173         this.w = parseFloat(w);
2174         this.h = parseFloat(h);
2175     },
2176
2177     /**
2178      * Method: toString
2179      * Return the string representation of a size object
2180      *
2181      * Returns:
2182      * {String} The string representation of OpenLayers.Size object. 
2183      * (e.g. <i>"w=55,h=66"</i>)
2184      */
2185     toString:function() {
2186         return ("w=" + this.w + ",h=" + this.h);
2187     },
2188
2189     /**
2190      * APIMethod: clone
2191      * Create a clone of this size object
2192      *
2193      * Returns:
2194      * {<OpenLayers.Size>} A new OpenLayers.Size object with the same w and h
2195      * values
2196      */
2197     clone:function() {
2198         return new OpenLayers.Size(this.w, this.h);
2199     },
2200
2201     /**
2202      *
2203      * APIMethod: equals
2204      * Determine where this size is equal to another
2205      *
2206      * Parameters:
2207      * sz - {<OpenLayers.Size>|Object} An OpenLayers.Size or an object with
2208      *                                  a 'w' and 'h' properties.
2209      *
2210      * Returns: 
2211      * {Boolean} The passed in size has the same h and w properties as this one.
2212      * Note that if sz passed in is null, returns false.
2213      */
2214     equals:function(sz) {
2215         var equals = false;
2216         if (sz != null) {
2217             equals = ((this.w == sz.w && this.h == sz.h) ||
2218                       (isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)));
2219         }
2220         return equals;
2221     },
2222
2223     CLASS_NAME: "OpenLayers.Size"
2224 });
2225 /* ======================================================================
2226     OpenLayers/Console.js
2227    ====================================================================== */
2228
2229 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
2230  * full list of contributors). Published under the 2-clause BSD license.
2231  * See license.txt in the OpenLayers distribution or repository for the
2232  * full text of the license. */
2233
2234 /**
2235  * @requires OpenLayers/BaseTypes/Class.js
2236  */
2237
2238 /**
2239  * Namespace: OpenLayers.Console
2240  * The OpenLayers.Console namespace is used for debugging and error logging.
2241  * If the Firebug Lite (../Firebug/firebug.js) is included before this script,
2242  * calls to OpenLayers.Console methods will get redirected to window.console.
2243  * This makes use of the Firebug extension where available and allows for
2244  * cross-browser debugging Firebug style.
2245  *
2246  * Note:
2247  * Note that behavior will differ with the Firebug extention and Firebug Lite.
2248  * Most notably, the Firebug Lite console does not currently allow for
2249  * hyperlinks to code or for clicking on object to explore their properties.
2250  * 
2251  */
2252 OpenLayers.Console = {
2253     /**
2254      * Create empty functions for all console methods.  The real value of these
2255      * properties will be set if Firebug Lite (../Firebug/firebug.js script) is
2256      * included.  We explicitly require the Firebug Lite script to trigger
2257      * functionality of the OpenLayers.Console methods.
2258      */
2259     
2260     /**
2261      * APIFunction: log
2262      * Log an object in the console.  The Firebug Lite console logs string
2263      * representation of objects.  Given multiple arguments, they will
2264      * be cast to strings and logged with a space delimiter.  If the first
2265      * argument is a string with printf-like formatting, subsequent arguments
2266      * will be used in string substitution.  Any additional arguments (beyond
2267      * the number substituted in a format string) will be appended in a space-
2268      * delimited line.
2269      * 
2270      * Parameters:
2271      * object - {Object}
2272      */
2273     log: function() {},
2274
2275     /**
2276      * APIFunction: debug
2277      * Writes a message to the console, including a hyperlink to the line
2278      * where it was called.
2279      *
2280      * May be called with multiple arguments as with OpenLayers.Console.log().
2281      * 
2282      * Parameters:
2283      * object - {Object}
2284      */
2285     debug: function() {},
2286
2287     /**
2288      * APIFunction: info
2289      * Writes a message to the console with the visual "info" icon and color
2290      * coding and a hyperlink to the line where it was called.
2291      *
2292      * May be called with multiple arguments as with OpenLayers.Console.log().
2293      * 
2294      * Parameters:
2295      * object - {Object}
2296      */
2297     info: function() {},
2298
2299     /**
2300      * APIFunction: warn
2301      * Writes a message to the console with the visual "warning" icon and
2302      * color coding and a hyperlink to the line where it was called.
2303      *
2304      * May be called with multiple arguments as with OpenLayers.Console.log().
2305      * 
2306      * Parameters:
2307      * object - {Object}
2308      */
2309     warn: function() {},
2310
2311     /**
2312      * APIFunction: error
2313      * Writes a message to the console with the visual "error" icon and color
2314      * coding and a hyperlink to the line where it was called.
2315      *
2316      * May be called with multiple arguments as with OpenLayers.Console.log().
2317      * 
2318      * Parameters:
2319      * object - {Object}
2320      */
2321     error: function() {},
2322     
2323     /**
2324      * APIFunction: userError
2325      * A single interface for showing error messages to the user. The default
2326      * behavior is a Javascript alert, though this can be overridden by
2327      * reassigning OpenLayers.Console.userError to a different function.
2328      *
2329      * Expects a single error message
2330      * 
2331      * Parameters:
2332      * error - {Object}
2333      */
2334     userError: function(error) {
2335         alert(error);
2336     },
2337
2338     /**
2339      * APIFunction: assert
2340      * Tests that an expression is true. If not, it will write a message to
2341      * the console and throw an exception.
2342      *
2343      * May be called with multiple arguments as with OpenLayers.Console.log().
2344      * 
2345      * Parameters:
2346      * object - {Object}
2347      */
2348     assert: function() {},
2349
2350     /**
2351      * APIFunction: dir
2352      * Prints an interactive listing of all properties of the object. This
2353      * looks identical to the view that you would see in the DOM tab.
2354      * 
2355      * Parameters:
2356      * object - {Object}
2357      */
2358     dir: function() {},
2359
2360     /**
2361      * APIFunction: dirxml
2362      * Prints the XML source tree of an HTML or XML element. This looks
2363      * identical to the view that you would see in the HTML tab. You can click
2364      * on any node to inspect it in the HTML tab.
2365      * 
2366      * Parameters:
2367      * object - {Object}
2368      */
2369     dirxml: function() {},
2370
2371     /**
2372      * APIFunction: trace
2373      * Prints an interactive stack trace of JavaScript execution at the point
2374      * where it is called.  The stack trace details the functions on the stack,
2375      * as well as the values that were passed as arguments to each function.
2376      * You can click each function to take you to its source in the Script tab,
2377      * and click each argument value to inspect it in the DOM or HTML tabs.
2378      * 
2379      */
2380     trace: function() {},
2381
2382     /**
2383      * APIFunction: group
2384      * Writes a message to the console and opens a nested block to indent all
2385      * future messages sent to the console. Call OpenLayers.Console.groupEnd()
2386      * to close the block.
2387      *
2388      * May be called with multiple arguments as with OpenLayers.Console.log().
2389      * 
2390      * Parameters:
2391      * object - {Object}
2392      */
2393     group: function() {},
2394
2395     /**
2396      * APIFunction: groupEnd
2397      * Closes the most recently opened block created by a call to
2398      * OpenLayers.Console.group
2399      */
2400     groupEnd: function() {},
2401     
2402     /**
2403      * APIFunction: time
2404      * Creates a new timer under the given name. Call
2405      * OpenLayers.Console.timeEnd(name)
2406      * with the same name to stop the timer and print the time elapsed.
2407      *
2408      * Parameters:
2409      * name - {String}
2410      */
2411     time: function() {},
2412
2413     /**
2414      * APIFunction: timeEnd
2415      * Stops a timer created by a call to OpenLayers.Console.time(name) and
2416      * writes the time elapsed.
2417      *
2418      * Parameters:
2419      * name - {String}
2420      */
2421     timeEnd: function() {},
2422
2423     /**
2424      * APIFunction: profile
2425      * Turns on the JavaScript profiler. The optional argument title would
2426      * contain the text to be printed in the header of the profile report.
2427      *
2428      * This function is not currently implemented in Firebug Lite.
2429      * 
2430      * Parameters:
2431      * title - {String} Optional title for the profiler
2432      */
2433     profile: function() {},
2434
2435     /**
2436      * APIFunction: profileEnd
2437      * Turns off the JavaScript profiler and prints its report.
2438      * 
2439      * This function is not currently implemented in Firebug Lite.
2440      */
2441     profileEnd: function() {},
2442
2443     /**
2444      * APIFunction: count
2445      * Writes the number of times that the line of code where count was called
2446      * was executed. The optional argument title will print a message in
2447      * addition to the number of the count.
2448      *
2449      * This function is not currently implemented in Firebug Lite.
2450      *
2451      * Parameters:
2452      * title - {String} Optional title to be printed with count
2453      */
2454     count: function() {},
2455
2456     CLASS_NAME: "OpenLayers.Console"
2457 };
2458
2459 /**
2460  * Execute an anonymous function to extend the OpenLayers.Console namespace
2461  * if the firebug.js script is included.  This closure is used so that the
2462  * "scripts" and "i" variables don't pollute the global namespace.
2463  */
2464 (function() {
2465     /**
2466      * If Firebug Lite is included (before this script), re-route all
2467      * OpenLayers.Console calls to the console object.
2468      */
2469     var scripts = document.getElementsByTagName("script");
2470     for(var i=0, len=scripts.length; i<len; ++i) {
2471         if(scripts[i].src.indexOf("firebug.js") != -1) {
2472             if(console) {
2473                 OpenLayers.Util.extend(OpenLayers.Console, console);
2474                 break;
2475             }
2476         }
2477     }
2478 })();
2479 /* ======================================================================
2480     OpenLayers/Lang.js
2481    ====================================================================== */
2482
2483 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
2484  * full list of contributors). Published under the 2-clause BSD license.
2485  * See license.txt in the OpenLayers distribution or repository for the
2486  * full text of the license. */
2487
2488 /**
2489  * @requires OpenLayers/BaseTypes.js
2490  * @requires OpenLayers/Console.js
2491  */
2492
2493 /**
2494  * Namespace: OpenLayers.Lang
2495  * Internationalization namespace.  Contains dictionaries in various languages
2496  *     and methods to set and get the current language.
2497  */
2498 OpenLayers.Lang = {
2499     
2500     /** 
2501      * Property: code
2502      * {String}  Current language code to use in OpenLayers.  Use the
2503      *     <setCode> method to set this value and the <getCode> method to
2504      *     retrieve it.
2505      */
2506     code: null,
2507
2508     /** 
2509      * APIProperty: defaultCode
2510      * {String} Default language to use when a specific language can't be
2511      *     found.  Default is "en".
2512      */
2513     defaultCode: "en",
2514         
2515     /**
2516      * APIFunction: getCode
2517      * Get the current language code.
2518      *
2519      * Returns:
2520      * {String} The current language code.
2521      */
2522     getCode: function() {
2523         if(!OpenLayers.Lang.code) {
2524             OpenLayers.Lang.setCode();
2525         }
2526         return OpenLayers.Lang.code;
2527     },
2528     
2529     /**
2530      * APIFunction: setCode
2531      * Set the language code for string translation.  This code is used by
2532      *     the <OpenLayers.Lang.translate> method.
2533      *
2534      * Parameters:
2535      * code - {String} These codes follow the IETF recommendations at
2536      *     http://www.ietf.org/rfc/rfc3066.txt.  If no value is set, the
2537      *     browser's language setting will be tested.  If no <OpenLayers.Lang>
2538      *     dictionary exists for the code, the <OpenLayers.String.defaultLang>
2539      *     will be used.
2540      */
2541     setCode: function(code) {
2542         var lang;
2543         if(!code) {
2544             code = (OpenLayers.BROWSER_NAME == "msie") ?
2545                 navigator.userLanguage : navigator.language;
2546         }
2547         var parts = code.split('-');
2548         parts[0] = parts[0].toLowerCase();
2549         if(typeof OpenLayers.Lang[parts[0]] == "object") {
2550             lang = parts[0];
2551         }
2552
2553         // check for regional extensions
2554         if(parts[1]) {
2555             var testLang = parts[0] + '-' + parts[1].toUpperCase();
2556             if(typeof OpenLayers.Lang[testLang] == "object") {
2557                 lang = testLang;
2558             }
2559         }
2560         if(!lang) {
2561             OpenLayers.Console.warn(
2562                 'Failed to find OpenLayers.Lang.' + parts.join("-") +
2563                 ' dictionary, falling back to default language'
2564             );
2565             lang = OpenLayers.Lang.defaultCode;
2566         }
2567         
2568         OpenLayers.Lang.code = lang;
2569     },
2570
2571     /**
2572      * APIMethod: translate
2573      * Looks up a key from a dictionary based on the current language string.
2574      *     The value of <getCode> will be used to determine the appropriate
2575      *     dictionary.  Dictionaries are stored in <OpenLayers.Lang>.
2576      *
2577      * Parameters:
2578      * key - {String} The key for an i18n string value in the dictionary.
2579      * context - {Object} Optional context to be used with
2580      *     <OpenLayers.String.format>.
2581      * 
2582      * Returns:
2583      * {String} A internationalized string.
2584      */
2585     translate: function(key, context) {
2586         var dictionary = OpenLayers.Lang[OpenLayers.Lang.getCode()];
2587         var message = dictionary && dictionary[key];
2588         if(!message) {
2589             // Message not found, fall back to message key
2590             message = key;
2591         }
2592         if(context) {
2593             message = OpenLayers.String.format(message, context);
2594         }
2595         return message;
2596     }
2597     
2598 };
2599
2600
2601 /**
2602  * APIMethod: OpenLayers.i18n
2603  * Alias for <OpenLayers.Lang.translate>.  Looks up a key from a dictionary
2604  *     based on the current language string. The value of
2605  *     <OpenLayers.Lang.getCode> will be used to determine the appropriate
2606  *     dictionary.  Dictionaries are stored in <OpenLayers.Lang>.
2607  *
2608  * Parameters:
2609  * key - {String} The key for an i18n string value in the dictionary.
2610  * context - {Object} Optional context to be used with
2611  *     <OpenLayers.String.format>.
2612  * 
2613  * Returns:
2614  * {String} A internationalized string.
2615  */
2616 OpenLayers.i18n = OpenLayers.Lang.translate;
2617 /* ======================================================================
2618     OpenLayers/Util.js
2619    ====================================================================== */
2620
2621 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
2622  * full list of contributors). Published under the 2-clause BSD license.
2623  * See license.txt in the OpenLayers distribution or repository for the
2624  * full text of the license. */
2625
2626 /**
2627  * @requires OpenLayers/BaseTypes.js
2628  * @requires OpenLayers/BaseTypes/Bounds.js
2629  * @requires OpenLayers/BaseTypes/Element.js
2630  * @requires OpenLayers/BaseTypes/LonLat.js
2631  * @requires OpenLayers/BaseTypes/Pixel.js
2632  * @requires OpenLayers/BaseTypes/Size.js
2633  * @requires OpenLayers/Lang.js
2634  */
2635
2636 /**
2637  * Namespace: Util
2638  */
2639 OpenLayers.Util = OpenLayers.Util || {};
2640
2641 /** 
2642  * Function: getElement
2643  * This is the old $() from prototype
2644  *
2645  * Parameters:
2646  * e - {String or DOMElement or Window}
2647  *
2648  * Returns:
2649  * {Array(DOMElement) or DOMElement}
2650  */
2651 OpenLayers.Util.getElement = function() {
2652     var elements = [];
2653
2654     for (var i=0, len=arguments.length; i<len; i++) {
2655         var element = arguments[i];
2656         if (typeof element == 'string') {
2657             element = document.getElementById(element);
2658         }
2659         if (arguments.length == 1) {
2660             return element;
2661         }
2662         elements.push(element);
2663     }
2664     return elements;
2665 };
2666
2667 /**
2668  * Function: isElement
2669  * A cross-browser implementation of "e instanceof Element".
2670  *
2671  * Parameters:
2672  * o - {Object} The object to test.
2673  *
2674  * Returns:
2675  * {Boolean}
2676  */
2677 OpenLayers.Util.isElement = function(o) {
2678     return !!(o && o.nodeType === 1);
2679 };
2680
2681 /**
2682  * Function: isArray
2683  * Tests that the provided object is an array.
2684  * This test handles the cross-IFRAME case not caught
2685  * by "a instanceof Array" and should be used instead.
2686  * 
2687  * Parameters:
2688  * a - {Object} the object test.
2689  * 
2690  * Returns:
2691  * {Boolean} true if the object is an array.
2692  */
2693 OpenLayers.Util.isArray = function(a) {
2694     return (Object.prototype.toString.call(a) === '[object Array]');
2695 };
2696
2697 /** 
2698  * Function: removeItem
2699  * Remove an object from an array. Iterates through the array
2700  *     to find the item, then removes it.
2701  *
2702  * Parameters:
2703  * array - {Array}
2704  * item - {Object}
2705  * 
2706  * Returns:
2707  * {Array} A reference to the array
2708  */
2709 OpenLayers.Util.removeItem = function(array, item) {
2710     for(var i = array.length - 1; i >= 0; i--) {
2711         if(array[i] == item) {
2712             array.splice(i,1);
2713             //break;more than once??
2714         }
2715     }
2716     return array;
2717 };
2718
2719 /** 
2720  * Function: indexOf
2721  * Seems to exist already in FF, but not in MOZ.
2722  * 
2723  * Parameters:
2724  * array - {Array}
2725  * obj - {*}
2726  * 
2727  * Returns:
2728  * {Integer} The index at which the first object was found in the array.
2729  *           If not found, returns -1.
2730  */
2731 OpenLayers.Util.indexOf = function(array, obj) {
2732     // use the build-in function if available.
2733     if (typeof array.indexOf == "function") {
2734         return array.indexOf(obj);
2735     } else {
2736         for (var i = 0, len = array.length; i < len; i++) {
2737             if (array[i] == obj) {
2738                 return i;
2739             }
2740         }
2741         return -1;   
2742     }
2743 };
2744
2745
2746 /**
2747  * Property: dotless
2748  * {RegExp}
2749  * Compiled regular expression to match dots (".").  This is used for replacing
2750  *     dots in identifiers.  Because object identifiers are frequently used for
2751  *     DOM element identifiers by the library, we avoid using dots to make for
2752  *     more sensible CSS selectors.
2753  *
2754  * TODO: Use a module pattern to avoid bloating the API with stuff like this.
2755  */
2756 OpenLayers.Util.dotless = /\./g;
2757
2758 /**
2759  * Function: modifyDOMElement
2760  * 
2761  * Modifies many properties of a DOM element all at once.  Passing in 
2762  * null to an individual parameter will avoid setting the attribute.
2763  *
2764  * Parameters:
2765  * element - {DOMElement} DOM element to modify.
2766  * id - {String} The element id attribute to set.  Note that dots (".") will be
2767  *     replaced with underscore ("_") in setting the element id.
2768  * px - {<OpenLayers.Pixel>|Object} The element left and top position,
2769  *                                  OpenLayers.Pixel or an object with
2770  *                                  a 'x' and 'y' properties.
2771  * sz - {<OpenLayers.Size>|Object} The element width and height,
2772  *                                 OpenLayers.Size or an object with a
2773  *                                 'w' and 'h' properties.
2774  * position - {String}       The position attribute.  eg: absolute, 
2775  *                           relative, etc.
2776  * border - {String}         The style.border attribute.  eg:
2777  *                           solid black 2px
2778  * overflow - {String}       The style.overview attribute.  
2779  * opacity - {Float}         Fractional value (0.0 - 1.0)
2780  */
2781 OpenLayers.Util.modifyDOMElement = function(element, id, px, sz, position, 
2782                                             border, overflow, opacity) {
2783
2784     if (id) {
2785         element.id = id.replace(OpenLayers.Util.dotless, "_");
2786     }
2787     if (px) {
2788         element.style.left = px.x + "px";
2789         element.style.top = px.y + "px";
2790     }
2791     if (sz) {
2792         element.style.width = sz.w + "px";
2793         element.style.height = sz.h + "px";
2794     }
2795     if (position) {
2796         element.style.position = position;
2797     }
2798     if (border) {
2799         element.style.border = border;
2800     }
2801     if (overflow) {
2802         element.style.overflow = overflow;
2803     }
2804     if (parseFloat(opacity) >= 0.0 && parseFloat(opacity) < 1.0) {
2805         element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
2806         element.style.opacity = opacity;
2807     } else if (parseFloat(opacity) == 1.0) {
2808         element.style.filter = '';
2809         element.style.opacity = '';
2810     }
2811 };
2812
2813 /** 
2814  * Function: createDiv
2815  * Creates a new div and optionally set some standard attributes.
2816  * Null may be passed to each parameter if you do not wish to
2817  * set a particular attribute.
2818  * Note - zIndex is NOT set on the resulting div.
2819  * 
2820  * Parameters:
2821  * id - {String} An identifier for this element.  If no id is
2822  *               passed an identifier will be created 
2823  *               automatically.  Note that dots (".") will be replaced with
2824  *               underscore ("_") when generating ids.
2825  * px - {<OpenLayers.Pixel>|Object} The element left and top position,
2826  *                                  OpenLayers.Pixel or an object with
2827  *                                  a 'x' and 'y' properties.
2828  * sz - {<OpenLayers.Size>|Object} The element width and height,
2829  *                                 OpenLayers.Size or an object with a
2830  *                                 'w' and 'h' properties.
2831  * imgURL - {String} A url pointing to an image to use as a 
2832  *                   background image.
2833  * position - {String} The style.position value. eg: absolute,
2834  *                     relative etc.
2835  * border - {String} The the style.border value. 
2836  *                   eg: 2px solid black
2837  * overflow - {String} The style.overflow value. Eg. hidden
2838  * opacity - {Float} Fractional value (0.0 - 1.0)
2839  * 
2840  * Returns: 
2841  * {DOMElement} A DOM Div created with the specified attributes.
2842  */
2843 OpenLayers.Util.createDiv = function(id, px, sz, imgURL, position, 
2844                                      border, overflow, opacity) {
2845
2846     var dom = document.createElement('div');
2847
2848     if (imgURL) {
2849         dom.style.backgroundImage = 'url(' + imgURL + ')';
2850     }
2851
2852     //set generic properties
2853     if (!id) {
2854         id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
2855     }
2856     if (!position) {
2857         position = "absolute";
2858     }
2859     OpenLayers.Util.modifyDOMElement(dom, id, px, sz, position, 
2860                                      border, overflow, opacity);
2861
2862     return dom;
2863 };
2864
2865 /**
2866  * Function: createImage
2867  * Creates an img element with specific attribute values.
2868  *  
2869  * Parameters:
2870  * id - {String} The id field for the img.  If none assigned one will be
2871  *               automatically generated.
2872  * px - {<OpenLayers.Pixel>|Object} The element left and top position,
2873  *                                  OpenLayers.Pixel or an object with
2874  *                                  a 'x' and 'y' properties.
2875  * sz - {<OpenLayers.Size>|Object} The element width and height,
2876  *                                 OpenLayers.Size or an object with a
2877  *                                 'w' and 'h' properties.
2878  * imgURL - {String} The url to use as the image source.
2879  * position - {String} The style.position value.
2880  * border - {String} The border to place around the image.
2881  * opacity - {Float} Fractional value (0.0 - 1.0)
2882  * delayDisplay - {Boolean} If true waits until the image has been
2883  *                          loaded.
2884  * 
2885  * Returns:
2886  * {DOMElement} A DOM Image created with the specified attributes.
2887  */
2888 OpenLayers.Util.createImage = function(id, px, sz, imgURL, position, border,
2889                                        opacity, delayDisplay) {
2890
2891     var image = document.createElement("img");
2892
2893     //set generic properties
2894     if (!id) {
2895         id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
2896     }
2897     if (!position) {
2898         position = "relative";
2899     }
2900     OpenLayers.Util.modifyDOMElement(image, id, px, sz, position, 
2901                                      border, null, opacity);
2902
2903     if (delayDisplay) {
2904         image.style.display = "none";
2905         function display() {
2906             image.style.display = "";
2907             OpenLayers.Event.stopObservingElement(image);
2908         }
2909         OpenLayers.Event.observe(image, "load", display);
2910         OpenLayers.Event.observe(image, "error", display);
2911     }
2912     
2913     //set special properties
2914     image.style.alt = id;
2915     image.galleryImg = "no";
2916     if (imgURL) {
2917         image.src = imgURL;
2918     }
2919         
2920     return image;
2921 };
2922
2923 /**
2924  * Property: IMAGE_RELOAD_ATTEMPTS
2925  * {Integer} How many times should we try to reload an image before giving up?
2926  *           Default is 0
2927  */
2928 OpenLayers.IMAGE_RELOAD_ATTEMPTS = 0;
2929
2930 /**
2931  * Property: alphaHackNeeded
2932  * {Boolean} true if the png alpha hack is necessary and possible, false otherwise.
2933  */
2934 OpenLayers.Util.alphaHackNeeded = null;
2935
2936 /**
2937  * Function: alphaHack
2938  * Checks whether it's necessary (and possible) to use the png alpha
2939  * hack which allows alpha transparency for png images under Internet
2940  * Explorer.
2941  * 
2942  * Returns:
2943  * {Boolean} true if the png alpha hack is necessary and possible, false otherwise.
2944  */
2945 OpenLayers.Util.alphaHack = function() {
2946     if (OpenLayers.Util.alphaHackNeeded == null) {
2947         var arVersion = navigator.appVersion.split("MSIE");
2948         var version = parseFloat(arVersion[1]);
2949         var filter = false;
2950     
2951         // IEs4Lin dies when trying to access document.body.filters, because 
2952         // the property is there, but requires a DLL that can't be provided. This
2953         // means that we need to wrap this in a try/catch so that this can
2954         // continue.
2955     
2956         try { 
2957             filter = !!(document.body.filters);
2958         } catch (e) {}    
2959     
2960         OpenLayers.Util.alphaHackNeeded = (filter && 
2961                                            (version >= 5.5) && (version < 7));
2962     }
2963     return OpenLayers.Util.alphaHackNeeded;
2964 };
2965
2966 /** 
2967  * Function: modifyAlphaImageDiv
2968  * 
2969  * Parameters:
2970  * div - {DOMElement} Div containing Alpha-adjusted Image
2971  * id - {String}
2972  * px - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or an object with
2973  *                                  a 'x' and 'y' properties.
2974  * sz - {<OpenLayers.Size>|Object} OpenLayers.Size or an object with
2975  *                                 a 'w' and 'h' properties.
2976  * imgURL - {String}
2977  * position - {String}
2978  * border - {String}
2979  * sizing - {String} 'crop', 'scale', or 'image'. Default is "scale"
2980  * opacity - {Float} Fractional value (0.0 - 1.0)
2981  */ 
2982 OpenLayers.Util.modifyAlphaImageDiv = function(div, id, px, sz, imgURL, 
2983                                                position, border, sizing, 
2984                                                opacity) {
2985
2986     OpenLayers.Util.modifyDOMElement(div, id, px, sz, position,
2987                                      null, null, opacity);
2988
2989     var img = div.childNodes[0];
2990
2991     if (imgURL) {
2992         img.src = imgURL;
2993     }
2994     OpenLayers.Util.modifyDOMElement(img, div.id + "_innerImage", null, sz, 
2995                                      "relative", border);
2996     
2997     if (OpenLayers.Util.alphaHack()) {
2998         if(div.style.display != "none") {
2999             div.style.display = "inline-block";
3000         }
3001         if (sizing == null) {
3002             sizing = "scale";
3003         }
3004         
3005         div.style.filter = "progid:DXImageTransform.Microsoft" +
3006                            ".AlphaImageLoader(src='" + img.src + "', " +
3007                            "sizingMethod='" + sizing + "')";
3008         if (parseFloat(div.style.opacity) >= 0.0 && 
3009             parseFloat(div.style.opacity) < 1.0) {
3010             div.style.filter += " alpha(opacity=" + div.style.opacity * 100 + ")";
3011         }
3012
3013         img.style.filter = "alpha(opacity=0)";
3014     }
3015 };
3016
3017 /** 
3018  * Function: createAlphaImageDiv
3019  * 
3020  * Parameters:
3021  * id - {String}
3022  * px - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or an object with
3023  *                                  a 'x' and 'y' properties.
3024  * sz - {<OpenLayers.Size>|Object} OpenLayers.Size or an object with
3025  *                                 a 'w' and 'h' properties.
3026  * imgURL - {String}
3027  * position - {String}
3028  * border - {String}
3029  * sizing - {String} 'crop', 'scale', or 'image'. Default is "scale"
3030  * opacity - {Float} Fractional value (0.0 - 1.0)
3031  * delayDisplay - {Boolean} If true waits until the image has been
3032  *                          loaded.
3033  * 
3034  * Returns:
3035  * {DOMElement} A DOM Div created with a DOM Image inside it. If the hack is 
3036  *              needed for transparency in IE, it is added.
3037  */ 
3038 OpenLayers.Util.createAlphaImageDiv = function(id, px, sz, imgURL, 
3039                                                position, border, sizing, 
3040                                                opacity, delayDisplay) {
3041     
3042     var div = OpenLayers.Util.createDiv();
3043     var img = OpenLayers.Util.createImage(null, null, null, null, null, null, 
3044                                           null, delayDisplay);
3045     img.className = "olAlphaImg";
3046     div.appendChild(img);
3047
3048     OpenLayers.Util.modifyAlphaImageDiv(div, id, px, sz, imgURL, position, 
3049                                         border, sizing, opacity);
3050     
3051     return div;
3052 };
3053
3054
3055 /** 
3056  * Function: upperCaseObject
3057  * Creates a new hashtable and copies over all the keys from the 
3058  *     passed-in object, but storing them under an uppercased
3059  *     version of the key at which they were stored.
3060  * 
3061  * Parameters: 
3062  * object - {Object}
3063  * 
3064  * Returns: 
3065  * {Object} A new Object with all the same keys but uppercased
3066  */
3067 OpenLayers.Util.upperCaseObject = function (object) {
3068     var uObject = {};
3069     for (var key in object) {
3070         uObject[key.toUpperCase()] = object[key];
3071     }
3072     return uObject;
3073 };
3074
3075 /** 
3076  * Function: applyDefaults
3077  * Takes an object and copies any properties that don't exist from
3078  *     another properties, by analogy with OpenLayers.Util.extend() from
3079  *     Prototype.js.
3080  * 
3081  * Parameters:
3082  * to - {Object} The destination object.
3083  * from - {Object} The source object.  Any properties of this object that
3084  *     are undefined in the to object will be set on the to object.
3085  *
3086  * Returns:
3087  * {Object} A reference to the to object.  Note that the to argument is modified
3088  *     in place and returned by this function.
3089  */
3090 OpenLayers.Util.applyDefaults = function (to, from) {
3091     to = to || {};
3092     /*
3093      * FF/Windows < 2.0.0.13 reports "Illegal operation on WrappedNative
3094      * prototype object" when calling hawOwnProperty if the source object is an
3095      * instance of window.Event.
3096      */
3097     var fromIsEvt = typeof window.Event == "function"
3098                     && from instanceof window.Event;
3099
3100     for (var key in from) {
3101         if (to[key] === undefined ||
3102             (!fromIsEvt && from.hasOwnProperty
3103              && from.hasOwnProperty(key) && !to.hasOwnProperty(key))) {
3104             to[key] = from[key];
3105         }
3106     }
3107     /**
3108      * IE doesn't include the toString property when iterating over an object's
3109      * properties with the for(property in object) syntax.  Explicitly check if
3110      * the source has its own toString property.
3111      */
3112     if(!fromIsEvt && from && from.hasOwnProperty
3113        && from.hasOwnProperty('toString') && !to.hasOwnProperty('toString')) {
3114         to.toString = from.toString;
3115     }
3116     
3117     return to;
3118 };
3119
3120 /**
3121  * Function: getParameterString
3122  * 
3123  * Parameters:
3124  * params - {Object}
3125  * 
3126  * Returns:
3127  * {String} A concatenation of the properties of an object in 
3128  *          http parameter notation. 
3129  *          (ex. <i>"key1=value1&key2=value2&key3=value3"</i>)
3130  *          If a parameter is actually a list, that parameter will then
3131  *          be set to a comma-seperated list of values (foo,bar) instead
3132  *          of being URL escaped (foo%3Abar). 
3133  */
3134 OpenLayers.Util.getParameterString = function(params) {
3135     var paramsArray = [];
3136     
3137     for (var key in params) {
3138       var value = params[key];
3139       if ((value != null) && (typeof value != 'function')) {
3140         var encodedValue;
3141         if (typeof value == 'object' && value.constructor == Array) {
3142           /* value is an array; encode items and separate with "," */
3143           var encodedItemArray = [];
3144           var item;
3145           for (var itemIndex=0, len=value.length; itemIndex<len; itemIndex++) {
3146             item = value[itemIndex];
3147             encodedItemArray.push(encodeURIComponent(
3148                 (item === null || item === undefined) ? "" : item)
3149             );
3150           }
3151           encodedValue = encodedItemArray.join(",");
3152         }
3153         else {
3154           /* value is a string; simply encode */
3155           encodedValue = encodeURIComponent(value);
3156         }
3157         paramsArray.push(encodeURIComponent(key) + "=" + encodedValue);
3158       }
3159     }
3160     
3161     return paramsArray.join("&");
3162 };
3163
3164 /**
3165  * Function: urlAppend
3166  * Appends a parameter string to a url. This function includes the logic for
3167  * using the appropriate character (none, & or ?) to append to the url before
3168  * appending the param string.
3169  * 
3170  * Parameters:
3171  * url - {String} The url to append to
3172  * paramStr - {String} The param string to append
3173  * 
3174  * Returns:
3175  * {String} The new url
3176  */
3177 OpenLayers.Util.urlAppend = function(url, paramStr) {
3178     var newUrl = url;
3179     if(paramStr) {
3180         var parts = (url + " ").split(/[?&]/);
3181         newUrl += (parts.pop() === " " ?
3182             paramStr :
3183             parts.length ? "&" + paramStr : "?" + paramStr);
3184     }
3185     return newUrl;
3186 };
3187
3188 /** 
3189  * Function: getImagesLocation
3190  * 
3191  * Returns:
3192  * {String} The fully formatted image location string
3193  */
3194 OpenLayers.Util.getImagesLocation = function() {
3195     return OpenLayers.ImgPath || (OpenLayers._getScriptLocation() + "img/");
3196 };
3197
3198 /** 
3199  * Function: getImageLocation
3200  * 
3201  * Returns:
3202  * {String} The fully formatted location string for a specified image
3203  */
3204 OpenLayers.Util.getImageLocation = function(image) {
3205     return OpenLayers.Util.getImagesLocation() + image;
3206 };
3207
3208
3209 /** 
3210  * Function: Try
3211  * Execute functions until one of them doesn't throw an error. 
3212  *     Capitalized because "try" is a reserved word in JavaScript.
3213  *     Taken directly from OpenLayers.Util.Try()
3214  * 
3215  * Parameters:
3216  * [*] - {Function} Any number of parameters may be passed to Try()
3217  *    It will attempt to execute each of them until one of them 
3218  *    successfully executes. 
3219  *    If none executes successfully, returns null.
3220  * 
3221  * Returns:
3222  * {*} The value returned by the first successfully executed function.
3223  */
3224 OpenLayers.Util.Try = function() {
3225     var returnValue = null;
3226
3227     for (var i=0, len=arguments.length; i<len; i++) {
3228       var lambda = arguments[i];
3229       try {
3230         returnValue = lambda();
3231         break;
3232       } catch (e) {}
3233     }
3234
3235     return returnValue;
3236 };
3237
3238 /**
3239  * Function: getXmlNodeValue
3240  * 
3241  * Parameters:
3242  * node - {XMLNode}
3243  * 
3244  * Returns:
3245  * {String} The text value of the given node, without breaking in firefox or IE
3246  */
3247 OpenLayers.Util.getXmlNodeValue = function(node) {
3248     var val = null;
3249     OpenLayers.Util.Try( 
3250         function() {
3251             val = node.text;
3252             if (!val) {
3253                 val = node.textContent;
3254             }
3255             if (!val) {
3256                 val = node.firstChild.nodeValue;
3257             }
3258         }, 
3259         function() {
3260             val = node.textContent;
3261         }); 
3262     return val;
3263 };
3264
3265 /** 
3266  * Function: mouseLeft
3267  * 
3268  * Parameters:
3269  * evt - {Event}
3270  * div - {HTMLDivElement}
3271  * 
3272  * Returns:
3273  * {Boolean}
3274  */
3275 OpenLayers.Util.mouseLeft = function (evt, div) {
3276     // start with the element to which the mouse has moved
3277     var target = (evt.relatedTarget) ? evt.relatedTarget : evt.toElement;
3278     // walk up the DOM tree.
3279     while (target != div && target != null) {
3280         target = target.parentNode;
3281     }
3282     // if the target we stop at isn't the div, then we've left the div.
3283     return (target != div);
3284 };
3285
3286 /**
3287  * Property: precision
3288  * {Number} The number of significant digits to retain to avoid
3289  * floating point precision errors.
3290  *
3291  * We use 14 as a "safe" default because, although IEEE 754 double floats
3292  * (standard on most modern operating systems) support up to about 16
3293  * significant digits, 14 significant digits are sufficient to represent
3294  * sub-millimeter accuracy in any coordinate system that anyone is likely to
3295  * use with OpenLayers.
3296  *
3297  * If DEFAULT_PRECISION is set to 0, the original non-truncating behavior
3298  * of OpenLayers <2.8 is preserved. Be aware that this will cause problems
3299  * with certain projections, e.g. spherical Mercator.
3300  *
3301  */
3302 OpenLayers.Util.DEFAULT_PRECISION = 14;
3303
3304 /**
3305  * Function: toFloat
3306  * Convenience method to cast an object to a Number, rounded to the
3307  * desired floating point precision.
3308  *
3309  * Parameters:
3310  * number    - {Number} The number to cast and round.
3311  * precision - {Number} An integer suitable for use with
3312  *      Number.toPrecision(). Defaults to OpenLayers.Util.DEFAULT_PRECISION.
3313  *      If set to 0, no rounding is performed.
3314  *
3315  * Returns:
3316  * {Number} The cast, rounded number.
3317  */
3318 OpenLayers.Util.toFloat = function (number, precision) {
3319     if (precision == null) {
3320         precision = OpenLayers.Util.DEFAULT_PRECISION;
3321     }
3322     if (typeof number !== "number") {
3323         number = parseFloat(number);
3324     }
3325     return precision === 0 ? number :
3326                              parseFloat(number.toPrecision(precision));
3327 };
3328
3329 /**
3330  * Function: rad
3331  * 
3332  * Parameters:
3333  * x - {Float}
3334  * 
3335  * Returns:
3336  * {Float}
3337  */
3338 OpenLayers.Util.rad = function(x) {return x*Math.PI/180;};
3339
3340 /**
3341  * Function: deg
3342  *
3343  * Parameters:
3344  * x - {Float}
3345  *
3346  * Returns:
3347  * {Float}
3348  */
3349 OpenLayers.Util.deg = function(x) {return x*180/Math.PI;};
3350
3351 /**
3352  * Property: VincentyConstants
3353  * {Object} Constants for Vincenty functions.
3354  */
3355 OpenLayers.Util.VincentyConstants = {
3356     a: 6378137,
3357     b: 6356752.3142,
3358     f: 1/298.257223563
3359 };
3360
3361 /**
3362  * APIFunction: distVincenty
3363  * Given two objects representing points with geographic coordinates, this
3364  *     calculates the distance between those points on the surface of an
3365  *     ellipsoid.
3366  *
3367  * Parameters:
3368  * p1 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties)
3369  * p2 - {<OpenLayers.LonLat>} (or any object with both .lat, .lon properties)
3370  *
3371  * Returns:
3372  * {Float} The distance (in km) between the two input points as measured on an
3373  *     ellipsoid.  Note that the input point objects must be in geographic
3374  *     coordinates (decimal degrees) and the return distance is in kilometers.
3375  */
3376 OpenLayers.Util.distVincenty = function(p1, p2) {
3377     var ct = OpenLayers.Util.VincentyConstants;
3378     var a = ct.a, b = ct.b, f = ct.f;
3379
3380     var L = OpenLayers.Util.rad(p2.lon - p1.lon);
3381     var U1 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p1.lat)));
3382     var U2 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p2.lat)));
3383     var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
3384     var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
3385     var lambda = L, lambdaP = 2*Math.PI;
3386     var iterLimit = 20;
3387     while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
3388         var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
3389         var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
3390         (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
3391         if (sinSigma==0) {
3392             return 0;  // co-incident points
3393         }
3394         var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
3395         var sigma = Math.atan2(sinSigma, cosSigma);
3396         var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
3397         var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
3398         var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
3399         var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
3400         lambdaP = lambda;
3401         lambda = L + (1-C) * f * Math.sin(alpha) *
3402         (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
3403     }
3404     if (iterLimit==0) {
3405         return NaN;  // formula failed to converge
3406     }
3407     var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
3408     var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
3409     var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
3410     var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
3411         B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
3412     var s = b*A*(sigma-deltaSigma);
3413     var d = s.toFixed(3)/1000; // round to 1mm precision
3414     return d;
3415 };
3416
3417 /**
3418  * APIFunction: destinationVincenty
3419  * Calculate destination point given start point lat/long (numeric degrees),
3420  * bearing (numeric degrees) & distance (in m).
3421  * Adapted from Chris Veness work, see
3422  * http://www.movable-type.co.uk/scripts/latlong-vincenty-direct.html
3423  *
3424  * Parameters:
3425  * lonlat  - {<OpenLayers.LonLat>} (or any object with both .lat, .lon
3426  *     properties) The start point.
3427  * brng     - {Float} The bearing (degrees).
3428  * dist     - {Float} The ground distance (meters).
3429  *
3430  * Returns:
3431  * {<OpenLayers.LonLat>} The destination point.
3432  */
3433 OpenLayers.Util.destinationVincenty = function(lonlat, brng, dist) {
3434     var u = OpenLayers.Util;
3435     var ct = u.VincentyConstants;
3436     var a = ct.a, b = ct.b, f = ct.f;
3437
3438     var lon1 = lonlat.lon;
3439     var lat1 = lonlat.lat;
3440
3441     var s = dist;
3442     var alpha1 = u.rad(brng);
3443     var sinAlpha1 = Math.sin(alpha1);
3444     var cosAlpha1 = Math.cos(alpha1);
3445
3446     var tanU1 = (1-f) * Math.tan(u.rad(lat1));
3447     var cosU1 = 1 / Math.sqrt((1 + tanU1*tanU1)), sinU1 = tanU1*cosU1;
3448     var sigma1 = Math.atan2(tanU1, cosAlpha1);
3449     var sinAlpha = cosU1 * sinAlpha1;
3450     var cosSqAlpha = 1 - sinAlpha*sinAlpha;
3451     var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
3452     var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
3453     var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
3454
3455     var sigma = s / (b*A), sigmaP = 2*Math.PI;
3456     while (Math.abs(sigma-sigmaP) > 1e-12) {
3457         var cos2SigmaM = Math.cos(2*sigma1 + sigma);
3458         var sinSigma = Math.sin(sigma);
3459         var cosSigma = Math.cos(sigma);
3460         var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
3461             B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
3462         sigmaP = sigma;
3463         sigma = s / (b*A) + deltaSigma;
3464     }
3465
3466     var tmp = sinU1*sinSigma - cosU1*cosSigma*cosAlpha1;
3467     var lat2 = Math.atan2(sinU1*cosSigma + cosU1*sinSigma*cosAlpha1,
3468         (1-f)*Math.sqrt(sinAlpha*sinAlpha + tmp*tmp));
3469     var lambda = Math.atan2(sinSigma*sinAlpha1, cosU1*cosSigma - sinU1*sinSigma*cosAlpha1);
3470     var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
3471     var L = lambda - (1-C) * f * sinAlpha *
3472         (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
3473
3474     var revAz = Math.atan2(sinAlpha, -tmp);  // final bearing
3475
3476     return new OpenLayers.LonLat(lon1+u.deg(L), u.deg(lat2));
3477 };
3478
3479 /**
3480  * Function: getParameters
3481  * Parse the parameters from a URL or from the current page itself into a 
3482  *     JavaScript Object. Note that parameter values with commas are separated
3483  *     out into an Array.
3484  * 
3485  * Parameters:
3486  * url - {String} Optional url used to extract the query string.
3487  *                If url is null or is not supplied, query string is taken 
3488  *                from the page location.
3489  * options - {Object} Additional options. Optional.
3490  *
3491  * Valid options:
3492  *   splitArgs - {Boolean} Split comma delimited params into arrays? Default is
3493  *       true.
3494  * 
3495  * Returns:
3496  * {Object} An object of key/value pairs from the query string.
3497  */
3498 OpenLayers.Util.getParameters = function(url, options) {
3499     options = options || {};
3500     // if no url specified, take it from the location bar
3501     url = (url === null || url === undefined) ? window.location.href : url;
3502
3503     //parse out parameters portion of url string
3504     var paramsString = "";
3505     if (OpenLayers.String.contains(url, '?')) {
3506         var start = url.indexOf('?') + 1;
3507         var end = OpenLayers.String.contains(url, "#") ?
3508                     url.indexOf('#') : url.length;
3509         paramsString = url.substring(start, end);
3510     }
3511
3512     var parameters = {};
3513     var pairs = paramsString.split(/[&;]/);
3514     for(var i=0, len=pairs.length; i<len; ++i) {
3515         var keyValue = pairs[i].split('=');
3516         if (keyValue[0]) {
3517
3518             var key = keyValue[0];
3519             try {
3520                 key = decodeURIComponent(key);
3521             } catch (err) {
3522                 key = unescape(key);
3523             }
3524             
3525             // being liberal by replacing "+" with " "
3526             var value = (keyValue[1] || '').replace(/\+/g, " ");
3527
3528             try {
3529                 value = decodeURIComponent(value);
3530             } catch (err) {
3531                 value = unescape(value);
3532             }
3533             
3534             // follow OGC convention of comma delimited values
3535             if (options.splitArgs !== false) {
3536                 value = value.split(",");
3537             }
3538
3539             //if there's only one value, do not return as array                    
3540             if (value.length == 1) {
3541                 value = value[0];
3542             }                
3543             
3544             parameters[key] = value;
3545          }
3546      }
3547     return parameters;
3548 };
3549
3550 /**
3551  * Property: lastSeqID
3552  * {Integer} The ever-incrementing count variable.
3553  *           Used for generating unique ids.
3554  */
3555 OpenLayers.Util.lastSeqID = 0;
3556
3557 /**
3558  * Function: createUniqueID
3559  * Create a unique identifier for this session.  Each time this function
3560  *     is called, a counter is incremented.  The return will be the optional
3561  *     prefix (defaults to "id_") appended with the counter value.
3562  * 
3563  * Parameters:
3564  * prefix - {String} Optional string to prefix unique id. Default is "id_".
3565  *     Note that dots (".") in the prefix will be replaced with underscore ("_").
3566  * 
3567  * Returns:
3568  * {String} A unique id string, built on the passed in prefix.
3569  */
3570 OpenLayers.Util.createUniqueID = function(prefix) {
3571     if (prefix == null) {
3572         prefix = "id_";
3573     } else {
3574         prefix = prefix.replace(OpenLayers.Util.dotless, "_");
3575     }
3576     OpenLayers.Util.lastSeqID += 1; 
3577     return prefix + OpenLayers.Util.lastSeqID;        
3578 };
3579
3580 /**
3581  * Constant: INCHES_PER_UNIT
3582  * {Object} Constant inches per unit -- borrowed from MapServer mapscale.c
3583  * derivation of nautical miles from http://en.wikipedia.org/wiki/Nautical_mile
3584  * Includes the full set of units supported by CS-MAP (http://trac.osgeo.org/csmap/)
3585  * and PROJ.4 (http://trac.osgeo.org/proj/)
3586  * The hardcoded table is maintain in a CS-MAP source code module named CSdataU.c
3587  * The hardcoded table of PROJ.4 units are in pj_units.c.
3588  */
3589 OpenLayers.INCHES_PER_UNIT = { 
3590     'inches': 1.0,
3591     'ft': 12.0,
3592     'mi': 63360.0,
3593     'm': 39.37,
3594     'km': 39370,
3595     'dd': 4374754,
3596     'yd': 36
3597 };
3598 OpenLayers.INCHES_PER_UNIT["in"]= OpenLayers.INCHES_PER_UNIT.inches;
3599 OpenLayers.INCHES_PER_UNIT["degrees"] = OpenLayers.INCHES_PER_UNIT.dd;
3600 OpenLayers.INCHES_PER_UNIT["nmi"] = 1852 * OpenLayers.INCHES_PER_UNIT.m;
3601
3602 // Units from CS-Map
3603 OpenLayers.METERS_PER_INCH = 0.02540005080010160020;
3604 OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT, {
3605     "Inch": OpenLayers.INCHES_PER_UNIT.inches,
3606     "Meter": 1.0 / OpenLayers.METERS_PER_INCH,   //EPSG:9001
3607     "Foot": 0.30480060960121920243 / OpenLayers.METERS_PER_INCH,   //EPSG:9003
3608     "IFoot": 0.30480000000000000000 / OpenLayers.METERS_PER_INCH,   //EPSG:9002
3609     "ClarkeFoot": 0.3047972651151 / OpenLayers.METERS_PER_INCH,   //EPSG:9005
3610     "SearsFoot": 0.30479947153867624624 / OpenLayers.METERS_PER_INCH,   //EPSG:9041
3611     "GoldCoastFoot": 0.30479971018150881758 / OpenLayers.METERS_PER_INCH,   //EPSG:9094
3612     "IInch": 0.02540000000000000000 / OpenLayers.METERS_PER_INCH,
3613     "MicroInch": 0.00002540000000000000 / OpenLayers.METERS_PER_INCH,
3614     "Mil": 0.00000002540000000000 / OpenLayers.METERS_PER_INCH,
3615     "Centimeter": 0.01000000000000000000 / OpenLayers.METERS_PER_INCH,
3616     "Kilometer": 1000.00000000000000000000 / OpenLayers.METERS_PER_INCH,   //EPSG:9036
3617     "Yard": 0.91440182880365760731 / OpenLayers.METERS_PER_INCH,
3618     "SearsYard": 0.914398414616029 / OpenLayers.METERS_PER_INCH,   //EPSG:9040
3619     "IndianYard": 0.91439853074444079983 / OpenLayers.METERS_PER_INCH,   //EPSG:9084
3620     "IndianYd37": 0.91439523 / OpenLayers.METERS_PER_INCH,   //EPSG:9085
3621     "IndianYd62": 0.9143988 / OpenLayers.METERS_PER_INCH,   //EPSG:9086
3622     "IndianYd75": 0.9143985 / OpenLayers.METERS_PER_INCH,   //EPSG:9087
3623     "IndianFoot": 0.30479951 / OpenLayers.METERS_PER_INCH,   //EPSG:9080
3624     "IndianFt37": 0.30479841 / OpenLayers.METERS_PER_INCH,   //EPSG:9081
3625     "IndianFt62": 0.3047996 / OpenLayers.METERS_PER_INCH,   //EPSG:9082
3626     "IndianFt75": 0.3047995 / OpenLayers.METERS_PER_INCH,   //EPSG:9083
3627     "Mile": 1609.34721869443738887477 / OpenLayers.METERS_PER_INCH,
3628     "IYard": 0.91440000000000000000 / OpenLayers.METERS_PER_INCH,   //EPSG:9096
3629     "IMile": 1609.34400000000000000000 / OpenLayers.METERS_PER_INCH,   //EPSG:9093
3630     "NautM": 1852.00000000000000000000 / OpenLayers.METERS_PER_INCH,   //EPSG:9030
3631     "Lat-66": 110943.316488932731 / OpenLayers.METERS_PER_INCH,
3632     "Lat-83": 110946.25736872234125 / OpenLayers.METERS_PER_INCH,
3633     "Decimeter": 0.10000000000000000000 / OpenLayers.METERS_PER_INCH,
3634     "Millimeter": 0.00100000000000000000 / OpenLayers.METERS_PER_INCH,
3635     "Dekameter": 10.00000000000000000000 / OpenLayers.METERS_PER_INCH,
3636     "Decameter": 10.00000000000000000000 / OpenLayers.METERS_PER_INCH,
3637     "Hectometer": 100.00000000000000000000 / OpenLayers.METERS_PER_INCH,
3638     "GermanMeter": 1.0000135965 / OpenLayers.METERS_PER_INCH,   //EPSG:9031
3639     "CaGrid": 0.999738 / OpenLayers.METERS_PER_INCH,
3640     "ClarkeChain": 20.1166194976 / OpenLayers.METERS_PER_INCH,   //EPSG:9038
3641     "GunterChain": 20.11684023368047 / OpenLayers.METERS_PER_INCH,   //EPSG:9033
3642     "BenoitChain": 20.116782494375872 / OpenLayers.METERS_PER_INCH,   //EPSG:9062
3643     "SearsChain": 20.11676512155 / OpenLayers.METERS_PER_INCH,   //EPSG:9042
3644     "ClarkeLink": 0.201166194976 / OpenLayers.METERS_PER_INCH,   //EPSG:9039
3645     "GunterLink": 0.2011684023368047 / OpenLayers.METERS_PER_INCH,   //EPSG:9034
3646     "BenoitLink": 0.20116782494375872 / OpenLayers.METERS_PER_INCH,   //EPSG:9063
3647     "SearsLink": 0.2011676512155 / OpenLayers.METERS_PER_INCH,   //EPSG:9043
3648     "Rod": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
3649     "IntnlChain": 20.1168 / OpenLayers.METERS_PER_INCH,   //EPSG:9097
3650     "IntnlLink": 0.201168 / OpenLayers.METERS_PER_INCH,   //EPSG:9098
3651     "Perch": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
3652     "Pole": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
3653     "Furlong": 201.1684023368046 / OpenLayers.METERS_PER_INCH,
3654     "Rood": 3.778266898 / OpenLayers.METERS_PER_INCH,
3655     "CapeFoot": 0.3047972615 / OpenLayers.METERS_PER_INCH,
3656     "Brealey": 375.00000000000000000000 / OpenLayers.METERS_PER_INCH,
3657     "ModAmFt": 0.304812252984505969011938 / OpenLayers.METERS_PER_INCH,
3658     "Fathom": 1.8288 / OpenLayers.METERS_PER_INCH,
3659     "NautM-UK": 1853.184 / OpenLayers.METERS_PER_INCH,
3660     "50kilometers": 50000.0 / OpenLayers.METERS_PER_INCH,
3661     "150kilometers": 150000.0 / OpenLayers.METERS_PER_INCH
3662 });
3663
3664 //unit abbreviations supported by PROJ.4
3665 OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT, {
3666     "mm": OpenLayers.INCHES_PER_UNIT["Meter"] / 1000.0,
3667     "cm": OpenLayers.INCHES_PER_UNIT["Meter"] / 100.0,
3668     "dm": OpenLayers.INCHES_PER_UNIT["Meter"] * 100.0,
3669     "km": OpenLayers.INCHES_PER_UNIT["Meter"] * 1000.0,
3670     "kmi": OpenLayers.INCHES_PER_UNIT["nmi"],    //International Nautical Mile
3671     "fath": OpenLayers.INCHES_PER_UNIT["Fathom"], //International Fathom
3672     "ch": OpenLayers.INCHES_PER_UNIT["IntnlChain"],  //International Chain
3673     "link": OpenLayers.INCHES_PER_UNIT["IntnlLink"], //International Link
3674     "us-in": OpenLayers.INCHES_PER_UNIT["inches"], //U.S. Surveyor's Inch
3675     "us-ft": OpenLayers.INCHES_PER_UNIT["Foot"], //U.S. Surveyor's Foot
3676     "us-yd": OpenLayers.INCHES_PER_UNIT["Yard"], //U.S. Surveyor's Yard
3677     "us-ch": OpenLayers.INCHES_PER_UNIT["GunterChain"], //U.S. Surveyor's Chain
3678     "us-mi": OpenLayers.INCHES_PER_UNIT["Mile"],   //U.S. Surveyor's Statute Mile
3679     "ind-yd": OpenLayers.INCHES_PER_UNIT["IndianYd37"],  //Indian Yard
3680     "ind-ft": OpenLayers.INCHES_PER_UNIT["IndianFt37"],  //Indian Foot
3681     "ind-ch": 20.11669506 / OpenLayers.METERS_PER_INCH  //Indian Chain
3682 });
3683
3684 /** 
3685  * Constant: DOTS_PER_INCH
3686  * {Integer} 72 (A sensible default)
3687  */
3688 OpenLayers.DOTS_PER_INCH = 72;
3689
3690 /**
3691  * Function: normalizeScale
3692  * 
3693  * Parameters:
3694  * scale - {float}
3695  * 
3696  * Returns:
3697  * {Float} A normalized scale value, in 1 / X format. 
3698  *         This means that if a value less than one ( already 1/x) is passed
3699  *         in, it just returns scale directly. Otherwise, it returns 
3700  *         1 / scale
3701  */
3702 OpenLayers.Util.normalizeScale = function (scale) {
3703     var normScale = (scale > 1.0) ? (1.0 / scale) 
3704                                   : scale;
3705     return normScale;
3706 };
3707
3708 /**
3709  * Function: getResolutionFromScale
3710  * 
3711  * Parameters:
3712  * scale - {Float}
3713  * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
3714  *                  Default is degrees
3715  * 
3716  * Returns:
3717  * {Float} The corresponding resolution given passed-in scale and unit 
3718  *     parameters.  If the given scale is falsey, the returned resolution will
3719  *     be undefined.
3720  */
3721 OpenLayers.Util.getResolutionFromScale = function (scale, units) {
3722     var resolution;
3723     if (scale) {
3724         if (units == null) {
3725             units = "degrees";
3726         }
3727         var normScale = OpenLayers.Util.normalizeScale(scale);
3728         resolution = 1 / (normScale * OpenLayers.INCHES_PER_UNIT[units]
3729                                         * OpenLayers.DOTS_PER_INCH);        
3730     }
3731     return resolution;
3732 };
3733
3734 /**
3735  * Function: getScaleFromResolution
3736  * 
3737  * Parameters:
3738  * resolution - {Float}
3739  * units - {String} Index into OpenLayers.INCHES_PER_UNIT hashtable.
3740  *                  Default is degrees
3741  * 
3742  * Returns:
3743  * {Float} The corresponding scale given passed-in resolution and unit 
3744  *         parameters.
3745  */
3746 OpenLayers.Util.getScaleFromResolution = function (resolution, units) {
3747
3748     if (units == null) {
3749         units = "degrees";
3750     }
3751
3752     var scale = resolution * OpenLayers.INCHES_PER_UNIT[units] *
3753                     OpenLayers.DOTS_PER_INCH;
3754     return scale;
3755 };
3756
3757 /**
3758  * Function: pagePosition
3759  * Calculates the position of an element on the page (see
3760  * http://code.google.com/p/doctype/wiki/ArticlePageOffset)
3761  *
3762  * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is
3763  * Copyright (c) 2006, Yahoo! Inc.
3764  * All rights reserved.
3765  * 
3766  * Redistribution and use of this software in source and binary forms, with or
3767  * without modification, are permitted provided that the following conditions
3768  * are met:
3769  * 
3770  * * Redistributions of source code must retain the above copyright notice,
3771  *   this list of conditions and the following disclaimer.
3772  * 
3773  * * Redistributions in binary form must reproduce the above copyright notice,
3774  *   this list of conditions and the following disclaimer in the documentation
3775  *   and/or other materials provided with the distribution.
3776  * 
3777  * * Neither the name of Yahoo! Inc. nor the names of its contributors may be
3778  *   used to endorse or promote products derived from this software without
3779  *   specific prior written permission of Yahoo! Inc.
3780  * 
3781  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
3782  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3783  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3784  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
3785  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3786  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3787  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3788  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3789  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3790  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
3791  * POSSIBILITY OF SUCH DAMAGE.
3792  *
3793  * Parameters:
3794  * forElement - {DOMElement}
3795  * 
3796  * Returns:
3797  * {Array} two item array, Left value then Top value.
3798  */
3799 OpenLayers.Util.pagePosition =  function(forElement) {
3800     // NOTE: If element is hidden (display none or disconnected or any the
3801     // ancestors are hidden) we get (0,0) by default but we still do the
3802     // accumulation of scroll position.
3803
3804     var pos = [0, 0];
3805     var viewportElement = OpenLayers.Util.getViewportElement();
3806     if (!forElement || forElement == window || forElement == viewportElement) {
3807         // viewport is always at 0,0 as that defined the coordinate system for
3808         // this function - this avoids special case checks in the code below
3809         return pos;
3810     }
3811
3812     // Gecko browsers normally use getBoxObjectFor to calculate the position.
3813     // When invoked for an element with an implicit absolute position though it
3814     // can be off by one. Therefore the recursive implementation is used in
3815     // those (relatively rare) cases.
3816     var BUGGY_GECKO_BOX_OBJECT =
3817         OpenLayers.IS_GECKO && document.getBoxObjectFor &&
3818         OpenLayers.Element.getStyle(forElement, 'position') == 'absolute' &&
3819         (forElement.style.top == '' || forElement.style.left == '');
3820
3821     var parent = null;
3822     var box;
3823
3824     if (forElement.getBoundingClientRect) { // IE
3825         box = forElement.getBoundingClientRect();
3826         var scrollTop = window.pageYOffset || viewportElement.scrollTop;
3827         var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;
3828         
3829         pos[0] = box.left + scrollLeft;
3830         pos[1] = box.top + scrollTop;
3831
3832     } else if (document.getBoxObjectFor && !BUGGY_GECKO_BOX_OBJECT) { // gecko
3833         // Gecko ignores the scroll values for ancestors, up to 1.9.  See:
3834         // https://bugzilla.mozilla.org/show_bug.cgi?id=328881 and
3835         // https://bugzilla.mozilla.org/show_bug.cgi?id=330619
3836
3837         box = document.getBoxObjectFor(forElement);
3838         var vpBox = document.getBoxObjectFor(viewportElement);
3839         pos[0] = box.screenX - vpBox.screenX;
3840         pos[1] = box.screenY - vpBox.screenY;
3841
3842     } else { // safari/opera
3843         pos[0] = forElement.offsetLeft;
3844         pos[1] = forElement.offsetTop;
3845         parent = forElement.offsetParent;
3846         if (parent != forElement) {
3847             while (parent) {
3848                 pos[0] += parent.offsetLeft;
3849                 pos[1] += parent.offsetTop;
3850                 parent = parent.offsetParent;
3851             }
3852         }
3853
3854         var browser = OpenLayers.BROWSER_NAME;
3855
3856         // opera & (safari absolute) incorrectly account for body offsetTop
3857         if (browser == "opera" || (browser == "safari" &&
3858               OpenLayers.Element.getStyle(forElement, 'position') == 'absolute')) {
3859             pos[1] -= document.body.offsetTop;
3860         }
3861
3862         // accumulate the scroll positions for everything but the body element
3863         parent = forElement.offsetParent;
3864         while (parent && parent != document.body) {
3865             pos[0] -= parent.scrollLeft;
3866             // see https://bugs.opera.com/show_bug.cgi?id=249965
3867             if (browser != "opera" || parent.tagName != 'TR') {
3868                 pos[1] -= parent.scrollTop;
3869             }
3870             parent = parent.offsetParent;
3871         }
3872     }
3873     
3874     return pos;
3875 };
3876
3877 /**
3878  * Function: getViewportElement
3879  * Returns die viewport element of the document. The viewport element is
3880  * usually document.documentElement, except in IE,where it is either
3881  * document.body or document.documentElement, depending on the document's
3882  * compatibility mode (see
3883  * http://code.google.com/p/doctype/wiki/ArticleClientViewportElement)
3884  *
3885  * Returns:
3886  * {DOMElement}
3887  */
3888 OpenLayers.Util.getViewportElement = function() {
3889     var viewportElement = arguments.callee.viewportElement;
3890     if (viewportElement == undefined) {
3891         viewportElement = (OpenLayers.BROWSER_NAME == "msie" &&
3892             document.compatMode != 'CSS1Compat') ? document.body :
3893             document.documentElement;
3894         arguments.callee.viewportElement = viewportElement;
3895     }
3896     return viewportElement;
3897 };
3898
3899 /** 
3900  * Function: isEquivalentUrl
3901  * Test two URLs for equivalence. 
3902  * 
3903  * Setting 'ignoreCase' allows for case-independent comparison.
3904  * 
3905  * Comparison is based on: 
3906  *  - Protocol
3907  *  - Host (evaluated without the port)
3908  *  - Port (set 'ignorePort80' to ignore "80" values)
3909  *  - Hash ( set 'ignoreHash' to disable)
3910  *  - Pathname (for relative <-> absolute comparison) 
3911  *  - Arguments (so they can be out of order)
3912  *  
3913  * Parameters:
3914  * url1 - {String}
3915  * url2 - {String}
3916  * options - {Object} Allows for customization of comparison:
3917  *                    'ignoreCase' - Default is True
3918  *                    'ignorePort80' - Default is True
3919  *                    'ignoreHash' - Default is True
3920  *
3921  * Returns:
3922  * {Boolean} Whether or not the two URLs are equivalent
3923  */
3924 OpenLayers.Util.isEquivalentUrl = function(url1, url2, options) {
3925     options = options || {};
3926
3927     OpenLayers.Util.applyDefaults(options, {
3928         ignoreCase: true,
3929         ignorePort80: true,
3930         ignoreHash: true,
3931         splitArgs: false
3932     });
3933
3934     var urlObj1 = OpenLayers.Util.createUrlObject(url1, options);
3935     var urlObj2 = OpenLayers.Util.createUrlObject(url2, options);
3936
3937     //compare all keys except for "args" (treated below)
3938     for(var key in urlObj1) {
3939         if(key !== "args") {
3940             if(urlObj1[key] != urlObj2[key]) {
3941                 return false;
3942             }
3943         }
3944     }
3945
3946     // compare search args - irrespective of order
3947     for(var key in urlObj1.args) {
3948         if(urlObj1.args[key] != urlObj2.args[key]) {
3949             return false;
3950         }
3951         delete urlObj2.args[key];
3952     }
3953     // urlObj2 shouldn't have any args left
3954     for(var key in urlObj2.args) {
3955         return false;
3956     }
3957     
3958     return true;
3959 };
3960
3961 /**
3962  * Function: createUrlObject
3963  * 
3964  * Parameters:
3965  * url - {String}
3966  * options - {Object} A hash of options.
3967  *
3968  * Valid options:
3969  *   ignoreCase - {Boolean} lowercase url,
3970  *   ignorePort80 - {Boolean} don't include explicit port if port is 80,
3971  *   ignoreHash - {Boolean} Don't include part of url after the hash (#).
3972  *   splitArgs - {Boolean} Split comma delimited params into arrays? Default is
3973  *       true.
3974  * 
3975  * Returns:
3976  * {Object} An object with separate url, a, port, host, and args parsed out 
3977  *          and ready for comparison
3978  */
3979 OpenLayers.Util.createUrlObject = function(url, options) {
3980     options = options || {};
3981
3982     // deal with relative urls first
3983     if(!(/^\w+:\/\//).test(url)) {
3984         var loc = window.location;
3985         var port = loc.port ? ":" + loc.port : "";
3986         var fullUrl = loc.protocol + "//" + loc.host.split(":").shift() + port;
3987         if(url.indexOf("/") === 0) {
3988             // full pathname
3989             url = fullUrl + url;
3990         } else {
3991             // relative to current path
3992             var parts = loc.pathname.split("/");
3993             parts.pop();
3994             url = fullUrl + parts.join("/") + "/" + url;
3995         }
3996     }
3997   
3998     if (options.ignoreCase) {
3999         url = url.toLowerCase(); 
4000     }
4001
4002     var a = document.createElement('a');
4003     a.href = url;
4004     
4005     var urlObject = {};
4006     
4007     //host (without port)
4008     urlObject.host = a.host.split(":").shift();
4009
4010     //protocol
4011     urlObject.protocol = a.protocol;  
4012
4013     //port (get uniform browser behavior with port 80 here)
4014     if(options.ignorePort80) {
4015         urlObject.port = (a.port == "80" || a.port == "0") ? "" : a.port;
4016     } else {
4017         urlObject.port = (a.port == "" || a.port == "0") ? "80" : a.port;
4018     }
4019
4020     //hash
4021     urlObject.hash = (options.ignoreHash || a.hash === "#") ? "" : a.hash;  
4022     
4023     //args
4024     var queryString = a.search;
4025     if (!queryString) {
4026         var qMark = url.indexOf("?");
4027         queryString = (qMark != -1) ? url.substr(qMark) : "";
4028     }
4029     urlObject.args = OpenLayers.Util.getParameters(queryString,
4030             {splitArgs: options.splitArgs});
4031
4032     // pathname
4033     //
4034     // This is a workaround for Internet Explorer where
4035     // window.location.pathname has a leading "/", but
4036     // a.pathname has no leading "/".
4037     urlObject.pathname = (a.pathname.charAt(0) == "/") ? a.pathname : "/" + a.pathname;
4038     
4039     return urlObject; 
4040 };
4041  
4042 /**
4043  * Function: removeTail
4044  * Takes a url and removes everything after the ? and #
4045  * 
4046  * Parameters:
4047  * url - {String} The url to process
4048  * 
4049  * Returns:
4050  * {String} The string with all queryString and Hash removed
4051  */
4052 OpenLayers.Util.removeTail = function(url) {
4053     var head = null;
4054     
4055     var qMark = url.indexOf("?");
4056     var hashMark = url.indexOf("#");
4057
4058     if (qMark == -1) {
4059         head = (hashMark != -1) ? url.substr(0,hashMark) : url;
4060     } else {
4061         head = (hashMark != -1) ? url.substr(0,Math.min(qMark, hashMark)) 
4062                                   : url.substr(0, qMark);
4063     }
4064     return head;
4065 };
4066
4067 /**
4068  * Constant: IS_GECKO
4069  * {Boolean} True if the userAgent reports the browser to use the Gecko engine
4070  */
4071 OpenLayers.IS_GECKO = (function() {
4072     var ua = navigator.userAgent.toLowerCase();
4073     return ua.indexOf("webkit") == -1 && ua.indexOf("gecko") != -1;
4074 })();
4075
4076 /**
4077  * Constant: CANVAS_SUPPORTED
4078  * {Boolean} True if canvas 2d is supported.
4079  */
4080 OpenLayers.CANVAS_SUPPORTED = (function() {
4081     var elem = document.createElement('canvas');
4082     return !!(elem.getContext && elem.getContext('2d'));
4083 })();
4084
4085 /**
4086  * Constant: BROWSER_NAME
4087  * {String}
4088  * A substring of the navigator.userAgent property.  Depending on the userAgent
4089  *     property, this will be the empty string or one of the following:
4090  *     * "opera" -- Opera
4091  *     * "msie"  -- Internet Explorer
4092  *     * "safari" -- Safari
4093  *     * "firefox" -- Firefox
4094  *     * "mozilla" -- Mozilla
4095  */
4096 OpenLayers.BROWSER_NAME = (function() {
4097     var name = "";
4098     var ua = navigator.userAgent.toLowerCase();
4099     if (ua.indexOf("opera") != -1) {
4100         name = "opera";
4101     } else if (ua.indexOf("msie") != -1) {
4102         name = "msie";
4103     } else if (ua.indexOf("safari") != -1) {
4104         name = "safari";
4105     } else if (ua.indexOf("mozilla") != -1) {
4106         if (ua.indexOf("firefox") != -1) {
4107             name = "firefox";
4108         } else {
4109             name = "mozilla";
4110         }
4111     }
4112     return name;
4113 })();
4114
4115 /**
4116  * Function: getBrowserName
4117  * 
4118  * Returns:
4119  * {String} A string which specifies which is the current 
4120  *          browser in which we are running. 
4121  * 
4122  *          Currently-supported browser detection and codes:
4123  *           * 'opera' -- Opera
4124  *           * 'msie'  -- Internet Explorer
4125  *           * 'safari' -- Safari
4126  *           * 'firefox' -- Firefox
4127  *           * 'mozilla' -- Mozilla
4128  * 
4129  *          If we are unable to property identify the browser, we 
4130  *           return an empty string.
4131  */
4132 OpenLayers.Util.getBrowserName = function() {
4133     return OpenLayers.BROWSER_NAME;
4134 };
4135
4136 /**
4137  * Method: getRenderedDimensions
4138  * Renders the contentHTML offscreen to determine actual dimensions for
4139  *     popup sizing. As we need layout to determine dimensions the content
4140  *     is rendered -9999px to the left and absolute to ensure the 
4141  *     scrollbars do not flicker
4142  *     
4143  * Parameters:
4144  * contentHTML
4145  * size - {<OpenLayers.Size>} If either the 'w' or 'h' properties is 
4146  *     specified, we fix that dimension of the div to be measured. This is 
4147  *     useful in the case where we have a limit in one dimension and must 
4148  *     therefore meaure the flow in the other dimension.
4149  * options - {Object}
4150  *
4151  * Allowed Options:
4152  *     displayClass - {String} Optional parameter.  A CSS class name(s) string
4153  *         to provide the CSS context of the rendered content.
4154  *     containerElement - {DOMElement} Optional parameter. Insert the HTML to 
4155  *         this node instead of the body root when calculating dimensions. 
4156  * 
4157  * Returns:
4158  * {<OpenLayers.Size>}
4159  */
4160 OpenLayers.Util.getRenderedDimensions = function(contentHTML, size, options) {
4161     
4162     var w, h;
4163     
4164     // create temp container div with restricted size
4165     var container = document.createElement("div");
4166     container.style.visibility = "hidden";
4167         
4168     var containerElement = (options && options.containerElement) 
4169         ? options.containerElement : document.body;
4170     
4171     // Opera and IE7 can't handle a node with position:aboslute if it inherits
4172     // position:absolute from a parent.
4173     var parentHasPositionAbsolute = false;
4174     var superContainer = null;
4175     var parent = containerElement;
4176     while (parent && parent.tagName.toLowerCase()!="body") {
4177         var parentPosition = OpenLayers.Element.getStyle(parent, "position");
4178         if(parentPosition == "absolute") {
4179             parentHasPositionAbsolute = true;
4180             break;
4181         } else if (parentPosition && parentPosition != "static") {
4182             break;
4183         }
4184         parent = parent.parentNode;
4185     }
4186     if(parentHasPositionAbsolute && (containerElement.clientHeight === 0 || 
4187                                      containerElement.clientWidth === 0) ){
4188         superContainer = document.createElement("div");
4189         superContainer.style.visibility = "hidden";
4190         superContainer.style.position = "absolute";
4191         superContainer.style.overflow = "visible";
4192         superContainer.style.width = document.body.clientWidth + "px";
4193         superContainer.style.height = document.body.clientHeight + "px";
4194         superContainer.appendChild(container);
4195     }
4196     container.style.position = "absolute";
4197
4198     //fix a dimension, if specified.
4199     if (size) {
4200         if (size.w) {
4201             w = size.w;
4202             container.style.width = w + "px";
4203         } else if (size.h) {
4204             h = size.h;
4205             container.style.height = h + "px";
4206         }
4207     }
4208
4209     //add css classes, if specified
4210     if (options && options.displayClass) {
4211         container.className = options.displayClass;
4212     }
4213     
4214     // create temp content div and assign content
4215     var content = document.createElement("div");
4216     content.innerHTML = contentHTML;
4217     
4218     // we need overflow visible when calculating the size
4219     content.style.overflow = "visible";
4220     if (content.childNodes) {
4221         for (var i=0, l=content.childNodes.length; i<l; i++) {
4222             if (!content.childNodes[i].style) continue;
4223             content.childNodes[i].style.overflow = "visible";
4224         }
4225     }
4226     
4227     // add content to restricted container 
4228     container.appendChild(content);
4229     
4230     // append container to body for rendering
4231     if (superContainer) {
4232         containerElement.appendChild(superContainer);
4233     } else {
4234         containerElement.appendChild(container);
4235     }
4236     
4237     // calculate scroll width of content and add corners and shadow width
4238     if (!w) {
4239         w = parseInt(content.scrollWidth);
4240     
4241         // update container width to allow height to adjust
4242         container.style.width = w + "px";
4243     }        
4244     // capture height and add shadow and corner image widths
4245     if (!h) {
4246         h = parseInt(content.scrollHeight);
4247     }
4248
4249     // remove elements
4250     container.removeChild(content);
4251     if (superContainer) {
4252         superContainer.removeChild(container);
4253         containerElement.removeChild(superContainer);
4254     } else {
4255         containerElement.removeChild(container);
4256     }
4257     
4258     return new OpenLayers.Size(w, h);
4259 };
4260
4261 /**
4262  * APIFunction: getScrollbarWidth
4263  * This function has been modified by the OpenLayers from the original version,
4264  *     written by Matthew Eernisse and released under the Apache 2 
4265  *     license here:
4266  * 
4267  *     http://www.fleegix.org/articles/2006/05/30/getting-the-scrollbar-width-in-pixels
4268  * 
4269  *     It has been modified simply to cache its value, since it is physically 
4270  *     impossible that this code could ever run in more than one browser at 
4271  *     once. 
4272  * 
4273  * Returns:
4274  * {Integer}
4275  */
4276 OpenLayers.Util.getScrollbarWidth = function() {
4277     
4278     var scrollbarWidth = OpenLayers.Util._scrollbarWidth;
4279     
4280     if (scrollbarWidth == null) {
4281         var scr = null;
4282         var inn = null;
4283         var wNoScroll = 0;
4284         var wScroll = 0;
4285     
4286         // Outer scrolling div
4287         scr = document.createElement('div');
4288         scr.style.position = 'absolute';
4289         scr.style.top = '-1000px';
4290         scr.style.left = '-1000px';
4291         scr.style.width = '100px';
4292         scr.style.height = '50px';
4293         // Start with no scrollbar
4294         scr.style.overflow = 'hidden';
4295     
4296         // Inner content div
4297         inn = document.createElement('div');
4298         inn.style.width = '100%';
4299         inn.style.height = '200px';
4300     
4301         // Put the inner div in the scrolling div
4302         scr.appendChild(inn);
4303         // Append the scrolling div to the doc
4304         document.body.appendChild(scr);
4305     
4306         // Width of the inner div sans scrollbar
4307         wNoScroll = inn.offsetWidth;
4308     
4309         // Add the scrollbar
4310         scr.style.overflow = 'scroll';
4311         // Width of the inner div width scrollbar
4312         wScroll = inn.offsetWidth;
4313     
4314         // Remove the scrolling div from the doc
4315         document.body.removeChild(document.body.lastChild);
4316     
4317         // Pixel width of the scroller
4318         OpenLayers.Util._scrollbarWidth = (wNoScroll - wScroll);
4319         scrollbarWidth = OpenLayers.Util._scrollbarWidth;
4320     }
4321
4322     return scrollbarWidth;
4323 };
4324
4325 /**
4326  * APIFunction: getFormattedLonLat
4327  * This function will return latitude or longitude value formatted as 
4328  *
4329  * Parameters:
4330  * coordinate - {Float} the coordinate value to be formatted
4331  * axis - {String} value of either 'lat' or 'lon' to indicate which axis is to
4332  *          to be formatted (default = lat)
4333  * dmsOption - {String} specify the precision of the output can be one of:
4334  *           'dms' show degrees minutes and seconds
4335  *           'dm' show only degrees and minutes
4336  *           'd' show only degrees
4337  * 
4338  * Returns:
4339  * {String} the coordinate value formatted as a string
4340  */
4341 OpenLayers.Util.getFormattedLonLat = function(coordinate, axis, dmsOption) {
4342     if (!dmsOption) {
4343         dmsOption = 'dms';    //default to show degree, minutes, seconds
4344     }
4345
4346     coordinate = (coordinate+540)%360 - 180; // normalize for sphere being round
4347
4348     var abscoordinate = Math.abs(coordinate);
4349     var coordinatedegrees = Math.floor(abscoordinate);
4350
4351     var coordinateminutes = (abscoordinate - coordinatedegrees)/(1/60);
4352     var tempcoordinateminutes = coordinateminutes;
4353     coordinateminutes = Math.floor(coordinateminutes);
4354     var coordinateseconds = (tempcoordinateminutes - coordinateminutes)/(1/60);
4355     coordinateseconds =  Math.round(coordinateseconds*10);
4356     coordinateseconds /= 10;
4357
4358     if( coordinateseconds >= 60) { 
4359         coordinateseconds -= 60; 
4360         coordinateminutes += 1; 
4361         if( coordinateminutes >= 60) { 
4362             coordinateminutes -= 60; 
4363             coordinatedegrees += 1; 
4364         } 
4365     }
4366     
4367     if( coordinatedegrees < 10 ) {
4368         coordinatedegrees = "0" + coordinatedegrees;
4369     }
4370     var str = coordinatedegrees + "\u00B0";
4371
4372     if (dmsOption.indexOf('dm') >= 0) {
4373         if( coordinateminutes < 10 ) {
4374             coordinateminutes = "0" + coordinateminutes;
4375         }
4376         str += coordinateminutes + "'";
4377   
4378         if (dmsOption.indexOf('dms') >= 0) {
4379             if( coordinateseconds < 10 ) {
4380                 coordinateseconds = "0" + coordinateseconds;
4381             }
4382             str += coordinateseconds + '"';
4383         }
4384     }
4385     
4386     if (axis == "lon") {
4387         str += coordinate < 0 ? OpenLayers.i18n("W") : OpenLayers.i18n("E");
4388     } else {
4389         str += coordinate < 0 ? OpenLayers.i18n("S") : OpenLayers.i18n("N");
4390     }
4391     return str;
4392 };
4393
4394 /* ======================================================================
4395     OpenLayers/Events.js
4396    ====================================================================== */
4397
4398 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
4399  * full list of contributors). Published under the 2-clause BSD license.
4400  * See license.txt in the OpenLayers distribution or repository for the
4401  * full text of the license. */
4402
4403
4404 /**
4405  * @requires OpenLayers/Util.js
4406  */
4407
4408 /**
4409  * Namespace: OpenLayers.Event
4410  * Utility functions for event handling.
4411  */
4412 OpenLayers.Event = {
4413
4414     /** 
4415      * Property: observers 
4416      * {Object} A hashtable cache of the event observers. Keyed by
4417      * element._eventCacheID 
4418      */
4419     observers: false,
4420
4421     /**
4422      * Constant: KEY_SPACE
4423      * {int}
4424      */
4425     KEY_SPACE: 32,
4426     
4427     /** 
4428      * Constant: KEY_BACKSPACE 
4429      * {int} 
4430      */
4431     KEY_BACKSPACE: 8,
4432
4433     /** 
4434      * Constant: KEY_TAB 
4435      * {int} 
4436      */
4437     KEY_TAB: 9,
4438
4439     /** 
4440      * Constant: KEY_RETURN 
4441      * {int} 
4442      */
4443     KEY_RETURN: 13,
4444
4445     /** 
4446      * Constant: KEY_ESC 
4447      * {int} 
4448      */
4449     KEY_ESC: 27,
4450
4451     /** 
4452      * Constant: KEY_LEFT 
4453      * {int} 
4454      */
4455     KEY_LEFT: 37,
4456
4457     /** 
4458      * Constant: KEY_UP 
4459      * {int} 
4460      */
4461     KEY_UP: 38,
4462
4463     /** 
4464      * Constant: KEY_RIGHT 
4465      * {int} 
4466      */
4467     KEY_RIGHT: 39,
4468
4469     /** 
4470      * Constant: KEY_DOWN 
4471      * {int} 
4472      */
4473     KEY_DOWN: 40,
4474
4475     /** 
4476      * Constant: KEY_DELETE 
4477      * {int} 
4478      */
4479     KEY_DELETE: 46,
4480
4481
4482     /**
4483      * Method: element
4484      * Cross browser event element detection.
4485      * 
4486      * Parameters:
4487      * event - {Event} 
4488      * 
4489      * Returns:
4490      * {DOMElement} The element that caused the event 
4491      */
4492     element: function(event) {
4493         return event.target || event.srcElement;
4494     },
4495
4496     /**
4497      * Method: isSingleTouch
4498      * Determine whether event was caused by a single touch
4499      *
4500      * Parameters:
4501      * event - {Event}
4502      *
4503      * Returns:
4504      * {Boolean}
4505      */
4506     isSingleTouch: function(event) {
4507         return event.touches && event.touches.length == 1;
4508     },
4509
4510     /**
4511      * Method: isMultiTouch
4512      * Determine whether event was caused by a multi touch
4513      *
4514      * Parameters:
4515      * event - {Event}
4516      *
4517      * Returns:
4518      * {Boolean}
4519      */
4520     isMultiTouch: function(event) {
4521         return event.touches && event.touches.length > 1;
4522     },
4523
4524     /**
4525      * Method: isLeftClick
4526      * Determine whether event was caused by a left click. 
4527      *
4528      * Parameters:
4529      * event - {Event} 
4530      * 
4531      * Returns:
4532      * {Boolean}
4533      */
4534     isLeftClick: function(event) {
4535         return (((event.which) && (event.which == 1)) ||
4536                 ((event.button) && (event.button == 1)));
4537     },
4538
4539     /**
4540      * Method: isRightClick
4541      * Determine whether event was caused by a right mouse click. 
4542      *
4543      * Parameters:
4544      * event - {Event} 
4545      * 
4546      * Returns:
4547      * {Boolean}
4548      */
4549      isRightClick: function(event) {
4550         return (((event.which) && (event.which == 3)) ||
4551                 ((event.button) && (event.button == 2)));
4552     },
4553      
4554     /**
4555      * Method: stop
4556      * Stops an event from propagating. 
4557      *
4558      * Parameters: 
4559      * event - {Event} 
4560      * allowDefault - {Boolean} If true, we stop the event chain but 
4561      *     still allow the default browser behaviour (text selection,
4562      *     radio-button clicking, etc).  Default is false.
4563      */
4564     stop: function(event, allowDefault) {
4565         
4566         if (!allowDefault) { 
4567             OpenLayers.Event.preventDefault(event);
4568         }
4569                 
4570         if (event.stopPropagation) {
4571             event.stopPropagation();
4572         } else {
4573             event.cancelBubble = true;
4574         }
4575     },
4576
4577     /**
4578      * Method: preventDefault
4579      * Cancels the event if it is cancelable, without stopping further
4580      * propagation of the event.
4581      *
4582      * Parameters:
4583      * event - {Event}
4584      */
4585     preventDefault: function(event) {
4586         if (event.preventDefault) {
4587             event.preventDefault();
4588         } else {
4589             event.returnValue = false;
4590         }
4591     },
4592
4593     /** 
4594      * Method: findElement
4595      * 
4596      * Parameters:
4597      * event - {Event} 
4598      * tagName - {String} 
4599      * 
4600      * Returns:
4601      * {DOMElement} The first node with the given tagName, starting from the
4602      * node the event was triggered on and traversing the DOM upwards
4603      */
4604     findElement: function(event, tagName) {
4605         var element = OpenLayers.Event.element(event);
4606         while (element.parentNode && (!element.tagName ||
4607               (element.tagName.toUpperCase() != tagName.toUpperCase()))){
4608             element = element.parentNode;
4609         }
4610         return element;
4611     },
4612
4613     /** 
4614      * Method: observe
4615      * 
4616      * Parameters:
4617      * elementParam - {DOMElement || String} 
4618      * name - {String} 
4619      * observer - {function} 
4620      * useCapture - {Boolean} 
4621      */
4622     observe: function(elementParam, name, observer, useCapture) {
4623         var element = OpenLayers.Util.getElement(elementParam);
4624         useCapture = useCapture || false;
4625
4626         if (name == 'keypress' &&
4627            (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
4628            || element.attachEvent)) {
4629             name = 'keydown';
4630         }
4631
4632         //if observers cache has not yet been created, create it
4633         if (!this.observers) {
4634             this.observers = {};
4635         }
4636
4637         //if not already assigned, make a new unique cache ID
4638         if (!element._eventCacheID) {
4639             var idPrefix = "eventCacheID_";
4640             if (element.id) {
4641                 idPrefix = element.id + "_" + idPrefix;
4642             }
4643             element._eventCacheID = OpenLayers.Util.createUniqueID(idPrefix);
4644         }
4645
4646         var cacheID = element._eventCacheID;
4647
4648         //if there is not yet a hash entry for this element, add one
4649         if (!this.observers[cacheID]) {
4650             this.observers[cacheID] = [];
4651         }
4652
4653         //add a new observer to this element's list
4654         this.observers[cacheID].push({
4655             'element': element,
4656             'name': name,
4657             'observer': observer,
4658             'useCapture': useCapture
4659         });
4660
4661         //add the actual browser event listener
4662         if (element.addEventListener) {
4663             element.addEventListener(name, observer, useCapture);
4664         } else if (element.attachEvent) {
4665             element.attachEvent('on' + name, observer);
4666         }
4667     },
4668
4669     /** 
4670      * Method: stopObservingElement
4671      * Given the id of an element to stop observing, cycle through the 
4672      *   element's cached observers, calling stopObserving on each one, 
4673      *   skipping those entries which can no longer be removed.
4674      * 
4675      * parameters:
4676      * elementParam - {DOMElement || String} 
4677      */
4678     stopObservingElement: function(elementParam) {
4679         var element = OpenLayers.Util.getElement(elementParam);
4680         var cacheID = element._eventCacheID;
4681
4682         this._removeElementObservers(OpenLayers.Event.observers[cacheID]);
4683     },
4684
4685     /**
4686      * Method: _removeElementObservers
4687      *
4688      * Parameters:
4689      * elementObservers - {Array(Object)} Array of (element, name, 
4690      *                                         observer, usecapture) objects, 
4691      *                                         taken directly from hashtable
4692      */
4693     _removeElementObservers: function(elementObservers) {
4694         if (elementObservers) {
4695             for(var i = elementObservers.length-1; i >= 0; i--) {
4696                 var entry = elementObservers[i];
4697                 OpenLayers.Event.stopObserving.apply(this, [
4698                     entry.element, entry.name, entry.observer, entry.useCapture
4699                 ]);
4700             }
4701         }
4702     },
4703
4704     /**
4705      * Method: stopObserving
4706      * 
4707      * Parameters:
4708      * elementParam - {DOMElement || String} 
4709      * name - {String} 
4710      * observer - {function} 
4711      * useCapture - {Boolean} 
4712      *  
4713      * Returns:
4714      * {Boolean} Whether or not the event observer was removed
4715      */
4716     stopObserving: function(elementParam, name, observer, useCapture) {
4717         useCapture = useCapture || false;
4718     
4719         var element = OpenLayers.Util.getElement(elementParam);
4720         var cacheID = element._eventCacheID;
4721
4722         if (name == 'keypress') {
4723             if ( navigator.appVersion.match(/Konqueror|Safari|KHTML/) || 
4724                  element.detachEvent) {
4725               name = 'keydown';
4726             }
4727         }
4728
4729         // find element's entry in this.observers cache and remove it
4730         var foundEntry = false;
4731         var elementObservers = OpenLayers.Event.observers[cacheID];
4732         if (elementObservers) {
4733     
4734             // find the specific event type in the element's list
4735             var i=0;
4736             while(!foundEntry && i < elementObservers.length) {
4737                 var cacheEntry = elementObservers[i];
4738     
4739                 if ((cacheEntry.name == name) &&
4740                     (cacheEntry.observer == observer) &&
4741                     (cacheEntry.useCapture == useCapture)) {
4742     
4743                     elementObservers.splice(i, 1);
4744                     if (elementObservers.length == 0) {
4745                         delete OpenLayers.Event.observers[cacheID];
4746                     }
4747                     foundEntry = true;
4748                     break; 
4749                 }
4750                 i++;           
4751             }
4752         }
4753     
4754         //actually remove the event listener from browser
4755         if (foundEntry) {
4756             if (element.removeEventListener) {
4757                 element.removeEventListener(name, observer, useCapture);
4758             } else if (element && element.detachEvent) {
4759                 element.detachEvent('on' + name, observer);
4760             }
4761         }
4762         return foundEntry;
4763     },
4764     
4765     /** 
4766      * Method: unloadCache
4767      * Cycle through all the element entries in the events cache and call
4768      *   stopObservingElement on each. 
4769      */
4770     unloadCache: function() {
4771         // check for OpenLayers.Event before checking for observers, because
4772         // OpenLayers.Event may be undefined in IE if no map instance was
4773         // created
4774         if (OpenLayers.Event && OpenLayers.Event.observers) {
4775             for (var cacheID in OpenLayers.Event.observers) {
4776                 var elementObservers = OpenLayers.Event.observers[cacheID];
4777                 OpenLayers.Event._removeElementObservers.apply(this, 
4778                                                            [elementObservers]);
4779             }
4780             OpenLayers.Event.observers = false;
4781         }
4782     },
4783
4784     CLASS_NAME: "OpenLayers.Event"
4785 };
4786
4787 /* prevent memory leaks in IE */
4788 OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false);
4789
4790 /**
4791  * Class: OpenLayers.Events
4792  */
4793 OpenLayers.Events = OpenLayers.Class({
4794
4795     /** 
4796      * Constant: BROWSER_EVENTS
4797      * {Array(String)} supported events 
4798      */
4799     BROWSER_EVENTS: [
4800         "mouseover", "mouseout",
4801         "mousedown", "mouseup", "mousemove", 
4802         "click", "dblclick", "rightclick", "dblrightclick",
4803         "resize", "focus", "blur",
4804         "touchstart", "touchmove", "touchend",
4805         "keydown"
4806     ],
4807
4808     /** 
4809      * Property: listeners 
4810      * {Object} Hashtable of Array(Function): events listener functions  
4811      */
4812     listeners: null,
4813
4814     /** 
4815      * Property: object 
4816      * {Object}  the code object issuing application events 
4817      */
4818     object: null,
4819
4820     /** 
4821      * Property: element 
4822      * {DOMElement}  the DOM element receiving browser events 
4823      */
4824     element: null,
4825
4826     /** 
4827      * Property: eventHandler 
4828      * {Function}  bound event handler attached to elements 
4829      */
4830     eventHandler: null,
4831
4832     /** 
4833      * APIProperty: fallThrough 
4834      * {Boolean} 
4835      */
4836     fallThrough: null,
4837
4838     /** 
4839      * APIProperty: includeXY
4840      * {Boolean} Should the .xy property automatically be created for browser
4841      *    mouse events? In general, this should be false. If it is true, then
4842      *    mouse events will automatically generate a '.xy' property on the 
4843      *    event object that is passed. (Prior to OpenLayers 2.7, this was true
4844      *    by default.) Otherwise, you can call the getMousePosition on the
4845      *    relevant events handler on the object available via the 'evt.object'
4846      *    property of the evt object. So, for most events, you can call:
4847      *    function named(evt) { 
4848      *        this.xy = this.object.events.getMousePosition(evt) 
4849      *    } 
4850      *
4851      *    This option typically defaults to false for performance reasons:
4852      *    when creating an events object whose primary purpose is to manage
4853      *    relatively positioned mouse events within a div, it may make
4854      *    sense to set it to true.
4855      *
4856      *    This option is also used to control whether the events object caches
4857      *    offsets. If this is false, it will not: the reason for this is that
4858      *    it is only expected to be called many times if the includeXY property
4859      *    is set to true. If you set this to true, you are expected to clear 
4860      *    the offset cache manually (using this.clearMouseCache()) if:
4861      *        the border of the element changes
4862      *        the location of the element in the page changes
4863     */
4864     includeXY: false,      
4865     
4866     /**
4867      * APIProperty: extensions
4868      * {Object} Event extensions registered with this instance. Keys are
4869      *     event types, values are {OpenLayers.Events.*} extension instances or
4870      *     {Boolean} for events that an instantiated extension provides in
4871      *     addition to the one it was created for.
4872      *
4873      * Extensions create an event in addition to browser events, which usually
4874      * fires when a sequence of browser events is completed. Extensions are
4875      * automatically instantiated when a listener is registered for an event
4876      * provided by an extension.
4877      *
4878      * Extensions are created in the <OpenLayers.Events> namespace using
4879      * <OpenLayers.Class>, and named after the event they provide.
4880      * The constructor receives the target <OpenLayers.Events> instance as
4881      * argument. Extensions that need to capture browser events before they
4882      * propagate can register their listeners events using <register>, with
4883      * {extension: true} as 4th argument.
4884      *
4885      * If an extension creates more than one event, an alias for each event
4886      * type should be created and reference the same class. The constructor
4887      * should set a reference in the target's extensions registry to itself.
4888      *
4889      * Below is a minimal extension that provides the "foostart" and "fooend"
4890      * event types, which replace the native "click" event type if clicked on
4891      * an element with the css class "foo":
4892      *
4893      * (code)
4894      *   OpenLayers.Events.foostart = OpenLayers.Class({
4895      *       initialize: function(target) {
4896      *           this.target = target;
4897      *           this.target.register("click", this, this.doStuff, {extension: true});
4898      *           // only required if extension provides more than one event type
4899      *           this.target.extensions["foostart"] = true;
4900      *           this.target.extensions["fooend"] = true;
4901      *       },
4902      *       destroy: function() {
4903      *           var target = this.target;
4904      *           target.unregister("click", this, this.doStuff);
4905      *           delete this.target;
4906      *           // only required if extension provides more than one event type
4907      *           delete target.extensions["foostart"];
4908      *           delete target.extensions["fooend"];
4909      *       },
4910      *       doStuff: function(evt) {
4911      *           var propagate = true;
4912      *           if (OpenLayers.Event.element(evt).className === "foo") {
4913      *               propagate = false;
4914      *               var target = this.target;
4915      *               target.triggerEvent("foostart");
4916      *               window.setTimeout(function() {
4917      *                   target.triggerEvent("fooend");
4918      *               }, 1000);
4919      *           }
4920      *           return propagate;
4921      *       }
4922      *   });
4923      *   // only required if extension provides more than one event type
4924      *   OpenLayers.Events.fooend = OpenLayers.Events.foostart;
4925      * (end)
4926      * 
4927      */
4928     extensions: null,
4929     
4930     /**
4931      * Property: extensionCount
4932      * {Object} Keys are event types (like in <listeners>), values are the
4933      *     number of extension listeners for each event type.
4934      */
4935     extensionCount: null,
4936
4937     /**
4938      * Method: clearMouseListener
4939      * A version of <clearMouseCache> that is bound to this instance so that
4940      *     it can be used with <OpenLayers.Event.observe> and
4941      *     <OpenLayers.Event.stopObserving>.
4942      */
4943     clearMouseListener: null,
4944
4945     /**
4946      * Constructor: OpenLayers.Events
4947      * Construct an OpenLayers.Events object.
4948      *
4949      * Parameters:
4950      * object - {Object} The js object to which this Events object  is being added
4951      * element - {DOMElement} A dom element to respond to browser events
4952      * eventTypes - {Array(String)} Deprecated.  Array of custom application
4953      *     events.  A listener may be registered for any named event, regardless
4954      *     of the values provided here.
4955      * fallThrough - {Boolean} Allow events to fall through after these have
4956      *                         been handled?
4957      * options - {Object} Options for the events object.
4958      */
4959     initialize: function (object, element, eventTypes, fallThrough, options) {
4960         OpenLayers.Util.extend(this, options);
4961         this.object     = object;
4962         this.fallThrough = fallThrough;
4963         this.listeners  = {};
4964         this.extensions = {};
4965         this.extensionCount = {};
4966         this._msTouches = [];
4967         
4968         // if a dom element is specified, add a listeners list 
4969         // for browser events on the element and register them
4970         if (element != null) {
4971             this.attachToElement(element);
4972         }
4973     },
4974
4975     /**
4976      * APIMethod: destroy
4977      */
4978     destroy: function () {
4979         for (var e in this.extensions) {
4980             if (typeof this.extensions[e] !== "boolean") {
4981                 this.extensions[e].destroy();
4982             }
4983         }
4984         this.extensions = null;
4985         if (this.element) {
4986             OpenLayers.Event.stopObservingElement(this.element);
4987             if(this.element.hasScrollEvent) {
4988                 OpenLayers.Event.stopObserving(
4989                     window, "scroll", this.clearMouseListener
4990                 );
4991             }
4992         }
4993         this.element = null;
4994
4995         this.listeners = null;
4996         this.object = null;
4997         this.fallThrough = null;
4998         this.eventHandler = null;
4999     },
5000
5001     /**
5002      * APIMethod: addEventType
5003      * Deprecated.  Any event can be triggered without adding it first.
5004      * 
5005      * Parameters:
5006      * eventName - {String}
5007      */
5008     addEventType: function(eventName) {
5009     },
5010
5011     /**
5012      * Method: attachToElement
5013      *
5014      * Parameters:
5015      * element - {HTMLDOMElement} a DOM element to attach browser events to
5016      */
5017     attachToElement: function (element) {
5018         if (this.element) {
5019             OpenLayers.Event.stopObservingElement(this.element);
5020         } else {
5021             // keep a bound copy of handleBrowserEvent() so that we can
5022             // pass the same function to both Event.observe() and .stopObserving()
5023             this.eventHandler = OpenLayers.Function.bindAsEventListener(
5024                 this.handleBrowserEvent, this
5025             );
5026             
5027             // to be used with observe and stopObserving
5028             this.clearMouseListener = OpenLayers.Function.bind(
5029                 this.clearMouseCache, this
5030             );
5031         }
5032         this.element = element;
5033         var msTouch = !!window.navigator.msMaxTouchPoints;
5034         var type;
5035         for (var i = 0, len = this.BROWSER_EVENTS.length; i < len; i++) {
5036             type = this.BROWSER_EVENTS[i];
5037             // register the event cross-browser
5038             OpenLayers.Event.observe(element, type, this.eventHandler
5039             );
5040             if (msTouch && type.indexOf('touch') === 0) {
5041                 this.addMsTouchListener(element, type, this.eventHandler);
5042             }
5043         }
5044         // disable dragstart in IE so that mousedown/move/up works normally
5045         OpenLayers.Event.observe(element, "dragstart", OpenLayers.Event.stop);
5046     },
5047     
5048     /**
5049      * APIMethod: on
5050      * Convenience method for registering listeners with a common scope.
5051      *     Internally, this method calls <register> as shown in the examples
5052      *     below.
5053      *
5054      * Example use:
5055      * (code)
5056      * // register a single listener for the "loadstart" event
5057      * events.on({"loadstart": loadStartListener});
5058      *
5059      * // this is equivalent to the following
5060      * events.register("loadstart", undefined, loadStartListener);
5061      *
5062      * // register multiple listeners to be called with the same `this` object
5063      * events.on({
5064      *     "loadstart": loadStartListener,
5065      *     "loadend": loadEndListener,
5066      *     scope: object
5067      * });
5068      *
5069      * // this is equivalent to the following
5070      * events.register("loadstart", object, loadStartListener);
5071      * events.register("loadend", object, loadEndListener);
5072      * (end)
5073      *
5074      * Parameters:
5075      *  object - {Object}     
5076      */
5077     on: function(object) {
5078         for(var type in object) {
5079             if(type != "scope" && object.hasOwnProperty(type)) {
5080                 this.register(type, object.scope, object[type]);
5081             }
5082         }
5083     },
5084
5085     /**
5086      * APIMethod: register
5087      * Register an event on the events object.
5088      *
5089      * When the event is triggered, the 'func' function will be called, in the
5090      * context of 'obj'. Imagine we were to register an event, specifying an 
5091      * OpenLayers.Bounds Object as 'obj'. When the event is triggered, the 
5092      * context in the callback function will be our Bounds object. This means
5093      * that within our callback function, we can access the properties and 
5094      * methods of the Bounds object through the "this" variable. So our 
5095      * callback could execute something like: 
5096      * :    leftStr = "Left: " + this.left;
5097      *   
5098      *                   or
5099      *  
5100      * :    centerStr = "Center: " + this.getCenterLonLat();
5101      *
5102      * Parameters:
5103      * type - {String} Name of the event to register
5104      * obj - {Object} The object to bind the context to for the callback#.
5105      *     If no object is specified, default is the Events's 'object' property.
5106      * func - {Function} The callback function. If no callback is 
5107      *     specified, this function does nothing.
5108      * priority - {Boolean|Object} If true, adds the new listener to the
5109      *     *front* of the events queue instead of to the end.
5110      *
5111      * Valid options for priority:
5112      * extension - {Boolean} If true, then the event will be registered as
5113      *     extension event. Extension events are handled before all other
5114      *     events.
5115      */
5116     register: function (type, obj, func, priority) {
5117         if (type in OpenLayers.Events && !this.extensions[type]) {
5118             this.extensions[type] = new OpenLayers.Events[type](this);
5119         }
5120         if (func != null) {
5121             if (obj == null)  {
5122                 obj = this.object;
5123             }
5124             var listeners = this.listeners[type];
5125             if (!listeners) {
5126                 listeners = [];
5127                 this.listeners[type] = listeners;
5128                 this.extensionCount[type] = 0;
5129             }
5130             var listener = {obj: obj, func: func};
5131             if (priority) {
5132                 listeners.splice(this.extensionCount[type], 0, listener);
5133                 if (typeof priority === "object" && priority.extension) {
5134                     this.extensionCount[type]++;
5135                 }
5136             } else {
5137                 listeners.push(listener);
5138             }
5139         }
5140     },
5141
5142     /**
5143      * APIMethod: registerPriority
5144      * Same as register() but adds the new listener to the *front* of the
5145      *     events queue instead of to the end.
5146      *    
5147      *     TODO: get rid of this in 3.0 - Decide whether listeners should be 
5148      *     called in the order they were registered or in reverse order.
5149      *
5150      *
5151      * Parameters:
5152      * type - {String} Name of the event to register
5153      * obj - {Object} The object to bind the context to for the callback#.
5154      *                If no object is specified, default is the Events's 
5155      *                'object' property.
5156      * func - {Function} The callback function. If no callback is 
5157      *                   specified, this function does nothing.
5158      */
5159     registerPriority: function (type, obj, func) {
5160         this.register(type, obj, func, true);
5161     },
5162     
5163     /**
5164      * APIMethod: un
5165      * Convenience method for unregistering listeners with a common scope.
5166      *     Internally, this method calls <unregister> as shown in the examples
5167      *     below.
5168      *
5169      * Example use:
5170      * (code)
5171      * // unregister a single listener for the "loadstart" event
5172      * events.un({"loadstart": loadStartListener});
5173      *
5174      * // this is equivalent to the following
5175      * events.unregister("loadstart", undefined, loadStartListener);
5176      *
5177      * // unregister multiple listeners with the same `this` object
5178      * events.un({
5179      *     "loadstart": loadStartListener,
5180      *     "loadend": loadEndListener,
5181      *     scope: object
5182      * });
5183      *
5184      * // this is equivalent to the following
5185      * events.unregister("loadstart", object, loadStartListener);
5186      * events.unregister("loadend", object, loadEndListener);
5187      * (end)
5188      */
5189     un: function(object) {
5190         for(var type in object) {
5191             if(type != "scope" && object.hasOwnProperty(type)) {
5192                 this.unregister(type, object.scope, object[type]);
5193             }
5194         }
5195     },
5196
5197     /**
5198      * APIMethod: unregister
5199      *
5200      * Parameters:
5201      * type - {String} 
5202      * obj - {Object} If none specified, defaults to this.object
5203      * func - {Function} 
5204      */
5205     unregister: function (type, obj, func) {
5206         if (obj == null)  {
5207             obj = this.object;
5208         }
5209         var listeners = this.listeners[type];
5210         if (listeners != null) {
5211             for (var i=0, len=listeners.length; i<len; i++) {
5212                 if (listeners[i].obj == obj && listeners[i].func == func) {
5213                     listeners.splice(i, 1);
5214                     break;
5215                 }
5216             }
5217         }
5218     },
5219
5220     /** 
5221      * Method: remove
5222      * Remove all listeners for a given event type. If type is not registered,
5223      *     does nothing.
5224      *
5225      * Parameters:
5226      * type - {String} 
5227      */
5228     remove: function(type) {
5229         if (this.listeners[type] != null) {
5230             this.listeners[type] = [];
5231         }
5232     },
5233
5234     /**
5235      * APIMethod: triggerEvent
5236      * Trigger a specified registered event.  
5237      * 
5238      * Parameters:
5239      * type - {String} 
5240      * evt - {Event || Object} will be passed to the listeners.
5241      *
5242      * Returns:
5243      * {Boolean} The last listener return.  If a listener returns false, the
5244      *     chain of listeners will stop getting called.
5245      */
5246     triggerEvent: function (type, evt) {
5247         var listeners = this.listeners[type];
5248
5249         // fast path
5250         if(!listeners || listeners.length == 0) {
5251             return undefined;
5252         }
5253
5254         // prep evt object with object & div references
5255         if (evt == null) {
5256             evt = {};
5257         }
5258         evt.object = this.object;
5259         evt.element = this.element;
5260         if(!evt.type) {
5261             evt.type = type;
5262         }
5263     
5264         // execute all callbacks registered for specified type
5265         // get a clone of the listeners array to
5266         // allow for splicing during callbacks
5267         listeners = listeners.slice();
5268         var continueChain;
5269         for (var i=0, len=listeners.length; i<len; i++) {
5270             var callback = listeners[i];
5271             // bind the context to callback.obj
5272             continueChain = callback.func.apply(callback.obj, [evt]);
5273
5274             if ((continueChain != undefined) && (continueChain == false)) {
5275                 // if callback returns false, execute no more callbacks.
5276                 break;
5277             }
5278         }
5279         // don't fall through to other DOM elements
5280         if (!this.fallThrough) {           
5281             OpenLayers.Event.stop(evt, true);
5282         }
5283         return continueChain;
5284     },
5285
5286     /**
5287      * Method: handleBrowserEvent
5288      * Basically just a wrapper to the triggerEvent() function, but takes 
5289      *     care to set a property 'xy' on the event with the current mouse 
5290      *     position.
5291      *
5292      * Parameters:
5293      * evt - {Event} 
5294      */
5295     handleBrowserEvent: function (evt) {
5296         var type = evt.type, listeners = this.listeners[type];
5297         if(!listeners || listeners.length == 0) {
5298             // noone's listening, bail out
5299             return;
5300         }
5301         // add clientX & clientY to all events - corresponds to average x, y
5302         var touches = evt.touches;
5303         if (touches && touches[0]) {
5304             var x = 0;
5305             var y = 0;
5306             var num = touches.length;
5307             var touch;
5308             for (var i=0; i<num; ++i) {
5309                 touch = this.getTouchClientXY(touches[i]);
5310                 x += touch.clientX;
5311                 y += touch.clientY;
5312             }
5313             evt.clientX = x / num;
5314             evt.clientY = y / num;
5315         }
5316         if (this.includeXY) {
5317             evt.xy = this.getMousePosition(evt);
5318         } 
5319         this.triggerEvent(type, evt);
5320     },
5321     
5322     /**
5323      * Method: getTouchClientXY
5324      * WebKit has a few bugs for clientX/clientY. This method detects them
5325      * and calculate the correct values.
5326      *
5327      * Parameters:
5328      * evt - {Touch} a Touch object from a TouchEvent
5329      * 
5330      * Returns:
5331      * {Object} An object with only clientX and clientY properties with the
5332      * calculated values.
5333      */
5334     getTouchClientXY: function (evt) {
5335         // olMochWin is to override window, used for testing
5336         var win = window.olMockWin || window,
5337             winPageX = win.pageXOffset,
5338             winPageY = win.pageYOffset,
5339             x = evt.clientX,
5340             y = evt.clientY;
5341         
5342         if (evt.pageY === 0 && Math.floor(y) > Math.floor(evt.pageY) ||
5343             evt.pageX === 0 && Math.floor(x) > Math.floor(evt.pageX)) {
5344             // iOS4 include scroll offset in clientX/Y
5345             x = x - winPageX;
5346             y = y - winPageY;
5347         } else if (y < (evt.pageY - winPageY) || x < (evt.pageX - winPageX) ) {
5348             // Some Android browsers have totally bogus values for clientX/Y
5349             // when scrolling/zooming a page
5350             x = evt.pageX - winPageX;
5351             y = evt.pageY - winPageY;
5352         }
5353         
5354         evt.olClientX = x;
5355         evt.olClientY = y;
5356         
5357         return {
5358             clientX: x,
5359             clientY: y
5360         };
5361     },
5362     
5363     /**
5364      * APIMethod: clearMouseCache
5365      * Clear cached data about the mouse position. This should be called any 
5366      *     time the element that events are registered on changes position 
5367      *     within the page.
5368      */
5369     clearMouseCache: function() { 
5370         this.element.scrolls = null;
5371         this.element.lefttop = null;
5372         this.element.offsets = null;
5373     },      
5374
5375     /**
5376      * Method: getMousePosition
5377      * 
5378      * Parameters:
5379      * evt - {Event} 
5380      * 
5381      * Returns:
5382      * {<OpenLayers.Pixel>} The current xy coordinate of the mouse, adjusted
5383      *                      for offsets
5384      */
5385     getMousePosition: function (evt) {
5386         if (!this.includeXY) {
5387             this.clearMouseCache();
5388         } else if (!this.element.hasScrollEvent) {
5389             OpenLayers.Event.observe(window, "scroll", this.clearMouseListener);
5390             this.element.hasScrollEvent = true;
5391         }
5392         
5393         if (!this.element.scrolls) {
5394             var viewportElement = OpenLayers.Util.getViewportElement();
5395             this.element.scrolls = [
5396                 window.pageXOffset || viewportElement.scrollLeft,
5397                 window.pageYOffset || viewportElement.scrollTop
5398             ];
5399         }
5400
5401         if (!this.element.lefttop) {
5402             this.element.lefttop = [
5403                 (document.documentElement.clientLeft || 0),
5404                 (document.documentElement.clientTop  || 0)
5405             ];
5406         }
5407         
5408         if (!this.element.offsets) {
5409             this.element.offsets = OpenLayers.Util.pagePosition(this.element);
5410         }
5411
5412         return new OpenLayers.Pixel(
5413             (evt.clientX + this.element.scrolls[0]) - this.element.offsets[0]
5414                          - this.element.lefttop[0], 
5415             (evt.clientY + this.element.scrolls[1]) - this.element.offsets[1]
5416                          - this.element.lefttop[1]
5417         ); 
5418     },
5419
5420     /**
5421      * Method: addMsTouchListener
5422      *
5423      * Parameters:
5424      * element - {DOMElement} The DOM element to register the listener on
5425      * type - {String} The event type
5426      * handler - {Function} the handler
5427      */
5428     addMsTouchListener: function (element, type, handler) {
5429         var eventHandler = this.eventHandler;
5430         var touches = this._msTouches;
5431
5432         function msHandler(evt) {
5433             handler(OpenLayers.Util.applyDefaults({
5434                 stopPropagation: function() {
5435                     for (var i=touches.length-1; i>=0; --i) {
5436                         touches[i].stopPropagation();
5437                     }
5438                 },
5439                 preventDefault: function() {
5440                     for (var i=touches.length-1; i>=0; --i) {
5441                         touches[i].preventDefault();
5442                     }
5443                 },
5444                 type: type
5445             }, evt));
5446         }
5447
5448         switch (type) {
5449             case 'touchstart':
5450                 return this.addMsTouchListenerStart(element, type, msHandler);
5451             case 'touchend':
5452                 return this.addMsTouchListenerEnd(element, type, msHandler);
5453             case 'touchmove':
5454                 return this.addMsTouchListenerMove(element, type, msHandler);
5455             default:
5456                 throw 'Unknown touch event type';
5457         }
5458     },
5459
5460     /**
5461      * Method: addMsTouchListenerStart
5462      *
5463      * Parameters:
5464      * element - {DOMElement} The DOM element to register the listener on
5465      * type - {String} The event type
5466      * handler - {Function} the handler
5467      */
5468     addMsTouchListenerStart: function(element, type, handler) {
5469         var touches = this._msTouches;
5470
5471         var cb = function(e) {
5472
5473             var alreadyInArray = false;
5474             for (var i=0, ii=touches.length; i<ii; ++i) {
5475                 if (touches[i].pointerId == e.pointerId) {
5476                     alreadyInArray = true;
5477                     break;
5478                 }
5479             }
5480             if (!alreadyInArray) {
5481                 touches.push(e);
5482             }
5483
5484             e.touches = touches.slice();
5485             handler(e);
5486         };
5487
5488         OpenLayers.Event.observe(element, 'MSPointerDown', cb);
5489
5490         // Need to also listen for end events to keep the _msTouches list
5491         // accurate
5492         var internalCb = function(e) {
5493             for (var i=0, ii=touches.length; i<ii; ++i) {
5494                 if (touches[i].pointerId == e.pointerId) {
5495                     touches.splice(i, 1);
5496                     break;
5497                 }
5498             }
5499         };
5500         OpenLayers.Event.observe(element, 'MSPointerUp', internalCb);
5501     },
5502
5503     /**
5504      * Method: addMsTouchListenerMove
5505      *
5506      * Parameters:
5507      * element - {DOMElement} The DOM element to register the listener on
5508      * type - {String} The event type
5509      * handler - {Function} the handler
5510      */
5511     addMsTouchListenerMove: function (element, type, handler) {
5512         var touches = this._msTouches;
5513         var cb = function(e) {
5514
5515             //Don't fire touch moves when mouse isn't down
5516             if (e.pointerType == e.MSPOINTER_TYPE_MOUSE && e.buttons == 0) {
5517                 return;
5518             }
5519
5520             if (touches.length == 1 && touches[0].pageX == e.pageX &&
5521                     touches[0].pageY == e.pageY) {
5522                 // don't trigger event when pointer has not moved
5523                 return;
5524             }
5525             for (var i=0, ii=touches.length; i<ii; ++i) {
5526                 if (touches[i].pointerId == e.pointerId) {
5527                     touches[i] = e;
5528                     break;
5529                 }
5530             }
5531
5532             e.touches = touches.slice();
5533             handler(e);
5534         };
5535
5536         OpenLayers.Event.observe(element, 'MSPointerMove', cb);
5537     },
5538
5539     /**
5540      * Method: addMsTouchListenerEnd
5541      *
5542      * Parameters:
5543      * element - {DOMElement} The DOM element to register the listener on
5544      * type - {String} The event type
5545      * handler - {Function} the handler
5546      */
5547     addMsTouchListenerEnd: function (element, type, handler) {
5548         var touches = this._msTouches;
5549
5550         var cb = function(e) {
5551
5552             for (var i=0, ii=touches.length; i<ii; ++i) {
5553                 if (touches[i].pointerId == e.pointerId) {
5554                     touches.splice(i, 1);
5555                     break;
5556                 }
5557             }
5558             
5559             e.touches = touches.slice();
5560             handler(e);
5561         };
5562
5563         OpenLayers.Event.observe(element, 'MSPointerUp', cb);
5564     },
5565
5566     CLASS_NAME: "OpenLayers.Events"
5567 });
5568 /* ======================================================================
5569     OpenLayers/Events/buttonclick.js
5570    ====================================================================== */
5571
5572 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
5573  * full list of contributors). Published under the 2-clause BSD license.
5574  * See license.txt in the OpenLayers distribution or repository for the
5575  * full text of the license. */
5576
5577 /**
5578  * @requires OpenLayers/Events.js
5579  */
5580
5581 /**
5582  * Class: OpenLayers.Events.buttonclick
5583  * Extension event type for handling buttons on top of a dom element. This
5584  *     event type fires "buttonclick" on its <target> when a button was
5585  *     clicked. Buttons are detected by the "olButton" class.
5586  *
5587  * This event type makes sure that button clicks do not interfere with other
5588  *     events that are registered on the same <element>.
5589  *
5590  * Event types provided by this extension:
5591  * - *buttonclick* Triggered when a button is clicked. Listeners receive an
5592  *     object with a *buttonElement* property referencing the dom element of
5593  *     the clicked button, and an *buttonXY* property with the click position
5594  *     relative to the button.
5595  */
5596 OpenLayers.Events.buttonclick = OpenLayers.Class({
5597     
5598     /**
5599      * Property: target
5600      * {<OpenLayers.Events>} The events instance that the buttonclick event will
5601      * be triggered on.
5602      */
5603     target: null,
5604     
5605     /**
5606      * Property: events
5607      * {Array} Events to observe and conditionally stop from propagating when
5608      *     an element with the olButton class (or its olAlphaImg child) is
5609      *     clicked.
5610      */
5611     events: [
5612         'mousedown', 'mouseup', 'click', 'dblclick',
5613         'touchstart', 'touchmove', 'touchend', 'keydown'
5614     ],
5615     
5616     /**
5617      * Property: startRegEx
5618      * {RegExp} Regular expression to test Event.type for events that start
5619      *     a buttonclick sequence.
5620      */
5621     startRegEx: /^mousedown|touchstart$/,
5622
5623     /**
5624      * Property: cancelRegEx
5625      * {RegExp} Regular expression to test Event.type for events that cancel
5626      *     a buttonclick sequence.
5627      */
5628     cancelRegEx: /^touchmove$/,
5629
5630     /**
5631      * Property: completeRegEx
5632      * {RegExp} Regular expression to test Event.type for events that complete
5633      *     a buttonclick sequence.
5634      */
5635     completeRegEx: /^mouseup|touchend$/,
5636     
5637     /**
5638      * Property: startEvt
5639      * {Event} The event that started the click sequence
5640      */
5641     
5642     /**
5643      * Constructor: OpenLayers.Events.buttonclick
5644      * Construct a buttonclick event type. Applications are not supposed to
5645      *     create instances of this class - they are created on demand by
5646      *     <OpenLayers.Events> instances.
5647      *
5648      * Parameters:
5649      * target - {<OpenLayers.Events>} The events instance that the buttonclick
5650      *     event will be triggered on.
5651      */
5652     initialize: function(target) {
5653         this.target = target;
5654         for (var i=this.events.length-1; i>=0; --i) {
5655             this.target.register(this.events[i], this, this.buttonClick, {
5656                 extension: true
5657             });
5658         }
5659     },
5660     
5661     /**
5662      * Method: destroy
5663      */
5664     destroy: function() {
5665         for (var i=this.events.length-1; i>=0; --i) {
5666             this.target.unregister(this.events[i], this, this.buttonClick);
5667         }
5668         delete this.target;
5669     },
5670
5671     /**
5672      * Method: getPressedButton
5673      * Get the pressed button, if any. Returns undefined if no button
5674      * was pressed.
5675      *
5676      * Arguments:
5677      * element - {DOMElement} The event target.
5678      *
5679      * Returns:
5680      * {DOMElement} The button element, or undefined.
5681      */
5682     getPressedButton: function(element) {
5683         var depth = 3, // limit the search depth
5684             button;
5685         do {
5686             if(OpenLayers.Element.hasClass(element, "olButton")) {
5687                 // hit!
5688                 button = element;
5689                 break;
5690             }
5691             element = element.parentNode;
5692         } while(--depth > 0 && element);
5693         return button;
5694     },
5695     
5696     /**
5697      * Method: ignore
5698      * Check for event target elements that should be ignored by OpenLayers.
5699      *
5700      * Parameters:
5701      * element - {DOMElement} The event target.
5702      */
5703     ignore: function(element) {
5704         var depth = 3,
5705             ignore = false;
5706         do {
5707             if (element.nodeName.toLowerCase() === 'a') {
5708                 ignore = true;
5709                 break;
5710             }
5711             element = element.parentNode;
5712         } while (--depth > 0 && element);
5713         return ignore;
5714     },
5715
5716     /**
5717      * Method: buttonClick
5718      * Check if a button was clicked, and fire the buttonclick event
5719      *
5720      * Parameters:
5721      * evt - {Event}
5722      */
5723     buttonClick: function(evt) {
5724         var propagate = true,
5725             element = OpenLayers.Event.element(evt);
5726         if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf("mouse"))) {
5727             // was a button pressed?
5728             var button = this.getPressedButton(element);
5729             if (button) {
5730                 if (evt.type === "keydown") {
5731                     switch (evt.keyCode) {
5732                     case OpenLayers.Event.KEY_RETURN:
5733                     case OpenLayers.Event.KEY_SPACE:
5734                         this.target.triggerEvent("buttonclick", {
5735                             buttonElement: button
5736                         });
5737                         OpenLayers.Event.stop(evt);
5738                         propagate = false;
5739                         break;
5740                     }
5741                 } else if (this.startEvt) {
5742                     if (this.completeRegEx.test(evt.type)) {
5743                         var pos = OpenLayers.Util.pagePosition(button);
5744                         var viewportElement = OpenLayers.Util.getViewportElement();
5745                         var scrollTop = window.pageYOffset || viewportElement.scrollTop;
5746                         var scrollLeft = window.pageXOffset || viewportElement.scrollLeft;
5747                         pos[0] = pos[0] - scrollLeft;
5748                         pos[1] = pos[1] - scrollTop;
5749                         
5750                         this.target.triggerEvent("buttonclick", {
5751                             buttonElement: button,
5752                             buttonXY: {
5753                                 x: this.startEvt.clientX - pos[0],
5754                                 y: this.startEvt.clientY - pos[1]
5755                             }
5756                         });
5757                     }
5758                     if (this.cancelRegEx.test(evt.type)) {
5759                         delete this.startEvt;
5760                     }
5761                     OpenLayers.Event.stop(evt);
5762                     propagate = false;
5763                 }
5764                 if (this.startRegEx.test(evt.type)) {
5765                     this.startEvt = evt;
5766                     OpenLayers.Event.stop(evt);
5767                     propagate = false;
5768                 }
5769             } else {
5770                 propagate = !this.ignore(OpenLayers.Event.element(evt));
5771                 delete this.startEvt;
5772             }
5773         }
5774         return propagate;
5775     }
5776     
5777 });
5778 /* ======================================================================
5779     OpenLayers/Util/vendorPrefix.js
5780    ====================================================================== */
5781
5782 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
5783  * full list of contributors). Published under the 2-clause BSD license.
5784  * See license.txt in the OpenLayers distribution or repository for the
5785  * full text of the license. */
5786
5787 /**
5788  * @requires OpenLayers/SingleFile.js
5789  */
5790
5791 OpenLayers.Util = OpenLayers.Util || {};
5792 /**
5793  * Namespace: OpenLayers.Util.vendorPrefix
5794  * A collection of utility functions to detect vendor prefixed features
5795  */
5796 OpenLayers.Util.vendorPrefix = (function() {
5797     "use strict";
5798     
5799     var VENDOR_PREFIXES = ["", "O", "ms", "Moz", "Webkit"],
5800         divStyle = document.createElement("div").style,
5801         cssCache = {},
5802         jsCache = {};
5803
5804     
5805     /**
5806      * Function: domToCss
5807      * Converts a upper camel case DOM style property name to a CSS property
5808      *      i.e. transformOrigin -> transform-origin
5809      *      or   WebkitTransformOrigin -> -webkit-transform-origin
5810      *
5811      * Parameters:
5812      * prefixedDom - {String} The property to convert
5813      *
5814      * Returns:
5815      * {String} The CSS property
5816      */
5817     function domToCss(prefixedDom) {
5818         if (!prefixedDom) { return null; }
5819         return prefixedDom.
5820             replace(/([A-Z])/g, function(c) { return "-" + c.toLowerCase(); }).
5821             replace(/^ms-/, "-ms-");
5822     }
5823
5824     /**
5825      * APIMethod: css
5826      * Detect which property is used for a CSS property
5827      *
5828      * Parameters:
5829      * property - {String} The standard (unprefixed) CSS property name
5830      *
5831      * Returns:
5832      * {String} The standard CSS property, prefixed property or null if not
5833      *          supported
5834      */
5835     function css(property) {
5836         if (cssCache[property] === undefined) {
5837             var domProperty = property.
5838                 replace(/(-[\s\S])/g, function(c) { return c.charAt(1).toUpperCase(); });
5839             var prefixedDom = style(domProperty);
5840             cssCache[property] = domToCss(prefixedDom);
5841         }
5842         return cssCache[property];
5843     }
5844
5845     /**
5846      * APIMethod: js
5847      * Detect which property is used for a JS property/method
5848      *
5849      * Parameters:
5850      * obj - {Object} The object to test on
5851      * property - {String} The standard (unprefixed) JS property name
5852      *
5853      * Returns:
5854      * {String} The standard JS property, prefixed property or null if not
5855      *          supported
5856      */
5857     function js(obj, property) {
5858         if (jsCache[property] === undefined) {
5859             var tmpProp,
5860                 i = 0,
5861                 l = VENDOR_PREFIXES.length,
5862                 prefix,
5863                 isStyleObj = (typeof obj.cssText !== "undefined");
5864
5865             jsCache[property] = null;
5866             for(; i<l; i++) {
5867                 prefix = VENDOR_PREFIXES[i];
5868                 if(prefix) {
5869                     if (!isStyleObj) {
5870                         // js prefix should be lower-case, while style
5871                         // properties have upper case on first character
5872                         prefix = prefix.toLowerCase();
5873                     }
5874                     tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1);
5875                 } else {
5876                     tmpProp = property;
5877                 }
5878
5879                 if(obj[tmpProp] !== undefined) {
5880                     jsCache[property] = tmpProp;
5881                     break;
5882                 }
5883             }
5884         }
5885         return jsCache[property];
5886     }
5887     
5888     /**
5889      * APIMethod: style
5890      * Detect which property is used for a DOM style property
5891      *
5892      * Parameters:
5893      * property - {String} The standard (unprefixed) style property name
5894      *
5895      * Returns:
5896      * {String} The standard style property, prefixed property or null if not
5897      *          supported
5898      */
5899     function style(property) {
5900         return js(divStyle, property);
5901     }
5902     
5903     return {
5904         css:      css,
5905         js:       js,
5906         style:    style,
5907         
5908         // used for testing
5909         cssCache:       cssCache,
5910         jsCache:        jsCache
5911     };
5912 }());
5913 /* ======================================================================
5914     OpenLayers/Animation.js
5915    ====================================================================== */
5916
5917 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
5918  * full list of contributors). Published under the 2-clause BSD license.
5919  * See license.txt in the OpenLayers distribution or repository for the
5920  * full text of the license. */
5921
5922 /**
5923  * @requires OpenLayers/SingleFile.js
5924  * @requires OpenLayers/Util/vendorPrefix.js
5925  */
5926
5927 /**
5928  * Namespace: OpenLayers.Animation
5929  * A collection of utility functions for executing methods that repaint a 
5930  *     portion of the browser window.  These methods take advantage of the
5931  *     browser's scheduled repaints where requestAnimationFrame is available.
5932  */
5933 OpenLayers.Animation = (function(window) {
5934     
5935     /**
5936      * Property: isNative
5937      * {Boolean} true if a native requestAnimationFrame function is available
5938      */
5939     var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, "requestAnimationFrame");
5940     var isNative = !!(requestAnimationFrame);
5941     
5942     /**
5943      * Function: requestFrame
5944      * Schedule a function to be called at the next available animation frame.
5945      *     Uses the native method where available.  Where requestAnimationFrame is
5946      *     not available, setTimeout will be called with a 16ms delay.
5947      *
5948      * Parameters:
5949      * callback - {Function} The function to be called at the next animation frame.
5950      * element - {DOMElement} Optional element that visually bounds the animation.
5951      */
5952     var requestFrame = (function() {
5953         var request = window[requestAnimationFrame] ||
5954             function(callback, element) {
5955                 window.setTimeout(callback, 16);
5956             };
5957         // bind to window to avoid illegal invocation of native function
5958         return function(callback, element) {
5959             request.apply(window, [callback, element]);
5960         };
5961     })();
5962     
5963     // private variables for animation loops
5964     var counter = 0;
5965     var loops = {};
5966     
5967     /**
5968      * Function: start
5969      * Executes a method with <requestFrame> in series for some 
5970      *     duration.
5971      *
5972      * Parameters:
5973      * callback - {Function} The function to be called at the next animation frame.
5974      * duration - {Number} Optional duration for the loop.  If not provided, the
5975      *     animation loop will execute indefinitely.
5976      * element - {DOMElement} Optional element that visually bounds the animation.
5977      *
5978      * Returns:
5979      * {Number} Identifier for the animation loop.  Used to stop animations with
5980      *     <stop>.
5981      */
5982     function start(callback, duration, element) {
5983         duration = duration > 0 ? duration : Number.POSITIVE_INFINITY;
5984         var id = ++counter;
5985         var start = +new Date;
5986         loops[id] = function() {
5987             if (loops[id] && +new Date - start <= duration) {
5988                 callback();
5989                 if (loops[id]) {
5990                     requestFrame(loops[id], element);
5991                 }
5992             } else {
5993                 delete loops[id];
5994             }
5995         };
5996         requestFrame(loops[id], element);
5997         return id;
5998     }
5999     
6000     /**
6001      * Function: stop
6002      * Terminates an animation loop started with <start>.
6003      *
6004      * Parameters:
6005      * id - {Number} Identifier returned from <start>.
6006      */
6007     function stop(id) {
6008         delete loops[id];
6009     }
6010     
6011     return {
6012         isNative: isNative,
6013         requestFrame: requestFrame,
6014         start: start,
6015         stop: stop
6016     };
6017     
6018 })(window);
6019 /* ======================================================================
6020     OpenLayers/Tween.js
6021    ====================================================================== */
6022
6023 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
6024  * full list of contributors). Published under the 2-clause BSD license.
6025  * See license.txt in the OpenLayers distribution or repository for the
6026  * full text of the license. */
6027
6028 /**
6029  * @requires OpenLayers/BaseTypes/Class.js
6030  * @requires OpenLayers/Animation.js
6031  */
6032
6033 /**
6034  * Namespace: OpenLayers.Tween
6035  */
6036 OpenLayers.Tween = OpenLayers.Class({
6037     
6038     /**
6039      * APIProperty: easing
6040      * {<OpenLayers.Easing>(Function)} Easing equation used for the animation
6041      *     Defaultly set to OpenLayers.Easing.Expo.easeOut
6042      */
6043     easing: null,
6044     
6045     /**
6046      * APIProperty: begin
6047      * {Object} Values to start the animation with
6048      */
6049     begin: null,
6050     
6051     /**
6052      * APIProperty: finish
6053      * {Object} Values to finish the animation with
6054      */
6055     finish: null,
6056     
6057     /**
6058      * APIProperty: duration
6059      * {int} duration of the tween (number of steps)
6060      */
6061     duration: null,
6062     
6063     /**
6064      * APIProperty: callbacks
6065      * {Object} An object with start, eachStep and done properties whose values
6066      *     are functions to be call during the animation. They are passed the
6067      *     current computed value as argument.
6068      */
6069     callbacks: null,
6070     
6071     /**
6072      * Property: time
6073      * {int} Step counter
6074      */
6075     time: null,
6076     
6077     /**
6078      * APIProperty: minFrameRate
6079      * {Number} The minimum framerate for animations in frames per second. After
6080      * each step, the time spent in the animation is compared to the calculated
6081      * time at this frame rate. If the animation runs longer than the calculated
6082      * time, the next step is skipped. Default is 30.
6083      */
6084     minFrameRate: null,
6085
6086     /**
6087      * Property: startTime
6088      * {Number} The timestamp of the first execution step. Used for skipping
6089      * frames
6090      */
6091     startTime: null,
6092     
6093     /**
6094      * Property: animationId
6095      * {int} Loop id returned by OpenLayers.Animation.start
6096      */
6097     animationId: null,
6098     
6099     /**
6100      * Property: playing
6101      * {Boolean} Tells if the easing is currently playing
6102      */
6103     playing: false,
6104     
6105     /** 
6106      * Constructor: OpenLayers.Tween
6107      * Creates a Tween.
6108      *
6109      * Parameters:
6110      * easing - {<OpenLayers.Easing>(Function)} easing function method to use
6111      */ 
6112     initialize: function(easing) {
6113         this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut;
6114     },
6115     
6116     /**
6117      * APIMethod: start
6118      * Plays the Tween, and calls the callback method on each step
6119      * 
6120      * Parameters:
6121      * begin - {Object} values to start the animation with
6122      * finish - {Object} values to finish the animation with
6123      * duration - {int} duration of the tween (number of steps)
6124      * options - {Object} hash of options (callbacks (start, eachStep, done),
6125      *     minFrameRate)
6126      */
6127     start: function(begin, finish, duration, options) {
6128         this.playing = true;
6129         this.begin = begin;
6130         this.finish = finish;
6131         this.duration = duration;
6132         this.callbacks = options.callbacks;
6133         this.minFrameRate = options.minFrameRate || 30;
6134         this.time = 0;
6135         this.startTime = new Date().getTime();
6136         OpenLayers.Animation.stop(this.animationId);
6137         this.animationId = null;
6138         if (this.callbacks && this.callbacks.start) {
6139             this.callbacks.start.call(this, this.begin);
6140         }
6141         this.animationId = OpenLayers.Animation.start(
6142             OpenLayers.Function.bind(this.play, this)
6143         );
6144     },
6145     
6146     /**
6147      * APIMethod: stop
6148      * Stops the Tween, and calls the done callback
6149      *     Doesn't do anything if animation is already finished
6150      */
6151     stop: function() {
6152         if (!this.playing) {
6153             return;
6154         }
6155         
6156         if (this.callbacks && this.callbacks.done) {
6157             this.callbacks.done.call(this, this.finish);
6158         }
6159         OpenLayers.Animation.stop(this.animationId);
6160         this.animationId = null;
6161         this.playing = false;
6162     },
6163     
6164     /**
6165      * Method: play
6166      * Calls the appropriate easing method
6167      */
6168     play: function() {
6169         var value = {};
6170         for (var i in this.begin) {
6171             var b = this.begin[i];
6172             var f = this.finish[i];
6173             if (b == null || f == null || isNaN(b) || isNaN(f)) {
6174                 throw new TypeError('invalid value for Tween');
6175             }
6176
6177             var c = f - b;
6178             value[i] = this.easing.apply(this, [this.time, b, c, this.duration]);
6179         }
6180         this.time++;
6181         
6182         if (this.callbacks && this.callbacks.eachStep) {
6183             // skip frames if frame rate drops below threshold
6184             if ((new Date().getTime() - this.startTime) / this.time <= 1000 / this.minFrameRate) {
6185                 this.callbacks.eachStep.call(this, value);
6186             }
6187         }
6188         
6189         if (this.time > this.duration) {
6190             this.stop();
6191         }
6192     },
6193     
6194     /**
6195      * Create empty functions for all easing methods.
6196      */
6197     CLASS_NAME: "OpenLayers.Tween"
6198 });
6199
6200 /**
6201  * Namespace: OpenLayers.Easing
6202  * 
6203  * Credits:
6204  *      Easing Equations by Robert Penner, <http://www.robertpenner.com/easing/>
6205  */
6206 OpenLayers.Easing = {
6207     /**
6208      * Create empty functions for all easing methods.
6209      */
6210     CLASS_NAME: "OpenLayers.Easing"
6211 };
6212
6213 /**
6214  * Namespace: OpenLayers.Easing.Linear
6215  */
6216 OpenLayers.Easing.Linear = {
6217     
6218     /**
6219      * Function: easeIn
6220      * 
6221      * Parameters:
6222      * t - {Float} time
6223      * b - {Float} beginning position
6224      * c - {Float} total change
6225      * d - {Float} duration of the transition
6226      *
6227      * Returns:
6228      * {Float}
6229      */
6230     easeIn: function(t, b, c, d) {
6231         return c*t/d + b;
6232     },
6233     
6234     /**
6235      * Function: easeOut
6236      * 
6237      * Parameters:
6238      * t - {Float} time
6239      * b - {Float} beginning position
6240      * c - {Float} total change
6241      * d - {Float} duration of the transition
6242      *
6243      * Returns:
6244      * {Float}
6245      */
6246     easeOut: function(t, b, c, d) {
6247         return c*t/d + b;
6248     },
6249     
6250     /**
6251      * Function: easeInOut
6252      * 
6253      * Parameters:
6254      * t - {Float} time
6255      * b - {Float} beginning position
6256      * c - {Float} total change
6257      * d - {Float} duration of the transition
6258      *
6259      * Returns:
6260      * {Float}
6261      */
6262     easeInOut: function(t, b, c, d) {
6263         return c*t/d + b;
6264     },
6265
6266     CLASS_NAME: "OpenLayers.Easing.Linear"
6267 };
6268
6269 /**
6270  * Namespace: OpenLayers.Easing.Expo
6271  */
6272 OpenLayers.Easing.Expo = {
6273     
6274     /**
6275      * Function: easeIn
6276      * 
6277      * Parameters:
6278      * t - {Float} time
6279      * b - {Float} beginning position
6280      * c - {Float} total change
6281      * d - {Float} duration of the transition
6282      *
6283      * Returns:
6284      * {Float}
6285      */
6286     easeIn: function(t, b, c, d) {
6287         return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
6288     },
6289     
6290     /**
6291      * Function: easeOut
6292      * 
6293      * Parameters:
6294      * t - {Float} time
6295      * b - {Float} beginning position
6296      * c - {Float} total change
6297      * d - {Float} duration of the transition
6298      *
6299      * Returns:
6300      * {Float}
6301      */
6302     easeOut: function(t, b, c, d) {
6303         return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
6304     },
6305     
6306     /**
6307      * Function: easeInOut
6308      * 
6309      * Parameters:
6310      * t - {Float} time
6311      * b - {Float} beginning position
6312      * c - {Float} total change
6313      * d - {Float} duration of the transition
6314      *
6315      * Returns:
6316      * {Float}
6317      */
6318     easeInOut: function(t, b, c, d) {
6319         if (t==0) return b;
6320         if (t==d) return b+c;
6321         if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
6322         return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
6323     },
6324
6325     CLASS_NAME: "OpenLayers.Easing.Expo"
6326 };
6327
6328 /**
6329  * Namespace: OpenLayers.Easing.Quad
6330  */
6331 OpenLayers.Easing.Quad = {
6332     
6333     /**
6334      * Function: easeIn
6335      * 
6336      * Parameters:
6337      * t - {Float} time
6338      * b - {Float} beginning position
6339      * c - {Float} total change
6340      * d - {Float} duration of the transition
6341      *
6342      * Returns:
6343      * {Float}
6344      */
6345     easeIn: function(t, b, c, d) {
6346         return c*(t/=d)*t + b;
6347     },
6348     
6349     /**
6350      * Function: easeOut
6351      * 
6352      * Parameters:
6353      * t - {Float} time
6354      * b - {Float} beginning position
6355      * c - {Float} total change
6356      * d - {Float} duration of the transition
6357      *
6358      * Returns:
6359      * {Float}
6360      */
6361     easeOut: function(t, b, c, d) {
6362         return -c *(t/=d)*(t-2) + b;
6363     },
6364     
6365     /**
6366      * Function: easeInOut
6367      * 
6368      * Parameters:
6369      * t - {Float} time
6370      * b - {Float} beginning position
6371      * c - {Float} total change
6372      * d - {Float} duration of the transition
6373      *
6374      * Returns:
6375      * {Float}
6376      */
6377     easeInOut: function(t, b, c, d) {
6378         if ((t/=d/2) < 1) return c/2*t*t + b;
6379         return -c/2 * ((--t)*(t-2) - 1) + b;
6380     },
6381
6382     CLASS_NAME: "OpenLayers.Easing.Quad"
6383 };
6384 /* ======================================================================
6385     OpenLayers/Projection.js
6386    ====================================================================== */
6387
6388 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
6389  * full list of contributors). Published under the 2-clause BSD license.
6390  * See license.txt in the OpenLayers distribution or repository for the
6391  * full text of the license. */
6392
6393 /**
6394  * @requires OpenLayers/BaseTypes/Class.js
6395  * @requires OpenLayers/Util.js
6396  */
6397
6398 /**
6399  * Namespace: OpenLayers.Projection
6400  * Methods for coordinate transforms between coordinate systems.  By default,
6401  *     OpenLayers ships with the ability to transform coordinates between
6402  *     geographic (EPSG:4326) and web or spherical mercator (EPSG:900913 et al.)
6403  *     coordinate reference systems.  See the <transform> method for details
6404  *     on usage.
6405  *
6406  * Additional transforms may be added by using the <proj4js at http://proj4js.org/>
6407  *     library.  If the proj4js library is included, the <transform> method 
6408  *     will work between any two coordinate reference systems with proj4js 
6409  *     definitions.
6410  *
6411  * If the proj4js library is not included, or if you wish to allow transforms
6412  *     between arbitrary coordinate reference systems, use the <addTransform>
6413  *     method to register a custom transform method.
6414  */
6415 OpenLayers.Projection = OpenLayers.Class({
6416
6417     /**
6418      * Property: proj
6419      * {Object} Proj4js.Proj instance.
6420      */
6421     proj: null,
6422     
6423     /**
6424      * Property: projCode
6425      * {String}
6426      */
6427     projCode: null,
6428     
6429     /**
6430      * Property: titleRegEx
6431      * {RegExp} regular expression to strip the title from a proj4js definition
6432      */
6433     titleRegEx: /\+title=[^\+]*/,
6434
6435     /**
6436      * Constructor: OpenLayers.Projection
6437      * This class offers several methods for interacting with a wrapped 
6438      *     pro4js projection object. 
6439      *
6440      * Parameters:
6441      * projCode - {String} A string identifying the Well Known Identifier for
6442      *    the projection.
6443      * options - {Object} An optional object to set additional properties
6444      *     on the projection.
6445      *
6446      * Returns:
6447      * {<OpenLayers.Projection>} A projection object.
6448      */
6449     initialize: function(projCode, options) {
6450         OpenLayers.Util.extend(this, options);
6451         this.projCode = projCode;
6452         if (typeof Proj4js == "object") {
6453             this.proj = new Proj4js.Proj(projCode);
6454         }
6455     },
6456     
6457     /**
6458      * APIMethod: getCode
6459      * Get the string SRS code.
6460      *
6461      * Returns:
6462      * {String} The SRS code.
6463      */
6464     getCode: function() {
6465         return this.proj ? this.proj.srsCode : this.projCode;
6466     },
6467    
6468     /**
6469      * APIMethod: getUnits
6470      * Get the units string for the projection -- returns null if 
6471      *     proj4js is not available.
6472      *
6473      * Returns:
6474      * {String} The units abbreviation.
6475      */
6476     getUnits: function() {
6477         return this.proj ? this.proj.units : null;
6478     },
6479
6480     /**
6481      * Method: toString
6482      * Convert projection to string (getCode wrapper).
6483      *
6484      * Returns:
6485      * {String} The projection code.
6486      */
6487     toString: function() {
6488         return this.getCode();
6489     },
6490
6491     /**
6492      * Method: equals
6493      * Test equality of two projection instances.  Determines equality based
6494      *     soley on the projection code.
6495      *
6496      * Returns:
6497      * {Boolean} The two projections are equivalent.
6498      */
6499     equals: function(projection) {
6500         var p = projection, equals = false;
6501         if (p) {
6502             if (!(p instanceof OpenLayers.Projection)) {
6503                 p = new OpenLayers.Projection(p);
6504             }
6505             if ((typeof Proj4js == "object") && this.proj.defData && p.proj.defData) {
6506                 equals = this.proj.defData.replace(this.titleRegEx, "") ==
6507                     p.proj.defData.replace(this.titleRegEx, "");
6508             } else if (p.getCode) {
6509                 var source = this.getCode(), target = p.getCode();
6510                 equals = source == target ||
6511                     !!OpenLayers.Projection.transforms[source] &&
6512                     OpenLayers.Projection.transforms[source][target] ===
6513                         OpenLayers.Projection.nullTransform;
6514             }
6515         }
6516         return equals;   
6517     },
6518
6519     /* Method: destroy
6520      * Destroy projection object.
6521      */
6522     destroy: function() {
6523         delete this.proj;
6524         delete this.projCode;
6525     },
6526     
6527     CLASS_NAME: "OpenLayers.Projection" 
6528 });     
6529
6530 /**
6531  * Property: transforms
6532  * {Object} Transforms is an object, with from properties, each of which may
6533  * have a to property. This allows you to define projections without 
6534  * requiring support for proj4js to be included.
6535  *
6536  * This object has keys which correspond to a 'source' projection object.  The
6537  * keys should be strings, corresponding to the projection.getCode() value.
6538  * Each source projection object should have a set of destination projection
6539  * keys included in the object. 
6540  * 
6541  * Each value in the destination object should be a transformation function,
6542  * where the function is expected to be passed an object with a .x and a .y
6543  * property.  The function should return the object, with the .x and .y
6544  * transformed according to the transformation function.
6545  *
6546  * Note - Properties on this object should not be set directly.  To add a
6547  *     transform method to this object, use the <addTransform> method.  For an
6548  *     example of usage, see the OpenLayers.Layer.SphericalMercator file.
6549  */
6550 OpenLayers.Projection.transforms = {};
6551
6552 /**
6553  * APIProperty: defaults
6554  * {Object} Defaults for the SRS codes known to OpenLayers (currently
6555  * EPSG:4326, CRS:84, urn:ogc:def:crs:EPSG:6.6:4326, EPSG:900913, EPSG:3857,
6556  * EPSG:102113 and EPSG:102100). Keys are the SRS code, values are units,
6557  * maxExtent (the validity extent for the SRS) and yx (true if this SRS is
6558  * known to have a reverse axis order).
6559  */
6560 OpenLayers.Projection.defaults = {
6561     "EPSG:4326": {
6562         units: "degrees",
6563         maxExtent: [-180, -90, 180, 90],
6564         yx: true
6565     },
6566     "CRS:84": {
6567         units: "degrees",
6568         maxExtent: [-180, -90, 180, 90]
6569     },
6570     "EPSG:900913": {
6571         units: "m",
6572         maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34]
6573     }
6574 };
6575
6576 /**
6577  * APIMethod: addTransform
6578  * Set a custom transform method between two projections.  Use this method in
6579  *     cases where the proj4js lib is not available or where custom projections
6580  *     need to be handled.
6581  *
6582  * Parameters:
6583  * from - {String} The code for the source projection
6584  * to - {String} the code for the destination projection
6585  * method - {Function} A function that takes a point as an argument and
6586  *     transforms that point from the source to the destination projection
6587  *     in place.  The original point should be modified.
6588  */
6589 OpenLayers.Projection.addTransform = function(from, to, method) {
6590     if (method === OpenLayers.Projection.nullTransform) {
6591         var defaults = OpenLayers.Projection.defaults[from];
6592         if (defaults && !OpenLayers.Projection.defaults[to]) {
6593             OpenLayers.Projection.defaults[to] = defaults;
6594         }
6595     }
6596     if(!OpenLayers.Projection.transforms[from]) {
6597         OpenLayers.Projection.transforms[from] = {};
6598     }
6599     OpenLayers.Projection.transforms[from][to] = method;
6600 };
6601
6602 /**
6603  * APIMethod: transform
6604  * Transform a point coordinate from one projection to another.  Note that
6605  *     the input point is transformed in place.
6606  * 
6607  * Parameters:
6608  * point - {<OpenLayers.Geometry.Point> | Object} An object with x and y
6609  *     properties representing coordinates in those dimensions.
6610  * source - {OpenLayers.Projection} Source map coordinate system
6611  * dest - {OpenLayers.Projection} Destination map coordinate system
6612  *
6613  * Returns:
6614  * point - {object} A transformed coordinate.  The original point is modified.
6615  */
6616 OpenLayers.Projection.transform = function(point, source, dest) {
6617     if (source && dest) {
6618         if (!(source instanceof OpenLayers.Projection)) {
6619             source = new OpenLayers.Projection(source);
6620         }
6621         if (!(dest instanceof OpenLayers.Projection)) {
6622             dest = new OpenLayers.Projection(dest);
6623         }
6624         if (source.proj && dest.proj) {
6625             point = Proj4js.transform(source.proj, dest.proj, point);
6626         } else {
6627             var sourceCode = source.getCode();
6628             var destCode = dest.getCode();
6629             var transforms = OpenLayers.Projection.transforms;
6630             if (transforms[sourceCode] && transforms[sourceCode][destCode]) {
6631                 transforms[sourceCode][destCode](point);
6632             }
6633         }
6634     }
6635     return point;
6636 };
6637
6638 /**
6639  * APIFunction: nullTransform
6640  * A null transformation - useful for defining projection aliases when
6641  * proj4js is not available:
6642  *
6643  * (code)
6644  * OpenLayers.Projection.addTransform("EPSG:3857", "EPSG:900913",
6645  *     OpenLayers.Projection.nullTransform);
6646  * OpenLayers.Projection.addTransform("EPSG:900913", "EPSG:3857",
6647  *     OpenLayers.Projection.nullTransform);
6648  * (end)
6649  */
6650 OpenLayers.Projection.nullTransform = function(point) {
6651     return point;
6652 };
6653
6654 /**
6655  * Note: Transforms for web mercator <-> geographic
6656  * OpenLayers recognizes EPSG:3857, EPSG:900913, EPSG:102113 and EPSG:102100.
6657  * OpenLayers originally started referring to EPSG:900913 as web mercator.
6658  * The EPSG has declared EPSG:3857 to be web mercator.
6659  * ArcGIS 10 recognizes the EPSG:3857, EPSG:102113, and EPSG:102100 as
6660  * equivalent.  See http://blogs.esri.com/Dev/blogs/arcgisserver/archive/2009/11/20/ArcGIS-Online-moving-to-Google-_2F00_-Bing-tiling-scheme_3A00_-What-does-this-mean-for-you_3F00_.aspx#12084.
6661  * For geographic, OpenLayers recognizes EPSG:4326, CRS:84 and
6662  * urn:ogc:def:crs:EPSG:6.6:4326. OpenLayers also knows about the reverse axis
6663  * order for EPSG:4326. 
6664  */
6665 (function() {
6666
6667     var pole = 20037508.34;
6668
6669     function inverseMercator(xy) {
6670         xy.x = 180 * xy.x / pole;
6671         xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp((xy.y / pole) * Math.PI)) - Math.PI / 2);
6672         return xy;
6673     }
6674
6675     function forwardMercator(xy) {
6676         xy.x = xy.x * pole / 180;
6677         var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole;
6678         xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34));
6679         return xy;
6680     }
6681
6682     function map(base, codes) {
6683         var add = OpenLayers.Projection.addTransform;
6684         var same = OpenLayers.Projection.nullTransform;
6685         var i, len, code, other, j;
6686         for (i=0, len=codes.length; i<len; ++i) {
6687             code = codes[i];
6688             add(base, code, forwardMercator);
6689             add(code, base, inverseMercator);
6690             for (j=i+1; j<len; ++j) {
6691                 other = codes[j];
6692                 add(code, other, same);
6693                 add(other, code, same);
6694             }
6695         }
6696     }
6697     
6698     // list of equivalent codes for web mercator
6699     var mercator = ["EPSG:900913", "EPSG:3857", "EPSG:102113", "EPSG:102100"],
6700         geographic = ["CRS:84", "urn:ogc:def:crs:EPSG:6.6:4326", "EPSG:4326"],
6701         i;
6702     for (i=mercator.length-1; i>=0; --i) {
6703         map(mercator[i], geographic);
6704     }
6705     for (i=geographic.length-1; i>=0; --i) {
6706         map(geographic[i], mercator);
6707     }
6708
6709 })();
6710 /* ======================================================================
6711     OpenLayers/Map.js
6712    ====================================================================== */
6713
6714 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
6715  * full list of contributors). Published under the 2-clause BSD license.
6716  * See license.txt in the OpenLayers distribution or repository for the
6717  * full text of the license. */
6718
6719 /**
6720  * @requires OpenLayers/BaseTypes/Class.js
6721  * @requires OpenLayers/Util.js
6722  * @requires OpenLayers/Util/vendorPrefix.js
6723  * @requires OpenLayers/Events.js
6724  * @requires OpenLayers/Tween.js
6725  * @requires OpenLayers/Projection.js
6726  */
6727
6728 /**
6729  * Class: OpenLayers.Map
6730  * Instances of OpenLayers.Map are interactive maps embedded in a web page.
6731  * Create a new map with the <OpenLayers.Map> constructor.
6732  * 
6733  * On their own maps do not provide much functionality.  To extend a map
6734  * it's necessary to add controls (<OpenLayers.Control>) and 
6735  * layers (<OpenLayers.Layer>) to the map. 
6736  */
6737 OpenLayers.Map = OpenLayers.Class({
6738     
6739     /**
6740      * Constant: Z_INDEX_BASE
6741      * {Object} Base z-indexes for different classes of thing 
6742      */
6743     Z_INDEX_BASE: {
6744         BaseLayer: 100,
6745         Overlay: 325,
6746         Feature: 725,
6747         Popup: 750,
6748         Control: 1000
6749     },
6750
6751     /**
6752      * APIProperty: events
6753      * {<OpenLayers.Events>}
6754      *
6755      * Register a listener for a particular event with the following syntax:
6756      * (code)
6757      * map.events.register(type, obj, listener);
6758      * (end)
6759      *
6760      * Listeners will be called with a reference to an event object.  The
6761      *     properties of this event depends on exactly what happened.
6762      *
6763      * All event objects have at least the following properties:
6764      * object - {Object} A reference to map.events.object.
6765      * element - {DOMElement} A reference to map.events.element.
6766      *
6767      * Browser events have the following additional properties:
6768      * xy - {<OpenLayers.Pixel>} The pixel location of the event (relative
6769      *     to the the map viewport).
6770      *
6771      * Supported map event types:
6772      * preaddlayer - triggered before a layer has been added.  The event
6773      *     object will include a *layer* property that references the layer  
6774      *     to be added. When a listener returns "false" the adding will be 
6775      *     aborted.
6776      * addlayer - triggered after a layer has been added.  The event object
6777      *     will include a *layer* property that references the added layer.
6778      * preremovelayer - triggered before a layer has been removed. The event
6779      *     object will include a *layer* property that references the layer  
6780      *     to be removed. When a listener returns "false" the removal will be 
6781      *     aborted.
6782      * removelayer - triggered after a layer has been removed.  The event
6783      *     object will include a *layer* property that references the removed
6784      *     layer.
6785      * changelayer - triggered after a layer name change, order change,
6786      *     opacity change, params change, visibility change (actual visibility,
6787      *     not the layer's visibility property) or attribution change (due to
6788      *     extent change). Listeners will receive an event object with *layer*
6789      *     and *property* properties. The *layer* property will be a reference
6790      *     to the changed layer. The *property* property will be a key to the
6791      *     changed property (name, order, opacity, params, visibility or
6792      *     attribution).
6793      * movestart - triggered after the start of a drag, pan, or zoom. The event
6794      *     object may include a *zoomChanged* property that tells whether the
6795      *     zoom has changed.
6796      * move - triggered after each drag, pan, or zoom
6797      * moveend - triggered after a drag, pan, or zoom completes
6798      * zoomend - triggered after a zoom completes
6799      * mouseover - triggered after mouseover the map
6800      * mouseout - triggered after mouseout the map
6801      * mousemove - triggered after mousemove the map
6802      * changebaselayer - triggered after the base layer changes
6803      * updatesize - triggered after the <updateSize> method was executed
6804      */
6805
6806     /**
6807      * Property: id
6808      * {String} Unique identifier for the map
6809      */
6810     id: null,
6811     
6812     /**
6813      * Property: fractionalZoom
6814      * {Boolean} For a base layer that supports it, allow the map resolution
6815      *     to be set to a value between one of the values in the resolutions
6816      *     array.  Default is false.
6817      *
6818      * When fractionalZoom is set to true, it is possible to zoom to
6819      *     an arbitrary extent.  This requires a base layer from a source
6820      *     that supports requests for arbitrary extents (i.e. not cached
6821      *     tiles on a regular lattice).  This means that fractionalZoom
6822      *     will not work with commercial layers (Google, Yahoo, VE), layers
6823      *     using TileCache, or any other pre-cached data sources.
6824      *
6825      * If you are using fractionalZoom, then you should also use
6826      *     <getResolutionForZoom> instead of layer.resolutions[zoom] as the
6827      *     former works for non-integer zoom levels.
6828      */
6829     fractionalZoom: false,
6830     
6831     /**
6832      * APIProperty: events
6833      * {<OpenLayers.Events>} An events object that handles all 
6834      *                       events on the map
6835      */
6836     events: null,
6837     
6838     /**
6839      * APIProperty: allOverlays
6840      * {Boolean} Allow the map to function with "overlays" only.  Defaults to
6841      *     false.  If true, the lowest layer in the draw order will act as
6842      *     the base layer.  In addition, if set to true, all layers will
6843      *     have isBaseLayer set to false when they are added to the map.
6844      *
6845      * Note:
6846      * If you set map.allOverlays to true, then you *cannot* use
6847      *     map.setBaseLayer or layer.setIsBaseLayer.  With allOverlays true,
6848      *     the lowest layer in the draw layer is the base layer.  So, to change
6849      *     the base layer, use <setLayerIndex> or <raiseLayer> to set the layer
6850      *     index to 0.
6851      */
6852     allOverlays: false,
6853
6854     /**
6855      * APIProperty: div
6856      * {DOMElement|String} The element that contains the map (or an id for
6857      *     that element).  If the <OpenLayers.Map> constructor is called
6858      *     with two arguments, this should be provided as the first argument.
6859      *     Alternatively, the map constructor can be called with the options
6860      *     object as the only argument.  In this case (one argument), a
6861      *     div property may or may not be provided.  If the div property
6862      *     is not provided, the map can be rendered to a container later
6863      *     using the <render> method.
6864      *     
6865      * Note:
6866      * If you are calling <render> after map construction, do not use
6867      *     <maxResolution>  auto.  Instead, divide your <maxExtent> by your
6868      *     maximum expected dimension.
6869      */
6870     div: null,
6871     
6872     /**
6873      * Property: dragging
6874      * {Boolean} The map is currently being dragged.
6875      */
6876     dragging: false,
6877
6878     /**
6879      * Property: size
6880      * {<OpenLayers.Size>} Size of the main div (this.div)
6881      */
6882     size: null,
6883     
6884     /**
6885      * Property: viewPortDiv
6886      * {HTMLDivElement} The element that represents the map viewport
6887      */
6888     viewPortDiv: null,
6889
6890     /**
6891      * Property: layerContainerOrigin
6892      * {<OpenLayers.LonLat>} The lonlat at which the later container was
6893      *                       re-initialized (on-zoom)
6894      */
6895     layerContainerOrigin: null,
6896
6897     /**
6898      * Property: layerContainerDiv
6899      * {HTMLDivElement} The element that contains the layers.
6900      */
6901     layerContainerDiv: null,
6902
6903     /**
6904      * APIProperty: layers
6905      * {Array(<OpenLayers.Layer>)} Ordered list of layers in the map
6906      */
6907     layers: null,
6908
6909     /**
6910      * APIProperty: controls
6911      * {Array(<OpenLayers.Control>)} List of controls associated with the map.
6912      *
6913      * If not provided in the map options at construction, the map will
6914      *     by default be given the following controls if present in the build:
6915      *  - <OpenLayers.Control.Navigation> or <OpenLayers.Control.TouchNavigation>
6916      *  - <OpenLayers.Control.Zoom> or <OpenLayers.Control.PanZoom>
6917      *  - <OpenLayers.Control.ArgParser>
6918      *  - <OpenLayers.Control.Attribution>
6919      */
6920     controls: null,
6921
6922     /**
6923      * Property: popups
6924      * {Array(<OpenLayers.Popup>)} List of popups associated with the map
6925      */
6926     popups: null,
6927
6928     /**
6929      * APIProperty: baseLayer
6930      * {<OpenLayers.Layer>} The currently selected base layer.  This determines
6931      * min/max zoom level, projection, etc.
6932      */
6933     baseLayer: null,
6934     
6935     /**
6936      * Property: center
6937      * {<OpenLayers.LonLat>} The current center of the map
6938      */
6939     center: null,
6940
6941     /**
6942      * Property: resolution
6943      * {Float} The resolution of the map.
6944      */
6945     resolution: null,
6946
6947     /**
6948      * Property: zoom
6949      * {Integer} The current zoom level of the map
6950      */
6951     zoom: 0,    
6952
6953     /**
6954      * Property: panRatio
6955      * {Float} The ratio of the current extent within
6956      *         which panning will tween.
6957      */
6958     panRatio: 1.5,    
6959
6960     /**
6961      * APIProperty: options
6962      * {Object} The options object passed to the class constructor. Read-only.
6963      */
6964     options: null,
6965
6966   // Options
6967
6968     /**
6969      * APIProperty: tileSize
6970      * {<OpenLayers.Size>} Set in the map options to override the default tile
6971      *                     size for this map.
6972      */
6973     tileSize: null,
6974
6975     /**
6976      * APIProperty: projection
6977      * {String} Set in the map options to specify the default projection 
6978      *          for layers added to this map. When using a projection other than EPSG:4326
6979      *          (CRS:84, Geographic) or EPSG:3857 (EPSG:900913, Web Mercator),
6980      *          also set maxExtent, maxResolution or resolutions.  Default is "EPSG:4326".
6981      *          Note that the projection of the map is usually determined
6982      *          by that of the current baseLayer (see <baseLayer> and <getProjectionObject>).
6983      */
6984     projection: "EPSG:4326",    
6985         
6986     /**
6987      * APIProperty: units
6988      * {String} The map units.  Possible values are 'degrees' (or 'dd'), 'm', 
6989      *     'ft', 'km', 'mi', 'inches'.  Normally taken from the projection.
6990      *     Only required if both map and layers do not define a projection,
6991      *     or if they define a projection which does not define units
6992      */
6993     units: null,
6994
6995     /**
6996      * APIProperty: resolutions
6997      * {Array(Float)} A list of map resolutions (map units per pixel) in 
6998      *     descending order.  If this is not set in the layer constructor, it 
6999      *     will be set based on other resolution related properties 
7000      *     (maxExtent, maxResolution, maxScale, etc.).
7001      */
7002     resolutions: null,
7003
7004     /**
7005      * APIProperty: maxResolution
7006      * {Float} Required if you are not displaying the whole world on a tile
7007      * with the size specified in <tileSize>.
7008      */
7009     maxResolution: null,
7010
7011     /**
7012      * APIProperty: minResolution
7013      * {Float}
7014      */
7015     minResolution: null,
7016
7017     /**
7018      * APIProperty: maxScale
7019      * {Float}
7020      */
7021     maxScale: null,
7022
7023     /**
7024      * APIProperty: minScale
7025      * {Float}
7026      */
7027     minScale: null,
7028
7029     /**
7030      * APIProperty: maxExtent
7031      * {<OpenLayers.Bounds>|Array} If provided as an array, the array
7032      *     should consist of four values (left, bottom, right, top).
7033      *     The maximum extent for the map.
7034      *     Default depends on projection; if this is one of those defined in OpenLayers.Projection.defaults
7035      *     (EPSG:4326 or web mercator), maxExtent will be set to the value defined there;
7036      *     else, defaults to null.
7037      *     To restrict user panning and zooming of the map, use <restrictedExtent> instead.
7038      *     The value for <maxExtent> will change calculations for tile URLs.
7039      */
7040     maxExtent: null,
7041     
7042     /**
7043      * APIProperty: minExtent
7044      * {<OpenLayers.Bounds>|Array} If provided as an array, the array
7045      *     should consist of four values (left, bottom, right, top).
7046      *     The minimum extent for the map.  Defaults to null.
7047      */
7048     minExtent: null,
7049     
7050     /**
7051      * APIProperty: restrictedExtent
7052      * {<OpenLayers.Bounds>|Array} If provided as an array, the array
7053      *     should consist of four values (left, bottom, right, top).
7054      *     Limit map navigation to this extent where possible.
7055      *     If a non-null restrictedExtent is set, panning will be restricted
7056      *     to the given bounds.  In addition, zooming to a resolution that
7057      *     displays more than the restricted extent will center the map
7058      *     on the restricted extent.  If you wish to limit the zoom level
7059      *     or resolution, use maxResolution.
7060      */
7061     restrictedExtent: null,
7062
7063     /**
7064      * APIProperty: numZoomLevels
7065      * {Integer} Number of zoom levels for the map.  Defaults to 16.  Set a
7066      *           different value in the map options if needed.
7067      */
7068     numZoomLevels: 16,
7069
7070     /**
7071      * APIProperty: theme
7072      * {String} Relative path to a CSS file from which to load theme styles.
7073      *          Specify null in the map options (e.g. {theme: null}) if you 
7074      *          want to get cascading style declarations - by putting links to 
7075      *          stylesheets or style declarations directly in your page.
7076      */
7077     theme: null,
7078     
7079     /** 
7080      * APIProperty: displayProjection
7081      * {<OpenLayers.Projection>} Requires proj4js support for projections other
7082      *     than EPSG:4326 or EPSG:900913/EPSG:3857. Projection used by
7083      *     several controls to display data to user. If this property is set,
7084      *     it will be set on any control which has a null displayProjection
7085      *     property at the time the control is added to the map. 
7086      */
7087     displayProjection: null,
7088
7089     /**
7090      * APIProperty: tileManager
7091      * {<OpenLayers.TileManager>|Object} By default, and if the build contains
7092      * TileManager.js, the map will use the TileManager to queue image requests
7093      * and to cache tile image elements. To create a map without a TileManager
7094      * configure the map with tileManager: null. To create a TileManager with
7095      * non-default options, supply the options instead or alternatively supply
7096      * an instance of {<OpenLayers.TileManager>}.
7097      */
7098
7099     /**
7100      * APIProperty: fallThrough
7101      * {Boolean} Should OpenLayers allow events on the map to fall through to
7102      *           other elements on the page, or should it swallow them? (#457)
7103      *           Default is to swallow.
7104      */
7105     fallThrough: false,
7106
7107     /**
7108      * APIProperty: autoUpdateSize
7109      * {Boolean} Should OpenLayers automatically update the size of the map
7110      * when the resize event is fired. Default is true.
7111      */
7112     autoUpdateSize: true,
7113     
7114     /**
7115      * APIProperty: eventListeners
7116      * {Object} If set as an option at construction, the eventListeners
7117      *     object will be registered with <OpenLayers.Events.on>.  Object
7118      *     structure must be a listeners object as shown in the example for
7119      *     the events.on method.
7120      */
7121     eventListeners: null,
7122
7123     /**
7124      * Property: panTween
7125      * {<OpenLayers.Tween>} Animated panning tween object, see panTo()
7126      */
7127     panTween: null,
7128
7129     /**
7130      * APIProperty: panMethod
7131      * {Function} The Easing function to be used for tweening.  Default is
7132      * OpenLayers.Easing.Expo.easeOut. Setting this to 'null' turns off
7133      * animated panning.
7134      */
7135     panMethod: OpenLayers.Easing.Expo.easeOut,
7136     
7137     /**
7138      * Property: panDuration
7139      * {Integer} The number of steps to be passed to the
7140      * OpenLayers.Tween.start() method when the map is
7141      * panned.
7142      * Default is 50.
7143      */
7144     panDuration: 50,
7145     
7146     /**
7147      * Property: zoomTween
7148      * {<OpenLayers.Tween>} Animated zooming tween object, see zoomTo()
7149      */
7150     zoomTween: null,
7151
7152     /**
7153      * APIProperty: zoomMethod
7154      * {Function} The Easing function to be used for tweening.  Default is
7155      * OpenLayers.Easing.Quad.easeOut. Setting this to 'null' turns off
7156      * animated zooming.
7157      */
7158     zoomMethod: OpenLayers.Easing.Quad.easeOut,
7159     
7160     /**
7161      * Property: zoomDuration
7162      * {Integer} The number of steps to be passed to the
7163      * OpenLayers.Tween.start() method when the map is zoomed.
7164      * Default is 20.
7165      */
7166     zoomDuration: 20,
7167     
7168     /**
7169      * Property: paddingForPopups
7170      * {<OpenLayers.Bounds>} Outside margin of the popup. Used to prevent 
7171      *     the popup from getting too close to the map border.
7172      */
7173     paddingForPopups : null,
7174     
7175     /**
7176      * Property: layerContainerOriginPx
7177      * {Object} Cached object representing the layer container origin (in pixels).
7178      */
7179     layerContainerOriginPx: null,
7180     
7181     /**
7182      * Property: minPx
7183      * {Object} An object with a 'x' and 'y' values that is the lower
7184      *     left of maxExtent in viewport pixel space.
7185      *     Used to verify in moveByPx that the new location we're moving to
7186      *     is valid. It is also used in the getLonLatFromViewPortPx function
7187      *     of Layer.
7188      */
7189     minPx: null,
7190     
7191     /**
7192      * Property: maxPx
7193      * {Object} An object with a 'x' and 'y' values that is the top
7194      *     right of maxExtent in viewport pixel space.
7195      *     Used to verify in moveByPx that the new location we're moving to
7196      *     is valid.
7197      */
7198     maxPx: null,
7199     
7200     /**
7201      * Constructor: OpenLayers.Map
7202      * Constructor for a new OpenLayers.Map instance.  There are two possible
7203      *     ways to call the map constructor.  See the examples below.
7204      *
7205      * Parameters:
7206      * div - {DOMElement|String}  The element or id of an element in your page
7207      *     that will contain the map.  May be omitted if the <div> option is
7208      *     provided or if you intend to call the <render> method later.
7209      * options - {Object} Optional object with properties to tag onto the map.
7210      *
7211      * Valid options (in addition to the listed API properties):
7212      * center - {<OpenLayers.LonLat>|Array} The default initial center of the map.
7213      *     If provided as array, the first value is the x coordinate,
7214      *     and the 2nd value is the y coordinate.
7215      *     Only specify if <layers> is provided.
7216      *     Note that if an ArgParser/Permalink control is present,
7217      *     and the querystring contains coordinates, center will be set
7218      *     by that, and this option will be ignored.
7219      * zoom - {Number} The initial zoom level for the map. Only specify if
7220      *     <layers> is provided.
7221      *     Note that if an ArgParser/Permalink control is present,
7222      *     and the querystring contains a zoom level, zoom will be set
7223      *     by that, and this option will be ignored.
7224      * extent - {<OpenLayers.Bounds>|Array} The initial extent of the map.
7225      *     If provided as an array, the array should consist of
7226      *     four values (left, bottom, right, top).
7227      *     Only specify if <center> and <zoom> are not provided.
7228      * 
7229      * Examples:
7230      * (code)
7231      * // create a map with default options in an element with the id "map1"
7232      * var map = new OpenLayers.Map("map1");
7233      *
7234      * // create a map with non-default options in an element with id "map2"
7235      * var options = {
7236      *     projection: "EPSG:3857",
7237      *     maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000),
7238      *     center: new OpenLayers.LonLat(-12356463.476333, 5621521.4854095)
7239      * };
7240      * var map = new OpenLayers.Map("map2", options);
7241      *
7242      * // map with non-default options - same as above but with a single argument,
7243      * // a restricted extent, and using arrays for bounds and center
7244      * var map = new OpenLayers.Map({
7245      *     div: "map_id",
7246      *     projection: "EPSG:3857",
7247      *     maxExtent: [-18924313.432222, -15538711.094146, 18924313.432222, 15538711.094146],
7248      *     restrictedExtent: [-13358338.893333, -9608371.5085962, 13358338.893333, 9608371.5085962],
7249      *     center: [-12356463.476333, 5621521.4854095]
7250      * });
7251      *
7252      * // create a map without a reference to a container - call render later
7253      * var map = new OpenLayers.Map({
7254      *     projection: "EPSG:3857",
7255      *     maxExtent: new OpenLayers.Bounds(-200000, -200000, 200000, 200000)
7256      * });
7257      * (end)
7258      */    
7259     initialize: function (div, options) {
7260         
7261         // If only one argument is provided, check if it is an object.
7262         if(arguments.length === 1 && typeof div === "object") {
7263             options = div;
7264             div = options && options.div;
7265         }
7266
7267         // Simple-type defaults are set in class definition. 
7268         //  Now set complex-type defaults 
7269         this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH,
7270                                             OpenLayers.Map.TILE_HEIGHT);
7271         
7272         this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);
7273
7274         this.theme = OpenLayers._getScriptLocation() + 
7275                              'theme/default/style.css'; 
7276
7277         // backup original options
7278         this.options = OpenLayers.Util.extend({}, options);
7279
7280         // now override default options 
7281         OpenLayers.Util.extend(this, options);
7282         
7283         var projCode = this.projection instanceof OpenLayers.Projection ?
7284             this.projection.projCode : this.projection;
7285         OpenLayers.Util.applyDefaults(this, OpenLayers.Projection.defaults[projCode]);
7286         
7287         // allow extents and center to be arrays
7288         if (this.maxExtent && !(this.maxExtent instanceof OpenLayers.Bounds)) {
7289             this.maxExtent = new OpenLayers.Bounds(this.maxExtent);
7290         }
7291         if (this.minExtent && !(this.minExtent instanceof OpenLayers.Bounds)) {
7292             this.minExtent = new OpenLayers.Bounds(this.minExtent);
7293         }
7294         if (this.restrictedExtent && !(this.restrictedExtent instanceof OpenLayers.Bounds)) {
7295             this.restrictedExtent = new OpenLayers.Bounds(this.restrictedExtent);
7296         }
7297         if (this.center && !(this.center instanceof OpenLayers.LonLat)) {
7298             this.center = new OpenLayers.LonLat(this.center);
7299         }
7300
7301         // initialize layers array
7302         this.layers = [];
7303
7304         this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_");
7305
7306         this.div = OpenLayers.Util.getElement(div);
7307         if(!this.div) {
7308             this.div = document.createElement("div");
7309             this.div.style.height = "1px";
7310             this.div.style.width = "1px";
7311         }
7312         
7313         OpenLayers.Element.addClass(this.div, 'olMap');
7314
7315         // the viewPortDiv is the outermost div we modify
7316         var id = this.id + "_OpenLayers_ViewPort";
7317         this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null,
7318                                                      "relative", null,
7319                                                      "hidden");
7320         this.viewPortDiv.style.width = "100%";
7321         this.viewPortDiv.style.height = "100%";
7322         this.viewPortDiv.className = "olMapViewport";
7323         this.div.appendChild(this.viewPortDiv);
7324
7325         this.events = new OpenLayers.Events(
7326             this, this.viewPortDiv, null, this.fallThrough, 
7327             {includeXY: true}
7328         );
7329         
7330         if (OpenLayers.TileManager && this.tileManager !== null) {
7331             if (!(this.tileManager instanceof OpenLayers.TileManager)) {
7332                 this.tileManager = new OpenLayers.TileManager(this.tileManager);
7333             }
7334             this.tileManager.addMap(this);
7335         }
7336
7337         // the layerContainerDiv is the one that holds all the layers
7338         id = this.id + "_OpenLayers_Container";
7339         this.layerContainerDiv = OpenLayers.Util.createDiv(id);
7340         this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1;
7341         this.layerContainerOriginPx = {x: 0, y: 0};
7342         this.applyTransform();
7343         
7344         this.viewPortDiv.appendChild(this.layerContainerDiv);
7345
7346         this.updateSize();
7347         if(this.eventListeners instanceof Object) {
7348             this.events.on(this.eventListeners);
7349         }
7350
7351         if (this.autoUpdateSize === true) {
7352             // updateSize on catching the window's resize
7353             // Note that this is ok, as updateSize() does nothing if the 
7354             // map's size has not actually changed.
7355             this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize, 
7356                 this);
7357             OpenLayers.Event.observe(window, 'resize',
7358                             this.updateSizeDestroy);
7359         }
7360         
7361         // only append link stylesheet if the theme property is set
7362         if(this.theme) {
7363             // check existing links for equivalent url
7364             var addNode = true;
7365             var nodes = document.getElementsByTagName('link');
7366             for(var i=0, len=nodes.length; i<len; ++i) {
7367                 if(OpenLayers.Util.isEquivalentUrl(nodes.item(i).href,
7368                                                    this.theme)) {
7369                     addNode = false;
7370                     break;
7371                 }
7372             }
7373             // only add a new node if one with an equivalent url hasn't already
7374             // been added
7375             if(addNode) {
7376                 var cssNode = document.createElement('link');
7377                 cssNode.setAttribute('rel', 'stylesheet');
7378                 cssNode.setAttribute('type', 'text/css');
7379                 cssNode.setAttribute('href', this.theme);
7380                 document.getElementsByTagName('head')[0].appendChild(cssNode);
7381             }
7382         }
7383         
7384         if (this.controls == null) { // default controls
7385             this.controls = [];
7386             if (OpenLayers.Control != null) { // running full or lite?
7387                 // Navigation or TouchNavigation depending on what is in build
7388                 if (OpenLayers.Control.Navigation) {
7389                     this.controls.push(new OpenLayers.Control.Navigation());
7390                 } else if (OpenLayers.Control.TouchNavigation) {
7391                     this.controls.push(new OpenLayers.Control.TouchNavigation());
7392                 }
7393                 if (OpenLayers.Control.Zoom) {
7394                     this.controls.push(new OpenLayers.Control.Zoom());
7395                 } else if (OpenLayers.Control.PanZoom) {
7396                     this.controls.push(new OpenLayers.Control.PanZoom());
7397                 }
7398
7399                 if (OpenLayers.Control.ArgParser) {
7400                     this.controls.push(new OpenLayers.Control.ArgParser());
7401                 }
7402                 if (OpenLayers.Control.Attribution) {
7403                     this.controls.push(new OpenLayers.Control.Attribution());
7404                 }
7405             }
7406         }
7407
7408         for(var i=0, len=this.controls.length; i<len; i++) {
7409             this.addControlToMap(this.controls[i]);
7410         }
7411
7412         this.popups = [];
7413
7414         this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);
7415         
7416
7417         // always call map.destroy()
7418         OpenLayers.Event.observe(window, 'unload', this.unloadDestroy);
7419         
7420         // add any initial layers
7421         if (options && options.layers) {
7422             /** 
7423              * If you have set options.center, the map center property will be
7424              * set at this point.  However, since setCenter has not been called,
7425              * addLayers gets confused.  So we delete the map center in this 
7426              * case.  Because the check below uses options.center, it will
7427              * be properly set below.
7428              */
7429             delete this.center;
7430             delete this.zoom;
7431             this.addLayers(options.layers);
7432             // set center (and optionally zoom)
7433             if (options.center && !this.getCenter()) {
7434                 // zoom can be undefined here
7435                 this.setCenter(options.center, options.zoom);
7436             }
7437         }
7438
7439         if (this.panMethod) {
7440             this.panTween = new OpenLayers.Tween(this.panMethod);
7441         }
7442         if (this.zoomMethod && this.applyTransform.transform) {
7443             this.zoomTween = new OpenLayers.Tween(this.zoomMethod);
7444         }
7445     },
7446
7447     /** 
7448      * APIMethod: getViewport
7449      * Get the DOMElement representing the view port.
7450      *
7451      * Returns:
7452      * {DOMElement}
7453      */
7454     getViewport: function() {
7455         return this.viewPortDiv;
7456     },
7457     
7458     /**
7459      * APIMethod: render
7460      * Render the map to a specified container.
7461      * 
7462      * Parameters:
7463      * div - {String|DOMElement} The container that the map should be rendered
7464      *     to. If different than the current container, the map viewport
7465      *     will be moved from the current to the new container.
7466      */
7467     render: function(div) {
7468         this.div = OpenLayers.Util.getElement(div);
7469         OpenLayers.Element.addClass(this.div, 'olMap');
7470         this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
7471         this.div.appendChild(this.viewPortDiv);
7472         this.updateSize();
7473     },
7474
7475     /**
7476      * Method: unloadDestroy
7477      * Function that is called to destroy the map on page unload. stored here
7478      *     so that if map is manually destroyed, we can unregister this.
7479      */
7480     unloadDestroy: null,
7481     
7482     /**
7483      * Method: updateSizeDestroy
7484      * When the map is destroyed, we need to stop listening to updateSize
7485      *    events: this method stores the function we need to unregister in 
7486      *    non-IE browsers.
7487      */
7488     updateSizeDestroy: null,
7489
7490     /**
7491      * APIMethod: destroy
7492      * Destroy this map.
7493      *    Note that if you are using an application which removes a container
7494      *    of the map from the DOM, you need to ensure that you destroy the
7495      *    map *before* this happens; otherwise, the page unload handler
7496      *    will fail because the DOM elements that map.destroy() wants
7497      *    to clean up will be gone. (See 
7498      *    http://trac.osgeo.org/openlayers/ticket/2277 for more information).
7499      *    This will apply to GeoExt and also to other applications which
7500      *    modify the DOM of the container of the OpenLayers Map.
7501      */
7502     destroy:function() {
7503         // if unloadDestroy is null, we've already been destroyed
7504         if (!this.unloadDestroy) {
7505             return false;
7506         }
7507         
7508         // make sure panning doesn't continue after destruction
7509         if(this.panTween) {
7510             this.panTween.stop();
7511             this.panTween = null;
7512         }
7513         // make sure zooming doesn't continue after destruction
7514         if(this.zoomTween) {
7515             this.zoomTween.stop();
7516             this.zoomTween = null;
7517         }
7518
7519         // map has been destroyed. dont do it again!
7520         OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy);
7521         this.unloadDestroy = null;
7522
7523         if (this.updateSizeDestroy) {
7524             OpenLayers.Event.stopObserving(window, 'resize', 
7525                                            this.updateSizeDestroy);
7526         }
7527         
7528         this.paddingForPopups = null;    
7529
7530         if (this.controls != null) {
7531             for (var i = this.controls.length - 1; i>=0; --i) {
7532                 this.controls[i].destroy();
7533             } 
7534             this.controls = null;
7535         }
7536         if (this.layers != null) {
7537             for (var i = this.layers.length - 1; i>=0; --i) {
7538                 //pass 'false' to destroy so that map wont try to set a new 
7539                 // baselayer after each baselayer is removed
7540                 this.layers[i].destroy(false);
7541             } 
7542             this.layers = null;
7543         }
7544         if (this.viewPortDiv && this.viewPortDiv.parentNode) {
7545             this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
7546         }
7547         this.viewPortDiv = null;
7548         
7549         if (this.tileManager) {
7550             this.tileManager.removeMap(this);
7551             this.tileManager = null;
7552         }
7553
7554         if(this.eventListeners) {
7555             this.events.un(this.eventListeners);
7556             this.eventListeners = null;
7557         }
7558         this.events.destroy();
7559         this.events = null;
7560
7561         this.options = null;
7562     },
7563
7564     /**
7565      * APIMethod: setOptions
7566      * Change the map options
7567      *
7568      * Parameters:
7569      * options - {Object} Hashtable of options to tag to the map
7570      */
7571     setOptions: function(options) {
7572         var updatePxExtent = this.minPx &&
7573             options.restrictedExtent != this.restrictedExtent;
7574         OpenLayers.Util.extend(this, options);
7575         // force recalculation of minPx and maxPx
7576         updatePxExtent && this.moveTo(this.getCachedCenter(), this.zoom, {
7577             forceZoomChange: true
7578         });
7579     },
7580
7581     /**
7582      * APIMethod: getTileSize
7583      * Get the tile size for the map
7584      *
7585      * Returns:
7586      * {<OpenLayers.Size>}
7587      */
7588      getTileSize: function() {
7589          return this.tileSize;
7590      },
7591
7592
7593     /**
7594      * APIMethod: getBy
7595      * Get a list of objects given a property and a match item.
7596      *
7597      * Parameters:
7598      * array - {String} A property on the map whose value is an array.
7599      * property - {String} A property on each item of the given array.
7600      * match - {String | Object} A string to match.  Can also be a regular
7601      *     expression literal or object.  In addition, it can be any object
7602      *     with a method named test.  For reqular expressions or other, if
7603      *     match.test(map[array][i][property]) evaluates to true, the item will
7604      *     be included in the array returned.  If no items are found, an empty
7605      *     array is returned.
7606      *
7607      * Returns:
7608      * {Array} An array of items where the given property matches the given
7609      *     criteria.
7610      */
7611     getBy: function(array, property, match) {
7612         var test = (typeof match.test == "function");
7613         var found = OpenLayers.Array.filter(this[array], function(item) {
7614             return item[property] == match || (test && match.test(item[property]));
7615         });
7616         return found;
7617     },
7618
7619     /**
7620      * APIMethod: getLayersBy
7621      * Get a list of layers with properties matching the given criteria.
7622      *
7623      * Parameters:
7624      * property - {String} A layer property to be matched.
7625      * match - {String | Object} A string to match.  Can also be a regular
7626      *     expression literal or object.  In addition, it can be any object
7627      *     with a method named test.  For reqular expressions or other, if
7628      *     match.test(layer[property]) evaluates to true, the layer will be
7629      *     included in the array returned.  If no layers are found, an empty
7630      *     array is returned.
7631      *
7632      * Returns:
7633      * {Array(<OpenLayers.Layer>)} A list of layers matching the given criteria.
7634      *     An empty array is returned if no matches are found.
7635      */
7636     getLayersBy: function(property, match) {
7637         return this.getBy("layers", property, match);
7638     },
7639
7640     /**
7641      * APIMethod: getLayersByName
7642      * Get a list of layers with names matching the given name.
7643      *
7644      * Parameters:
7645      * match - {String | Object} A layer name.  The name can also be a regular
7646      *     expression literal or object.  In addition, it can be any object
7647      *     with a method named test.  For reqular expressions or other, if
7648      *     name.test(layer.name) evaluates to true, the layer will be included
7649      *     in the list of layers returned.  If no layers are found, an empty
7650      *     array is returned.
7651      *
7652      * Returns:
7653      * {Array(<OpenLayers.Layer>)} A list of layers matching the given name.
7654      *     An empty array is returned if no matches are found.
7655      */
7656     getLayersByName: function(match) {
7657         return this.getLayersBy("name", match);
7658     },
7659
7660     /**
7661      * APIMethod: getLayersByClass
7662      * Get a list of layers of a given class (CLASS_NAME).
7663      *
7664      * Parameters:
7665      * match - {String | Object} A layer class name.  The match can also be a
7666      *     regular expression literal or object.  In addition, it can be any
7667      *     object with a method named test.  For reqular expressions or other,
7668      *     if type.test(layer.CLASS_NAME) evaluates to true, the layer will
7669      *     be included in the list of layers returned.  If no layers are
7670      *     found, an empty array is returned.
7671      *
7672      * Returns:
7673      * {Array(<OpenLayers.Layer>)} A list of layers matching the given class.
7674      *     An empty array is returned if no matches are found.
7675      */
7676     getLayersByClass: function(match) {
7677         return this.getLayersBy("CLASS_NAME", match);
7678     },
7679
7680     /**
7681      * APIMethod: getControlsBy
7682      * Get a list of controls with properties matching the given criteria.
7683      *
7684      * Parameters:
7685      * property - {String} A control property to be matched.
7686      * match - {String | Object} A string to match.  Can also be a regular
7687      *     expression literal or object.  In addition, it can be any object
7688      *     with a method named test.  For reqular expressions or other, if
7689      *     match.test(layer[property]) evaluates to true, the layer will be
7690      *     included in the array returned.  If no layers are found, an empty
7691      *     array is returned.
7692      *
7693      * Returns:
7694      * {Array(<OpenLayers.Control>)} A list of controls matching the given
7695      *     criteria.  An empty array is returned if no matches are found.
7696      */
7697     getControlsBy: function(property, match) {
7698         return this.getBy("controls", property, match);
7699     },
7700
7701     /**
7702      * APIMethod: getControlsByClass
7703      * Get a list of controls of a given class (CLASS_NAME).
7704      *
7705      * Parameters:
7706      * match - {String | Object} A control class name.  The match can also be a
7707      *     regular expression literal or object.  In addition, it can be any
7708      *     object with a method named test.  For reqular expressions or other,
7709      *     if type.test(control.CLASS_NAME) evaluates to true, the control will
7710      *     be included in the list of controls returned.  If no controls are
7711      *     found, an empty array is returned.
7712      *
7713      * Returns:
7714      * {Array(<OpenLayers.Control>)} A list of controls matching the given class.
7715      *     An empty array is returned if no matches are found.
7716      */
7717     getControlsByClass: function(match) {
7718         return this.getControlsBy("CLASS_NAME", match);
7719     },
7720
7721   /********************************************************/
7722   /*                                                      */
7723   /*                  Layer Functions                     */
7724   /*                                                      */
7725   /*     The following functions deal with adding and     */
7726   /*        removing Layers to and from the Map           */
7727   /*                                                      */
7728   /********************************************************/         
7729
7730     /**
7731      * APIMethod: getLayer
7732      * Get a layer based on its id
7733      *
7734      * Parameters:
7735      * id - {String} A layer id
7736      *
7737      * Returns:
7738      * {<OpenLayers.Layer>} The Layer with the corresponding id from the map's 
7739      *                      layer collection, or null if not found.
7740      */
7741     getLayer: function(id) {
7742         var foundLayer = null;
7743         for (var i=0, len=this.layers.length; i<len; i++) {
7744             var layer = this.layers[i];
7745             if (layer.id == id) {
7746                 foundLayer = layer;
7747                 break;
7748             }
7749         }
7750         return foundLayer;
7751     },
7752
7753     /**
7754     * Method: setLayerZIndex
7755     * 
7756     * Parameters:
7757     * layer - {<OpenLayers.Layer>} 
7758     * zIdx - {int} 
7759     */    
7760     setLayerZIndex: function (layer, zIdx) {
7761         layer.setZIndex(
7762             this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay']
7763             + zIdx * 5 );
7764     },
7765
7766     /**
7767      * Method: resetLayersZIndex
7768      * Reset each layer's z-index based on layer's array index
7769      */
7770     resetLayersZIndex: function() {
7771         for (var i=0, len=this.layers.length; i<len; i++) {
7772             var layer = this.layers[i];
7773             this.setLayerZIndex(layer, i);
7774         }
7775     },
7776
7777     /**
7778     * APIMethod: addLayer
7779     *
7780     * Parameters:
7781     * layer - {<OpenLayers.Layer>} 
7782     *
7783     * Returns:
7784     * {Boolean} True if the layer has been added to the map.
7785     */    
7786     addLayer: function (layer) {
7787         for(var i = 0, len = this.layers.length; i < len; i++) {
7788             if (this.layers[i] == layer) {
7789                 return false;
7790             }
7791         }
7792         if (this.events.triggerEvent("preaddlayer", {layer: layer}) === false) {
7793             return false;
7794         }
7795         if(this.allOverlays) {
7796             layer.isBaseLayer = false;
7797         }
7798         
7799         layer.div.className = "olLayerDiv";
7800         layer.div.style.overflow = "";
7801         this.setLayerZIndex(layer, this.layers.length);
7802
7803         if (layer.isFixed) {
7804             this.viewPortDiv.appendChild(layer.div);
7805         } else {
7806             this.layerContainerDiv.appendChild(layer.div);
7807         }
7808         this.layers.push(layer);
7809         layer.setMap(this);
7810
7811         if (layer.isBaseLayer || (this.allOverlays && !this.baseLayer))  {
7812             if (this.baseLayer == null) {
7813                 // set the first baselaye we add as the baselayer
7814                 this.setBaseLayer(layer);
7815             } else {
7816                 layer.setVisibility(false);
7817             }
7818         } else {
7819             layer.redraw();
7820         }
7821
7822         this.events.triggerEvent("addlayer", {layer: layer});
7823         layer.events.triggerEvent("added", {map: this, layer: layer});
7824         layer.afterAdd();
7825
7826         return true;
7827     },
7828
7829     /**
7830     * APIMethod: addLayers 
7831     *
7832     * Parameters:
7833     * layers - {Array(<OpenLayers.Layer>)} 
7834     */    
7835     addLayers: function (layers) {
7836         for (var i=0, len=layers.length; i<len; i++) {
7837             this.addLayer(layers[i]);
7838         }
7839     },
7840
7841     /** 
7842      * APIMethod: removeLayer
7843      * Removes a layer from the map by removing its visual element (the 
7844      *   layer.div property), then removing it from the map's internal list 
7845      *   of layers, setting the layer's map property to null. 
7846      * 
7847      *   a "removelayer" event is triggered.
7848      * 
7849      *   very worthy of mention is that simply removing a layer from a map
7850      *   will not cause the removal of any popups which may have been created
7851      *   by the layer. this is due to the fact that it was decided at some
7852      *   point that popups would not belong to layers. thus there is no way 
7853      *   for us to know here to which layer the popup belongs.
7854      *    
7855      *     A simple solution to this is simply to call destroy() on the layer.
7856      *     the default OpenLayers.Layer class's destroy() function
7857      *     automatically takes care to remove itself from whatever map it has
7858      *     been attached to. 
7859      * 
7860      *     The correct solution is for the layer itself to register an 
7861      *     event-handler on "removelayer" and when it is called, if it 
7862      *     recognizes itself as the layer being removed, then it cycles through
7863      *     its own personal list of popups, removing them from the map.
7864      * 
7865      * Parameters:
7866      * layer - {<OpenLayers.Layer>} 
7867      * setNewBaseLayer - {Boolean} Default is true
7868      */
7869     removeLayer: function(layer, setNewBaseLayer) {
7870         if (this.events.triggerEvent("preremovelayer", {layer: layer}) === false) {
7871             return;
7872         }
7873         if (setNewBaseLayer == null) {
7874             setNewBaseLayer = true;
7875         }
7876
7877         if (layer.isFixed) {
7878             this.viewPortDiv.removeChild(layer.div);
7879         } else {
7880             this.layerContainerDiv.removeChild(layer.div);
7881         }
7882         OpenLayers.Util.removeItem(this.layers, layer);
7883         layer.removeMap(this);
7884         layer.map = null;
7885
7886         // if we removed the base layer, need to set a new one
7887         if(this.baseLayer == layer) {
7888             this.baseLayer = null;
7889             if(setNewBaseLayer) {
7890                 for(var i=0, len=this.layers.length; i<len; i++) {
7891                     var iLayer = this.layers[i];
7892                     if (iLayer.isBaseLayer || this.allOverlays) {
7893                         this.setBaseLayer(iLayer);
7894                         break;
7895                     }
7896                 }
7897             }
7898         }
7899
7900         this.resetLayersZIndex();
7901
7902         this.events.triggerEvent("removelayer", {layer: layer});
7903         layer.events.triggerEvent("removed", {map: this, layer: layer});
7904     },
7905
7906     /**
7907      * APIMethod: getNumLayers
7908      * 
7909      * Returns:
7910      * {Int} The number of layers attached to the map.
7911      */
7912     getNumLayers: function () {
7913         return this.layers.length;
7914     },
7915
7916     /** 
7917      * APIMethod: getLayerIndex
7918      *
7919      * Parameters:
7920      * layer - {<OpenLayers.Layer>}
7921      *
7922      * Returns:
7923      * {Integer} The current (zero-based) index of the given layer in the map's
7924      *           layer stack. Returns -1 if the layer isn't on the map.
7925      */
7926     getLayerIndex: function (layer) {
7927         return OpenLayers.Util.indexOf(this.layers, layer);
7928     },
7929     
7930     /** 
7931      * APIMethod: setLayerIndex
7932      * Move the given layer to the specified (zero-based) index in the layer
7933      *     list, changing its z-index in the map display. Use
7934      *     map.getLayerIndex() to find out the current index of a layer. Note
7935      *     that this cannot (or at least should not) be effectively used to
7936      *     raise base layers above overlays.
7937      *
7938      * Parameters:
7939      * layer - {<OpenLayers.Layer>} 
7940      * idx - {int} 
7941      */
7942     setLayerIndex: function (layer, idx) {
7943         var base = this.getLayerIndex(layer);
7944         if (idx < 0) {
7945             idx = 0;
7946         } else if (idx > this.layers.length) {
7947             idx = this.layers.length;
7948         }
7949         if (base != idx) {
7950             this.layers.splice(base, 1);
7951             this.layers.splice(idx, 0, layer);
7952             for (var i=0, len=this.layers.length; i<len; i++) {
7953                 this.setLayerZIndex(this.layers[i], i);
7954             }
7955             this.events.triggerEvent("changelayer", {
7956                 layer: layer, property: "order"
7957             });
7958             if(this.allOverlays) {
7959                 if(idx === 0) {
7960                     this.setBaseLayer(layer);
7961                 } else if(this.baseLayer !== this.layers[0]) {
7962                     this.setBaseLayer(this.layers[0]);
7963                 }
7964             }
7965         }
7966     },
7967
7968     /** 
7969      * APIMethod: raiseLayer
7970      * Change the index of the given layer by delta. If delta is positive, 
7971      *     the layer is moved up the map's layer stack; if delta is negative,
7972      *     the layer is moved down.  Again, note that this cannot (or at least
7973      *     should not) be effectively used to raise base layers above overlays.
7974      *
7975      * Paremeters:
7976      * layer - {<OpenLayers.Layer>} 
7977      * delta - {int} 
7978      */
7979     raiseLayer: function (layer, delta) {
7980         var idx = this.getLayerIndex(layer) + delta;
7981         this.setLayerIndex(layer, idx);
7982     },
7983     
7984     /** 
7985      * APIMethod: setBaseLayer
7986      * Allows user to specify one of the currently-loaded layers as the Map's
7987      *     new base layer.
7988      * 
7989      * Parameters:
7990      * newBaseLayer - {<OpenLayers.Layer>}
7991      */
7992     setBaseLayer: function(newBaseLayer) {
7993         
7994         if (newBaseLayer != this.baseLayer) {
7995           
7996             // ensure newBaseLayer is already loaded
7997             if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {
7998
7999                 // preserve center and scale when changing base layers
8000                 var center = this.getCachedCenter();
8001                 var newResolution = OpenLayers.Util.getResolutionFromScale(
8002                     this.getScale(), newBaseLayer.units
8003                 );
8004
8005                 // make the old base layer invisible 
8006                 if (this.baseLayer != null && !this.allOverlays) {
8007                     this.baseLayer.setVisibility(false);
8008                 }
8009
8010                 // set new baselayer
8011                 this.baseLayer = newBaseLayer;
8012                 
8013                 if(!this.allOverlays || this.baseLayer.visibility) {
8014                     this.baseLayer.setVisibility(true);
8015                     // Layer may previously have been visible but not in range.
8016                     // In this case we need to redraw it to make it visible.
8017                     if (this.baseLayer.inRange === false) {
8018                         this.baseLayer.redraw();
8019                     }
8020                 }
8021
8022                 // recenter the map
8023                 if (center != null) {
8024                     // new zoom level derived from old scale
8025                     var newZoom = this.getZoomForResolution(
8026                         newResolution || this.resolution, true
8027                     );
8028                     // zoom and force zoom change
8029                     this.setCenter(center, newZoom, false, true);
8030                 }
8031
8032                 this.events.triggerEvent("changebaselayer", {
8033                     layer: this.baseLayer
8034                 });
8035             }        
8036         }
8037     },
8038
8039
8040   /********************************************************/
8041   /*                                                      */
8042   /*                 Control Functions                    */
8043   /*                                                      */
8044   /*     The following functions deal with adding and     */
8045   /*        removing Controls to and from the Map         */
8046   /*                                                      */
8047   /********************************************************/         
8048
8049     /**
8050      * APIMethod: addControl
8051      * Add the passed over control to the map. Optionally 
8052      *     position the control at the given pixel.
8053      * 
8054      * Parameters:
8055      * control - {<OpenLayers.Control>}
8056      * px - {<OpenLayers.Pixel>}
8057      */    
8058     addControl: function (control, px) {
8059         this.controls.push(control);
8060         this.addControlToMap(control, px);
8061     },
8062     
8063     /**
8064      * APIMethod: addControls
8065      * Add all of the passed over controls to the map. 
8066      *     You can pass over an optional second array
8067      *     with pixel-objects to position the controls.
8068      *     The indices of the two arrays should match and
8069      *     you can add null as pixel for those controls 
8070      *     you want to be autopositioned.   
8071      *     
8072      * Parameters:
8073      * controls - {Array(<OpenLayers.Control>)}
8074      * pixels - {Array(<OpenLayers.Pixel>)}
8075      */    
8076     addControls: function (controls, pixels) {
8077         var pxs = (arguments.length === 1) ? [] : pixels;
8078         for (var i=0, len=controls.length; i<len; i++) {
8079             var ctrl = controls[i];
8080             var px = (pxs[i]) ? pxs[i] : null;
8081             this.addControl( ctrl, px );
8082         }
8083     },
8084
8085     /**
8086      * Method: addControlToMap
8087      * 
8088      * Parameters:
8089      * 
8090      * control - {<OpenLayers.Control>}
8091      * px - {<OpenLayers.Pixel>}
8092      */    
8093     addControlToMap: function (control, px) {
8094         // If a control doesn't have a div at this point, it belongs in the
8095         // viewport.
8096         control.outsideViewport = (control.div != null);
8097         
8098         // If the map has a displayProjection, and the control doesn't, set 
8099         // the display projection.
8100         if (this.displayProjection && !control.displayProjection) {
8101             control.displayProjection = this.displayProjection;
8102         }    
8103         
8104         control.setMap(this);
8105         var div = control.draw(px);
8106         if (div) {
8107             if(!control.outsideViewport) {
8108                 div.style.zIndex = this.Z_INDEX_BASE['Control'] +
8109                                     this.controls.length;
8110                 this.viewPortDiv.appendChild( div );
8111             }
8112         }
8113         if(control.autoActivate) {
8114             control.activate();
8115         }
8116     },
8117     
8118     /**
8119      * APIMethod: getControl
8120      * 
8121      * Parameters:
8122      * id - {String} ID of the control to return.
8123      * 
8124      * Returns:
8125      * {<OpenLayers.Control>} The control from the map's list of controls 
8126      *                        which has a matching 'id'. If none found, 
8127      *                        returns null.
8128      */    
8129     getControl: function (id) {
8130         var returnControl = null;
8131         for(var i=0, len=this.controls.length; i<len; i++) {
8132             var control = this.controls[i];
8133             if (control.id == id) {
8134                 returnControl = control;
8135                 break;
8136             }
8137         }
8138         return returnControl;
8139     },
8140     
8141     /** 
8142      * APIMethod: removeControl
8143      * Remove a control from the map. Removes the control both from the map 
8144      *     object's internal array of controls, as well as from the map's 
8145      *     viewPort (assuming the control was not added outsideViewport)
8146      * 
8147      * Parameters:
8148      * control - {<OpenLayers.Control>} The control to remove.
8149      */    
8150     removeControl: function (control) {
8151         //make sure control is non-null and actually part of our map
8152         if ( (control) && (control == this.getControl(control.id)) ) {
8153             if (control.div && (control.div.parentNode == this.viewPortDiv)) {
8154                 this.viewPortDiv.removeChild(control.div);
8155             }
8156             OpenLayers.Util.removeItem(this.controls, control);
8157         }
8158     },
8159
8160   /********************************************************/
8161   /*                                                      */
8162   /*                  Popup Functions                     */
8163   /*                                                      */
8164   /*     The following functions deal with adding and     */
8165   /*        removing Popups to and from the Map           */
8166   /*                                                      */
8167   /********************************************************/         
8168
8169     /** 
8170      * APIMethod: addPopup
8171      * 
8172      * Parameters:
8173      * popup - {<OpenLayers.Popup>}
8174      * exclusive - {Boolean} If true, closes all other popups first
8175      */
8176     addPopup: function(popup, exclusive) {
8177
8178         if (exclusive) {
8179             //remove all other popups from screen
8180             for (var i = this.popups.length - 1; i >= 0; --i) {
8181                 this.removePopup(this.popups[i]);
8182             }
8183         }
8184
8185         popup.map = this;
8186         this.popups.push(popup);
8187         var popupDiv = popup.draw();
8188         if (popupDiv) {
8189             popupDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] +
8190                                     this.popups.length;
8191             this.layerContainerDiv.appendChild(popupDiv);
8192         }
8193     },
8194     
8195     /** 
8196     * APIMethod: removePopup
8197     * 
8198     * Parameters:
8199     * popup - {<OpenLayers.Popup>}
8200     */
8201     removePopup: function(popup) {
8202         OpenLayers.Util.removeItem(this.popups, popup);
8203         if (popup.div) {
8204             try { this.layerContainerDiv.removeChild(popup.div); }
8205             catch (e) { } // Popups sometimes apparently get disconnected
8206                       // from the layerContainerDiv, and cause complaints.
8207         }
8208         popup.map = null;
8209     },
8210
8211   /********************************************************/
8212   /*                                                      */
8213   /*              Container Div Functions                 */
8214   /*                                                      */
8215   /*   The following functions deal with the access to    */
8216   /*    and maintenance of the size of the container div  */
8217   /*                                                      */
8218   /********************************************************/     
8219
8220     /**
8221      * APIMethod: getSize
8222      * 
8223      * Returns:
8224      * {<OpenLayers.Size>} An <OpenLayers.Size> object that represents the 
8225      *                     size, in pixels, of the div into which OpenLayers 
8226      *                     has been loaded. 
8227      *                     Note - A clone() of this locally cached variable is
8228      *                     returned, so as not to allow users to modify it.
8229      */
8230     getSize: function () {
8231         var size = null;
8232         if (this.size != null) {
8233             size = this.size.clone();
8234         }
8235         return size;
8236     },
8237
8238     /**
8239      * APIMethod: updateSize
8240      * This function should be called by any external code which dynamically
8241      *     changes the size of the map div (because mozilla wont let us catch 
8242      *     the "onresize" for an element)
8243      */
8244     updateSize: function() {
8245         // the div might have moved on the page, also
8246         var newSize = this.getCurrentSize();
8247         if (newSize && !isNaN(newSize.h) && !isNaN(newSize.w)) {
8248             this.events.clearMouseCache();
8249             var oldSize = this.getSize();
8250             if (oldSize == null) {
8251                 this.size = oldSize = newSize;
8252             }
8253             if (!newSize.equals(oldSize)) {
8254                 
8255                 // store the new size
8256                 this.size = newSize;
8257     
8258                 //notify layers of mapresize
8259                 for(var i=0, len=this.layers.length; i<len; i++) {
8260                     this.layers[i].onMapResize();                
8261                 }
8262     
8263                 var center = this.getCachedCenter();
8264     
8265                 if (this.baseLayer != null && center != null) {
8266                     var zoom = this.getZoom();
8267                     this.zoom = null;
8268                     this.setCenter(center, zoom);
8269                 }
8270     
8271             }
8272         }
8273         this.events.triggerEvent("updatesize");
8274     },
8275     
8276     /**
8277      * Method: getCurrentSize
8278      * 
8279      * Returns:
8280      * {<OpenLayers.Size>} A new <OpenLayers.Size> object with the dimensions 
8281      *                     of the map div
8282      */
8283     getCurrentSize: function() {
8284
8285         var size = new OpenLayers.Size(this.div.clientWidth, 
8286                                        this.div.clientHeight);
8287
8288         if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
8289             size.w = this.div.offsetWidth;
8290             size.h = this.div.offsetHeight;
8291         }
8292         if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
8293             size.w = parseInt(this.div.style.width);
8294             size.h = parseInt(this.div.style.height);
8295         }
8296         return size;
8297     },
8298
8299     /** 
8300      * Method: calculateBounds
8301      * 
8302      * Parameters:
8303      * center - {<OpenLayers.LonLat>} Default is this.getCenter()
8304      * resolution - {float} Default is this.getResolution() 
8305      * 
8306      * Returns:
8307      * {<OpenLayers.Bounds>} A bounds based on resolution, center, and 
8308      *                       current mapsize.
8309      */
8310     calculateBounds: function(center, resolution) {
8311
8312         var extent = null;
8313         
8314         if (center == null) {
8315             center = this.getCachedCenter();
8316         }                
8317         if (resolution == null) {
8318             resolution = this.getResolution();
8319         }
8320     
8321         if ((center != null) && (resolution != null)) {
8322             var halfWDeg = (this.size.w * resolution) / 2;
8323             var halfHDeg = (this.size.h * resolution) / 2;
8324         
8325             extent = new OpenLayers.Bounds(center.lon - halfWDeg,
8326                                            center.lat - halfHDeg,
8327                                            center.lon + halfWDeg,
8328                                            center.lat + halfHDeg);
8329         }
8330
8331         return extent;
8332     },
8333
8334
8335   /********************************************************/
8336   /*                                                      */
8337   /*            Zoom, Center, Pan Functions               */
8338   /*                                                      */
8339   /*    The following functions handle the validation,    */
8340   /*   getting and setting of the Zoom Level and Center   */
8341   /*       as well as the panning of the Map              */
8342   /*                                                      */
8343   /********************************************************/
8344     /**
8345      * APIMethod: getCenter
8346      * 
8347      * Returns:
8348      * {<OpenLayers.LonLat>}
8349      */
8350     getCenter: function () {
8351         var center = null;
8352         var cachedCenter = this.getCachedCenter();
8353         if (cachedCenter) {
8354             center = cachedCenter.clone();
8355         }
8356         return center;
8357     },
8358
8359     /**
8360      * Method: getCachedCenter
8361      *
8362      * Returns:
8363      * {<OpenLayers.LonLat>}
8364      */
8365     getCachedCenter: function() {
8366         if (!this.center && this.size) {
8367             this.center = this.getLonLatFromViewPortPx({
8368                 x: this.size.w / 2,
8369                 y: this.size.h / 2
8370             });
8371         }
8372         return this.center;
8373     },
8374
8375     /**
8376      * APIMethod: getZoom
8377      * 
8378      * Returns:
8379      * {Integer}
8380      */
8381     getZoom: function () {
8382         return this.zoom;
8383     },
8384     
8385     /** 
8386      * APIMethod: pan
8387      * Allows user to pan by a value of screen pixels
8388      * 
8389      * Parameters:
8390      * dx - {Integer}
8391      * dy - {Integer}
8392      * options - {Object} Options to configure panning:
8393      *  - *animate* {Boolean} Use panTo instead of setCenter. Default is true.
8394      *  - *dragging* {Boolean} Call setCenter with dragging true.  Default is
8395      *    false.
8396      */
8397     pan: function(dx, dy, options) {
8398         options = OpenLayers.Util.applyDefaults(options, {
8399             animate: true,
8400             dragging: false
8401         });
8402         if (options.dragging) {
8403             if (dx != 0 || dy != 0) {
8404                 this.moveByPx(dx, dy);
8405             }
8406         } else {
8407             // getCenter
8408             var centerPx = this.getViewPortPxFromLonLat(this.getCachedCenter());
8409
8410             // adjust
8411             var newCenterPx = centerPx.add(dx, dy);
8412
8413             if (this.dragging || !newCenterPx.equals(centerPx)) {
8414                 var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);
8415                 if (options.animate) {
8416                     this.panTo(newCenterLonLat);
8417                 } else {
8418                     this.moveTo(newCenterLonLat);
8419                     if(this.dragging) {
8420                         this.dragging = false;
8421                         this.events.triggerEvent("moveend");
8422                     }
8423                 }    
8424             }
8425         }        
8426
8427    },
8428    
8429    /** 
8430      * APIMethod: panTo
8431      * Allows user to pan to a new lonlat
8432      * If the new lonlat is in the current extent the map will slide smoothly
8433      * 
8434      * Parameters:
8435      * lonlat - {<OpenLayers.LonLat>}
8436      */
8437     panTo: function(lonlat) {
8438         if (this.panTween && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) {
8439             var center = this.getCachedCenter();
8440
8441             // center will not change, don't do nothing
8442             if (lonlat.equals(center)) {
8443                 return;
8444             }
8445
8446             var from = this.getPixelFromLonLat(center);
8447             var to = this.getPixelFromLonLat(lonlat);
8448             var vector = { x: to.x - from.x, y: to.y - from.y };
8449             var last = { x: 0, y: 0 };
8450
8451             this.panTween.start( { x: 0, y: 0 }, vector, this.panDuration, {
8452                 callbacks: {
8453                     eachStep: OpenLayers.Function.bind(function(px) {
8454                         var x = px.x - last.x,
8455                             y = px.y - last.y;
8456                         this.moveByPx(x, y);
8457                         last.x = Math.round(px.x);
8458                         last.y = Math.round(px.y);
8459                     }, this),
8460                     done: OpenLayers.Function.bind(function(px) {
8461                         this.moveTo(lonlat);
8462                         this.dragging = false;
8463                         this.events.triggerEvent("moveend");
8464                     }, this)
8465                 }
8466             });
8467         } else {
8468             this.setCenter(lonlat);
8469         }
8470     },
8471
8472     /**
8473      * APIMethod: setCenter
8474      * Set the map center (and optionally, the zoom level).
8475      * 
8476      * Parameters:
8477      * lonlat - {<OpenLayers.LonLat>|Array} The new center location.
8478      *     If provided as array, the first value is the x coordinate,
8479      *     and the 2nd value is the y coordinate.
8480      * zoom - {Integer} Optional zoom level.
8481      * dragging - {Boolean} Specifies whether or not to trigger 
8482      *                      movestart/end events
8483      * forceZoomChange - {Boolean} Specifies whether or not to trigger zoom 
8484      *                             change events (needed on baseLayer change)
8485      *
8486      * TBD: reconsider forceZoomChange in 3.0
8487      */
8488     setCenter: function(lonlat, zoom, dragging, forceZoomChange) {
8489         if (this.panTween) {
8490             this.panTween.stop();
8491         }
8492         if (this.zoomTween) {
8493             this.zoomTween.stop();
8494         }            
8495         this.moveTo(lonlat, zoom, {
8496             'dragging': dragging,
8497             'forceZoomChange': forceZoomChange
8498         });
8499     },
8500     
8501     /** 
8502      * Method: moveByPx
8503      * Drag the map by pixels.
8504      *
8505      * Parameters:
8506      * dx - {Number}
8507      * dy - {Number}
8508      */
8509     moveByPx: function(dx, dy) {
8510         var hw = this.size.w / 2;
8511         var hh = this.size.h / 2;
8512         var x = hw + dx;
8513         var y = hh + dy;
8514         var wrapDateLine = this.baseLayer.wrapDateLine;
8515         var xRestriction = 0;
8516         var yRestriction = 0;
8517         if (this.restrictedExtent) {
8518             xRestriction = hw;
8519             yRestriction = hh;
8520             // wrapping the date line makes no sense for restricted extents
8521             wrapDateLine = false;
8522         }
8523         dx = wrapDateLine ||
8524                     x <= this.maxPx.x - xRestriction &&
8525                     x >= this.minPx.x + xRestriction ? Math.round(dx) : 0;
8526         dy = y <= this.maxPx.y - yRestriction &&
8527                     y >= this.minPx.y + yRestriction ? Math.round(dy) : 0;
8528         if (dx || dy) {
8529             if (!this.dragging) {
8530                 this.dragging = true;
8531                 this.events.triggerEvent("movestart");
8532             }
8533             this.center = null;
8534             if (dx) {
8535                 this.layerContainerOriginPx.x -= dx;
8536                 this.minPx.x -= dx;
8537                 this.maxPx.x -= dx;
8538             }
8539             if (dy) {
8540                 this.layerContainerOriginPx.y -= dy;
8541                 this.minPx.y -= dy;
8542                 this.maxPx.y -= dy;
8543             }
8544             this.applyTransform();
8545             var layer, i, len;
8546             for (i=0, len=this.layers.length; i<len; ++i) {
8547                 layer = this.layers[i];
8548                 if (layer.visibility &&
8549                     (layer === this.baseLayer || layer.inRange)) {
8550                     layer.moveByPx(dx, dy);
8551                     layer.events.triggerEvent("move");
8552                 }
8553             }
8554             this.events.triggerEvent("move");
8555         }
8556     },
8557     
8558     /**
8559      * Method: adjustZoom
8560      *
8561      * Parameters:
8562      * zoom - {Number} The zoom level to adjust
8563      *
8564      * Returns:
8565      * {Integer} Adjusted zoom level that shows a map not wider than its
8566      * <baseLayer>'s maxExtent.
8567      */
8568     adjustZoom: function(zoom) {
8569         if (this.baseLayer && this.baseLayer.wrapDateLine) {
8570             var resolution, resolutions = this.baseLayer.resolutions,
8571                 maxResolution = this.getMaxExtent().getWidth() / this.size.w;
8572             if (this.getResolutionForZoom(zoom) > maxResolution) {
8573                 if (this.fractionalZoom) {
8574                     zoom = this.getZoomForResolution(maxResolution);
8575                 } else {
8576                     for (var i=zoom|0, ii=resolutions.length; i<ii; ++i) {
8577                         if (resolutions[i] <= maxResolution) {
8578                             zoom = i;
8579                             break;
8580                         }
8581                     }
8582                 } 
8583             }
8584         }
8585         return zoom;
8586     },
8587     
8588     /**
8589      * APIMethod: getMinZoom
8590      * Returns the minimum zoom level for the current map view. If the base
8591      * layer is configured with <wrapDateLine> set to true, this will be the
8592      * first zoom level that shows no more than one world width in the current
8593      * map viewport. Components that rely on this value (e.g. zoom sliders)
8594      * should also listen to the map's "updatesize" event and call this method
8595      * in the "updatesize" listener.
8596      *
8597      * Returns:
8598      * {Number} Minimum zoom level that shows a map not wider than its
8599      * <baseLayer>'s maxExtent. This is an Integer value, unless the map is
8600      * configured with <fractionalZoom> set to true.
8601      */
8602     getMinZoom: function() {
8603         return this.adjustZoom(0);
8604     },
8605
8606     /**
8607      * Method: moveTo
8608      *
8609      * Parameters:
8610      * lonlat - {<OpenLayers.LonLat>}
8611      * zoom - {Integer}
8612      * options - {Object}
8613      */
8614     moveTo: function(lonlat, zoom, options) {
8615         if (lonlat != null && !(lonlat instanceof OpenLayers.LonLat)) {
8616             lonlat = new OpenLayers.LonLat(lonlat);
8617         }
8618         if (!options) { 
8619             options = {};
8620         }
8621         if (zoom != null) {
8622             zoom = parseFloat(zoom);
8623             if (!this.fractionalZoom) {
8624                 zoom = Math.round(zoom);
8625             }
8626         }
8627         var requestedZoom = zoom;
8628         zoom = this.adjustZoom(zoom);
8629         if (zoom !== requestedZoom) {
8630             // zoom was adjusted, so keep old lonlat to avoid panning
8631             lonlat = this.getCenter();
8632         }
8633         // dragging is false by default
8634         var dragging = options.dragging || this.dragging;
8635         // forceZoomChange is false by default
8636         var forceZoomChange = options.forceZoomChange;
8637
8638         if (!this.getCachedCenter() && !this.isValidLonLat(lonlat)) {
8639             lonlat = this.maxExtent.getCenterLonLat();
8640             this.center = lonlat.clone();
8641         }
8642
8643         if(this.restrictedExtent != null) {
8644             // In 3.0, decide if we want to change interpretation of maxExtent.
8645             if(lonlat == null) { 
8646                 lonlat = this.center; 
8647             }
8648             if(zoom == null) { 
8649                 zoom = this.getZoom(); 
8650             }
8651             var resolution = this.getResolutionForZoom(zoom);
8652             var extent = this.calculateBounds(lonlat, resolution); 
8653             if(!this.restrictedExtent.containsBounds(extent)) {
8654                 var maxCenter = this.restrictedExtent.getCenterLonLat(); 
8655                 if(extent.getWidth() > this.restrictedExtent.getWidth()) { 
8656                     lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat); 
8657                 } else if(extent.left < this.restrictedExtent.left) {
8658                     lonlat = lonlat.add(this.restrictedExtent.left -
8659                                         extent.left, 0); 
8660                 } else if(extent.right > this.restrictedExtent.right) { 
8661                     lonlat = lonlat.add(this.restrictedExtent.right -
8662                                         extent.right, 0); 
8663                 } 
8664                 if(extent.getHeight() > this.restrictedExtent.getHeight()) { 
8665                     lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat); 
8666                 } else if(extent.bottom < this.restrictedExtent.bottom) { 
8667                     lonlat = lonlat.add(0, this.restrictedExtent.bottom -
8668                                         extent.bottom); 
8669                 } 
8670                 else if(extent.top > this.restrictedExtent.top) { 
8671                     lonlat = lonlat.add(0, this.restrictedExtent.top -
8672                                         extent.top); 
8673                 } 
8674             }
8675         }
8676         
8677         var zoomChanged = forceZoomChange || (
8678                             (this.isValidZoomLevel(zoom)) && 
8679                             (zoom != this.getZoom()) );
8680
8681         var centerChanged = (this.isValidLonLat(lonlat)) && 
8682                             (!lonlat.equals(this.center));
8683
8684         // if neither center nor zoom will change, no need to do anything
8685         if (zoomChanged || centerChanged || dragging) {
8686             dragging || this.events.triggerEvent("movestart", {
8687                 zoomChanged: zoomChanged
8688             });
8689
8690             if (centerChanged) {
8691                 if (!zoomChanged && this.center) { 
8692                     // if zoom hasnt changed, just slide layerContainer
8693                     //  (must be done before setting this.center to new value)
8694                     this.centerLayerContainer(lonlat);
8695                 }
8696                 this.center = lonlat.clone();
8697             }
8698
8699             var res = zoomChanged ?
8700                 this.getResolutionForZoom(zoom) : this.getResolution();
8701             // (re)set the layerContainerDiv's location
8702             if (zoomChanged || this.layerContainerOrigin == null) {
8703                 this.layerContainerOrigin = this.getCachedCenter();
8704                 this.layerContainerOriginPx.x = 0;
8705                 this.layerContainerOriginPx.y = 0;
8706                 this.applyTransform();
8707                 var maxExtent = this.getMaxExtent({restricted: true});
8708                 var maxExtentCenter = maxExtent.getCenterLonLat();
8709                 var lonDelta = this.center.lon - maxExtentCenter.lon;
8710                 var latDelta = maxExtentCenter.lat - this.center.lat;
8711                 var extentWidth = Math.round(maxExtent.getWidth() / res);
8712                 var extentHeight = Math.round(maxExtent.getHeight() / res);
8713                 this.minPx = {
8714                     x: (this.size.w - extentWidth) / 2 - lonDelta / res,
8715                     y: (this.size.h - extentHeight) / 2 - latDelta / res
8716                 };
8717                 this.maxPx = {
8718                     x: this.minPx.x + Math.round(maxExtent.getWidth() / res),
8719                     y: this.minPx.y + Math.round(maxExtent.getHeight() / res)
8720                 };
8721             }
8722
8723             if (zoomChanged) {
8724                 this.zoom = zoom;
8725                 this.resolution = res;
8726             }    
8727             
8728             var bounds = this.getExtent();
8729             
8730             //send the move call to the baselayer and all the overlays    
8731
8732             if(this.baseLayer.visibility) {
8733                 this.baseLayer.moveTo(bounds, zoomChanged, options.dragging);
8734                 options.dragging || this.baseLayer.events.triggerEvent(
8735                     "moveend", {zoomChanged: zoomChanged}
8736                 );
8737             }
8738             
8739             bounds = this.baseLayer.getExtent();
8740             
8741             for (var i=this.layers.length-1; i>=0; --i) {
8742                 var layer = this.layers[i];
8743                 if (layer !== this.baseLayer && !layer.isBaseLayer) {
8744                     var inRange = layer.calculateInRange();
8745                     if (layer.inRange != inRange) {
8746                         // the inRange property has changed. If the layer is
8747                         // no longer in range, we turn it off right away. If
8748                         // the layer is no longer out of range, the moveTo
8749                         // call below will turn on the layer.
8750                         layer.inRange = inRange;
8751                         if (!inRange) {
8752                             layer.display(false);
8753                         }
8754                         this.events.triggerEvent("changelayer", {
8755                             layer: layer, property: "visibility"
8756                         });
8757                     }
8758                     if (inRange && layer.visibility) {
8759                         layer.moveTo(bounds, zoomChanged, options.dragging);
8760                         options.dragging || layer.events.triggerEvent(
8761                             "moveend", {zoomChanged: zoomChanged}
8762                         );
8763                     }
8764                 }                
8765             }
8766             
8767             this.events.triggerEvent("move");
8768             dragging || this.events.triggerEvent("moveend");
8769
8770             if (zoomChanged) {
8771                 //redraw popups
8772                 for (var i=0, len=this.popups.length; i<len; i++) {
8773                     this.popups[i].updatePosition();
8774                 }
8775                 this.events.triggerEvent("zoomend");
8776             }
8777         }
8778     },
8779
8780     /** 
8781      * Method: centerLayerContainer
8782      * This function takes care to recenter the layerContainerDiv.
8783      * 
8784      * Parameters:
8785      * lonlat - {<OpenLayers.LonLat>}
8786      */
8787     centerLayerContainer: function (lonlat) {
8788         var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);
8789         var newPx = this.getViewPortPxFromLonLat(lonlat);
8790
8791         if ((originPx != null) && (newPx != null)) {
8792             var oldLeft = this.layerContainerOriginPx.x;
8793             var oldTop = this.layerContainerOriginPx.y;
8794             var newLeft = Math.round(originPx.x - newPx.x);
8795             var newTop = Math.round(originPx.y - newPx.y);
8796             this.applyTransform(
8797                 (this.layerContainerOriginPx.x = newLeft),
8798                 (this.layerContainerOriginPx.y = newTop));
8799             var dx = oldLeft - newLeft;
8800             var dy = oldTop - newTop;
8801             this.minPx.x -= dx;
8802             this.maxPx.x -= dx;
8803             this.minPx.y -= dy;
8804             this.maxPx.y -= dy;
8805         }        
8806     },
8807
8808     /**
8809      * Method: isValidZoomLevel
8810      * 
8811      * Parameters:
8812      * zoomLevel - {Integer}
8813      * 
8814      * Returns:
8815      * {Boolean} Whether or not the zoom level passed in is non-null and 
8816      *           within the min/max range of zoom levels.
8817      */
8818     isValidZoomLevel: function(zoomLevel) {
8819         return ( (zoomLevel != null) &&
8820                  (zoomLevel >= 0) && 
8821                  (zoomLevel < this.getNumZoomLevels()) );
8822     },
8823     
8824     /**
8825      * Method: isValidLonLat
8826      * 
8827      * Parameters:
8828      * lonlat - {<OpenLayers.LonLat>}
8829      * 
8830      * Returns:
8831      * {Boolean} Whether or not the lonlat passed in is non-null and within
8832      *           the maxExtent bounds
8833      */
8834     isValidLonLat: function(lonlat) {
8835         var valid = false;
8836         if (lonlat != null) {
8837             var maxExtent = this.getMaxExtent();
8838             var worldBounds = this.baseLayer.wrapDateLine && maxExtent;
8839             valid = maxExtent.containsLonLat(lonlat, {worldBounds: worldBounds});
8840         }
8841         return valid;
8842     },
8843
8844   /********************************************************/
8845   /*                                                      */
8846   /*                 Layer Options                        */
8847   /*                                                      */
8848   /*    Accessor functions to Layer Options parameters    */
8849   /*                                                      */
8850   /********************************************************/
8851     
8852     /**
8853      * APIMethod: getProjection
8854      * This method returns a string representing the projection. In 
8855      *     the case of projection support, this will be the srsCode which
8856      *     is loaded -- otherwise it will simply be the string value that
8857      *     was passed to the projection at startup.
8858      *
8859      * FIXME: In 3.0, we will remove getProjectionObject, and instead
8860      *     return a Projection object from this function. 
8861      * 
8862      * Returns:
8863      * {String} The Projection string from the base layer or null. 
8864      */
8865     getProjection: function() {
8866         var projection = this.getProjectionObject();
8867         return projection ? projection.getCode() : null;
8868     },
8869     
8870     /**
8871      * APIMethod: getProjectionObject
8872      * Returns the projection obect from the baselayer.
8873      *
8874      * Returns:
8875      * {<OpenLayers.Projection>} The Projection of the base layer.
8876      */
8877     getProjectionObject: function() {
8878         var projection = null;
8879         if (this.baseLayer != null) {
8880             projection = this.baseLayer.projection;
8881         }
8882         return projection;
8883     },
8884     
8885     /**
8886      * APIMethod: getMaxResolution
8887      * 
8888      * Returns:
8889      * {String} The Map's Maximum Resolution
8890      */
8891     getMaxResolution: function() {
8892         var maxResolution = null;
8893         if (this.baseLayer != null) {
8894             maxResolution = this.baseLayer.maxResolution;
8895         }
8896         return maxResolution;
8897     },
8898         
8899     /**
8900      * APIMethod: getMaxExtent
8901      *
8902      * Parameters:
8903      * options - {Object} 
8904      * 
8905      * Allowed Options:
8906      * restricted - {Boolean} If true, returns restricted extent (if it is 
8907      *     available.)
8908      *
8909      * Returns:
8910      * {<OpenLayers.Bounds>} The maxExtent property as set on the current 
8911      *     baselayer, unless the 'restricted' option is set, in which case
8912      *     the 'restrictedExtent' option from the map is returned (if it
8913      *     is set).
8914      */
8915     getMaxExtent: function (options) {
8916         var maxExtent = null;
8917         if(options && options.restricted && this.restrictedExtent){
8918             maxExtent = this.restrictedExtent;
8919         } else if (this.baseLayer != null) {
8920             maxExtent = this.baseLayer.maxExtent;
8921         }        
8922         return maxExtent;
8923     },
8924     
8925     /**
8926      * APIMethod: getNumZoomLevels
8927      * 
8928      * Returns:
8929      * {Integer} The total number of zoom levels that can be displayed by the 
8930      *           current baseLayer.
8931      */
8932     getNumZoomLevels: function() {
8933         var numZoomLevels = null;
8934         if (this.baseLayer != null) {
8935             numZoomLevels = this.baseLayer.numZoomLevels;
8936         }
8937         return numZoomLevels;
8938     },
8939
8940   /********************************************************/
8941   /*                                                      */
8942   /*                 Baselayer Functions                  */
8943   /*                                                      */
8944   /*    The following functions, all publicly exposed     */
8945   /*       in the API?, are all merely wrappers to the    */
8946   /*       the same calls on whatever layer is set as     */
8947   /*                the current base layer                */
8948   /*                                                      */
8949   /********************************************************/
8950
8951     /**
8952      * APIMethod: getExtent
8953      * 
8954      * Returns:
8955      * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat 
8956      *                       bounds of the current viewPort. 
8957      *                       If no baselayer is set, returns null.
8958      */
8959     getExtent: function () {
8960         var extent = null;
8961         if (this.baseLayer != null) {
8962             extent = this.baseLayer.getExtent();
8963         }
8964         return extent;
8965     },
8966
8967     /**
8968      * APIMethod: getResolution
8969      * 
8970      * Returns:
8971      * {Float} The current resolution of the map. 
8972      *         If no baselayer is set, returns null.
8973      */
8974     getResolution: function () {
8975         var resolution = null;
8976         if (this.baseLayer != null) {
8977             resolution = this.baseLayer.getResolution();
8978         } else if(this.allOverlays === true && this.layers.length > 0) {
8979             // while adding the 1st layer to the map in allOverlays mode,
8980             // this.baseLayer is not set yet when we need the resolution
8981             // for calculateInRange.
8982             resolution = this.layers[0].getResolution();
8983         }
8984         return resolution;
8985     },
8986
8987     /**
8988      * APIMethod: getUnits
8989      * 
8990      * Returns:
8991      * {Float} The current units of the map. 
8992      *         If no baselayer is set, returns null.
8993      */
8994     getUnits: function () {
8995         var units = null;
8996         if (this.baseLayer != null) {
8997             units = this.baseLayer.units;
8998         }
8999         return units;
9000     },
9001
9002      /**
9003       * APIMethod: getScale
9004       * 
9005       * Returns:
9006       * {Float} The current scale denominator of the map. 
9007       *         If no baselayer is set, returns null.
9008       */
9009     getScale: function () {
9010         var scale = null;
9011         if (this.baseLayer != null) {
9012             var res = this.getResolution();
9013             var units = this.baseLayer.units;
9014             scale = OpenLayers.Util.getScaleFromResolution(res, units);
9015         }
9016         return scale;
9017     },
9018
9019
9020     /**
9021      * APIMethod: getZoomForExtent
9022      * 
9023      * Parameters: 
9024      * bounds - {<OpenLayers.Bounds>}
9025      * closest - {Boolean} Find the zoom level that most closely fits the 
9026      *     specified bounds. Note that this may result in a zoom that does 
9027      *     not exactly contain the entire extent.
9028      *     Default is false.
9029      * 
9030      * Returns:
9031      * {Integer} A suitable zoom level for the specified bounds.
9032      *           If no baselayer is set, returns null.
9033      */
9034     getZoomForExtent: function (bounds, closest) {
9035         var zoom = null;
9036         if (this.baseLayer != null) {
9037             zoom = this.baseLayer.getZoomForExtent(bounds, closest);
9038         }
9039         return zoom;
9040     },
9041
9042     /**
9043      * APIMethod: getResolutionForZoom
9044      * 
9045      * Parameters:
9046      * zoom - {Float}
9047      * 
9048      * Returns:
9049      * {Float} A suitable resolution for the specified zoom.  If no baselayer
9050      *     is set, returns null.
9051      */
9052     getResolutionForZoom: function(zoom) {
9053         var resolution = null;
9054         if(this.baseLayer) {
9055             resolution = this.baseLayer.getResolutionForZoom(zoom);
9056         }
9057         return resolution;
9058     },
9059
9060     /**
9061      * APIMethod: getZoomForResolution
9062      * 
9063      * Parameters:
9064      * resolution - {Float}
9065      * closest - {Boolean} Find the zoom level that corresponds to the absolute 
9066      *     closest resolution, which may result in a zoom whose corresponding
9067      *     resolution is actually smaller than we would have desired (if this
9068      *     is being called from a getZoomForExtent() call, then this means that
9069      *     the returned zoom index might not actually contain the entire 
9070      *     extent specified... but it'll be close).
9071      *     Default is false.
9072      * 
9073      * Returns:
9074      * {Integer} A suitable zoom level for the specified resolution.
9075      *           If no baselayer is set, returns null.
9076      */
9077     getZoomForResolution: function(resolution, closest) {
9078         var zoom = null;
9079         if (this.baseLayer != null) {
9080             zoom = this.baseLayer.getZoomForResolution(resolution, closest);
9081         }
9082         return zoom;
9083     },
9084
9085   /********************************************************/
9086   /*                                                      */
9087   /*                  Zooming Functions                   */
9088   /*                                                      */
9089   /*    The following functions, all publicly exposed     */
9090   /*       in the API, are all merely wrappers to the     */
9091   /*               the setCenter() function               */
9092   /*                                                      */
9093   /********************************************************/
9094   
9095     /** 
9096      * APIMethod: zoomTo
9097      * Zoom to a specific zoom level. Zooming will be animated unless the map
9098      * is configured with {zoomMethod: null}. To zoom without animation, use
9099      * <setCenter> without a lonlat argument.
9100      * 
9101      * Parameters:
9102      * zoom - {Integer}
9103      */
9104     zoomTo: function(zoom, xy) {
9105         // non-API arguments:
9106         // xy - {<OpenLayers.Pixel>} optional zoom origin
9107         
9108         var map = this;
9109         if (map.isValidZoomLevel(zoom)) {
9110             if (map.baseLayer.wrapDateLine) {
9111                 zoom = map.adjustZoom(zoom);
9112             }
9113             if (map.zoomTween) {
9114                 var currentRes = map.getResolution(),
9115                     targetRes = map.getResolutionForZoom(zoom),
9116                     start = {scale: 1},
9117                     end = {scale: currentRes / targetRes};
9118                 if (map.zoomTween.playing && map.zoomTween.duration < 3 * map.zoomDuration) {
9119                     // update the end scale, and reuse the running zoomTween
9120                     map.zoomTween.finish = {
9121                         scale: map.zoomTween.finish.scale * end.scale
9122                     };
9123                 } else {
9124                     if (!xy) {
9125                         var size = map.getSize();
9126                         xy = {x: size.w / 2, y: size.h / 2};
9127                     }
9128                     map.zoomTween.start(start, end, map.zoomDuration, {
9129                         minFrameRate: 50, // don't spend much time zooming
9130                         callbacks: {
9131                             eachStep: function(data) {
9132                                 var containerOrigin = map.layerContainerOriginPx,
9133                                     scale = data.scale,
9134                                     dx = ((scale - 1) * (containerOrigin.x - xy.x)) | 0,
9135                                     dy = ((scale - 1) * (containerOrigin.y - xy.y)) | 0;
9136                                 map.applyTransform(containerOrigin.x + dx, containerOrigin.y + dy, scale);
9137                             },
9138                             done: function(data) {
9139                                 map.applyTransform();
9140                                 var resolution = map.getResolution() / data.scale,
9141                                     zoom = map.getZoomForResolution(resolution, true)
9142                                 map.moveTo(map.getZoomTargetCenter(xy, resolution), zoom, true);
9143                             }
9144                         }
9145                     });
9146                 }
9147             } else {
9148                 var center = xy ?
9149                     map.getZoomTargetCenter(xy, map.getResolutionForZoom(zoom)) :
9150                     null;
9151                 map.setCenter(center, zoom);
9152             }
9153         }
9154     },
9155         
9156     /**
9157      * APIMethod: zoomIn
9158      * 
9159      */
9160     zoomIn: function() {
9161         this.zoomTo(this.getZoom() + 1);
9162     },
9163     
9164     /**
9165      * APIMethod: zoomOut
9166      * 
9167      */
9168     zoomOut: function() {
9169         this.zoomTo(this.getZoom() - 1);
9170     },
9171
9172     /**
9173      * APIMethod: zoomToExtent
9174      * Zoom to the passed in bounds, recenter
9175      * 
9176      * Parameters:
9177      * bounds - {<OpenLayers.Bounds>|Array} If provided as an array, the array
9178      *     should consist of four values (left, bottom, right, top).
9179      * closest - {Boolean} Find the zoom level that most closely fits the 
9180      *     specified bounds. Note that this may result in a zoom that does 
9181      *     not exactly contain the entire extent.
9182      *     Default is false.
9183      * 
9184      */
9185     zoomToExtent: function(bounds, closest) {
9186         if (!(bounds instanceof OpenLayers.Bounds)) {
9187             bounds = new OpenLayers.Bounds(bounds);
9188         }
9189         var center = bounds.getCenterLonLat();
9190         if (this.baseLayer.wrapDateLine) {
9191             var maxExtent = this.getMaxExtent();
9192
9193             //fix straddling bounds (in the case of a bbox that straddles the 
9194             // dateline, it's left and right boundaries will appear backwards. 
9195             // we fix this by allowing a right value that is greater than the
9196             // max value at the dateline -- this allows us to pass a valid 
9197             // bounds to calculate zoom)
9198             //
9199             bounds = bounds.clone();
9200             while (bounds.right < bounds.left) {
9201                 bounds.right += maxExtent.getWidth();
9202             }
9203             //if the bounds was straddling (see above), then the center point 
9204             // we got from it was wrong. So we take our new bounds and ask it
9205             // for the center.
9206             //
9207             center = bounds.getCenterLonLat().wrapDateLine(maxExtent);
9208         }
9209         this.setCenter(center, this.getZoomForExtent(bounds, closest));
9210     },
9211
9212     /** 
9213      * APIMethod: zoomToMaxExtent
9214      * Zoom to the full extent and recenter.
9215      *
9216      * Parameters:
9217      * options - {Object}
9218      * 
9219      * Allowed Options:
9220      * restricted - {Boolean} True to zoom to restricted extent if it is 
9221      *     set. Defaults to true.
9222      */
9223     zoomToMaxExtent: function(options) {
9224         //restricted is true by default
9225         var restricted = (options) ? options.restricted : true;
9226
9227         var maxExtent = this.getMaxExtent({
9228             'restricted': restricted 
9229         });
9230         this.zoomToExtent(maxExtent);
9231     },
9232
9233     /** 
9234      * APIMethod: zoomToScale
9235      * Zoom to a specified scale 
9236      * 
9237      * Parameters:
9238      * scale - {float}
9239      * closest - {Boolean} Find the zoom level that most closely fits the 
9240      *     specified scale. Note that this may result in a zoom that does 
9241      *     not exactly contain the entire extent.
9242      *     Default is false.
9243      * 
9244      */
9245     zoomToScale: function(scale, closest) {
9246         var res = OpenLayers.Util.getResolutionFromScale(scale, 
9247                                                          this.baseLayer.units);
9248
9249         var halfWDeg = (this.size.w * res) / 2;
9250         var halfHDeg = (this.size.h * res) / 2;
9251         var center = this.getCachedCenter();
9252
9253         var extent = new OpenLayers.Bounds(center.lon - halfWDeg,
9254                                            center.lat - halfHDeg,
9255                                            center.lon + halfWDeg,
9256                                            center.lat + halfHDeg);
9257         this.zoomToExtent(extent, closest);
9258     },
9259     
9260   /********************************************************/
9261   /*                                                      */
9262   /*             Translation Functions                    */
9263   /*                                                      */
9264   /*      The following functions translate between       */
9265   /*           LonLat, LayerPx, and ViewPortPx            */
9266   /*                                                      */
9267   /********************************************************/
9268       
9269   //
9270   // TRANSLATION: LonLat <-> ViewPortPx
9271   //
9272
9273     /**
9274      * Method: getLonLatFromViewPortPx
9275      * 
9276      * Parameters:
9277      * viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or
9278      *                                          an object with a 'x'
9279      *                                          and 'y' properties.
9280      * 
9281      * Returns:
9282      * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view 
9283      *                       port <OpenLayers.Pixel>, translated into lon/lat
9284      *                       by the current base layer.
9285      */
9286     getLonLatFromViewPortPx: function (viewPortPx) {
9287         var lonlat = null; 
9288         if (this.baseLayer != null) {
9289             lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);
9290         }
9291         return lonlat;
9292     },
9293
9294     /**
9295      * APIMethod: getViewPortPxFromLonLat
9296      * 
9297      * Parameters:
9298      * lonlat - {<OpenLayers.LonLat>}
9299      * 
9300      * Returns:
9301      * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in 
9302      *                      <OpenLayers.LonLat>, translated into view port 
9303      *                      pixels by the current base layer.
9304      */
9305     getViewPortPxFromLonLat: function (lonlat) {
9306         var px = null; 
9307         if (this.baseLayer != null) {
9308             px = this.baseLayer.getViewPortPxFromLonLat(lonlat);
9309         }
9310         return px;
9311     },
9312
9313     /**
9314      * Method: getZoomTargetCenter
9315      *
9316      * Parameters:
9317      * xy - {<OpenLayers.Pixel>} The zoom origin pixel location on the screen
9318      * resolution - {Float} The resolution we want to get the center for
9319      *
9320      * Returns:
9321      * {<OpenLayers.LonLat>} The location of the map center after the
9322      *     transformation described by the origin xy and the target resolution.
9323      */
9324     getZoomTargetCenter: function (xy, resolution) {
9325         var lonlat = null,
9326             size = this.getSize(),
9327             deltaX  = size.w/2 - xy.x,
9328             deltaY  = xy.y - size.h/2,
9329             zoomPoint = this.getLonLatFromPixel(xy);
9330         if (zoomPoint) {
9331             lonlat = new OpenLayers.LonLat(
9332                 zoomPoint.lon + deltaX * resolution,
9333                 zoomPoint.lat + deltaY * resolution
9334             );
9335         }
9336         return lonlat;
9337     },
9338         
9339   //
9340   // CONVENIENCE TRANSLATION FUNCTIONS FOR API
9341   //
9342
9343     /**
9344      * APIMethod: getLonLatFromPixel
9345      * 
9346      * Parameters:
9347      * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object with
9348      *                                  a 'x' and 'y' properties.
9349      *
9350      * Returns:
9351      * {<OpenLayers.LonLat>} An OpenLayers.LonLat corresponding to the given
9352      *                       OpenLayers.Pixel, translated into lon/lat by the 
9353      *                       current base layer
9354      */
9355     getLonLatFromPixel: function (px) {
9356         return this.getLonLatFromViewPortPx(px);
9357     },
9358
9359     /**
9360      * APIMethod: getPixelFromLonLat
9361      * Returns a pixel location given a map location.  The map location is
9362      *     translated to an integer pixel location (in viewport pixel
9363      *     coordinates) by the current base layer.
9364      * 
9365      * Parameters:
9366      * lonlat - {<OpenLayers.LonLat>} A map location.
9367      * 
9368      * Returns: 
9369      * {<OpenLayers.Pixel>} An OpenLayers.Pixel corresponding to the 
9370      *     <OpenLayers.LonLat> translated into view port pixels by the current
9371      *     base layer.
9372      */
9373     getPixelFromLonLat: function (lonlat) {
9374         var px = this.getViewPortPxFromLonLat(lonlat);
9375         px.x = Math.round(px.x);
9376         px.y = Math.round(px.y);
9377         return px;
9378     },
9379     
9380     /**
9381      * Method: getGeodesicPixelSize
9382      * 
9383      * Parameters:
9384      * px - {<OpenLayers.Pixel>} The pixel to get the geodesic length for. If
9385      *     not provided, the center pixel of the map viewport will be used.
9386      * 
9387      * Returns:
9388      * {<OpenLayers.Size>} The geodesic size of the pixel in kilometers.
9389      */
9390     getGeodesicPixelSize: function(px) {
9391         var lonlat = px ? this.getLonLatFromPixel(px) : (
9392             this.getCachedCenter() || new OpenLayers.LonLat(0, 0));
9393         var res = this.getResolution();
9394         var left = lonlat.add(-res / 2, 0);
9395         var right = lonlat.add(res / 2, 0);
9396         var bottom = lonlat.add(0, -res / 2);
9397         var top = lonlat.add(0, res / 2);
9398         var dest = new OpenLayers.Projection("EPSG:4326");
9399         var source = this.getProjectionObject() || dest;
9400         if(!source.equals(dest)) {
9401             left.transform(source, dest);
9402             right.transform(source, dest);
9403             bottom.transform(source, dest);
9404             top.transform(source, dest);
9405         }
9406         
9407         return new OpenLayers.Size(
9408             OpenLayers.Util.distVincenty(left, right),
9409             OpenLayers.Util.distVincenty(bottom, top)
9410         );
9411     },
9412
9413
9414
9415   //
9416   // TRANSLATION: ViewPortPx <-> LayerPx
9417   //
9418
9419     /**
9420      * APIMethod: getViewPortPxFromLayerPx
9421      * 
9422      * Parameters:
9423      * layerPx - {<OpenLayers.Pixel>}
9424      * 
9425      * Returns:
9426      * {<OpenLayers.Pixel>} Layer Pixel translated into ViewPort Pixel 
9427      *                      coordinates
9428      */
9429     getViewPortPxFromLayerPx:function(layerPx) {
9430         var viewPortPx = null;
9431         if (layerPx != null) {
9432             var dX = this.layerContainerOriginPx.x;
9433             var dY = this.layerContainerOriginPx.y;
9434             viewPortPx = layerPx.add(dX, dY);            
9435         }
9436         return viewPortPx;
9437     },
9438     
9439     /**
9440      * APIMethod: getLayerPxFromViewPortPx
9441      * 
9442      * Parameters:
9443      * viewPortPx - {<OpenLayers.Pixel>}
9444      * 
9445      * Returns:
9446      * {<OpenLayers.Pixel>} ViewPort Pixel translated into Layer Pixel 
9447      *                      coordinates
9448      */
9449     getLayerPxFromViewPortPx:function(viewPortPx) {
9450         var layerPx = null;
9451         if (viewPortPx != null) {
9452             var dX = -this.layerContainerOriginPx.x;
9453             var dY = -this.layerContainerOriginPx.y;
9454             layerPx = viewPortPx.add(dX, dY);
9455             if (isNaN(layerPx.x) || isNaN(layerPx.y)) {
9456                 layerPx = null;
9457             }
9458         }
9459         return layerPx;
9460     },
9461     
9462   //
9463   // TRANSLATION: LonLat <-> LayerPx
9464   //
9465
9466     /**
9467      * Method: getLonLatFromLayerPx
9468      * 
9469      * Parameters:
9470      * px - {<OpenLayers.Pixel>}
9471      *
9472      * Returns:
9473      * {<OpenLayers.LonLat>}
9474      */
9475     getLonLatFromLayerPx: function (px) {
9476        //adjust for displacement of layerContainerDiv
9477        px = this.getViewPortPxFromLayerPx(px);
9478        return this.getLonLatFromViewPortPx(px);         
9479     },
9480     
9481     /**
9482      * APIMethod: getLayerPxFromLonLat
9483      * 
9484      * Parameters:
9485      * lonlat - {<OpenLayers.LonLat>} lonlat
9486      *
9487      * Returns:
9488      * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in 
9489      *                      <OpenLayers.LonLat>, translated into layer pixels 
9490      *                      by the current base layer
9491      */
9492     getLayerPxFromLonLat: function (lonlat) {
9493        //adjust for displacement of layerContainerDiv
9494        var px = this.getPixelFromLonLat(lonlat);
9495        return this.getLayerPxFromViewPortPx(px);         
9496     },
9497
9498     /**
9499      * Method: applyTransform
9500      * Applies the given transform to the <layerContainerDiv>. This method has
9501      * a 2-stage fallback from translate3d/scale3d via translate/scale to plain
9502      * style.left/style.top, in which case no scaling is supported.
9503      *
9504      * Parameters:
9505      * x - {Number} x parameter for the translation. Defaults to the x value of
9506      *     the map's <layerContainerOriginPx>
9507      * y - {Number} y parameter for the translation. Defaults to the y value of
9508      *     the map's <layerContainerOriginPx>
9509      * scale - {Number} scale. Defaults to 1 if not provided.
9510      */
9511      applyTransform: function(x, y, scale) {
9512          scale = scale || 1;
9513          var origin = this.layerContainerOriginPx,
9514              needTransform = scale !== 1;
9515          x = x || origin.x;
9516          y = y || origin.y;
9517             
9518          var style = this.layerContainerDiv.style,
9519              transform = this.applyTransform.transform,
9520              template = this.applyTransform.template;
9521         
9522          if (transform === undefined) {
9523              transform = OpenLayers.Util.vendorPrefix.style('transform');
9524              this.applyTransform.transform = transform;
9525              if (transform) {
9526                  // Try translate3d, but only if the viewPortDiv has a transform
9527                  // defined in a stylesheet
9528                  var computedStyle = OpenLayers.Element.getStyle(this.viewPortDiv,
9529                      OpenLayers.Util.vendorPrefix.css('transform'));
9530                  if (!computedStyle || computedStyle !== 'none') {
9531                      template = ['translate3d(', ',0) ', 'scale3d(', ',1)'];
9532                      style[transform] = [template[0], '0,0', template[1]].join('');
9533                  }
9534                  // If no transform is defined in the stylesheet or translate3d
9535                  // does not stick, use translate and scale
9536                  if (!template || !~style[transform].indexOf(template[0])) {
9537                      template = ['translate(', ') ', 'scale(', ')'];
9538                  }
9539                  this.applyTransform.template = template;
9540              }
9541          }
9542          
9543          // If we do 3d transforms, we always want to use them. If we do 2d
9544          // transforms, we only use them when we need to.
9545          if (transform !== null && (template[0] === 'translate3d(' || needTransform === true)) {
9546              // Our 2d transforms are combined with style.left and style.top, so
9547              // adjust x and y values and set the origin as left and top
9548              if (needTransform === true && template[0] === 'translate(') {
9549                  x -= origin.x;
9550                  y -= origin.y;
9551                  style.left = origin.x + 'px';
9552                  style.top = origin.y + 'px';
9553              }
9554              style[transform] = [
9555                  template[0], x, 'px,', y, 'px', template[1],
9556                  template[2], scale, ',', scale, template[3]
9557              ].join('');
9558          } else {
9559              style.left = x + 'px';
9560              style.top = y + 'px';
9561              // We previously might have had needTransform, so remove transform
9562              if (transform !== null) {
9563                  style[transform] = '';
9564              }
9565          }
9566      },
9567     
9568     CLASS_NAME: "OpenLayers.Map"
9569 });
9570
9571 /**
9572  * Constant: TILE_WIDTH
9573  * {Integer} 256 Default tile width (unless otherwise specified)
9574  */
9575 OpenLayers.Map.TILE_WIDTH = 256;
9576 /**
9577  * Constant: TILE_HEIGHT
9578  * {Integer} 256 Default tile height (unless otherwise specified)
9579  */
9580 OpenLayers.Map.TILE_HEIGHT = 256;
9581 /* ======================================================================
9582     OpenLayers/Layer.js
9583    ====================================================================== */
9584
9585 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
9586  * full list of contributors). Published under the 2-clause BSD license.
9587  * See license.txt in the OpenLayers distribution or repository for the
9588  * full text of the license. */
9589
9590
9591 /**
9592  * @requires OpenLayers/BaseTypes/Class.js
9593  * @requires OpenLayers/Map.js
9594  * @requires OpenLayers/Projection.js
9595  */
9596
9597 /**
9598  * Class: OpenLayers.Layer
9599  */
9600 OpenLayers.Layer = OpenLayers.Class({
9601
9602     /**
9603      * APIProperty: id
9604      * {String}
9605      */
9606     id: null,
9607
9608     /** 
9609      * APIProperty: name
9610      * {String}
9611      */
9612     name: null,
9613
9614     /** 
9615      * APIProperty: div
9616      * {DOMElement}
9617      */
9618     div: null,
9619
9620     /**
9621      * APIProperty: opacity
9622      * {Float} The layer's opacity. Float number between 0.0 and 1.0. Default
9623      * is 1.
9624      */
9625     opacity: 1,
9626
9627     /**
9628      * APIProperty: alwaysInRange
9629      * {Boolean} If a layer's display should not be scale-based, this should 
9630      *     be set to true. This will cause the layer, as an overlay, to always 
9631      *     be 'active', by always returning true from the calculateInRange() 
9632      *     function. 
9633      * 
9634      *     If not explicitly specified for a layer, its value will be 
9635      *     determined on startup in initResolutions() based on whether or not 
9636      *     any scale-specific properties have been set as options on the 
9637      *     layer. If no scale-specific options have been set on the layer, we 
9638      *     assume that it should always be in range.
9639      * 
9640      *     See #987 for more info.
9641      */
9642     alwaysInRange: null,   
9643
9644     /**
9645      * Constant: RESOLUTION_PROPERTIES
9646      * {Array} The properties that are used for calculating resolutions
9647      *     information.
9648      */
9649     RESOLUTION_PROPERTIES: [
9650         'scales', 'resolutions',
9651         'maxScale', 'minScale',
9652         'maxResolution', 'minResolution',
9653         'numZoomLevels', 'maxZoomLevel'
9654     ],
9655
9656     /**
9657      * APIProperty: events
9658      * {<OpenLayers.Events>}
9659      *
9660      * Register a listener for a particular event with the following syntax:
9661      * (code)
9662      * layer.events.register(type, obj, listener);
9663      * (end)
9664      *
9665      * Listeners will be called with a reference to an event object.  The
9666      *     properties of this event depends on exactly what happened.
9667      *
9668      * All event objects have at least the following properties:
9669      * object - {Object} A reference to layer.events.object.
9670      * element - {DOMElement} A reference to layer.events.element.
9671      *
9672      * Supported map event types:
9673      * loadstart - Triggered when layer loading starts.  When using a Vector 
9674      *     layer with a Fixed or BBOX strategy, the event object includes 
9675      *     a *filter* property holding the OpenLayers.Filter used when 
9676      *     calling read on the protocol.
9677      * loadend - Triggered when layer loading ends.  When using a Vector layer
9678      *     with a Fixed or BBOX strategy, the event object includes a 
9679      *     *response* property holding an OpenLayers.Protocol.Response object.
9680      * visibilitychanged - Triggered when the layer's visibility property is
9681      *     changed, e.g. by turning the layer on or off in the layer switcher.
9682      *     Note that the actual visibility of the layer can also change if it
9683      *     gets out of range (see <calculateInRange>). If you also want to catch
9684      *     these cases, register for the map's 'changelayer' event instead.
9685      * move - Triggered when layer moves (triggered with every mousemove
9686      *     during a drag).
9687      * moveend - Triggered when layer is done moving, object passed as
9688      *     argument has a zoomChanged boolean property which tells that the
9689      *     zoom has changed.
9690      * added - Triggered after the layer is added to a map.  Listeners will
9691      *     receive an object with a *map* property referencing the map and a
9692      *     *layer* property referencing the layer.
9693      * removed - Triggered after the layer is removed from the map.  Listeners
9694      *     will receive an object with a *map* property referencing the map and
9695      *     a *layer* property referencing the layer.
9696      */
9697     events: null,
9698
9699     /**
9700      * APIProperty: map
9701      * {<OpenLayers.Map>} This variable is set when the layer is added to 
9702      *     the map, via the accessor function setMap().
9703      */
9704     map: null,
9705     
9706     /**
9707      * APIProperty: isBaseLayer
9708      * {Boolean} Whether or not the layer is a base layer. This should be set 
9709      *     individually by all subclasses. Default is false
9710      */
9711     isBaseLayer: false,
9712  
9713     /**
9714      * Property: alpha
9715      * {Boolean} The layer's images have an alpha channel.  Default is false.
9716      */
9717     alpha: false,
9718
9719     /** 
9720      * APIProperty: displayInLayerSwitcher
9721      * {Boolean} Display the layer's name in the layer switcher.  Default is
9722      *     true.
9723      */
9724     displayInLayerSwitcher: true,
9725
9726     /**
9727      * APIProperty: visibility
9728      * {Boolean} The layer should be displayed in the map.  Default is true.
9729      */
9730     visibility: true,
9731
9732     /**
9733      * APIProperty: attribution
9734      * {String} Attribution string, displayed when an 
9735      *     <OpenLayers.Control.Attribution> has been added to the map.
9736      */
9737     attribution: null, 
9738
9739     /** 
9740      * Property: inRange
9741      * {Boolean} The current map resolution is within the layer's min/max 
9742      *     range. This is set in <OpenLayers.Map.setCenter> whenever the zoom 
9743      *     changes.
9744      */
9745     inRange: false,
9746     
9747     /**
9748      * Propery: imageSize
9749      * {<OpenLayers.Size>} For layers with a gutter, the image is larger than 
9750      *     the tile by twice the gutter in each dimension.
9751      */
9752     imageSize: null,
9753     
9754   // OPTIONS
9755
9756     /** 
9757      * Property: options
9758      * {Object} An optional object whose properties will be set on the layer.
9759      *     Any of the layer properties can be set as a property of the options
9760      *     object and sent to the constructor when the layer is created.
9761      */
9762     options: null,
9763
9764     /**
9765      * APIProperty: eventListeners
9766      * {Object} If set as an option at construction, the eventListeners
9767      *     object will be registered with <OpenLayers.Events.on>.  Object
9768      *     structure must be a listeners object as shown in the example for
9769      *     the events.on method.
9770      */
9771     eventListeners: null,
9772
9773     /**
9774      * APIProperty: gutter
9775      * {Integer} Determines the width (in pixels) of the gutter around image
9776      *     tiles to ignore.  By setting this property to a non-zero value,
9777      *     images will be requested that are wider and taller than the tile
9778      *     size by a value of 2 x gutter.  This allows artifacts of rendering
9779      *     at tile edges to be ignored.  Set a gutter value that is equal to
9780      *     half the size of the widest symbol that needs to be displayed.
9781      *     Defaults to zero.  Non-tiled layers always have zero gutter.
9782      */ 
9783     gutter: 0, 
9784
9785     /**
9786      * APIProperty: projection
9787      * {<OpenLayers.Projection>} or {<String>} Specifies the projection of the layer.
9788      *     Can be set in the layer options. If not specified in the layer options,
9789      *     it is set to the default projection specified in the map,
9790      *     when the layer is added to the map.
9791      *     Projection along with default maxExtent and resolutions
9792      *     are set automatically with commercial baselayers in EPSG:3857,
9793      *     such as Google, Bing and OpenStreetMap, and do not need to be specified.
9794      *     Otherwise, if specifying projection, also set maxExtent,
9795      *     maxResolution or resolutions as appropriate.
9796      *     When using vector layers with strategies, layer projection should be set
9797      *     to the projection of the source data if that is different from the map default.
9798      * 
9799      *     Can be either a string or an <OpenLayers.Projection> object;
9800      *     if a string is passed, will be converted to an object when
9801      *     the layer is added to the map.
9802      * 
9803      */
9804     projection: null,    
9805     
9806     /**
9807      * APIProperty: units
9808      * {String} The layer map units.  Defaults to null.  Possible values
9809      *     are 'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'.
9810      *     Normally taken from the projection.
9811      *     Only required if both map and layers do not define a projection,
9812      *     or if they define a projection which does not define units.
9813      */
9814     units: null,
9815
9816     /**
9817      * APIProperty: scales
9818      * {Array}  An array of map scales in descending order.  The values in the
9819      *     array correspond to the map scale denominator.  Note that these
9820      *     values only make sense if the display (monitor) resolution of the
9821      *     client is correctly guessed by whomever is configuring the
9822      *     application.  In addition, the units property must also be set.
9823      *     Use <resolutions> instead wherever possible.
9824      */
9825     scales: null,
9826
9827     /**
9828      * APIProperty: resolutions
9829      * {Array} A list of map resolutions (map units per pixel) in descending
9830      *     order.  If this is not set in the layer constructor, it will be set
9831      *     based on other resolution related properties (maxExtent,
9832      *     maxResolution, maxScale, etc.).
9833      */
9834     resolutions: null,
9835     
9836     /**
9837      * APIProperty: maxExtent
9838      * {<OpenLayers.Bounds>|Array} If provided as an array, the array
9839      *     should consist of four values (left, bottom, right, top).
9840      *     The maximum extent for the layer.  Defaults to null.
9841      * 
9842      *     The center of these bounds will not stray outside
9843      *     of the viewport extent during panning.  In addition, if
9844      *     <displayOutsideMaxExtent> is set to false, data will not be
9845      *     requested that falls completely outside of these bounds.
9846      */
9847     maxExtent: null,
9848     
9849     /**
9850      * APIProperty: minExtent
9851      * {<OpenLayers.Bounds>|Array} If provided as an array, the array
9852      *     should consist of four values (left, bottom, right, top).
9853      *     The minimum extent for the layer.  Defaults to null.
9854      */
9855     minExtent: null,
9856     
9857     /**
9858      * APIProperty: maxResolution
9859      * {Float} Default max is 360 deg / 256 px, which corresponds to
9860      *     zoom level 0 on gmaps.  Specify a different value in the layer 
9861      *     options if you are not using the default <OpenLayers.Map.tileSize>
9862      *     and displaying the whole world.
9863      */
9864     maxResolution: null,
9865
9866     /**
9867      * APIProperty: minResolution
9868      * {Float}
9869      */
9870     minResolution: null,
9871
9872     /**
9873      * APIProperty: numZoomLevels
9874      * {Integer}
9875      */
9876     numZoomLevels: null,
9877     
9878     /**
9879      * APIProperty: minScale
9880      * {Float}
9881      */
9882     minScale: null,
9883     
9884     /**
9885      * APIProperty: maxScale
9886      * {Float}
9887      */
9888     maxScale: null,
9889
9890     /**
9891      * APIProperty: displayOutsideMaxExtent
9892      * {Boolean} Request map tiles that are completely outside of the max 
9893      *     extent for this layer. Defaults to false.
9894      */
9895     displayOutsideMaxExtent: false,
9896
9897     /**
9898      * APIProperty: wrapDateLine
9899      * {Boolean} Wraps the world at the international dateline, so the map can
9900      * be panned infinitely in longitudinal direction. Only use this on the
9901      * base layer, and only if the layer's maxExtent equals the world bounds.
9902      * #487 for more info.   
9903      */
9904     wrapDateLine: false,
9905     
9906     /**
9907      * Property: metadata
9908      * {Object} This object can be used to store additional information on a
9909      *     layer object.
9910      */
9911     metadata: null,
9912     
9913     /**
9914      * Constructor: OpenLayers.Layer
9915      *
9916      * Parameters:
9917      * name - {String} The layer name
9918      * options - {Object} Hashtable of extra options to tag onto the layer
9919      */
9920     initialize: function(name, options) {
9921
9922         this.metadata = {};
9923         
9924         options = OpenLayers.Util.extend({}, options);
9925         // make sure we respect alwaysInRange if set on the prototype
9926         if (this.alwaysInRange != null) {
9927             options.alwaysInRange = this.alwaysInRange;
9928         }
9929         this.addOptions(options);
9930
9931         this.name = name;
9932         
9933         if (this.id == null) {
9934
9935             this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
9936
9937             this.div = OpenLayers.Util.createDiv(this.id);
9938             this.div.style.width = "100%";
9939             this.div.style.height = "100%";
9940             this.div.dir = "ltr";
9941
9942             this.events = new OpenLayers.Events(this, this.div);
9943             if(this.eventListeners instanceof Object) {
9944                 this.events.on(this.eventListeners);
9945             }
9946
9947         }
9948     },
9949     
9950     /**
9951      * Method: destroy
9952      * Destroy is a destructor: this is to alleviate cyclic references which
9953      *     the Javascript garbage cleaner can not take care of on its own.
9954      *
9955      * Parameters:
9956      * setNewBaseLayer - {Boolean} Set a new base layer when this layer has
9957      *     been destroyed.  Default is true.
9958      */
9959     destroy: function(setNewBaseLayer) {
9960         if (setNewBaseLayer == null) {
9961             setNewBaseLayer = true;
9962         }
9963         if (this.map != null) {
9964             this.map.removeLayer(this, setNewBaseLayer);
9965         }
9966         this.projection = null;
9967         this.map = null;
9968         this.name = null;
9969         this.div = null;
9970         this.options = null;
9971
9972         if (this.events) {
9973             if(this.eventListeners) {
9974                 this.events.un(this.eventListeners);
9975             }
9976             this.events.destroy();
9977         }
9978         this.eventListeners = null;
9979         this.events = null;
9980     },
9981     
9982    /**
9983     * Method: clone
9984     *
9985     * Parameters:
9986     * obj - {<OpenLayers.Layer>} The layer to be cloned
9987     *
9988     * Returns:
9989     * {<OpenLayers.Layer>} An exact clone of this <OpenLayers.Layer>
9990     */
9991     clone: function (obj) {
9992         
9993         if (obj == null) {
9994             obj = new OpenLayers.Layer(this.name, this.getOptions());
9995         }
9996         
9997         // catch any randomly tagged-on properties
9998         OpenLayers.Util.applyDefaults(obj, this);
9999         
10000         // a cloned layer should never have its map property set
10001         //  because it has not been added to a map yet. 
10002         obj.map = null;
10003         
10004         return obj;
10005     },
10006     
10007     /**
10008      * Method: getOptions
10009      * Extracts an object from the layer with the properties that were set as
10010      *     options, but updates them with the values currently set on the
10011      *     instance.
10012      * 
10013      * Returns:
10014      * {Object} the <options> of the layer, representing the current state.
10015      */
10016     getOptions: function() {
10017         var options = {};
10018         for(var o in this.options) {
10019             options[o] = this[o];
10020         }
10021         return options;
10022     },
10023     
10024     /** 
10025      * APIMethod: setName
10026      * Sets the new layer name for this layer.  Can trigger a changelayer event
10027      *     on the map.
10028      *
10029      * Parameters:
10030      * newName - {String} The new name.
10031      */
10032     setName: function(newName) {
10033         if (newName != this.name) {
10034             this.name = newName;
10035             if (this.map != null) {
10036                 this.map.events.triggerEvent("changelayer", {
10037                     layer: this,
10038                     property: "name"
10039                 });
10040             }
10041         }
10042     },    
10043     
10044    /**
10045     * APIMethod: addOptions
10046     * 
10047     * Parameters:
10048     * newOptions - {Object}
10049     * reinitialize - {Boolean} If set to true, and if resolution options of the
10050     *     current baseLayer were changed, the map will be recentered to make
10051     *     sure that it is displayed with a valid resolution, and a
10052     *     changebaselayer event will be triggered.
10053     */
10054     addOptions: function (newOptions, reinitialize) {
10055
10056         if (this.options == null) {
10057             this.options = {};
10058         }
10059         
10060         if (newOptions) {
10061             // make sure this.projection references a projection object
10062             if(typeof newOptions.projection == "string") {
10063                 newOptions.projection = new OpenLayers.Projection(newOptions.projection);
10064             }
10065             if (newOptions.projection) {
10066                 // get maxResolution, units and maxExtent from projection defaults if
10067                 // they are not defined already
10068                 OpenLayers.Util.applyDefaults(newOptions,
10069                     OpenLayers.Projection.defaults[newOptions.projection.getCode()]);
10070             }
10071             // allow array for extents
10072             if (newOptions.maxExtent && !(newOptions.maxExtent instanceof OpenLayers.Bounds)) {
10073                 newOptions.maxExtent = new OpenLayers.Bounds(newOptions.maxExtent);
10074             }
10075             if (newOptions.minExtent && !(newOptions.minExtent instanceof OpenLayers.Bounds)) {
10076                 newOptions.minExtent = new OpenLayers.Bounds(newOptions.minExtent);
10077             }
10078         }
10079
10080         // update our copy for clone
10081         OpenLayers.Util.extend(this.options, newOptions);
10082
10083         // add new options to this
10084         OpenLayers.Util.extend(this, newOptions);
10085         
10086         // get the units from the projection, if we have a projection
10087         // and it it has units
10088         if(this.projection && this.projection.getUnits()) {
10089             this.units = this.projection.getUnits();
10090         }
10091
10092         // re-initialize resolutions if necessary, i.e. if any of the
10093         // properties of the "properties" array defined below is set
10094         // in the new options
10095         if(this.map) {
10096             // store current resolution so we can try to restore it later
10097             var resolution = this.map.getResolution();
10098             var properties = this.RESOLUTION_PROPERTIES.concat(
10099                 ["projection", "units", "minExtent", "maxExtent"]
10100             );
10101             for(var o in newOptions) {
10102                 if(newOptions.hasOwnProperty(o) &&
10103                    OpenLayers.Util.indexOf(properties, o) >= 0) {
10104
10105                     this.initResolutions();
10106                     if (reinitialize && this.map.baseLayer === this) {
10107                         // update map position, and restore previous resolution
10108                         this.map.setCenter(this.map.getCenter(),
10109                             this.map.getZoomForResolution(resolution),
10110                             false, true
10111                         );
10112                         // trigger a changebaselayer event to make sure that
10113                         // all controls (especially
10114                         // OpenLayers.Control.PanZoomBar) get notified of the
10115                         // new options
10116                         this.map.events.triggerEvent("changebaselayer", {
10117                             layer: this
10118                         });
10119                     }
10120                     break;
10121                 }
10122             }
10123         }
10124     },
10125
10126     /**
10127      * APIMethod: onMapResize
10128      * This function can be implemented by subclasses
10129      */
10130     onMapResize: function() {
10131         //this function can be implemented by subclasses  
10132     },
10133
10134     /**
10135      * APIMethod: redraw
10136      * Redraws the layer.  Returns true if the layer was redrawn, false if not.
10137      *
10138      * Returns:
10139      * {Boolean} The layer was redrawn.
10140      */
10141     redraw: function() {
10142         var redrawn = false;
10143         if (this.map) {
10144
10145             // min/max Range may have changed
10146             this.inRange = this.calculateInRange();
10147
10148             // map's center might not yet be set
10149             var extent = this.getExtent();
10150
10151             if (extent && this.inRange && this.visibility) {
10152                 var zoomChanged = true;
10153                 this.moveTo(extent, zoomChanged, false);
10154                 this.events.triggerEvent("moveend",
10155                     {"zoomChanged": zoomChanged});
10156                 redrawn = true;
10157             }
10158         }
10159         return redrawn;
10160     },
10161
10162     /**
10163      * Method: moveTo
10164      * 
10165      * Parameters:
10166      * bounds - {<OpenLayers.Bounds>}
10167      * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
10168      *     do some init work in that case.
10169      * dragging - {Boolean}
10170      */
10171     moveTo:function(bounds, zoomChanged, dragging) {
10172         var display = this.visibility;
10173         if (!this.isBaseLayer) {
10174             display = display && this.inRange;
10175         }
10176         this.display(display);
10177     },
10178
10179     /**
10180      * Method: moveByPx
10181      * Move the layer based on pixel vector. To be implemented by subclasses.
10182      *
10183      * Parameters:
10184      * dx - {Number} The x coord of the displacement vector.
10185      * dy - {Number} The y coord of the displacement vector.
10186      */
10187     moveByPx: function(dx, dy) {
10188     },
10189
10190     /**
10191      * Method: setMap
10192      * Set the map property for the layer. This is done through an accessor
10193      *     so that subclasses can override this and take special action once 
10194      *     they have their map variable set. 
10195      * 
10196      *     Here we take care to bring over any of the necessary default 
10197      *     properties from the map. 
10198      * 
10199      * Parameters:
10200      * map - {<OpenLayers.Map>}
10201      */
10202     setMap: function(map) {
10203         if (this.map == null) {
10204         
10205             this.map = map;
10206             
10207             // grab some essential layer data from the map if it hasn't already
10208             //  been set
10209             this.maxExtent = this.maxExtent || this.map.maxExtent;
10210             this.minExtent = this.minExtent || this.map.minExtent;
10211
10212             this.projection = this.projection || this.map.projection;
10213             if (typeof this.projection == "string") {
10214                 this.projection = new OpenLayers.Projection(this.projection);
10215             }
10216
10217             // Check the projection to see if we can get units -- if not, refer
10218             // to properties.
10219             this.units = this.projection.getUnits() ||
10220                          this.units || this.map.units;
10221             
10222             this.initResolutions();
10223             
10224             if (!this.isBaseLayer) {
10225                 this.inRange = this.calculateInRange();
10226                 var show = ((this.visibility) && (this.inRange));
10227                 this.div.style.display = show ? "" : "none";
10228             }
10229             
10230             // deal with gutters
10231             this.setTileSize();
10232         }
10233     },
10234     
10235     /**
10236      * Method: afterAdd
10237      * Called at the end of the map.addLayer sequence.  At this point, the map
10238      *     will have a base layer.  To be overridden by subclasses.
10239      */
10240     afterAdd: function() {
10241     },
10242     
10243     /**
10244      * APIMethod: removeMap
10245      * Just as setMap() allows each layer the possibility to take a 
10246      *     personalized action on being added to the map, removeMap() allows
10247      *     each layer to take a personalized action on being removed from it. 
10248      *     For now, this will be mostly unused, except for the EventPane layer,
10249      *     which needs this hook so that it can remove the special invisible
10250      *     pane. 
10251      * 
10252      * Parameters:
10253      * map - {<OpenLayers.Map>}
10254      */
10255     removeMap: function(map) {
10256         //to be overridden by subclasses
10257     },
10258     
10259     /**
10260      * APIMethod: getImageSize
10261      *
10262      * Parameters:
10263      * bounds - {<OpenLayers.Bounds>} optional tile bounds, can be used
10264      *     by subclasses that have to deal with different tile sizes at the
10265      *     layer extent edges (e.g. Zoomify)
10266      * 
10267      * Returns:
10268      * {<OpenLayers.Size>} The size that the image should be, taking into 
10269      *     account gutters.
10270      */ 
10271     getImageSize: function(bounds) { 
10272         return (this.imageSize || this.tileSize); 
10273     },    
10274   
10275     /**
10276      * APIMethod: setTileSize
10277      * Set the tile size based on the map size.  This also sets layer.imageSize
10278      *     or use by Tile.Image.
10279      * 
10280      * Parameters:
10281      * size - {<OpenLayers.Size>}
10282      */
10283     setTileSize: function(size) {
10284         var tileSize = (size) ? size :
10285                                 ((this.tileSize) ? this.tileSize :
10286                                                    this.map.getTileSize());
10287         this.tileSize = tileSize;
10288         if(this.gutter) {
10289           // layers with gutters need non-null tile sizes
10290           //if(tileSize == null) {
10291           //    OpenLayers.console.error("Error in layer.setMap() for " +
10292           //                              this.name + ": layers with " +
10293           //                              "gutters need non-null tile sizes");
10294           //}
10295             this.imageSize = new OpenLayers.Size(tileSize.w + (2*this.gutter), 
10296                                                  tileSize.h + (2*this.gutter)); 
10297         }
10298     },
10299
10300     /**
10301      * APIMethod: getVisibility
10302      * 
10303      * Returns:
10304      * {Boolean} The layer should be displayed (if in range).
10305      */
10306     getVisibility: function() {
10307         return this.visibility;
10308     },
10309
10310     /** 
10311      * APIMethod: setVisibility
10312      * Set the visibility flag for the layer and hide/show & redraw 
10313      *     accordingly. Fire event unless otherwise specified
10314      * 
10315      * Note that visibility is no longer simply whether or not the layer's
10316      *     style.display is set to "block". Now we store a 'visibility' state 
10317      *     property on the layer class, this allows us to remember whether or 
10318      *     not we *desire* for a layer to be visible. In the case where the 
10319      *     map's resolution is out of the layer's range, this desire may be 
10320      *     subverted.
10321      * 
10322      * Parameters:
10323      * visibility - {Boolean} Whether or not to display the layer (if in range)
10324      */
10325     setVisibility: function(visibility) {
10326         if (visibility != this.visibility) {
10327             this.visibility = visibility;
10328             this.display(visibility);
10329             this.redraw();
10330             if (this.map != null) {
10331                 this.map.events.triggerEvent("changelayer", {
10332                     layer: this,
10333                     property: "visibility"
10334                 });
10335             }
10336             this.events.triggerEvent("visibilitychanged");
10337         }
10338     },
10339
10340     /** 
10341      * APIMethod: display
10342      * Hide or show the Layer. This is designed to be used internally, and 
10343      *     is not generally the way to enable or disable the layer. For that,
10344      *     use the setVisibility function instead..
10345      * 
10346      * Parameters:
10347      * display - {Boolean}
10348      */
10349     display: function(display) {
10350         if (display != (this.div.style.display != "none")) {
10351             this.div.style.display = (display && this.calculateInRange()) ? "block" : "none";
10352         }
10353     },
10354
10355     /**
10356      * APIMethod: calculateInRange
10357      * 
10358      * Returns:
10359      * {Boolean} The layer is displayable at the current map's current
10360      *     resolution. Note that if 'alwaysInRange' is true for the layer, 
10361      *     this function will always return true.
10362      */
10363     calculateInRange: function() {
10364         var inRange = false;
10365
10366         if (this.alwaysInRange) {
10367             inRange = true;
10368         } else {
10369             if (this.map) {
10370                 var resolution = this.map.getResolution();
10371                 inRange = ( (resolution >= this.minResolution) &&
10372                             (resolution <= this.maxResolution) );
10373             }
10374         }
10375         return inRange;
10376     },
10377
10378     /** 
10379      * APIMethod: setIsBaseLayer
10380      * 
10381      * Parameters:
10382      * isBaseLayer - {Boolean}
10383      */
10384     setIsBaseLayer: function(isBaseLayer) {
10385         if (isBaseLayer != this.isBaseLayer) {
10386             this.isBaseLayer = isBaseLayer;
10387             if (this.map != null) {
10388                 this.map.events.triggerEvent("changebaselayer", {
10389                     layer: this
10390                 });
10391             }
10392         }
10393     },
10394
10395   /********************************************************/
10396   /*                                                      */
10397   /*                 Baselayer Functions                  */
10398   /*                                                      */
10399   /********************************************************/
10400   
10401     /** 
10402      * Method: initResolutions
10403      * This method's responsibility is to set up the 'resolutions' array 
10404      *     for the layer -- this array is what the layer will use to interface
10405      *     between the zoom levels of the map and the resolution display 
10406      *     of the layer.
10407      * 
10408      * The user has several options that determine how the array is set up.
10409      *  
10410      * For a detailed explanation, see the following wiki from the 
10411      *     openlayers.org homepage:
10412      *     http://trac.openlayers.org/wiki/SettingZoomLevels
10413      */
10414     initResolutions: function() {
10415
10416         // ok we want resolutions, here's our strategy:
10417         //
10418         // 1. if resolutions are defined in the layer config, use them
10419         // 2. else, if scales are defined in the layer config then derive
10420         //    resolutions from these scales
10421         // 3. else, attempt to calculate resolutions from maxResolution,
10422         //    minResolution, numZoomLevels, maxZoomLevel set in the
10423         //    layer config
10424         // 4. if we still don't have resolutions, and if resolutions
10425         //    are defined in the same, use them
10426         // 5. else, if scales are defined in the map then derive
10427         //    resolutions from these scales
10428         // 6. else, attempt to calculate resolutions from maxResolution,
10429         //    minResolution, numZoomLevels, maxZoomLevel set in the
10430         //    map
10431         // 7. hope for the best!
10432
10433         var i, len, p;
10434         var props = {}, alwaysInRange = true;
10435
10436         // get resolution data from layer config
10437         // (we also set alwaysInRange in the layer as appropriate)
10438         for(i=0, len=this.RESOLUTION_PROPERTIES.length; i<len; i++) {
10439             p = this.RESOLUTION_PROPERTIES[i];
10440             props[p] = this.options[p];
10441             if(alwaysInRange && this.options[p]) {
10442                 alwaysInRange = false;
10443             }
10444         }
10445         if(this.options.alwaysInRange == null) {
10446             this.alwaysInRange = alwaysInRange;
10447         }
10448
10449         // if we don't have resolutions then attempt to derive them from scales
10450         if(props.resolutions == null) {
10451             props.resolutions = this.resolutionsFromScales(props.scales);
10452         }
10453
10454         // if we still don't have resolutions then attempt to calculate them
10455         if(props.resolutions == null) {
10456             props.resolutions = this.calculateResolutions(props);
10457         }
10458
10459         // if we couldn't calculate resolutions then we look at we have
10460         // in the map
10461         if(props.resolutions == null) {
10462             for(i=0, len=this.RESOLUTION_PROPERTIES.length; i<len; i++) {
10463                 p = this.RESOLUTION_PROPERTIES[i];
10464                 props[p] = this.options[p] != null ?
10465                     this.options[p] : this.map[p];
10466             }
10467             if(props.resolutions == null) {
10468                 props.resolutions = this.resolutionsFromScales(props.scales);
10469             }
10470             if(props.resolutions == null) {
10471                 props.resolutions = this.calculateResolutions(props);
10472             }
10473         }
10474
10475         // ok, we new need to set properties in the instance
10476
10477         // get maxResolution from the config if it's defined there
10478         var maxResolution;
10479         if(this.options.maxResolution &&
10480            this.options.maxResolution !== "auto") {
10481             maxResolution = this.options.maxResolution;
10482         }
10483         if(this.options.minScale) {
10484             maxResolution = OpenLayers.Util.getResolutionFromScale(
10485                 this.options.minScale, this.units);
10486         }
10487
10488         // get minResolution from the config if it's defined there
10489         var minResolution;
10490         if(this.options.minResolution &&
10491            this.options.minResolution !== "auto") {
10492             minResolution = this.options.minResolution;
10493         }
10494         if(this.options.maxScale) {
10495             minResolution = OpenLayers.Util.getResolutionFromScale(
10496                 this.options.maxScale, this.units);
10497         }
10498
10499         if(props.resolutions) {
10500
10501             //sort resolutions array descendingly
10502             props.resolutions.sort(function(a, b) {
10503                 return (b - a);
10504             });
10505
10506             // if we still don't have a maxResolution get it from the
10507             // resolutions array
10508             if(!maxResolution) {
10509                 maxResolution = props.resolutions[0];
10510             }
10511
10512             // if we still don't have a minResolution get it from the
10513             // resolutions array
10514             if(!minResolution) {
10515                 var lastIdx = props.resolutions.length - 1;
10516                 minResolution = props.resolutions[lastIdx];
10517             }
10518         }
10519
10520         this.resolutions = props.resolutions;
10521         if(this.resolutions) {
10522             len = this.resolutions.length;
10523             this.scales = new Array(len);
10524             for(i=0; i<len; i++) {
10525                 this.scales[i] = OpenLayers.Util.getScaleFromResolution(
10526                     this.resolutions[i], this.units);
10527             }
10528             this.numZoomLevels = len;
10529         }
10530         this.minResolution = minResolution;
10531         if(minResolution) {
10532             this.maxScale = OpenLayers.Util.getScaleFromResolution(
10533                 minResolution, this.units);
10534         }
10535         this.maxResolution = maxResolution;
10536         if(maxResolution) {
10537             this.minScale = OpenLayers.Util.getScaleFromResolution(
10538                 maxResolution, this.units);
10539         }
10540     },
10541
10542     /**
10543      * Method: resolutionsFromScales
10544      * Derive resolutions from scales.
10545      *
10546      * Parameters:
10547      * scales - {Array(Number)} Scales
10548      *
10549      * Returns
10550      * {Array(Number)} Resolutions
10551      */
10552     resolutionsFromScales: function(scales) {
10553         if(scales == null) {
10554             return;
10555         }
10556         var resolutions, i, len;
10557         len = scales.length;
10558         resolutions = new Array(len);
10559         for(i=0; i<len; i++) {
10560             resolutions[i] = OpenLayers.Util.getResolutionFromScale(
10561                 scales[i], this.units);
10562         }
10563         return resolutions;
10564     },
10565
10566     /**
10567      * Method: calculateResolutions
10568      * Calculate resolutions based on the provided properties.
10569      *
10570      * Parameters:
10571      * props - {Object} Properties
10572      *
10573      * Returns:
10574      * {Array({Number})} Array of resolutions.
10575      */
10576     calculateResolutions: function(props) {
10577
10578         var viewSize, wRes, hRes;
10579
10580         // determine maxResolution
10581         var maxResolution = props.maxResolution;
10582         if(props.minScale != null) {
10583             maxResolution =
10584                 OpenLayers.Util.getResolutionFromScale(props.minScale,
10585                                                        this.units);
10586         } else if(maxResolution == "auto" && this.maxExtent != null) {
10587             viewSize = this.map.getSize();
10588             wRes = this.maxExtent.getWidth() / viewSize.w;
10589             hRes = this.maxExtent.getHeight() / viewSize.h;
10590             maxResolution = Math.max(wRes, hRes);
10591         }
10592
10593         // determine minResolution
10594         var minResolution = props.minResolution;
10595         if(props.maxScale != null) {
10596             minResolution =
10597                 OpenLayers.Util.getResolutionFromScale(props.maxScale,
10598                                                        this.units);
10599         } else if(props.minResolution == "auto" && this.minExtent != null) {
10600             viewSize = this.map.getSize();
10601             wRes = this.minExtent.getWidth() / viewSize.w;
10602             hRes = this.minExtent.getHeight()/ viewSize.h;
10603             minResolution = Math.max(wRes, hRes);
10604         }
10605
10606         if(typeof maxResolution !== "number" &&
10607            typeof minResolution !== "number" &&
10608            this.maxExtent != null) {
10609             // maxResolution for default grid sets assumes that at zoom
10610             // level zero, the whole world fits on one tile.
10611             var tileSize = this.map.getTileSize();
10612             maxResolution = Math.max(
10613                 this.maxExtent.getWidth() / tileSize.w,
10614                 this.maxExtent.getHeight() / tileSize.h
10615             );
10616         }
10617
10618         // determine numZoomLevels
10619         var maxZoomLevel = props.maxZoomLevel;
10620         var numZoomLevels = props.numZoomLevels;
10621         if(typeof minResolution === "number" &&
10622            typeof maxResolution === "number" && numZoomLevels === undefined) {
10623             var ratio = maxResolution / minResolution;
10624             numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1;
10625         } else if(numZoomLevels === undefined && maxZoomLevel != null) {
10626             numZoomLevels = maxZoomLevel + 1;
10627         }
10628
10629         // are we able to calculate resolutions?
10630         if(typeof numZoomLevels !== "number" || numZoomLevels <= 0 ||
10631            (typeof maxResolution !== "number" &&
10632                 typeof minResolution !== "number")) {
10633             return;
10634         }
10635
10636         // now we have numZoomLevels and at least one of maxResolution
10637         // or minResolution, we can populate the resolutions array
10638
10639         var resolutions = new Array(numZoomLevels);
10640         var base = 2;
10641         if(typeof minResolution == "number" &&
10642            typeof maxResolution == "number") {
10643             // if maxResolution and minResolution are set, we calculate
10644             // the base for exponential scaling that starts at
10645             // maxResolution and ends at minResolution in numZoomLevels
10646             // steps.
10647             base = Math.pow(
10648                     (maxResolution / minResolution),
10649                 (1 / (numZoomLevels - 1))
10650             );
10651         }
10652
10653         var i;
10654         if(typeof maxResolution === "number") {
10655             for(i=0; i<numZoomLevels; i++) {
10656                 resolutions[i] = maxResolution / Math.pow(base, i);
10657             }
10658         } else {
10659             for(i=0; i<numZoomLevels; i++) {
10660                 resolutions[numZoomLevels - 1 - i] =
10661                     minResolution * Math.pow(base, i);
10662             }
10663         }
10664
10665         return resolutions;
10666     },
10667
10668     /**
10669      * APIMethod: getResolution
10670      * 
10671      * Returns:
10672      * {Float} The currently selected resolution of the map, taken from the
10673      *     resolutions array, indexed by current zoom level.
10674      */
10675     getResolution: function() {
10676         var zoom = this.map.getZoom();
10677         return this.getResolutionForZoom(zoom);
10678     },
10679
10680     /** 
10681      * APIMethod: getExtent
10682      * 
10683      * Returns:
10684      * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat 
10685      *     bounds of the current viewPort.
10686      */
10687     getExtent: function() {
10688         // just use stock map calculateBounds function -- passing no arguments
10689         //  means it will user map's current center & resolution
10690         //
10691         return this.map.calculateBounds();
10692     },
10693
10694     /**
10695      * APIMethod: getZoomForExtent
10696      * 
10697      * Parameters:
10698      * extent - {<OpenLayers.Bounds>}
10699      * closest - {Boolean} Find the zoom level that most closely fits the 
10700      *     specified bounds. Note that this may result in a zoom that does 
10701      *     not exactly contain the entire extent.
10702      *     Default is false.
10703      *
10704      * Returns:
10705      * {Integer} The index of the zoomLevel (entry in the resolutions array) 
10706      *     for the passed-in extent. We do this by calculating the ideal 
10707      *     resolution for the given extent (based on the map size) and then 
10708      *     calling getZoomForResolution(), passing along the 'closest'
10709      *     parameter.
10710      */
10711     getZoomForExtent: function(extent, closest) {
10712         var viewSize = this.map.getSize();
10713         var idealResolution = Math.max( extent.getWidth()  / viewSize.w,
10714                                         extent.getHeight() / viewSize.h );
10715
10716         return this.getZoomForResolution(idealResolution, closest);
10717     },
10718     
10719     /** 
10720      * Method: getDataExtent
10721      * Calculates the max extent which includes all of the data for the layer.
10722      *     This function is to be implemented by subclasses.
10723      * 
10724      * Returns:
10725      * {<OpenLayers.Bounds>}
10726      */
10727     getDataExtent: function () {
10728         //to be implemented by subclasses
10729     },
10730
10731     /**
10732      * APIMethod: getResolutionForZoom
10733      * 
10734      * Parameters:
10735      * zoom - {Float}
10736      * 
10737      * Returns:
10738      * {Float} A suitable resolution for the specified zoom.
10739      */
10740     getResolutionForZoom: function(zoom) {
10741         zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));
10742         var resolution;
10743         if(this.map.fractionalZoom) {
10744             var low = Math.floor(zoom);
10745             var high = Math.ceil(zoom);
10746             resolution = this.resolutions[low] -
10747                 ((zoom-low) * (this.resolutions[low]-this.resolutions[high]));
10748         } else {
10749             resolution = this.resolutions[Math.round(zoom)];
10750         }
10751         return resolution;
10752     },
10753
10754     /**
10755      * APIMethod: getZoomForResolution
10756      * 
10757      * Parameters:
10758      * resolution - {Float}
10759      * closest - {Boolean} Find the zoom level that corresponds to the absolute 
10760      *     closest resolution, which may result in a zoom whose corresponding
10761      *     resolution is actually smaller than we would have desired (if this
10762      *     is being called from a getZoomForExtent() call, then this means that
10763      *     the returned zoom index might not actually contain the entire 
10764      *     extent specified... but it'll be close).
10765      *     Default is false.
10766      * 
10767      * Returns:
10768      * {Integer} The index of the zoomLevel (entry in the resolutions array) 
10769      *     that corresponds to the best fit resolution given the passed in 
10770      *     value and the 'closest' specification.
10771      */
10772     getZoomForResolution: function(resolution, closest) {
10773         var zoom, i, len;
10774         if(this.map.fractionalZoom) {
10775             var lowZoom = 0;
10776             var highZoom = this.resolutions.length - 1;
10777             var highRes = this.resolutions[lowZoom];
10778             var lowRes = this.resolutions[highZoom];
10779             var res;
10780             for(i=0, len=this.resolutions.length; i<len; ++i) {
10781                 res = this.resolutions[i];
10782                 if(res >= resolution) {
10783                     highRes = res;
10784                     lowZoom = i;
10785                 }
10786                 if(res <= resolution) {
10787                     lowRes = res;
10788                     highZoom = i;
10789                     break;
10790                 }
10791             }
10792             var dRes = highRes - lowRes;
10793             if(dRes > 0) {
10794                 zoom = lowZoom + ((highRes - resolution) / dRes);
10795             } else {
10796                 zoom = lowZoom;
10797             }
10798         } else {
10799             var diff;
10800             var minDiff = Number.POSITIVE_INFINITY;
10801             for(i=0, len=this.resolutions.length; i<len; i++) {            
10802                 if (closest) {
10803                     diff = Math.abs(this.resolutions[i] - resolution);
10804                     if (diff > minDiff) {
10805                         break;
10806                     }
10807                     minDiff = diff;
10808                 } else {
10809                     if (this.resolutions[i] < resolution) {
10810                         break;
10811                     }
10812                 }
10813             }
10814             zoom = Math.max(0, i-1);
10815         }
10816         return zoom;
10817     },
10818     
10819     /**
10820      * APIMethod: getLonLatFromViewPortPx
10821      * 
10822      * Parameters:
10823      * viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or
10824      *                                          an object with a 'x'
10825      *                                          and 'y' properties.
10826      *
10827      * Returns:
10828      * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in 
10829      *     view port <OpenLayers.Pixel>, translated into lon/lat by the layer.
10830      */
10831     getLonLatFromViewPortPx: function (viewPortPx) {
10832         var lonlat = null;
10833         var map = this.map;
10834         if (viewPortPx != null && map.minPx) {
10835             var res = map.getResolution();
10836             var maxExtent = map.getMaxExtent({restricted: true});
10837             var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left;
10838             var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top;
10839             lonlat = new OpenLayers.LonLat(lon, lat);
10840
10841             if (this.wrapDateLine) {
10842                 lonlat = lonlat.wrapDateLine(this.maxExtent);
10843             }
10844         }
10845         return lonlat;
10846     },
10847
10848     /**
10849      * APIMethod: getViewPortPxFromLonLat
10850      * Returns a pixel location given a map location.  This method will return
10851      *     fractional pixel values.
10852      * 
10853      * Parameters:
10854      * lonlat - {<OpenLayers.LonLat>|Object} An OpenLayers.LonLat or
10855      *                                       an object with a 'lon'
10856      *                                       and 'lat' properties.
10857      *
10858      * Returns: 
10859      * {<OpenLayers.Pixel>} An <OpenLayers.Pixel> which is the passed-in 
10860      *     lonlat translated into view port pixels.
10861      */
10862     getViewPortPxFromLonLat: function (lonlat, resolution) {
10863         var px = null; 
10864         if (lonlat != null) {
10865             resolution = resolution || this.map.getResolution();
10866             var extent = this.map.calculateBounds(null, resolution);
10867             px = new OpenLayers.Pixel(
10868                 (1/resolution * (lonlat.lon - extent.left)),
10869                 (1/resolution * (extent.top - lonlat.lat))
10870             );    
10871         }
10872         return px;
10873     },
10874     
10875     /**
10876      * APIMethod: setOpacity
10877      * Sets the opacity for the entire layer (all images)
10878      * 
10879      * Parameters:
10880      * opacity - {Float}
10881      */
10882     setOpacity: function(opacity) {
10883         if (opacity != this.opacity) {
10884             this.opacity = opacity;
10885             var childNodes = this.div.childNodes;
10886             for(var i = 0, len = childNodes.length; i < len; ++i) {
10887                 var element = childNodes[i].firstChild || childNodes[i];
10888                 var lastChild = childNodes[i].lastChild;
10889                 //TODO de-uglify this
10890                 if (lastChild && lastChild.nodeName.toLowerCase() === "iframe") {
10891                     element = lastChild.parentNode;
10892                 }
10893                 OpenLayers.Util.modifyDOMElement(element, null, null, null, 
10894                                                  null, null, null, opacity);
10895             }
10896             if (this.map != null) {
10897                 this.map.events.triggerEvent("changelayer", {
10898                     layer: this,
10899                     property: "opacity"
10900                 });
10901             }
10902         }
10903     },
10904
10905     /**
10906      * Method: getZIndex
10907      * 
10908      * Returns: 
10909      * {Integer} the z-index of this layer
10910      */    
10911     getZIndex: function () {
10912         return this.div.style.zIndex;
10913     },
10914
10915     /**
10916      * Method: setZIndex
10917      * 
10918      * Parameters: 
10919      * zIndex - {Integer}
10920      */    
10921     setZIndex: function (zIndex) {
10922         this.div.style.zIndex = zIndex;
10923     },
10924
10925     /**
10926      * Method: adjustBounds
10927      * This function will take a bounds, and if wrapDateLine option is set
10928      *     on the layer, it will return a bounds which is wrapped around the 
10929      *     world. We do not wrap for bounds which *cross* the 
10930      *     maxExtent.left/right, only bounds which are entirely to the left 
10931      *     or entirely to the right.
10932      * 
10933      * Parameters:
10934      * bounds - {<OpenLayers.Bounds>}
10935      */
10936     adjustBounds: function (bounds) {
10937
10938         if (this.gutter) {
10939             // Adjust the extent of a bounds in map units by the 
10940             // layer's gutter in pixels.
10941             var mapGutter = this.gutter * this.map.getResolution();
10942             bounds = new OpenLayers.Bounds(bounds.left - mapGutter,
10943                                            bounds.bottom - mapGutter,
10944                                            bounds.right + mapGutter,
10945                                            bounds.top + mapGutter);
10946         }
10947
10948         if (this.wrapDateLine) {
10949             // wrap around the date line, within the limits of rounding error
10950             var wrappingOptions = { 
10951                 'rightTolerance':this.getResolution(),
10952                 'leftTolerance':this.getResolution()
10953             };    
10954             bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions);
10955                               
10956         }
10957         return bounds;
10958     },
10959
10960     CLASS_NAME: "OpenLayers.Layer"
10961 });
10962 /* ======================================================================
10963     OpenLayers/Layer/SphericalMercator.js
10964    ====================================================================== */
10965
10966 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
10967  * full list of contributors). Published under the 2-clause BSD license.
10968  * See license.txt in the OpenLayers distribution or repository for the
10969  * full text of the license. */
10970
10971 /**
10972  * @requires OpenLayers/Layer.js
10973  * @requires OpenLayers/Projection.js
10974  */
10975
10976 /**
10977  * Class: OpenLayers.Layer.SphericalMercator
10978  * A mixin for layers that wraps up the pieces neccesary to have a coordinate
10979  *     conversion for working with commercial APIs which use a spherical
10980  *     mercator projection.  Using this layer as a base layer, additional
10981  *     layers can be used as overlays if they are in the same projection.
10982  *
10983  * A layer is given properties of this object by setting the sphericalMercator
10984  *     property to true.
10985  *
10986  * More projection information:
10987  *  - http://spatialreference.org/ref/user/google-projection/
10988  *
10989  * Proj4 Text:
10990  *     +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0
10991  *     +k=1.0 +units=m +nadgrids=@null +no_defs
10992  *
10993  * WKT:
10994  *     900913=PROJCS["WGS84 / Simple Mercator", GEOGCS["WGS 84",
10995  *     DATUM["WGS_1984", SPHEROID["WGS_1984", 6378137.0, 298.257223563]], 
10996  *     PRIMEM["Greenwich", 0.0], UNIT["degree", 0.017453292519943295], 
10997  *     AXIS["Longitude", EAST], AXIS["Latitude", NORTH]],
10998  *     PROJECTION["Mercator_1SP_Google"], 
10999  *     PARAMETER["latitude_of_origin", 0.0], PARAMETER["central_meridian", 0.0], 
11000  *     PARAMETER["scale_factor", 1.0], PARAMETER["false_easting", 0.0], 
11001  *     PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["x", EAST],
11002  *     AXIS["y", NORTH], AUTHORITY["EPSG","900913"]]
11003  */
11004 OpenLayers.Layer.SphericalMercator = {
11005
11006     /**
11007      * Method: getExtent
11008      * Get the map's extent.
11009      *
11010      * Returns:
11011      * {<OpenLayers.Bounds>} The map extent.
11012      */
11013     getExtent: function() {
11014         var extent = null;
11015         if (this.sphericalMercator) {
11016             extent = this.map.calculateBounds();
11017         } else {
11018             extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this);
11019         }
11020         return extent;
11021     },
11022
11023     /**
11024      * Method: getLonLatFromViewPortPx
11025      * Get a map location from a pixel location
11026      * 
11027      * Parameters:
11028      * viewPortPx - {<OpenLayers.Pixel>}
11029      *
11030      * Returns:
11031      *  {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
11032      *  port OpenLayers.Pixel, translated into lon/lat by map lib
11033      *  If the map lib is not loaded or not centered, returns null
11034      */
11035     getLonLatFromViewPortPx: function (viewPortPx) {
11036         return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments);
11037     },
11038     
11039     /**
11040      * Method: getViewPortPxFromLonLat
11041      * Get a pixel location from a map location
11042      *
11043      * Parameters:
11044      * lonlat - {<OpenLayers.LonLat>}
11045      *
11046      * Returns:
11047      * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
11048      * OpenLayers.LonLat, translated into view port pixels by map lib
11049      * If map lib is not loaded or not centered, returns null
11050      */
11051     getViewPortPxFromLonLat: function (lonlat) {
11052         return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments);
11053     },
11054
11055     /** 
11056      * Method: initMercatorParameters 
11057      * Set up the mercator parameters on the layer: resolutions,
11058      *     projection, units.
11059      */
11060     initMercatorParameters: function() {
11061         // set up properties for Mercator - assume EPSG:900913
11062         this.RESOLUTIONS = [];
11063         var maxResolution = 156543.03390625;
11064         for(var zoom=0; zoom<=this.MAX_ZOOM_LEVEL; ++zoom) {
11065             this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom);
11066         }
11067         this.units = "m";
11068         this.projection = this.projection || "EPSG:900913";
11069     },
11070
11071     /**
11072      * APIMethod: forwardMercator
11073      * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator.
11074      *
11075      * Parameters:
11076      * lon - {float} 
11077      * lat - {float}
11078      * 
11079      * Returns:
11080      * {<OpenLayers.LonLat>} The coordinates transformed to Mercator.
11081      */
11082     forwardMercator: (function() {
11083         var gg = new OpenLayers.Projection("EPSG:4326");
11084         var sm = new OpenLayers.Projection("EPSG:900913");
11085         return function(lon, lat) {
11086             var point = OpenLayers.Projection.transform({x: lon, y: lat}, gg, sm);
11087             return new OpenLayers.LonLat(point.x, point.y);
11088         };
11089     })(),
11090
11091     /**
11092      * APIMethod: inverseMercator
11093      * Given a x,y in Spherical Mercator, return a point in EPSG:4326.
11094      *
11095      * Parameters:
11096      * x - {float} A map x in Spherical Mercator.
11097      * y - {float} A map y in Spherical Mercator.
11098      * 
11099      * Returns:
11100      * {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326.
11101      */
11102     inverseMercator: (function() {
11103         var gg = new OpenLayers.Projection("EPSG:4326");
11104         var sm = new OpenLayers.Projection("EPSG:900913");
11105         return function(x, y) {
11106             var point = OpenLayers.Projection.transform({x: x, y: y}, sm, gg);
11107             return new OpenLayers.LonLat(point.x, point.y);
11108         };
11109     })()
11110
11111 };
11112 /* ======================================================================
11113     OpenLayers/Layer/EventPane.js
11114    ====================================================================== */
11115
11116 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
11117  * full list of contributors). Published under the 2-clause BSD license.
11118  * See license.txt in the OpenLayers distribution or repository for the
11119  * full text of the license. */
11120
11121
11122 /**
11123  * @requires OpenLayers/Layer.js
11124  * @requires OpenLayers/Util.js
11125  */
11126
11127 /**
11128  * Class: OpenLayers.Layer.EventPane
11129  * Base class for 3rd party layers, providing a DOM element which isolates
11130  * the 3rd-party layer from mouse events.
11131  * Only used by Google layers.
11132  *
11133  * Automatically instantiated by the Google constructor, and not usually instantiated directly.
11134  *
11135  * Create a new event pane layer with the
11136  * <OpenLayers.Layer.EventPane> constructor.
11137  * 
11138  * Inherits from:
11139  *  - <OpenLayers.Layer>
11140  */
11141 OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, {
11142     
11143     /**
11144      * APIProperty: smoothDragPan
11145      * {Boolean} smoothDragPan determines whether non-public/internal API
11146      *     methods are used for better performance while dragging EventPane 
11147      *     layers. When not in sphericalMercator mode, the smoother dragging 
11148      *     doesn't actually move north/south directly with the number of 
11149      *     pixels moved, resulting in a slight offset when you drag your mouse 
11150      *     north south with this option on. If this visual disparity bothers 
11151      *     you, you should turn this option off, or use spherical mercator. 
11152      *     Default is on.
11153      */
11154     smoothDragPan: true,
11155
11156     /**
11157      * Property: isBaseLayer
11158      * {Boolean} EventPaned layers are always base layers, by necessity.
11159      */ 
11160     isBaseLayer: true,
11161
11162     /**
11163      * APIProperty: isFixed
11164      * {Boolean} EventPaned layers are fixed by default.
11165      */ 
11166     isFixed: true,
11167
11168     /**
11169      * Property: pane
11170      * {DOMElement} A reference to the element that controls the events.
11171      */
11172     pane: null,
11173
11174
11175     /**
11176      * Property: mapObject
11177      * {Object} This is the object which will be used to load the 3rd party library
11178      * in the case of the google layer, this will be of type GMap, 
11179      * in the case of the ve layer, this will be of type VEMap
11180      */ 
11181     mapObject: null,
11182
11183
11184     /**
11185      * Constructor: OpenLayers.Layer.EventPane
11186      * Create a new event pane layer
11187      *
11188      * Parameters:
11189      * name - {String}
11190      * options - {Object} Hashtable of extra options to tag onto the layer
11191      */
11192     initialize: function(name, options) {
11193         OpenLayers.Layer.prototype.initialize.apply(this, arguments);
11194         if (this.pane == null) {
11195             this.pane = OpenLayers.Util.createDiv(this.div.id + "_EventPane");
11196         }
11197     },
11198     
11199     /**
11200      * APIMethod: destroy
11201      * Deconstruct this layer.
11202      */
11203     destroy: function() {
11204         this.mapObject = null;
11205         this.pane = null;
11206         OpenLayers.Layer.prototype.destroy.apply(this, arguments); 
11207     },
11208
11209     
11210     /**
11211      * Method: setMap
11212      * Set the map property for the layer. This is done through an accessor
11213      * so that subclasses can override this and take special action once 
11214      * they have their map variable set. 
11215      *
11216      * Parameters:
11217      * map - {<OpenLayers.Map>}
11218      */
11219     setMap: function(map) {
11220         OpenLayers.Layer.prototype.setMap.apply(this, arguments);
11221         
11222         this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;
11223         this.pane.style.display = this.div.style.display;
11224         this.pane.style.width="100%";
11225         this.pane.style.height="100%";
11226         if (OpenLayers.BROWSER_NAME == "msie") {
11227             this.pane.style.background = 
11228                 "url(" + OpenLayers.Util.getImageLocation("blank.gif") + ")";
11229         }
11230
11231         if (this.isFixed) {
11232             this.map.viewPortDiv.appendChild(this.pane);
11233         } else {
11234             this.map.layerContainerDiv.appendChild(this.pane);
11235         }
11236
11237         // once our layer has been added to the map, we can load it
11238         this.loadMapObject();
11239     
11240         // if map didn't load, display warning
11241         if (this.mapObject == null) {
11242             this.loadWarningMessage();
11243         }
11244     },
11245
11246     /**
11247      * APIMethod: removeMap
11248      * On being removed from the map, we'll like to remove the invisible 'pane'
11249      *     div that we added to it on creation. 
11250      * 
11251      * Parameters:
11252      * map - {<OpenLayers.Map>}
11253      */
11254     removeMap: function(map) {
11255         if (this.pane && this.pane.parentNode) {
11256             this.pane.parentNode.removeChild(this.pane);
11257         }
11258         OpenLayers.Layer.prototype.removeMap.apply(this, arguments);
11259     },
11260   
11261     /**
11262      * Method: loadWarningMessage
11263      * If we can't load the map lib, then display an error message to the 
11264      *     user and tell them where to go for help.
11265      * 
11266      *     This function sets up the layout for the warning message. Each 3rd
11267      *     party layer must implement its own getWarningHTML() function to 
11268      *     provide the actual warning message.
11269      */
11270     loadWarningMessage:function() {
11271
11272         this.div.style.backgroundColor = "darkblue";
11273
11274         var viewSize = this.map.getSize();
11275         
11276         var msgW = Math.min(viewSize.w, 300);
11277         var msgH = Math.min(viewSize.h, 200);
11278         var size = new OpenLayers.Size(msgW, msgH);
11279
11280         var centerPx = new OpenLayers.Pixel(viewSize.w/2, viewSize.h/2);
11281
11282         var topLeft = centerPx.add(-size.w/2, -size.h/2);            
11283
11284         var div = OpenLayers.Util.createDiv(this.name + "_warning", 
11285                                             topLeft, 
11286                                             size,
11287                                             null,
11288                                             null,
11289                                             null,
11290                                             "auto");
11291
11292         div.style.padding = "7px";
11293         div.style.backgroundColor = "yellow";
11294
11295         div.innerHTML = this.getWarningHTML();
11296         this.div.appendChild(div);
11297     },
11298   
11299     /** 
11300      * Method: getWarningHTML
11301      * To be implemented by subclasses.
11302      * 
11303      * Returns:
11304      * {String} String with information on why layer is broken, how to get
11305      *          it working.
11306      */
11307     getWarningHTML:function() {
11308         //should be implemented by subclasses
11309         return "";
11310     },
11311   
11312     /**
11313      * Method: display
11314      * Set the display on the pane
11315      *
11316      * Parameters:
11317      * display - {Boolean}
11318      */
11319     display: function(display) {
11320         OpenLayers.Layer.prototype.display.apply(this, arguments);
11321         this.pane.style.display = this.div.style.display;
11322     },
11323   
11324     /**
11325      * Method: setZIndex
11326      * Set the z-index order for the pane.
11327      * 
11328      * Parameters:
11329      * zIndex - {int}
11330      */
11331     setZIndex: function (zIndex) {
11332         OpenLayers.Layer.prototype.setZIndex.apply(this, arguments);
11333         this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;
11334     },
11335     
11336     /**
11337      * Method: moveByPx
11338      * Move the layer based on pixel vector. To be implemented by subclasses.
11339      *
11340      * Parameters:
11341      * dx - {Number} The x coord of the displacement vector.
11342      * dy - {Number} The y coord of the displacement vector.
11343      */
11344     moveByPx: function(dx, dy) {
11345         OpenLayers.Layer.prototype.moveByPx.apply(this, arguments);
11346         
11347         if (this.dragPanMapObject) {
11348             this.dragPanMapObject(dx, -dy);
11349         } else {
11350             this.moveTo(this.map.getCachedCenter());
11351         }
11352     },
11353
11354     /**
11355      * Method: moveTo
11356      * Handle calls to move the layer.
11357      * 
11358      * Parameters:
11359      * bounds - {<OpenLayers.Bounds>}
11360      * zoomChanged - {Boolean}
11361      * dragging - {Boolean}
11362      */
11363     moveTo:function(bounds, zoomChanged, dragging) {
11364         OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
11365
11366         if (this.mapObject != null) {
11367
11368             var newCenter = this.map.getCenter();
11369             var newZoom = this.map.getZoom();
11370
11371             if (newCenter != null) {
11372
11373                 var moOldCenter = this.getMapObjectCenter();
11374                 var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter);
11375
11376                 var moOldZoom = this.getMapObjectZoom();
11377                 var oldZoom= this.getOLZoomFromMapObjectZoom(moOldZoom);
11378
11379                 if (!(newCenter.equals(oldCenter)) || newZoom != oldZoom) {
11380
11381                     if (!zoomChanged && oldCenter && this.dragPanMapObject && 
11382                         this.smoothDragPan) {
11383                         var oldPx = this.map.getViewPortPxFromLonLat(oldCenter);
11384                         var newPx = this.map.getViewPortPxFromLonLat(newCenter);
11385                         this.dragPanMapObject(newPx.x-oldPx.x, oldPx.y-newPx.y);
11386                     } else {
11387                         var center = this.getMapObjectLonLatFromOLLonLat(newCenter);
11388                         var zoom = this.getMapObjectZoomFromOLZoom(newZoom);
11389                         this.setMapObjectCenter(center, zoom, dragging);
11390                     }
11391                 }
11392             }
11393         }
11394     },
11395
11396
11397   /********************************************************/
11398   /*                                                      */
11399   /*                 Baselayer Functions                  */
11400   /*                                                      */
11401   /********************************************************/
11402
11403     /**
11404      * Method: getLonLatFromViewPortPx
11405      * Get a map location from a pixel location
11406      * 
11407      * Parameters:
11408      * viewPortPx - {<OpenLayers.Pixel>}
11409      *
11410      * Returns:
11411      *  {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view
11412      *  port OpenLayers.Pixel, translated into lon/lat by map lib
11413      *  If the map lib is not loaded or not centered, returns null
11414      */
11415     getLonLatFromViewPortPx: function (viewPortPx) {
11416         var lonlat = null;
11417         if ( (this.mapObject != null) && 
11418              (this.getMapObjectCenter() != null) ) {
11419             var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx);
11420             var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel);
11421             lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat);
11422         }
11423         return lonlat;
11424     },
11425
11426  
11427     /**
11428      * Method: getViewPortPxFromLonLat
11429      * Get a pixel location from a map location
11430      *
11431      * Parameters:
11432      * lonlat - {<OpenLayers.LonLat>}
11433      *
11434      * Returns:
11435      * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in
11436      * OpenLayers.LonLat, translated into view port pixels by map lib
11437      * If map lib is not loaded or not centered, returns null
11438      */
11439     getViewPortPxFromLonLat: function (lonlat) {
11440         var viewPortPx = null;
11441         if ( (this.mapObject != null) && 
11442              (this.getMapObjectCenter() != null) ) {
11443
11444             var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat);
11445             var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat);
11446         
11447             viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel);
11448         }
11449         return viewPortPx;
11450     },
11451
11452   /********************************************************/
11453   /*                                                      */
11454   /*               Translation Functions                  */
11455   /*                                                      */
11456   /*   The following functions translate Map Object and   */
11457   /*            OL formats for Pixel, LonLat              */
11458   /*                                                      */
11459   /********************************************************/
11460
11461   //
11462   // TRANSLATION: MapObject LatLng <-> OpenLayers.LonLat
11463   //
11464
11465     /**
11466      * Method: getOLLonLatFromMapObjectLonLat
11467      * Get an OL style map location from a 3rd party style map location
11468      *
11469      * Parameters
11470      * moLonLat - {Object}
11471      * 
11472      * Returns:
11473      * {<OpenLayers.LonLat>} An OpenLayers.LonLat, translated from the passed in 
11474      *          MapObject LonLat
11475      *          Returns null if null value is passed in
11476      */
11477     getOLLonLatFromMapObjectLonLat: function(moLonLat) {
11478         var olLonLat = null;
11479         if (moLonLat != null) {
11480             var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
11481             var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
11482             olLonLat = new OpenLayers.LonLat(lon, lat);
11483         }
11484         return olLonLat;
11485     },
11486
11487     /**
11488      * Method: getMapObjectLonLatFromOLLonLat
11489      * Get a 3rd party map location from an OL map location.
11490      *
11491      * Parameters:
11492      * olLonLat - {<OpenLayers.LonLat>}
11493      * 
11494      * Returns:
11495      * {Object} A MapObject LonLat, translated from the passed in 
11496      *          OpenLayers.LonLat
11497      *          Returns null if null value is passed in
11498      */
11499     getMapObjectLonLatFromOLLonLat: function(olLonLat) {
11500         var moLatLng = null;
11501         if (olLonLat != null) {
11502             moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon,
11503                                                          olLonLat.lat);
11504         }
11505         return moLatLng;
11506     },
11507
11508
11509   //
11510   // TRANSLATION: MapObject Pixel <-> OpenLayers.Pixel
11511   //
11512
11513     /**
11514      * Method: getOLPixelFromMapObjectPixel
11515      * Get an OL pixel location from a 3rd party pixel location.
11516      *
11517      * Parameters:
11518      * moPixel - {Object}
11519      * 
11520      * Returns:
11521      * {<OpenLayers.Pixel>} An OpenLayers.Pixel, translated from the passed in 
11522      *          MapObject Pixel
11523      *          Returns null if null value is passed in
11524      */
11525     getOLPixelFromMapObjectPixel: function(moPixel) {
11526         var olPixel = null;
11527         if (moPixel != null) {
11528             var x = this.getXFromMapObjectPixel(moPixel);
11529             var y = this.getYFromMapObjectPixel(moPixel);
11530             olPixel = new OpenLayers.Pixel(x, y);
11531         }
11532         return olPixel;
11533     },
11534
11535     /**
11536      * Method: getMapObjectPixelFromOLPixel
11537      * Get a 3rd party pixel location from an OL pixel location
11538      *
11539      * Parameters:
11540      * olPixel - {<OpenLayers.Pixel>}
11541      * 
11542      * Returns:
11543      * {Object} A MapObject Pixel, translated from the passed in 
11544      *          OpenLayers.Pixel
11545      *          Returns null if null value is passed in
11546      */
11547     getMapObjectPixelFromOLPixel: function(olPixel) {
11548         var moPixel = null;
11549         if (olPixel != null) {
11550             moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y);
11551         }
11552         return moPixel;
11553     },
11554
11555     CLASS_NAME: "OpenLayers.Layer.EventPane"
11556 });
11557 /* ======================================================================
11558     OpenLayers/Layer/FixedZoomLevels.js
11559    ====================================================================== */
11560
11561 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
11562  * full list of contributors). Published under the 2-clause BSD license.
11563  * See license.txt in the OpenLayers distribution or repository for the
11564  * full text of the license. */
11565
11566 /**
11567  * @requires OpenLayers/Layer.js
11568  */
11569
11570 /**
11571  * Class: OpenLayers.Layer.FixedZoomLevels
11572  *   Some Layers will already have established zoom levels (like google 
11573  *    or ve). Instead of trying to determine them and populate a resolutions[]
11574  *    Array with those values, we will hijack the resolution functionality
11575  *    here.
11576  * 
11577  *   When you subclass FixedZoomLevels: 
11578  * 
11579  *   The initResolutions() call gets nullified, meaning no resolutions[] array 
11580  *    is set up. Which would be a big problem getResolution() in Layer, since 
11581  *    it merely takes map.zoom and indexes into resolutions[]... but....
11582  * 
11583  *   The getResolution() call is also overridden. Instead of using the 
11584  *    resolutions[] array, we simply calculate the current resolution based
11585  *    on the current extent and the current map size. But how will we be able
11586  *    to calculate the current extent without knowing the resolution...?
11587  *  
11588  *   The getExtent() function is also overridden. Instead of calculating extent
11589  *    based on the center point and the current resolution, we instead 
11590  *    calculate the extent by getting the lonlats at the top-left and 
11591  *    bottom-right by using the getLonLatFromViewPortPx() translation function,
11592  *    taken from the pixel locations (0,0) and the size of the map. But how 
11593  *    will we be able to do lonlat-px translation without resolution....?
11594  * 
11595  *   The getZoomForResolution() method is overridden. Instead of indexing into
11596  *    the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in
11597  *    the desired resolution. With this extent, we then call getZoomForExtent() 
11598  * 
11599  * 
11600  *   Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels, 
11601  *    it is your responsibility to provide the following three functions:
11602  * 
11603  *   - getLonLatFromViewPortPx
11604  *   - getViewPortPxFromLonLat
11605  *   - getZoomForExtent
11606  * 
11607  *  ...those three functions should generally be provided by any reasonable 
11608  *  API that you might be working from.
11609  *
11610  */
11611 OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({
11612       
11613   /********************************************************/
11614   /*                                                      */
11615   /*                 Baselayer Functions                  */
11616   /*                                                      */
11617   /*    The following functions must all be implemented   */
11618   /*                  by all base layers                  */
11619   /*                                                      */
11620   /********************************************************/
11621     
11622     /**
11623      * Constructor: OpenLayers.Layer.FixedZoomLevels
11624      * Create a new fixed zoom levels layer.
11625      */
11626     initialize: function() {
11627         //this class is only just to add the following functions... 
11628         // nothing to actually do here... but it is probably a good
11629         // idea to have layers that use these functions call this 
11630         // inititalize() anyways, in case at some point we decide we 
11631         // do want to put some functionality or state in here. 
11632     },
11633     
11634     /**
11635      * Method: initResolutions
11636      * Populate the resolutions array
11637      */
11638     initResolutions: function() {
11639
11640         var props = ['minZoomLevel', 'maxZoomLevel', 'numZoomLevels'];
11641           
11642         for(var i=0, len=props.length; i<len; i++) {
11643             var property = props[i];
11644             this[property] = (this.options[property] != null)  
11645                                      ? this.options[property] 
11646                                      : this.map[property];
11647         }
11648
11649         if ( (this.minZoomLevel == null) ||
11650              (this.minZoomLevel < this.MIN_ZOOM_LEVEL) ){
11651             this.minZoomLevel = this.MIN_ZOOM_LEVEL;
11652         }        
11653
11654         //
11655         // At this point, we know what the minimum desired zoom level is, and
11656         //  we must calculate the total number of zoom levels. 
11657         //  
11658         //  Because we allow for the setting of either the 'numZoomLevels'
11659         //   or the 'maxZoomLevel' properties... on either the layer or the  
11660         //   map, we have to define some rules to see which we take into
11661         //   account first in this calculation. 
11662         //
11663         // The following is the precedence list for these properties:
11664         // 
11665         // (1) numZoomLevels set on layer
11666         // (2) maxZoomLevel set on layer
11667         // (3) numZoomLevels set on map
11668         // (4) maxZoomLevel set on map*
11669         // (5) none of the above*
11670         //
11671         // *Note that options (4) and (5) are only possible if the user 
11672         //  _explicitly_ sets the 'numZoomLevels' property on the map to 
11673         //  null, since it is set by default to 16. 
11674         //
11675
11676         //
11677         // Note to future: In 3.0, I think we should remove the default 
11678         // value of 16 for map.numZoomLevels. Rather, I think that value 
11679         // should be set as a default on the Layer.WMS class. If someone
11680         // creates a 3rd party layer and does not specify any 'minZoomLevel', 
11681         // 'maxZoomLevel', or 'numZoomLevels', and has not explicitly 
11682         // specified any of those on the map object either.. then I think
11683         // it is fair to say that s/he wants all the zoom levels available.
11684         // 
11685         // By making map.numZoomLevels *null* by default, that will be the 
11686         // case. As it is, I don't feel comfortable changing that right now
11687         // as it would be a glaring API change and actually would probably
11688         // break many peoples' codes. 
11689         //
11690
11691         //the number of zoom levels we'd like to have.
11692         var desiredZoomLevels;
11693
11694         //this is the maximum number of zoom levels the layer will allow, 
11695         // given the specified starting minimum zoom level.
11696         var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1;
11697
11698         if ( ((this.options.numZoomLevels == null) && 
11699               (this.options.maxZoomLevel != null)) // (2)
11700               ||
11701              ((this.numZoomLevels == null) &&
11702               (this.maxZoomLevel != null)) // (4)
11703            ) {
11704             //calculate based on specified maxZoomLevel (on layer or map)
11705             desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1;
11706         } else {
11707             //calculate based on specified numZoomLevels (on layer or map)
11708             // this covers cases (1) and (3)
11709             desiredZoomLevels = this.numZoomLevels;
11710         }
11711
11712         if (desiredZoomLevels != null) {
11713             //Now that we know what we would *like* the number of zoom levels
11714             // to be, based on layer or map options, we have to make sure that
11715             // it does not conflict with the actual limit, as specified by 
11716             // the constants on the layer itself (and calculated into the
11717             // 'limitZoomLevels' variable). 
11718             this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels);
11719         } else {
11720             // case (5) -- neither 'numZoomLevels' not 'maxZoomLevel' was 
11721             // set on either the layer or the map. So we just use the 
11722             // maximum limit as calculated by the layer's constants.
11723             this.numZoomLevels = limitZoomLevels;
11724         }
11725
11726         //now that the 'numZoomLevels' is appropriately, safely set, 
11727         // we go back and re-calculate the 'maxZoomLevel'.
11728         this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1;
11729
11730         if (this.RESOLUTIONS != null) {
11731             var resolutionsIndex = 0;
11732             this.resolutions = [];
11733             for(var i= this.minZoomLevel; i <= this.maxZoomLevel; i++) {
11734                 this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i];            
11735             }
11736             this.maxResolution = this.resolutions[0];
11737             this.minResolution = this.resolutions[this.resolutions.length - 1];
11738         }       
11739     },
11740     
11741     /**
11742      * APIMethod: getResolution
11743      * Get the current map resolution
11744      * 
11745      * Returns:
11746      * {Float} Map units per Pixel
11747      */
11748     getResolution: function() {
11749
11750         if (this.resolutions != null) {
11751             return OpenLayers.Layer.prototype.getResolution.apply(this, arguments);
11752         } else {
11753             var resolution = null;
11754             
11755             var viewSize = this.map.getSize();
11756             var extent = this.getExtent();
11757             
11758             if ((viewSize != null) && (extent != null)) {
11759                 resolution = Math.max( extent.getWidth()  / viewSize.w,
11760                                        extent.getHeight() / viewSize.h );
11761             }
11762             return resolution;
11763         }
11764      },
11765
11766     /**
11767      * APIMethod: getExtent
11768      * Calculates using px-> lonlat translation functions on tl and br 
11769      *     corners of viewport
11770      * 
11771      * Returns:
11772      * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat 
11773      *                       bounds of the current viewPort.
11774      */
11775     getExtent: function () {
11776         var size = this.map.getSize();
11777         var tl = this.getLonLatFromViewPortPx({
11778             x: 0, y: 0
11779         });
11780         var br = this.getLonLatFromViewPortPx({
11781             x: size.w, y: size.h
11782         });
11783         
11784         if ((tl != null) && (br != null)) {
11785             return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat);
11786         } else {
11787             return null;
11788         }
11789     },
11790
11791     /**
11792      * Method: getZoomForResolution
11793      * Get the zoom level for a given resolution
11794      *
11795      * Parameters:
11796      * resolution - {Float}
11797      *
11798      * Returns:
11799      * {Integer} A suitable zoom level for the specified resolution.
11800      *           If no baselayer is set, returns null.
11801      */
11802     getZoomForResolution: function(resolution) {
11803       
11804         if (this.resolutions != null) {
11805             return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments);
11806         } else {
11807             var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []);
11808             return this.getZoomForExtent(extent);
11809         }
11810     },
11811
11812
11813
11814     
11815     /********************************************************/
11816     /*                                                      */
11817     /*             Translation Functions                    */
11818     /*                                                      */
11819     /*    The following functions translate GMaps and OL    */ 
11820     /*     formats for Pixel, LonLat, Bounds, and Zoom      */
11821     /*                                                      */
11822     /********************************************************/
11823     
11824     
11825     //
11826     // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom
11827     //
11828   
11829     /**
11830      * Method: getOLZoomFromMapObjectZoom
11831      * Get the OL zoom index from the map object zoom level
11832      *
11833      * Parameters:
11834      * moZoom - {Integer}
11835      * 
11836      * Returns:
11837      * {Integer} An OpenLayers Zoom level, translated from the passed in zoom
11838      *           Returns null if null value is passed in
11839      */
11840     getOLZoomFromMapObjectZoom: function(moZoom) {
11841         var zoom = null;
11842         if (moZoom != null) {
11843             zoom = moZoom - this.minZoomLevel;
11844             if (this.map.baseLayer !== this) {
11845                 zoom = this.map.baseLayer.getZoomForResolution(
11846                     this.getResolutionForZoom(zoom)
11847                 );
11848             }
11849         }
11850         return zoom;
11851     },
11852     
11853     /**
11854      * Method: getMapObjectZoomFromOLZoom
11855      * Get the map object zoom level from the OL zoom level
11856      *
11857      * Parameters:
11858      * olZoom - {Integer}
11859      * 
11860      * Returns:
11861      * {Integer} A MapObject level, translated from the passed in olZoom
11862      *           Returns null if null value is passed in
11863      */
11864     getMapObjectZoomFromOLZoom: function(olZoom) {
11865         var zoom = null; 
11866         if (olZoom != null) {
11867             zoom = olZoom + this.minZoomLevel;
11868             if (this.map.baseLayer !== this) {
11869                 zoom = this.getZoomForResolution(
11870                     this.map.baseLayer.getResolutionForZoom(zoom)
11871                 );
11872             }
11873         }
11874         return zoom;
11875     },
11876
11877     CLASS_NAME: "OpenLayers.Layer.FixedZoomLevels"
11878 });
11879
11880 /* ======================================================================
11881     OpenLayers/Layer/Google.js
11882    ====================================================================== */
11883
11884 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
11885  * full list of contributors). Published under the 2-clause BSD license.
11886  * See license.txt in the OpenLayers distribution or repository for the
11887  * full text of the license. */
11888
11889
11890 /**
11891  * @requires OpenLayers/Layer/SphericalMercator.js
11892  * @requires OpenLayers/Layer/EventPane.js
11893  * @requires OpenLayers/Layer/FixedZoomLevels.js
11894  * @requires OpenLayers/Lang.js
11895  */
11896
11897 /**
11898  * Class: OpenLayers.Layer.Google
11899  * 
11900  * Provides a wrapper for Google's Maps API
11901  * Normally the Terms of Use for this API do not allow wrapping, but Google
11902  * have provided written consent to OpenLayers for this - see email in 
11903  * http://osgeo-org.1560.n6.nabble.com/Google-Maps-API-Terms-of-Use-changes-tp4910013p4911981.html
11904  * 
11905  * Inherits from:
11906  *  - <OpenLayers.Layer.SphericalMercator>
11907  *  - <OpenLayers.Layer.EventPane>
11908  *  - <OpenLayers.Layer.FixedZoomLevels>
11909  */
11910 OpenLayers.Layer.Google = OpenLayers.Class(
11911     OpenLayers.Layer.EventPane, 
11912     OpenLayers.Layer.FixedZoomLevels, {
11913     
11914     /** 
11915      * Constant: MIN_ZOOM_LEVEL
11916      * {Integer} 0 
11917      */
11918     MIN_ZOOM_LEVEL: 0,
11919     
11920     /** 
11921      * Constant: MAX_ZOOM_LEVEL
11922      * {Integer} 21
11923      */
11924     MAX_ZOOM_LEVEL: 21,
11925
11926     /** 
11927      * Constant: RESOLUTIONS
11928      * {Array(Float)} Hardcode these resolutions so that they are more closely
11929      *                tied with the standard wms projection
11930      */
11931     RESOLUTIONS: [
11932         1.40625, 
11933         0.703125, 
11934         0.3515625, 
11935         0.17578125, 
11936         0.087890625, 
11937         0.0439453125,
11938         0.02197265625, 
11939         0.010986328125, 
11940         0.0054931640625, 
11941         0.00274658203125,
11942         0.001373291015625, 
11943         0.0006866455078125, 
11944         0.00034332275390625,
11945         0.000171661376953125, 
11946         0.0000858306884765625, 
11947         0.00004291534423828125,
11948         0.00002145767211914062, 
11949         0.00001072883605957031,
11950         0.00000536441802978515, 
11951         0.00000268220901489257,
11952         0.0000013411045074462891,
11953         0.00000067055225372314453
11954     ],
11955
11956     /**
11957      * APIProperty: type
11958      * {GMapType}
11959      */
11960     type: null,
11961
11962     /**
11963      * APIProperty: wrapDateLine
11964      * {Boolean} Allow user to pan forever east/west.  Default is true.  
11965      *     Setting this to false only restricts panning if 
11966      *     <sphericalMercator> is true. 
11967      */
11968     wrapDateLine: true,
11969
11970     /**
11971      * APIProperty: sphericalMercator
11972      * {Boolean} Should the map act as a mercator-projected map? This will
11973      *     cause all interactions with the map to be in the actual map 
11974      *     projection, which allows support for vector drawing, overlaying 
11975      *     other maps, etc. 
11976      */
11977     sphericalMercator: false, 
11978     
11979     /**
11980      * Property: version
11981      * {Number} The version of the Google Maps API
11982      */
11983     version: null,
11984
11985     /** 
11986      * Constructor: OpenLayers.Layer.Google
11987      * 
11988      * Parameters:
11989      * name - {String} A name for the layer.
11990      * options - {Object} An optional object whose properties will be set
11991      *     on the layer.
11992      */
11993     initialize: function(name, options) {
11994         options = options || {};
11995         if(!options.version) {
11996             options.version = typeof GMap2 === "function" ? "2" : "3";
11997         }
11998         var mixin = OpenLayers.Layer.Google["v" +
11999             options.version.replace(/\./g, "_")];
12000         if (mixin) {
12001             OpenLayers.Util.applyDefaults(options, mixin);
12002         } else {
12003             throw "Unsupported Google Maps API version: " + options.version;
12004         }
12005
12006         OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS);
12007         if (options.maxExtent) {
12008             options.maxExtent = options.maxExtent.clone();
12009         }
12010
12011         OpenLayers.Layer.EventPane.prototype.initialize.apply(this,
12012             [name, options]);
12013         OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, 
12014             [name, options]);
12015
12016         if (this.sphericalMercator) {
12017             OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
12018             this.initMercatorParameters();
12019         }    
12020     },
12021
12022     /**
12023      * Method: clone
12024      * Create a clone of this layer
12025      *
12026      * Returns:
12027      * {<OpenLayers.Layer.Google>} An exact clone of this layer
12028      */
12029     clone: function() {
12030         /**
12031          * This method isn't intended to be called by a subclass and it
12032          * doesn't call the same method on the superclass.  We don't call
12033          * the super's clone because we don't want properties that are set
12034          * on this layer after initialize (i.e. this.mapObject etc.).
12035          */
12036         return new OpenLayers.Layer.Google(
12037             this.name, this.getOptions()
12038         );
12039     },
12040
12041     /**
12042      * APIMethod: setVisibility
12043      * Set the visibility flag for the layer and hide/show & redraw 
12044      *     accordingly. Fire event unless otherwise specified
12045      * 
12046      * Note that visibility is no longer simply whether or not the layer's
12047      *     style.display is set to "block". Now we store a 'visibility' state 
12048      *     property on the layer class, this allows us to remember whether or 
12049      *     not we *desire* for a layer to be visible. In the case where the 
12050      *     map's resolution is out of the layer's range, this desire may be 
12051      *     subverted.
12052      * 
12053      * Parameters:
12054      * visible - {Boolean} Display the layer (if in range)
12055      */
12056     setVisibility: function(visible) {
12057         // sharing a map container, opacity has to be set per layer
12058         var opacity = this.opacity == null ? 1 : this.opacity;
12059         OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments);
12060         this.setOpacity(opacity);
12061     },
12062     
12063     /** 
12064      * APIMethod: display
12065      * Hide or show the Layer
12066      * 
12067      * Parameters:
12068      * visible - {Boolean}
12069      */
12070     display: function(visible) {
12071         if (!this._dragging) {
12072             this.setGMapVisibility(visible);
12073         }
12074         OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments);
12075     },
12076     
12077     /**
12078      * Method: moveTo
12079      * 
12080      * Parameters:
12081      * bounds - {<OpenLayers.Bounds>}
12082      * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
12083      *     do some init work in that case.
12084      * dragging - {Boolean}
12085      */
12086     moveTo: function(bounds, zoomChanged, dragging) {
12087         this._dragging = dragging;
12088         OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments);
12089         delete this._dragging;
12090     },
12091     
12092     /**
12093      * APIMethod: setOpacity
12094      * Sets the opacity for the entire layer (all images)
12095      * 
12096      * Parameters:
12097      * opacity - {Float}
12098      */
12099     setOpacity: function(opacity) {
12100         if (opacity !== this.opacity) {
12101             if (this.map != null) {
12102                 this.map.events.triggerEvent("changelayer", {
12103                     layer: this,
12104                     property: "opacity"
12105                 });
12106             }
12107             this.opacity = opacity;
12108         }
12109         // Though this layer's opacity may not change, we're sharing a container
12110         // and need to update the opacity for the entire container.
12111         if (this.getVisibility()) {
12112             var container = this.getMapContainer();
12113             OpenLayers.Util.modifyDOMElement(
12114                 container, null, null, null, null, null, null, opacity
12115             );
12116         }
12117     },
12118
12119     /**
12120      * APIMethod: destroy
12121      * Clean up this layer.
12122      */
12123     destroy: function() {
12124         /**
12125          * We have to override this method because the event pane destroy
12126          * deletes the mapObject reference before removing this layer from
12127          * the map.
12128          */
12129         if (this.map) {
12130             this.setGMapVisibility(false);
12131             var cache = OpenLayers.Layer.Google.cache[this.map.id];
12132             if (cache && cache.count <= 1) {
12133                 this.removeGMapElements();
12134             }            
12135         }
12136         OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments);
12137     },
12138     
12139     /**
12140      * Method: removeGMapElements
12141      * Remove all elements added to the dom.  This should only be called if
12142      * this is the last of the Google layers for the given map.
12143      */
12144     removeGMapElements: function() {
12145         var cache = OpenLayers.Layer.Google.cache[this.map.id];
12146         if (cache) {
12147             // remove shared elements from dom
12148             var container = this.mapObject && this.getMapContainer();                
12149             if (container && container.parentNode) {
12150                 container.parentNode.removeChild(container);
12151             }
12152             var termsOfUse = cache.termsOfUse;
12153             if (termsOfUse && termsOfUse.parentNode) {
12154                 termsOfUse.parentNode.removeChild(termsOfUse);
12155             }
12156             var poweredBy = cache.poweredBy;
12157             if (poweredBy && poweredBy.parentNode) {
12158                 poweredBy.parentNode.removeChild(poweredBy);
12159             }
12160             if (this.mapObject && window.google && google.maps &&
12161                     google.maps.event && google.maps.event.clearListeners) {
12162                 google.maps.event.clearListeners(this.mapObject, 'tilesloaded');
12163             }
12164         }
12165     },
12166
12167     /**
12168      * APIMethod: removeMap
12169      * On being removed from the map, also remove termsOfUse and poweredBy divs
12170      * 
12171      * Parameters:
12172      * map - {<OpenLayers.Map>}
12173      */
12174     removeMap: function(map) {
12175         // hide layer before removing
12176         if (this.visibility && this.mapObject) {
12177             this.setGMapVisibility(false);
12178         }
12179         // check to see if last Google layer in this map
12180         var cache = OpenLayers.Layer.Google.cache[map.id];
12181         if (cache) {
12182             if (cache.count <= 1) {
12183                 this.removeGMapElements();
12184                 delete OpenLayers.Layer.Google.cache[map.id];
12185             } else {
12186                 // decrement the layer count
12187                 --cache.count;
12188             }
12189         }
12190         // remove references to gmap elements
12191         delete this.termsOfUse;
12192         delete this.poweredBy;
12193         delete this.mapObject;
12194         delete this.dragObject;
12195         OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments);
12196     },
12197     
12198   //
12199   // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
12200   //
12201
12202     /**
12203      * APIMethod: getOLBoundsFromMapObjectBounds
12204      * 
12205      * Parameters:
12206      * moBounds - {Object}
12207      * 
12208      * Returns:
12209      * {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the 
12210      *                       passed-in MapObject Bounds.
12211      *                       Returns null if null value is passed in.
12212      */
12213     getOLBoundsFromMapObjectBounds: function(moBounds) {
12214         var olBounds = null;
12215         if (moBounds != null) {
12216             var sw = moBounds.getSouthWest();
12217             var ne = moBounds.getNorthEast();
12218             if (this.sphericalMercator) {
12219                 sw = this.forwardMercator(sw.lng(), sw.lat());
12220                 ne = this.forwardMercator(ne.lng(), ne.lat());
12221             } else {
12222                 sw = new OpenLayers.LonLat(sw.lng(), sw.lat()); 
12223                 ne = new OpenLayers.LonLat(ne.lng(), ne.lat()); 
12224             }    
12225             olBounds = new OpenLayers.Bounds(sw.lon, 
12226                                              sw.lat, 
12227                                              ne.lon, 
12228                                              ne.lat );
12229         }
12230         return olBounds;
12231     },
12232
12233     /** 
12234      * APIMethod: getWarningHTML
12235      * 
12236      * Returns: 
12237      * {String} String with information on why layer is broken, how to get
12238      *          it working.
12239      */
12240     getWarningHTML:function() {
12241         return OpenLayers.i18n("googleWarning");
12242     },
12243
12244
12245     /************************************
12246      *                                  *
12247      *   MapObject Interface Controls   *
12248      *                                  *
12249      ************************************/
12250
12251
12252   // Get&Set Center, Zoom
12253
12254     /**
12255      * APIMethod: getMapObjectCenter
12256      * 
12257      * Returns: 
12258      * {Object} The mapObject's current center in Map Object format
12259      */
12260     getMapObjectCenter: function() {
12261         return this.mapObject.getCenter();
12262     },
12263
12264     /** 
12265      * APIMethod: getMapObjectZoom
12266      * 
12267      * Returns:
12268      * {Integer} The mapObject's current zoom, in Map Object format
12269      */
12270     getMapObjectZoom: function() {
12271         return this.mapObject.getZoom();
12272     },
12273
12274   
12275     /************************************
12276      *                                  *
12277      *       MapObject Primitives       *
12278      *                                  *
12279      ************************************/
12280
12281
12282   // LonLat
12283     
12284     /**
12285      * APIMethod: getLongitudeFromMapObjectLonLat
12286      * 
12287      * Parameters:
12288      * moLonLat - {Object} MapObject LonLat format
12289      * 
12290      * Returns:
12291      * {Float} Longitude of the given MapObject LonLat
12292      */
12293     getLongitudeFromMapObjectLonLat: function(moLonLat) {
12294         return this.sphericalMercator ? 
12295           this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon :
12296           moLonLat.lng();  
12297     },
12298
12299     /**
12300      * APIMethod: getLatitudeFromMapObjectLonLat
12301      * 
12302      * Parameters:
12303      * moLonLat - {Object} MapObject LonLat format
12304      * 
12305      * Returns:
12306      * {Float} Latitude of the given MapObject LonLat
12307      */
12308     getLatitudeFromMapObjectLonLat: function(moLonLat) {
12309         var lat = this.sphericalMercator ? 
12310           this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat :
12311           moLonLat.lat(); 
12312         return lat;  
12313     },
12314     
12315   // Pixel
12316     
12317     /**
12318      * APIMethod: getXFromMapObjectPixel
12319      * 
12320      * Parameters:
12321      * moPixel - {Object} MapObject Pixel format
12322      * 
12323      * Returns:
12324      * {Integer} X value of the MapObject Pixel
12325      */
12326     getXFromMapObjectPixel: function(moPixel) {
12327         return moPixel.x;
12328     },
12329
12330     /**
12331      * APIMethod: getYFromMapObjectPixel
12332      * 
12333      * Parameters:
12334      * moPixel - {Object} MapObject Pixel format
12335      * 
12336      * Returns:
12337      * {Integer} Y value of the MapObject Pixel
12338      */
12339     getYFromMapObjectPixel: function(moPixel) {
12340         return moPixel.y;
12341     },
12342     
12343     CLASS_NAME: "OpenLayers.Layer.Google"
12344 });
12345
12346 /**
12347  * Property: OpenLayers.Layer.Google.cache
12348  * {Object} Cache for elements that should only be created once per map.
12349  */
12350 OpenLayers.Layer.Google.cache = {};
12351
12352
12353 /**
12354  * Constant: OpenLayers.Layer.Google.v2
12355  * 
12356  * Mixin providing functionality specific to the Google Maps API v2.
12357  * 
12358  * This API has been deprecated by Google.
12359  * Developers are encouraged to migrate to v3 of the API; support for this
12360  * is provided by <OpenLayers.Layer.Google.v3>
12361  */
12362 OpenLayers.Layer.Google.v2 = {
12363     
12364     /**
12365      * Property: termsOfUse
12366      * {DOMElement} Div for Google's copyright and terms of use link
12367      */
12368     termsOfUse: null, 
12369
12370     /**
12371      * Property: poweredBy
12372      * {DOMElement} Div for Google's powered by logo and link
12373      */
12374     poweredBy: null, 
12375
12376     /**
12377      * Property: dragObject
12378      * {GDraggableObject} Since 2.93, Google has exposed the ability to get
12379      *     the maps GDraggableObject. We can now use this for smooth panning
12380      */
12381     dragObject: null, 
12382     
12383     /** 
12384      * Method: loadMapObject
12385      * Load the GMap and register appropriate event listeners. If we can't 
12386      *     load GMap2, then display a warning message.
12387      */
12388     loadMapObject:function() {
12389         if (!this.type) {
12390             this.type = G_NORMAL_MAP;
12391         }
12392         var mapObject, termsOfUse, poweredBy;
12393         var cache = OpenLayers.Layer.Google.cache[this.map.id];
12394         if (cache) {
12395             // there are already Google layers added to this map
12396             mapObject = cache.mapObject;
12397             termsOfUse = cache.termsOfUse;
12398             poweredBy = cache.poweredBy;
12399             // increment the layer count
12400             ++cache.count;
12401         } else {
12402             // this is the first Google layer for this map
12403
12404             var container = this.map.viewPortDiv;
12405             var div = document.createElement("div");
12406             div.id = this.map.id + "_GMap2Container";
12407             div.style.position = "absolute";
12408             div.style.width = "100%";
12409             div.style.height = "100%";
12410             container.appendChild(div);
12411
12412             // create GMap and shuffle elements
12413             try {
12414                 mapObject = new GMap2(div);
12415                 
12416                 // move the ToS and branding stuff up to the container div
12417                 termsOfUse = div.lastChild;
12418                 container.appendChild(termsOfUse);
12419                 termsOfUse.style.zIndex = "1100";
12420                 termsOfUse.style.right = "";
12421                 termsOfUse.style.bottom = "";
12422                 termsOfUse.className = "olLayerGoogleCopyright";
12423
12424                 poweredBy = div.lastChild;
12425                 container.appendChild(poweredBy);
12426                 poweredBy.style.zIndex = "1100";
12427                 poweredBy.style.right = "";
12428                 poweredBy.style.bottom = "";
12429                 poweredBy.className = "olLayerGooglePoweredBy gmnoprint";
12430                 
12431             } catch (e) {
12432                 throw(e);
12433             }
12434             // cache elements for use by any other google layers added to
12435             // this same map
12436             OpenLayers.Layer.Google.cache[this.map.id] = {
12437                 mapObject: mapObject,
12438                 termsOfUse: termsOfUse,
12439                 poweredBy: poweredBy,
12440                 count: 1
12441             };
12442         }
12443
12444         this.mapObject = mapObject;
12445         this.termsOfUse = termsOfUse;
12446         this.poweredBy = poweredBy;
12447         
12448         // ensure this layer type is one of the mapObject types
12449         if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(),
12450                                     this.type) === -1) {
12451             this.mapObject.addMapType(this.type);
12452         }
12453
12454         //since v 2.93 getDragObject is now available.
12455         if(typeof mapObject.getDragObject == "function") {
12456             this.dragObject = mapObject.getDragObject();
12457         } else {
12458             this.dragPanMapObject = null;
12459         }
12460         
12461         if(this.isBaseLayer === false) {
12462             this.setGMapVisibility(this.div.style.display !== "none");
12463         }
12464
12465     },
12466
12467     /**
12468      * APIMethod: onMapResize
12469      */
12470     onMapResize: function() {
12471         // workaround for resizing of invisible or not yet fully loaded layers
12472         // where GMap2.checkResize() does not work. We need to load the GMap
12473         // for the old div size, then checkResize(), and then call
12474         // layer.moveTo() to trigger GMap.setCenter() (which will finish
12475         // the GMap initialization).
12476         if(this.visibility && this.mapObject.isLoaded()) {
12477             this.mapObject.checkResize();
12478         } else {
12479             if(!this._resized) {
12480                 var layer = this;
12481                 var handle = GEvent.addListener(this.mapObject, "load", function() {
12482                     GEvent.removeListener(handle);
12483                     delete layer._resized;
12484                     layer.mapObject.checkResize();
12485                     layer.moveTo(layer.map.getCenter(), layer.map.getZoom());
12486                 });
12487             }
12488             this._resized = true;
12489         }
12490     },
12491
12492     /**
12493      * Method: setGMapVisibility
12494      * Display the GMap container and associated elements.
12495      * 
12496      * Parameters:
12497      * visible - {Boolean} Display the GMap elements.
12498      */
12499     setGMapVisibility: function(visible) {
12500         var cache = OpenLayers.Layer.Google.cache[this.map.id];
12501         if (cache) {
12502             var container = this.mapObject.getContainer();
12503             if (visible === true) {
12504                 this.mapObject.setMapType(this.type);
12505                 container.style.display = "";
12506                 this.termsOfUse.style.left = "";
12507                 this.termsOfUse.style.display = "";
12508                 this.poweredBy.style.display = "";            
12509                 cache.displayed = this.id;
12510             } else {
12511                 if (cache.displayed === this.id) {
12512                     delete cache.displayed;
12513                 }
12514                 if (!cache.displayed) {
12515                     container.style.display = "none";
12516                     this.termsOfUse.style.display = "none";
12517                     // move ToU far to the left in addition to setting display
12518                     // to "none", because at the end of the GMap2 load
12519                     // sequence, display: none will be unset and ToU would be
12520                     // visible after loading a map with a google layer that is
12521                     // initially hidden. 
12522                     this.termsOfUse.style.left = "-9999px";
12523                     this.poweredBy.style.display = "none";
12524                 }
12525             }
12526         }
12527     },
12528     
12529     /**
12530      * Method: getMapContainer
12531      * 
12532      * Returns:
12533      * {DOMElement} the GMap container's div
12534      */
12535     getMapContainer: function() {
12536         return this.mapObject.getContainer();
12537     },
12538
12539   //
12540   // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
12541   //
12542
12543     /**
12544      * APIMethod: getMapObjectBoundsFromOLBounds
12545      * 
12546      * Parameters:
12547      * olBounds - {<OpenLayers.Bounds>}
12548      * 
12549      * Returns:
12550      * {Object} A MapObject Bounds, translated from olBounds
12551      *          Returns null if null value is passed in
12552      */
12553     getMapObjectBoundsFromOLBounds: function(olBounds) {
12554         var moBounds = null;
12555         if (olBounds != null) {
12556             var sw = this.sphericalMercator ? 
12557               this.inverseMercator(olBounds.bottom, olBounds.left) : 
12558               new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
12559             var ne = this.sphericalMercator ? 
12560               this.inverseMercator(olBounds.top, olBounds.right) : 
12561               new OpenLayers.LonLat(olBounds.top, olBounds.right);
12562             moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon),
12563                                          new GLatLng(ne.lat, ne.lon));
12564         }
12565         return moBounds;
12566     },
12567
12568
12569     /************************************
12570      *                                  *
12571      *   MapObject Interface Controls   *
12572      *                                  *
12573      ************************************/
12574
12575
12576   // Get&Set Center, Zoom
12577
12578     /** 
12579      * APIMethod: setMapObjectCenter
12580      * Set the mapObject to the specified center and zoom
12581      * 
12582      * Parameters:
12583      * center - {Object} MapObject LonLat format
12584      * zoom - {int} MapObject zoom format
12585      */
12586     setMapObjectCenter: function(center, zoom) {
12587         this.mapObject.setCenter(center, zoom); 
12588     },
12589    
12590     /**
12591      * APIMethod: dragPanMapObject
12592      * 
12593      * Parameters:
12594      * dX - {Integer}
12595      * dY - {Integer}
12596      */
12597     dragPanMapObject: function(dX, dY) {
12598         this.dragObject.moveBy(new GSize(-dX, dY));
12599     },
12600
12601
12602   // LonLat - Pixel Translation
12603   
12604     /**
12605      * APIMethod: getMapObjectLonLatFromMapObjectPixel
12606      * 
12607      * Parameters:
12608      * moPixel - {Object} MapObject Pixel format
12609      * 
12610      * Returns:
12611      * {Object} MapObject LonLat translated from MapObject Pixel
12612      */
12613     getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
12614         return this.mapObject.fromContainerPixelToLatLng(moPixel);
12615     },
12616
12617     /**
12618      * APIMethod: getMapObjectPixelFromMapObjectLonLat
12619      * 
12620      * Parameters:
12621      * moLonLat - {Object} MapObject LonLat format
12622      * 
12623      * Returns:
12624      * {Object} MapObject Pixel transtlated from MapObject LonLat
12625      */
12626     getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
12627         return this.mapObject.fromLatLngToContainerPixel(moLonLat);
12628     },
12629
12630   
12631   // Bounds
12632   
12633     /** 
12634      * APIMethod: getMapObjectZoomFromMapObjectBounds
12635      * 
12636      * Parameters:
12637      * moBounds - {Object} MapObject Bounds format
12638      * 
12639      * Returns:
12640      * {Object} MapObject Zoom for specified MapObject Bounds
12641      */
12642     getMapObjectZoomFromMapObjectBounds: function(moBounds) {
12643         return this.mapObject.getBoundsZoomLevel(moBounds);
12644     },
12645
12646     /************************************
12647      *                                  *
12648      *       MapObject Primitives       *
12649      *                                  *
12650      ************************************/
12651
12652
12653   // LonLat
12654     
12655     /**
12656      * APIMethod: getMapObjectLonLatFromLonLat
12657      * 
12658      * Parameters:
12659      * lon - {Float}
12660      * lat - {Float}
12661      * 
12662      * Returns:
12663      * {Object} MapObject LonLat built from lon and lat params
12664      */
12665     getMapObjectLonLatFromLonLat: function(lon, lat) {
12666         var gLatLng;
12667         if(this.sphericalMercator) {
12668             var lonlat = this.inverseMercator(lon, lat);
12669             gLatLng = new GLatLng(lonlat.lat, lonlat.lon);
12670         } else {
12671             gLatLng = new GLatLng(lat, lon);
12672         }
12673         return gLatLng;
12674     },
12675
12676   // Pixel
12677     
12678     /**
12679      * APIMethod: getMapObjectPixelFromXY
12680      * 
12681      * Parameters:
12682      * x - {Integer}
12683      * y - {Integer}
12684      * 
12685      * Returns:
12686      * {Object} MapObject Pixel from x and y parameters
12687      */
12688     getMapObjectPixelFromXY: function(x, y) {
12689         return new GPoint(x, y);
12690     }
12691     
12692 };
12693 /* ======================================================================
12694     OpenLayers/Control.js
12695    ====================================================================== */
12696
12697 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
12698  * full list of contributors). Published under the 2-clause BSD license.
12699  * See license.txt in the OpenLayers distribution or repository for the
12700  * full text of the license. */
12701
12702 /**
12703  * @requires OpenLayers/BaseTypes/Class.js
12704  */
12705
12706 /**
12707  * Class: OpenLayers.Control
12708  * Controls affect the display or behavior of the map. They allow everything
12709  * from panning and zooming to displaying a scale indicator. Controls by 
12710  * default are added to the map they are contained within however it is
12711  * possible to add a control to an external div by passing the div in the
12712  * options parameter.
12713  * 
12714  * Example:
12715  * The following example shows how to add many of the common controls
12716  * to a map.
12717  * 
12718  * > var map = new OpenLayers.Map('map', { controls: [] });
12719  * >
12720  * > map.addControl(new OpenLayers.Control.PanZoomBar());
12721  * > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false}));
12722  * > map.addControl(new OpenLayers.Control.Permalink());
12723  * > map.addControl(new OpenLayers.Control.Permalink('permalink'));
12724  * > map.addControl(new OpenLayers.Control.MousePosition());
12725  * > map.addControl(new OpenLayers.Control.OverviewMap());
12726  * > map.addControl(new OpenLayers.Control.KeyboardDefaults());
12727  *
12728  * The next code fragment is a quick example of how to intercept 
12729  * shift-mouse click to display the extent of the bounding box
12730  * dragged out by the user.  Usually controls are not created
12731  * in exactly this manner.  See the source for a more complete 
12732  * example:
12733  *
12734  * > var control = new OpenLayers.Control();
12735  * > OpenLayers.Util.extend(control, {
12736  * >     draw: function () {
12737  * >         // this Handler.Box will intercept the shift-mousedown
12738  * >         // before Control.MouseDefault gets to see it
12739  * >         this.box = new OpenLayers.Handler.Box( control, 
12740  * >             {"done": this.notice},
12741  * >             {keyMask: OpenLayers.Handler.MOD_SHIFT});
12742  * >         this.box.activate();
12743  * >     },
12744  * >
12745  * >     notice: function (bounds) {
12746  * >         OpenLayers.Console.userError(bounds);
12747  * >     }
12748  * > }); 
12749  * > map.addControl(control);
12750  * 
12751  */
12752 OpenLayers.Control = OpenLayers.Class({
12753
12754     /** 
12755      * Property: id 
12756      * {String} 
12757      */
12758     id: null,
12759     
12760     /** 
12761      * Property: map 
12762      * {<OpenLayers.Map>} this gets set in the addControl() function in
12763      * OpenLayers.Map 
12764      */
12765     map: null,
12766
12767     /** 
12768      * APIProperty: div 
12769      * {DOMElement} The element that contains the control, if not present the 
12770      *     control is placed inside the map.
12771      */
12772     div: null,
12773
12774     /** 
12775      * APIProperty: type 
12776      * {Number} Controls can have a 'type'. The type determines the type of
12777      * interactions which are possible with them when they are placed in an
12778      * <OpenLayers.Control.Panel>. 
12779      */
12780     type: null, 
12781
12782     /** 
12783      * Property: allowSelection
12784      * {Boolean} By default, controls do not allow selection, because
12785      * it may interfere with map dragging. If this is true, OpenLayers
12786      * will not prevent selection of the control.
12787      * Default is false.
12788      */
12789     allowSelection: false,  
12790
12791     /** 
12792      * Property: displayClass 
12793      * {string}  This property is used for CSS related to the drawing of the
12794      * Control. 
12795      */
12796     displayClass: "",
12797     
12798     /**
12799     * APIProperty: title  
12800     * {string}  This property is used for showing a tooltip over the  
12801     * Control.  
12802     */ 
12803     title: "",
12804
12805     /**
12806      * APIProperty: autoActivate
12807      * {Boolean} Activate the control when it is added to a map.  Default is
12808      *     false.
12809      */
12810     autoActivate: false,
12811
12812     /** 
12813      * APIProperty: active 
12814      * {Boolean} The control is active (read-only).  Use <activate> and 
12815      *     <deactivate> to change control state.
12816      */
12817     active: null,
12818
12819     /**
12820      * Property: handlerOptions
12821      * {Object} Used to set non-default properties on the control's handler
12822      */
12823     handlerOptions: null,
12824
12825     /** 
12826      * Property: handler 
12827      * {<OpenLayers.Handler>} null
12828      */
12829     handler: null,
12830
12831     /**
12832      * APIProperty: eventListeners
12833      * {Object} If set as an option at construction, the eventListeners
12834      *     object will be registered with <OpenLayers.Events.on>.  Object
12835      *     structure must be a listeners object as shown in the example for
12836      *     the events.on method.
12837      */
12838     eventListeners: null,
12839
12840     /** 
12841      * APIProperty: events
12842      * {<OpenLayers.Events>} Events instance for listeners and triggering
12843      *     control specific events.
12844      *
12845      * Register a listener for a particular event with the following syntax:
12846      * (code)
12847      * control.events.register(type, obj, listener);
12848      * (end)
12849      *
12850      * Listeners will be called with a reference to an event object.  The
12851      *     properties of this event depends on exactly what happened.
12852      *
12853      * All event objects have at least the following properties:
12854      * object - {Object} A reference to control.events.object (a reference
12855      *      to the control).
12856      * element - {DOMElement} A reference to control.events.element (which
12857      *      will be null unless documented otherwise).
12858      *
12859      * Supported map event types:
12860      * activate - Triggered when activated.
12861      * deactivate - Triggered when deactivated.
12862      */
12863     events: null,
12864
12865     /**
12866      * Constructor: OpenLayers.Control
12867      * Create an OpenLayers Control.  The options passed as a parameter
12868      * directly extend the control.  For example passing the following:
12869      * 
12870      * > var control = new OpenLayers.Control({div: myDiv});
12871      *
12872      * Overrides the default div attribute value of null.
12873      * 
12874      * Parameters:
12875      * options - {Object} 
12876      */
12877     initialize: function (options) {
12878         // We do this before the extend so that instances can override
12879         // className in options.
12880         this.displayClass = 
12881             this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, "");
12882         
12883         OpenLayers.Util.extend(this, options);
12884         
12885         this.events = new OpenLayers.Events(this);
12886         if(this.eventListeners instanceof Object) {
12887             this.events.on(this.eventListeners);
12888         }
12889         if (this.id == null) {
12890             this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
12891         }
12892     },
12893
12894     /**
12895      * Method: destroy
12896      * The destroy method is used to perform any clean up before the control
12897      * is dereferenced.  Typically this is where event listeners are removed
12898      * to prevent memory leaks.
12899      */
12900     destroy: function () {
12901         if(this.events) {
12902             if(this.eventListeners) {
12903                 this.events.un(this.eventListeners);
12904             }
12905             this.events.destroy();
12906             this.events = null;
12907         }
12908         this.eventListeners = null;
12909
12910         // eliminate circular references
12911         if (this.handler) {
12912             this.handler.destroy();
12913             this.handler = null;
12914         }
12915         if(this.handlers) {
12916             for(var key in this.handlers) {
12917                 if(this.handlers.hasOwnProperty(key) &&
12918                    typeof this.handlers[key].destroy == "function") {
12919                     this.handlers[key].destroy();
12920                 }
12921             }
12922             this.handlers = null;
12923         }
12924         if (this.map) {
12925             this.map.removeControl(this);
12926             this.map = null;
12927         }
12928         this.div = null;
12929     },
12930
12931     /** 
12932      * Method: setMap
12933      * Set the map property for the control. This is done through an accessor
12934      * so that subclasses can override this and take special action once 
12935      * they have their map variable set. 
12936      *
12937      * Parameters:
12938      * map - {<OpenLayers.Map>} 
12939      */
12940     setMap: function(map) {
12941         this.map = map;
12942         if (this.handler) {
12943             this.handler.setMap(map);
12944         }
12945     },
12946   
12947     /**
12948      * Method: draw
12949      * The draw method is called when the control is ready to be displayed
12950      * on the page.  If a div has not been created one is created.  Controls
12951      * with a visual component will almost always want to override this method 
12952      * to customize the look of control. 
12953      *
12954      * Parameters:
12955      * px - {<OpenLayers.Pixel>} The top-left pixel position of the control
12956      *      or null.
12957      *
12958      * Returns:
12959      * {DOMElement} A reference to the DIV DOMElement containing the control
12960      */
12961     draw: function (px) {
12962         if (this.div == null) {
12963             this.div = OpenLayers.Util.createDiv(this.id);
12964             this.div.className = this.displayClass;
12965             if (!this.allowSelection) {
12966                 this.div.className += " olControlNoSelect";
12967                 this.div.setAttribute("unselectable", "on", 0);
12968                 this.div.onselectstart = OpenLayers.Function.False; 
12969             }    
12970             if (this.title != "") {
12971                 this.div.title = this.title;
12972             }
12973         }
12974         if (px != null) {
12975             this.position = px.clone();
12976         }
12977         this.moveTo(this.position);
12978         return this.div;
12979     },
12980
12981     /**
12982      * Method: moveTo
12983      * Sets the left and top style attributes to the passed in pixel 
12984      * coordinates.
12985      *
12986      * Parameters:
12987      * px - {<OpenLayers.Pixel>}
12988      */
12989     moveTo: function (px) {
12990         if ((px != null) && (this.div != null)) {
12991             this.div.style.left = px.x + "px";
12992             this.div.style.top = px.y + "px";
12993         }
12994     },
12995
12996     /**
12997      * APIMethod: activate
12998      * Explicitly activates a control and it's associated
12999      * handler if one has been set.  Controls can be
13000      * deactivated by calling the deactivate() method.
13001      * 
13002      * Returns:
13003      * {Boolean}  True if the control was successfully activated or
13004      *            false if the control was already active.
13005      */
13006     activate: function () {
13007         if (this.active) {
13008             return false;
13009         }
13010         if (this.handler) {
13011             this.handler.activate();
13012         }
13013         this.active = true;
13014         if(this.map) {
13015             OpenLayers.Element.addClass(
13016                 this.map.viewPortDiv,
13017                 this.displayClass.replace(/ /g, "") + "Active"
13018             );
13019         }
13020         this.events.triggerEvent("activate");
13021         return true;
13022     },
13023     
13024     /**
13025      * APIMethod: deactivate
13026      * Deactivates a control and it's associated handler if any.  The exact
13027      * effect of this depends on the control itself.
13028      * 
13029      * Returns:
13030      * {Boolean} True if the control was effectively deactivated or false
13031      *           if the control was already inactive.
13032      */
13033     deactivate: function () {
13034         if (this.active) {
13035             if (this.handler) {
13036                 this.handler.deactivate();
13037             }
13038             this.active = false;
13039             if(this.map) {
13040                 OpenLayers.Element.removeClass(
13041                     this.map.viewPortDiv,
13042                     this.displayClass.replace(/ /g, "") + "Active"
13043                 );
13044             }
13045             this.events.triggerEvent("deactivate");
13046             return true;
13047         }
13048         return false;
13049     },
13050
13051     CLASS_NAME: "OpenLayers.Control"
13052 });
13053
13054 /**
13055  * Constant: OpenLayers.Control.TYPE_BUTTON
13056  */
13057 OpenLayers.Control.TYPE_BUTTON = 1;
13058
13059 /**
13060  * Constant: OpenLayers.Control.TYPE_TOGGLE
13061  */
13062 OpenLayers.Control.TYPE_TOGGLE = 2;
13063
13064 /**
13065  * Constant: OpenLayers.Control.TYPE_TOOL
13066  */
13067 OpenLayers.Control.TYPE_TOOL   = 3;
13068 /* ======================================================================
13069     OpenLayers/Strategy.js
13070    ====================================================================== */
13071
13072 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
13073  * full list of contributors). Published under the 2-clause BSD license.
13074  * See license.txt in the OpenLayers distribution or repository for the
13075  * full text of the license. */
13076
13077 /**
13078  * @requires OpenLayers/BaseTypes/Class.js
13079  */
13080
13081 /**
13082  * Class: OpenLayers.Strategy
13083  * Abstract vector layer strategy class.  Not to be instantiated directly.  Use
13084  *     one of the strategy subclasses instead.
13085  */
13086 OpenLayers.Strategy = OpenLayers.Class({
13087     
13088     /**
13089      * Property: layer
13090      * {<OpenLayers.Layer.Vector>} The layer this strategy belongs to.
13091      */
13092     layer: null,
13093     
13094     /**
13095      * Property: options
13096      * {Object} Any options sent to the constructor.
13097      */
13098     options: null,
13099
13100     /** 
13101      * Property: active 
13102      * {Boolean} The control is active.
13103      */
13104     active: null,
13105
13106     /**
13107      * Property: autoActivate
13108      * {Boolean} The creator of the strategy can set autoActivate to false
13109      *      to fully control when the protocol is activated and deactivated.
13110      *      Defaults to true.
13111      */
13112     autoActivate: true,
13113
13114     /**
13115      * Property: autoDestroy
13116      * {Boolean} The creator of the strategy can set autoDestroy to false
13117      *      to fully control when the strategy is destroyed. Defaults to
13118      *      true.
13119      */
13120     autoDestroy: true,
13121
13122     /**
13123      * Constructor: OpenLayers.Strategy
13124      * Abstract class for vector strategies.  Create instances of a subclass.
13125      *
13126      * Parameters:
13127      * options - {Object} Optional object whose properties will be set on the
13128      *     instance.
13129      */
13130     initialize: function(options) {
13131         OpenLayers.Util.extend(this, options);
13132         this.options = options;
13133         // set the active property here, so that user cannot override it
13134         this.active = false;
13135     },
13136     
13137     /**
13138      * APIMethod: destroy
13139      * Clean up the strategy.
13140      */
13141     destroy: function() {
13142         this.deactivate();
13143         this.layer = null;
13144         this.options = null;
13145     },
13146
13147     /**
13148      * Method: setLayer
13149      * Called to set the <layer> property.
13150      *
13151      * Parameters:
13152      * layer - {<OpenLayers.Layer.Vector>}
13153      */
13154     setLayer: function(layer) {
13155         this.layer = layer;
13156     },
13157     
13158     /**
13159      * Method: activate
13160      * Activate the strategy.  Register any listeners, do appropriate setup.
13161      *
13162      * Returns:
13163      * {Boolean} True if the strategy was successfully activated or false if
13164      *      the strategy was already active.
13165      */
13166     activate: function() {
13167         if (!this.active) {
13168             this.active = true;
13169             return true;
13170         }
13171         return false;
13172     },
13173     
13174     /**
13175      * Method: deactivate
13176      * Deactivate the strategy.  Unregister any listeners, do appropriate
13177      *     tear-down.
13178      *
13179      * Returns:
13180      * {Boolean} True if the strategy was successfully deactivated or false if
13181      *      the strategy was already inactive.
13182      */
13183     deactivate: function() {
13184         if (this.active) {
13185             this.active = false;
13186             return true;
13187         }
13188         return false;
13189     },
13190    
13191     CLASS_NAME: "OpenLayers.Strategy" 
13192 });
13193 /* ======================================================================
13194     OpenLayers/Feature.js
13195    ====================================================================== */
13196
13197 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
13198  * full list of contributors). Published under the 2-clause BSD license.
13199  * See license.txt in the OpenLayers distribution or repository for the
13200  * full text of the license. */
13201
13202
13203 /**
13204  * @requires OpenLayers/BaseTypes/Class.js
13205  * @requires OpenLayers/Util.js
13206  */
13207
13208 /**
13209  * Class: OpenLayers.Feature
13210  * Features are combinations of geography and attributes. The OpenLayers.Feature
13211  *     class specifically combines a marker and a lonlat.
13212  */
13213 OpenLayers.Feature = OpenLayers.Class({
13214
13215     /** 
13216      * Property: layer 
13217      * {<OpenLayers.Layer>} 
13218      */
13219     layer: null,
13220
13221     /** 
13222      * Property: id 
13223      * {String} 
13224      */
13225     id: null,
13226     
13227     /** 
13228      * Property: lonlat 
13229      * {<OpenLayers.LonLat>} 
13230      */
13231     lonlat: null,
13232
13233     /** 
13234      * Property: data 
13235      * {Object} 
13236      */
13237     data: null,
13238
13239     /** 
13240      * Property: marker 
13241      * {<OpenLayers.Marker>} 
13242      */
13243     marker: null,
13244
13245     /**
13246      * APIProperty: popupClass
13247      * {<OpenLayers.Class>} The class which will be used to instantiate
13248      *     a new Popup. Default is <OpenLayers.Popup.Anchored>.
13249      */
13250     popupClass: null,
13251
13252     /** 
13253      * Property: popup 
13254      * {<OpenLayers.Popup>} 
13255      */
13256     popup: null,
13257
13258     /** 
13259      * Constructor: OpenLayers.Feature
13260      * Constructor for features.
13261      *
13262      * Parameters:
13263      * layer - {<OpenLayers.Layer>} 
13264      * lonlat - {<OpenLayers.LonLat>} 
13265      * data - {Object} 
13266      * 
13267      * Returns:
13268      * {<OpenLayers.Feature>}
13269      */
13270     initialize: function(layer, lonlat, data) {
13271         this.layer = layer;
13272         this.lonlat = lonlat;
13273         this.data = (data != null) ? data : {};
13274         this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); 
13275     },
13276
13277     /** 
13278      * Method: destroy
13279      * nullify references to prevent circular references and memory leaks
13280      */
13281     destroy: function() {
13282
13283         //remove the popup from the map
13284         if ((this.layer != null) && (this.layer.map != null)) {
13285             if (this.popup != null) {
13286                 this.layer.map.removePopup(this.popup);
13287             }
13288         }
13289         // remove the marker from the layer
13290         if (this.layer != null && this.marker != null) {
13291             this.layer.removeMarker(this.marker);
13292         }
13293
13294         this.layer = null;
13295         this.id = null;
13296         this.lonlat = null;
13297         this.data = null;
13298         if (this.marker != null) {
13299             this.destroyMarker(this.marker);
13300             this.marker = null;
13301         }
13302         if (this.popup != null) {
13303             this.destroyPopup(this.popup);
13304             this.popup = null;
13305         }
13306     },
13307     
13308     /**
13309      * Method: onScreen
13310      * 
13311      * Returns:
13312      * {Boolean} Whether or not the feature is currently visible on screen
13313      *           (based on its 'lonlat' property)
13314      */
13315     onScreen:function() {
13316         
13317         var onScreen = false;
13318         if ((this.layer != null) && (this.layer.map != null)) {
13319             var screenBounds = this.layer.map.getExtent();
13320             onScreen = screenBounds.containsLonLat(this.lonlat);
13321         }    
13322         return onScreen;
13323     },
13324     
13325
13326     /**
13327      * Method: createMarker
13328      * Based on the data associated with the Feature, create and return a marker object.
13329      *
13330      * Returns: 
13331      * {<OpenLayers.Marker>} A Marker Object created from the 'lonlat' and 'icon' properties
13332      *          set in this.data. If no 'lonlat' is set, returns null. If no
13333      *          'icon' is set, OpenLayers.Marker() will load the default image.
13334      *          
13335      *          Note - this.marker is set to return value
13336      * 
13337      */
13338     createMarker: function() {
13339
13340         if (this.lonlat != null) {
13341             this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);
13342         }
13343         return this.marker;
13344     },
13345
13346     /**
13347      * Method: destroyMarker
13348      * Destroys marker.
13349      * If user overrides the createMarker() function, s/he should be able
13350      *   to also specify an alternative function for destroying it
13351      */
13352     destroyMarker: function() {
13353         this.marker.destroy();  
13354     },
13355
13356     /**
13357      * Method: createPopup
13358      * Creates a popup object created from the 'lonlat', 'popupSize',
13359      *     and 'popupContentHTML' properties set in this.data. It uses
13360      *     this.marker.icon as default anchor. 
13361      *  
13362      *  If no 'lonlat' is set, returns null. 
13363      *  If no this.marker has been created, no anchor is sent.
13364      *
13365      *  Note - the returned popup object is 'owned' by the feature, so you
13366      *      cannot use the popup's destroy method to discard the popup.
13367      *      Instead, you must use the feature's destroyPopup
13368      * 
13369      *  Note - this.popup is set to return value
13370      * 
13371      * Parameters: 
13372      * closeBox - {Boolean} create popup with closebox or not
13373      * 
13374      * Returns:
13375      * {<OpenLayers.Popup>} Returns the created popup, which is also set
13376      *     as 'popup' property of this feature. Will be of whatever type
13377      *     specified by this feature's 'popupClass' property, but must be
13378      *     of type <OpenLayers.Popup>.
13379      * 
13380      */
13381     createPopup: function(closeBox) {
13382
13383         if (this.lonlat != null) {
13384             if (!this.popup) {
13385                 var anchor = (this.marker) ? this.marker.icon : null;
13386                 var popupClass = this.popupClass ? 
13387                     this.popupClass : OpenLayers.Popup.Anchored;
13388                 this.popup = new popupClass(this.id + "_popup", 
13389                                             this.lonlat,
13390                                             this.data.popupSize,
13391                                             this.data.popupContentHTML,
13392                                             anchor, 
13393                                             closeBox); 
13394             }    
13395             if (this.data.overflow != null) {
13396                 this.popup.contentDiv.style.overflow = this.data.overflow;
13397             }    
13398             
13399             this.popup.feature = this;
13400         }        
13401         return this.popup;
13402     },
13403
13404     
13405     /**
13406      * Method: destroyPopup
13407      * Destroys the popup created via createPopup.
13408      *
13409      * As with the marker, if user overrides the createPopup() function, s/he 
13410      *   should also be able to override the destruction
13411      */
13412     destroyPopup: function() {
13413         if (this.popup) {
13414             this.popup.feature = null;
13415             this.popup.destroy();
13416             this.popup = null;
13417         }    
13418     },
13419
13420     CLASS_NAME: "OpenLayers.Feature"
13421 });
13422 /* ======================================================================
13423     OpenLayers/Feature/Vector.js
13424    ====================================================================== */
13425
13426 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
13427  * full list of contributors). Published under the 2-clause BSD license.
13428  * See license.txt in the OpenLayers distribution or repository for the
13429  * full text of the license. */
13430
13431 // TRASH THIS
13432 OpenLayers.State = {
13433     /** states */
13434     UNKNOWN: 'Unknown',
13435     INSERT: 'Insert',
13436     UPDATE: 'Update',
13437     DELETE: 'Delete'
13438 };
13439
13440 /**
13441  * @requires OpenLayers/Feature.js
13442  * @requires OpenLayers/Util.js
13443  */
13444
13445 /**
13446  * Class: OpenLayers.Feature.Vector
13447  * Vector features use the OpenLayers.Geometry classes as geometry description.
13448  * They have an 'attributes' property, which is the data object, and a 'style'
13449  * property, the default values of which are defined in the 
13450  * <OpenLayers.Feature.Vector.style> objects.
13451  * 
13452  * Inherits from:
13453  *  - <OpenLayers.Feature>
13454  */
13455 OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {
13456
13457     /** 
13458      * Property: fid 
13459      * {String} 
13460      */
13461     fid: null,
13462     
13463     /** 
13464      * APIProperty: geometry 
13465      * {<OpenLayers.Geometry>} 
13466      */
13467     geometry: null,
13468
13469     /** 
13470      * APIProperty: attributes 
13471      * {Object} This object holds arbitrary, serializable properties that
13472      *     describe the feature.
13473      */
13474     attributes: null,
13475
13476     /**
13477      * Property: bounds
13478      * {<OpenLayers.Bounds>} The box bounding that feature's geometry, that
13479      *     property can be set by an <OpenLayers.Format> object when
13480      *     deserializing the feature, so in most cases it represents an
13481      *     information set by the server. 
13482      */
13483     bounds: null,
13484
13485     /** 
13486      * Property: state 
13487      * {String} 
13488      */
13489     state: null,
13490     
13491     /** 
13492      * APIProperty: style 
13493      * {Object} 
13494      */
13495     style: null,
13496
13497     /**
13498      * APIProperty: url
13499      * {String} If this property is set it will be taken into account by
13500      *     {<OpenLayers.HTTP>} when upadting or deleting the feature.
13501      */
13502     url: null,
13503     
13504     /**
13505      * Property: renderIntent
13506      * {String} rendering intent currently being used
13507      */
13508     renderIntent: "default",
13509     
13510     /**
13511      * APIProperty: modified
13512      * {Object} An object with the originals of the geometry and attributes of
13513      * the feature, if they were changed. Currently this property is only read
13514      * by <OpenLayers.Format.WFST.v1>, and written by
13515      * <OpenLayers.Control.ModifyFeature>, which sets the geometry property.
13516      * Applications can set the originals of modified attributes in the
13517      * attributes property. Note that applications have to check if this
13518      * object and the attributes property is already created before using it.
13519      * After a change made with ModifyFeature, this object could look like
13520      *
13521      * (code)
13522      * {
13523      *     geometry: >Object
13524      * }
13525      * (end)
13526      *
13527      * When an application has made changes to feature attributes, it could
13528      * have set the attributes to something like this:
13529      *
13530      * (code)
13531      * {
13532      *     attributes: {
13533      *         myAttribute: "original"
13534      *     }
13535      * }
13536      * (end)
13537      *
13538      * Note that <OpenLayers.Format.WFST.v1> only checks for truthy values in
13539      * *modified.geometry* and the attribute names in *modified.attributes*,
13540      * but it is recommended to set the original values (and not just true) as
13541      * attribute value, so applications could use this information to undo
13542      * changes.
13543      */
13544     modified: null,
13545
13546     /** 
13547      * Constructor: OpenLayers.Feature.Vector
13548      * Create a vector feature. 
13549      * 
13550      * Parameters:
13551      * geometry - {<OpenLayers.Geometry>} The geometry that this feature
13552      *     represents.
13553      * attributes - {Object} An optional object that will be mapped to the
13554      *     <attributes> property. 
13555      * style - {Object} An optional style object.
13556      */
13557     initialize: function(geometry, attributes, style) {
13558         OpenLayers.Feature.prototype.initialize.apply(this,
13559                                                       [null, null, attributes]);
13560         this.lonlat = null;
13561         this.geometry = geometry ? geometry : null;
13562         this.state = null;
13563         this.attributes = {};
13564         if (attributes) {
13565             this.attributes = OpenLayers.Util.extend(this.attributes,
13566                                                      attributes);
13567         }
13568         this.style = style ? style : null; 
13569     },
13570     
13571     /** 
13572      * Method: destroy
13573      * nullify references to prevent circular references and memory leaks
13574      */
13575     destroy: function() {
13576         if (this.layer) {
13577             this.layer.removeFeatures(this);
13578             this.layer = null;
13579         }
13580             
13581         this.geometry = null;
13582         this.modified = null;
13583         OpenLayers.Feature.prototype.destroy.apply(this, arguments);
13584     },
13585     
13586     /**
13587      * Method: clone
13588      * Create a clone of this vector feature.  Does not set any non-standard
13589      *     properties.
13590      *
13591      * Returns:
13592      * {<OpenLayers.Feature.Vector>} An exact clone of this vector feature.
13593      */
13594     clone: function () {
13595         return new OpenLayers.Feature.Vector(
13596             this.geometry ? this.geometry.clone() : null,
13597             this.attributes,
13598             this.style);
13599     },
13600
13601     /**
13602      * Method: onScreen
13603      * Determine whether the feature is within the map viewport.  This method
13604      *     tests for an intersection between the geometry and the viewport
13605      *     bounds.  If a more effecient but less precise geometry bounds
13606      *     intersection is desired, call the method with the boundsOnly
13607      *     parameter true.
13608      *
13609      * Parameters:
13610      * boundsOnly - {Boolean} Only test whether a feature's bounds intersects
13611      *     the viewport bounds.  Default is false.  If false, the feature's
13612      *     geometry must intersect the viewport for onScreen to return true.
13613      * 
13614      * Returns:
13615      * {Boolean} The feature is currently visible on screen (optionally
13616      *     based on its bounds if boundsOnly is true).
13617      */
13618     onScreen:function(boundsOnly) {
13619         var onScreen = false;
13620         if(this.layer && this.layer.map) {
13621             var screenBounds = this.layer.map.getExtent();
13622             if(boundsOnly) {
13623                 var featureBounds = this.geometry.getBounds();
13624                 onScreen = screenBounds.intersectsBounds(featureBounds);
13625             } else {
13626                 var screenPoly = screenBounds.toGeometry();
13627                 onScreen = screenPoly.intersects(this.geometry);
13628             }
13629         }    
13630         return onScreen;
13631     },
13632
13633     /**
13634      * Method: getVisibility
13635      * Determine whether the feature is displayed or not. It may not displayed
13636      *     because:
13637      *     - its style display property is set to 'none',
13638      *     - it doesn't belong to any layer,
13639      *     - the styleMap creates a symbolizer with display property set to 'none'
13640      *          for it,
13641      *     - the layer which it belongs to is not visible.
13642      * 
13643      * Returns:
13644      * {Boolean} The feature is currently displayed.
13645      */
13646     getVisibility: function() {
13647         return !(this.style && this.style.display == 'none' ||
13648                  !this.layer ||
13649                  this.layer && this.layer.styleMap &&
13650                  this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == 'none' ||
13651                  this.layer && !this.layer.getVisibility());
13652     },
13653     
13654     /**
13655      * Method: createMarker
13656      * HACK - we need to decide if all vector features should be able to
13657      *     create markers
13658      * 
13659      * Returns:
13660      * {<OpenLayers.Marker>} For now just returns null
13661      */
13662     createMarker: function() {
13663         return null;
13664     },
13665
13666     /**
13667      * Method: destroyMarker
13668      * HACK - we need to decide if all vector features should be able to
13669      *     delete markers
13670      * 
13671      * If user overrides the createMarker() function, s/he should be able
13672      *   to also specify an alternative function for destroying it
13673      */
13674     destroyMarker: function() {
13675         // pass
13676     },
13677
13678     /**
13679      * Method: createPopup
13680      * HACK - we need to decide if all vector features should be able to
13681      *     create popups
13682      * 
13683      * Returns:
13684      * {<OpenLayers.Popup>} For now just returns null
13685      */
13686     createPopup: function() {
13687         return null;
13688     },
13689
13690     /**
13691      * Method: atPoint
13692      * Determins whether the feature intersects with the specified location.
13693      * 
13694      * Parameters: 
13695      * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
13696      *     object with a 'lon' and 'lat' properties.
13697      * toleranceLon - {float} Optional tolerance in Geometric Coords
13698      * toleranceLat - {float} Optional tolerance in Geographic Coords
13699      * 
13700      * Returns:
13701      * {Boolean} Whether or not the feature is at the specified location
13702      */
13703     atPoint: function(lonlat, toleranceLon, toleranceLat) {
13704         var atPoint = false;
13705         if(this.geometry) {
13706             atPoint = this.geometry.atPoint(lonlat, toleranceLon, 
13707                                                     toleranceLat);
13708         }
13709         return atPoint;
13710     },
13711
13712     /**
13713      * Method: destroyPopup
13714      * HACK - we need to decide if all vector features should be able to
13715      * delete popups
13716      */
13717     destroyPopup: function() {
13718         // pass
13719     },
13720
13721     /**
13722      * Method: move
13723      * Moves the feature and redraws it at its new location
13724      *
13725      * Parameters:
13726      * location - {<OpenLayers.LonLat> or <OpenLayers.Pixel>} the
13727      *         location to which to move the feature.
13728      */
13729     move: function(location) {
13730
13731         if(!this.layer || !this.geometry.move){
13732             //do nothing if no layer or immoveable geometry
13733             return undefined;
13734         }
13735
13736         var pixel;
13737         if (location.CLASS_NAME == "OpenLayers.LonLat") {
13738             pixel = this.layer.getViewPortPxFromLonLat(location);
13739         } else {
13740             pixel = location;
13741         }
13742         
13743         var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());
13744         var res = this.layer.map.getResolution();
13745         this.geometry.move(res * (pixel.x - lastPixel.x),
13746                            res * (lastPixel.y - pixel.y));
13747         this.layer.drawFeature(this);
13748         return lastPixel;
13749     },
13750     
13751     /**
13752      * Method: toState
13753      * Sets the new state
13754      *
13755      * Parameters:
13756      * state - {String} 
13757      */
13758     toState: function(state) {
13759         if (state == OpenLayers.State.UPDATE) {
13760             switch (this.state) {
13761                 case OpenLayers.State.UNKNOWN:
13762                 case OpenLayers.State.DELETE:
13763                     this.state = state;
13764                     break;
13765                 case OpenLayers.State.UPDATE:
13766                 case OpenLayers.State.INSERT:
13767                     break;
13768             }
13769         } else if (state == OpenLayers.State.INSERT) {
13770             switch (this.state) {
13771                 case OpenLayers.State.UNKNOWN:
13772                     break;
13773                 default:
13774                     this.state = state;
13775                     break;
13776             }
13777         } else if (state == OpenLayers.State.DELETE) {
13778             switch (this.state) {
13779                 case OpenLayers.State.INSERT:
13780                     // the feature should be destroyed
13781                     break;
13782                 case OpenLayers.State.DELETE:
13783                     break;
13784                 case OpenLayers.State.UNKNOWN:
13785                 case OpenLayers.State.UPDATE:
13786                     this.state = state;
13787                     break;
13788             }
13789         } else if (state == OpenLayers.State.UNKNOWN) {
13790             this.state = state;
13791         }
13792     },
13793     
13794     CLASS_NAME: "OpenLayers.Feature.Vector"
13795 });
13796
13797
13798 /**
13799  * Constant: OpenLayers.Feature.Vector.style
13800  * OpenLayers features can have a number of style attributes. The 'default' 
13801  *     style will typically be used if no other style is specified. These
13802  *     styles correspond for the most part, to the styling properties defined
13803  *     by the SVG standard. 
13804  *     Information on fill properties: http://www.w3.org/TR/SVG/painting.html#FillProperties
13805  *     Information on stroke properties: http://www.w3.org/TR/SVG/painting.html#StrokeProperties
13806  *
13807  * Symbolizer properties:
13808  * fill - {Boolean} Set to false if no fill is desired.
13809  * fillColor - {String} Hex fill color.  Default is "#ee9900".
13810  * fillOpacity - {Number} Fill opacity (0-1).  Default is 0.4 
13811  * stroke - {Boolean} Set to false if no stroke is desired.
13812  * strokeColor - {String} Hex stroke color.  Default is "#ee9900".
13813  * strokeOpacity - {Number} Stroke opacity (0-1).  Default is 1.
13814  * strokeWidth - {Number} Pixel stroke width.  Default is 1.
13815  * strokeLinecap - {String} Stroke cap type.  Default is "round".  [butt | round | square]
13816  * strokeDashstyle - {String} Stroke dash style.  Default is "solid". [dot | dash | dashdot | longdash | longdashdot | solid]
13817  * graphic - {Boolean} Set to false if no graphic is desired.
13818  * pointRadius - {Number} Pixel point radius.  Default is 6.
13819  * pointerEvents - {String}  Default is "visiblePainted".
13820  * cursor - {String} Default is "".
13821  * externalGraphic - {String} Url to an external graphic that will be used for rendering points.
13822  * graphicWidth - {Number} Pixel width for sizing an external graphic.
13823  * graphicHeight - {Number} Pixel height for sizing an external graphic.
13824  * graphicOpacity - {Number} Opacity (0-1) for an external graphic.
13825  * graphicXOffset - {Number} Pixel offset along the positive x axis for displacing an external graphic.
13826  * graphicYOffset - {Number} Pixel offset along the positive y axis for displacing an external graphic.
13827  * rotation - {Number} For point symbolizers, this is the rotation of a graphic in the clockwise direction about its center point (or any point off center as specified by graphicXOffset and graphicYOffset).
13828  * graphicZIndex - {Number} The integer z-index value to use in rendering.
13829  * graphicName - {String} Named graphic to use when rendering points.  Supported values include "circle" (default),
13830  *     "square", "star", "x", "cross", "triangle".
13831  * graphicTitle - {String} Tooltip when hovering over a feature. *deprecated*, use title instead
13832  * title - {String} Tooltip when hovering over a feature. Not supported by the canvas renderer.
13833  * backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic.
13834  * backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic.
13835  * backgroundXOffset - {Number} The x offset (in pixels) for the background graphic.
13836  * backgroundYOffset - {Number} The y offset (in pixels) for the background graphic.
13837  * backgroundHeight - {Number} The height of the background graphic.  If not provided, the graphicHeight will be used.
13838  * backgroundWidth - {Number} The width of the background width.  If not provided, the graphicWidth will be used.
13839  * label - {String} The text for an optional label. For browsers that use the canvas renderer, this requires either
13840  *     fillText or mozDrawText to be available.
13841  * labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string
13842  *     composed of two characters. The first character is for the horizontal alignment, the second for the vertical
13843  *     alignment. Valid values for horizontal alignment: "l"=left, "c"=center, "r"=right. Valid values for vertical
13844  *     alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb". Default is "cm".
13845  * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer.
13846  * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer.
13847  * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls.
13848  *     Default is false.
13849  * labelOutlineColor - {String} The color of the label outline. Default is 'white'. Only supported by the canvas & SVG renderers.
13850  * labelOutlineWidth - {Number} The width of the label outline. Default is 3, set to 0 or null to disable. Only supported by the  SVG renderers.
13851  * labelOutlineOpacity - {Number} The opacity (0-1) of the label outline. Default is fontOpacity. Only supported by the canvas & SVG renderers.
13852  * fontColor - {String} The font color for the label, to be provided like CSS.
13853  * fontOpacity - {Number} Opacity (0-1) for the label
13854  * fontFamily - {String} The font family for the label, to be provided like in CSS.
13855  * fontSize - {String} The font size for the label, to be provided like in CSS.
13856  * fontStyle - {String} The font style for the label, to be provided like in CSS.
13857  * fontWeight - {String} The font weight for the label, to be provided like in CSS.
13858  * display - {String} Symbolizers will have no effect if display is set to "none".  All other values have no effect.
13859  */ 
13860 OpenLayers.Feature.Vector.style = {
13861     'default': {
13862         fillColor: "#ee9900",
13863         fillOpacity: 0.4, 
13864         hoverFillColor: "white",
13865         hoverFillOpacity: 0.8,
13866         strokeColor: "#ee9900",
13867         strokeOpacity: 1,
13868         strokeWidth: 1,
13869         strokeLinecap: "round",
13870         strokeDashstyle: "solid",
13871         hoverStrokeColor: "red",
13872         hoverStrokeOpacity: 1,
13873         hoverStrokeWidth: 0.2,
13874         pointRadius: 6,
13875         hoverPointRadius: 1,
13876         hoverPointUnit: "%",
13877         pointerEvents: "visiblePainted",
13878         cursor: "inherit",
13879         fontColor: "#000000",
13880         labelAlign: "cm",
13881         labelOutlineColor: "white",
13882         labelOutlineWidth: 3
13883     },
13884     'select': {
13885         fillColor: "blue",
13886         fillOpacity: 0.4, 
13887         hoverFillColor: "white",
13888         hoverFillOpacity: 0.8,
13889         strokeColor: "blue",
13890         strokeOpacity: 1,
13891         strokeWidth: 2,
13892         strokeLinecap: "round",
13893         strokeDashstyle: "solid",
13894         hoverStrokeColor: "red",
13895         hoverStrokeOpacity: 1,
13896         hoverStrokeWidth: 0.2,
13897         pointRadius: 6,
13898         hoverPointRadius: 1,
13899         hoverPointUnit: "%",
13900         pointerEvents: "visiblePainted",
13901         cursor: "pointer",
13902         fontColor: "#000000",
13903         labelAlign: "cm",
13904         labelOutlineColor: "white",
13905         labelOutlineWidth: 3
13906
13907     },
13908     'temporary': {
13909         fillColor: "#66cccc",
13910         fillOpacity: 0.2, 
13911         hoverFillColor: "white",
13912         hoverFillOpacity: 0.8,
13913         strokeColor: "#66cccc",
13914         strokeOpacity: 1,
13915         strokeLinecap: "round",
13916         strokeWidth: 2,
13917         strokeDashstyle: "solid",
13918         hoverStrokeColor: "red",
13919         hoverStrokeOpacity: 1,
13920         hoverStrokeWidth: 0.2,
13921         pointRadius: 6,
13922         hoverPointRadius: 1,
13923         hoverPointUnit: "%",
13924         pointerEvents: "visiblePainted",
13925         cursor: "inherit",
13926         fontColor: "#000000",
13927         labelAlign: "cm",
13928         labelOutlineColor: "white",
13929         labelOutlineWidth: 3
13930
13931     },
13932     'delete': {
13933         display: "none"
13934     }
13935 };    
13936 /* ======================================================================
13937     OpenLayers/Style.js
13938    ====================================================================== */
13939
13940 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
13941  * full list of contributors). Published under the 2-clause BSD license.
13942  * See license.txt in the OpenLayers distribution or repository for the
13943  * full text of the license. */
13944
13945
13946 /**
13947  * @requires OpenLayers/BaseTypes/Class.js
13948  * @requires OpenLayers/Util.js
13949  * @requires OpenLayers/Feature/Vector.js
13950  */
13951
13952 /**
13953  * Class: OpenLayers.Style
13954  * This class represents a UserStyle obtained
13955  *     from a SLD, containing styling rules.
13956  */
13957 OpenLayers.Style = OpenLayers.Class({
13958
13959     /**
13960      * Property: id
13961      * {String} A unique id for this session.
13962      */
13963     id: null,
13964     
13965     /**
13966      * APIProperty: name
13967      * {String}
13968      */
13969     name: null,
13970     
13971     /**
13972      * Property: title
13973      * {String} Title of this style (set if included in SLD)
13974      */
13975     title: null,
13976     
13977     /**
13978      * Property: description
13979      * {String} Description of this style (set if abstract is included in SLD)
13980      */
13981     description: null,
13982
13983     /**
13984      * APIProperty: layerName
13985      * {<String>} name of the layer that this style belongs to, usually
13986      * according to the NamedLayer attribute of an SLD document.
13987      */
13988     layerName: null,
13989     
13990     /**
13991      * APIProperty: isDefault
13992      * {Boolean}
13993      */
13994     isDefault: false,
13995      
13996     /** 
13997      * Property: rules 
13998      * {Array(<OpenLayers.Rule>)}
13999      */
14000     rules: null,
14001     
14002     /**
14003      * APIProperty: context
14004      * {Object} An optional object with properties that symbolizers' property
14005      * values should be evaluated against. If no context is specified,
14006      * feature.attributes will be used
14007      */
14008     context: null,
14009
14010     /**
14011      * Property: defaultStyle
14012      * {Object} hash of style properties to use as default for merging
14013      * rule-based style symbolizers onto. If no rules are defined,
14014      * createSymbolizer will return this style. If <defaultsPerSymbolizer> is set to
14015      * true, the defaultStyle will only be taken into account if there are
14016      * rules defined.
14017      */
14018     defaultStyle: null,
14019     
14020     /**
14021      * Property: defaultsPerSymbolizer
14022      * {Boolean} If set to true, the <defaultStyle> will extend the symbolizer
14023      * of every rule. Properties of the <defaultStyle> will also be used to set
14024      * missing symbolizer properties if the symbolizer has stroke, fill or
14025      * graphic set to true. Default is false.
14026      */
14027     defaultsPerSymbolizer: false,
14028     
14029     /**
14030      * Property: propertyStyles
14031      * {Hash of Boolean} cache of style properties that need to be parsed for
14032      * propertyNames. Property names are keys, values won't be used.
14033      */
14034     propertyStyles: null,
14035     
14036
14037     /** 
14038      * Constructor: OpenLayers.Style
14039      * Creates a UserStyle.
14040      *
14041      * Parameters:
14042      * style        - {Object} Optional hash of style properties that will be
14043      *                used as default style for this style object. This style
14044      *                applies if no rules are specified. Symbolizers defined in
14045      *                rules will extend this default style.
14046      * options - {Object} An optional object with properties to set on the
14047      *     style.
14048      *
14049      * Valid options:
14050      * rules - {Array(<OpenLayers.Rule>)} List of rules to be added to the
14051      *     style.
14052      * 
14053      * Returns:
14054      * {<OpenLayers.Style>}
14055      */
14056     initialize: function(style, options) {
14057
14058         OpenLayers.Util.extend(this, options);
14059         this.rules = [];
14060         if(options && options.rules) {
14061             this.addRules(options.rules);
14062         }
14063
14064         // use the default style from OpenLayers.Feature.Vector if no style
14065         // was given in the constructor
14066         this.setDefaultStyle(style ||
14067                              OpenLayers.Feature.Vector.style["default"]);
14068
14069         this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
14070     },
14071
14072     /** 
14073      * APIMethod: destroy
14074      * nullify references to prevent circular references and memory leaks
14075      */
14076     destroy: function() {
14077         for (var i=0, len=this.rules.length; i<len; i++) {
14078             this.rules[i].destroy();
14079             this.rules[i] = null;
14080         }
14081         this.rules = null;
14082         this.defaultStyle = null;
14083     },
14084     
14085     /**
14086      * Method: createSymbolizer
14087      * creates a style by applying all feature-dependent rules to the base
14088      * style.
14089      * 
14090      * Parameters:
14091      * feature - {<OpenLayers.Feature>} feature to evaluate rules for
14092      * 
14093      * Returns:
14094      * {Object} symbolizer hash
14095      */
14096     createSymbolizer: function(feature) {
14097         var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(
14098             OpenLayers.Util.extend({}, this.defaultStyle), feature);
14099         
14100         var rules = this.rules;
14101
14102         var rule, context;
14103         var elseRules = [];
14104         var appliedRules = false;
14105         for(var i=0, len=rules.length; i<len; i++) {
14106             rule = rules[i];
14107             // does the rule apply?
14108             var applies = rule.evaluate(feature);
14109             
14110             if(applies) {
14111                 if(rule instanceof OpenLayers.Rule && rule.elseFilter) {
14112                     elseRules.push(rule);
14113                 } else {
14114                     appliedRules = true;
14115                     this.applySymbolizer(rule, style, feature);
14116                 }
14117             }
14118         }
14119         
14120         // if no other rules apply, apply the rules with else filters
14121         if(appliedRules == false && elseRules.length > 0) {
14122             appliedRules = true;
14123             for(var i=0, len=elseRules.length; i<len; i++) {
14124                 this.applySymbolizer(elseRules[i], style, feature);
14125             }
14126         }
14127
14128         // don't display if there were rules but none applied
14129         if(rules.length > 0 && appliedRules == false) {
14130             style.display = "none";
14131         }
14132         
14133         if (style.label != null && typeof style.label !== "string") {
14134             style.label = String(style.label);
14135         }
14136         
14137         return style;
14138     },
14139     
14140     /**
14141      * Method: applySymbolizer
14142      *
14143      * Parameters:
14144      * rule - {<OpenLayers.Rule>}
14145      * style - {Object}
14146      * feature - {<OpenLayer.Feature.Vector>}
14147      *
14148      * Returns:
14149      * {Object} A style with new symbolizer applied.
14150      */
14151     applySymbolizer: function(rule, style, feature) {
14152         var symbolizerPrefix = feature.geometry ?
14153                 this.getSymbolizerPrefix(feature.geometry) :
14154                 OpenLayers.Style.SYMBOLIZER_PREFIXES[0];
14155
14156         var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;
14157         
14158         if(this.defaultsPerSymbolizer === true) {
14159             var defaults = this.defaultStyle;
14160             OpenLayers.Util.applyDefaults(symbolizer, {
14161                 pointRadius: defaults.pointRadius
14162             });
14163             if(symbolizer.stroke === true || symbolizer.graphic === true) {
14164                 OpenLayers.Util.applyDefaults(symbolizer, {
14165                     strokeWidth: defaults.strokeWidth,
14166                     strokeColor: defaults.strokeColor,
14167                     strokeOpacity: defaults.strokeOpacity,
14168                     strokeDashstyle: defaults.strokeDashstyle,
14169                     strokeLinecap: defaults.strokeLinecap
14170                 });
14171             }
14172             if(symbolizer.fill === true || symbolizer.graphic === true) {
14173                 OpenLayers.Util.applyDefaults(symbolizer, {
14174                     fillColor: defaults.fillColor,
14175                     fillOpacity: defaults.fillOpacity
14176                 });
14177             }
14178             if(symbolizer.graphic === true) {
14179                 OpenLayers.Util.applyDefaults(symbolizer, {
14180                     pointRadius: this.defaultStyle.pointRadius,
14181                     externalGraphic: this.defaultStyle.externalGraphic,
14182                     graphicName: this.defaultStyle.graphicName,
14183                     graphicOpacity: this.defaultStyle.graphicOpacity,
14184                     graphicWidth: this.defaultStyle.graphicWidth,
14185                     graphicHeight: this.defaultStyle.graphicHeight,
14186                     graphicXOffset: this.defaultStyle.graphicXOffset,
14187                     graphicYOffset: this.defaultStyle.graphicYOffset
14188                 });
14189             }
14190         }
14191
14192         // merge the style with the current style
14193         return this.createLiterals(
14194                 OpenLayers.Util.extend(style, symbolizer), feature);
14195     },
14196     
14197     /**
14198      * Method: createLiterals
14199      * creates literals for all style properties that have an entry in
14200      * <this.propertyStyles>.
14201      * 
14202      * Parameters:
14203      * style   - {Object} style to create literals for. Will be modified
14204      *           inline.
14205      * feature - {Object}
14206      * 
14207      * Returns:
14208      * {Object} the modified style
14209      */
14210     createLiterals: function(style, feature) {
14211         var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);
14212         OpenLayers.Util.extend(context, this.context);
14213         
14214         for (var i in this.propertyStyles) {
14215             style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);
14216         }
14217         return style;
14218     },
14219     
14220     /**
14221      * Method: findPropertyStyles
14222      * Looks into all rules for this style and the defaultStyle to collect
14223      * all the style hash property names containing ${...} strings that have
14224      * to be replaced using the createLiteral method before returning them.
14225      * 
14226      * Returns:
14227      * {Object} hash of property names that need createLiteral parsing. The
14228      * name of the property is the key, and the value is true;
14229      */
14230     findPropertyStyles: function() {
14231         var propertyStyles = {};
14232
14233         // check the default style
14234         var style = this.defaultStyle;
14235         this.addPropertyStyles(propertyStyles, style);
14236
14237         // walk through all rules to check for properties in their symbolizer
14238         var rules = this.rules;
14239         var symbolizer, value;
14240         for (var i=0, len=rules.length; i<len; i++) {
14241             symbolizer = rules[i].symbolizer;
14242             for (var key in symbolizer) {
14243                 value = symbolizer[key];
14244                 if (typeof value == "object") {
14245                     // symbolizer key is "Point", "Line" or "Polygon"
14246                     this.addPropertyStyles(propertyStyles, value);
14247                 } else {
14248                     // symbolizer is a hash of style properties
14249                     this.addPropertyStyles(propertyStyles, symbolizer);
14250                     break;
14251                 }
14252             }
14253         }
14254         return propertyStyles;
14255     },
14256     
14257     /**
14258      * Method: addPropertyStyles
14259      * 
14260      * Parameters:
14261      * propertyStyles - {Object} hash to add new property styles to. Will be
14262      *                  modified inline
14263      * symbolizer     - {Object} search this symbolizer for property styles
14264      * 
14265      * Returns:
14266      * {Object} propertyStyles hash
14267      */
14268     addPropertyStyles: function(propertyStyles, symbolizer) {
14269         var property;
14270         for (var key in symbolizer) {
14271             property = symbolizer[key];
14272             if (typeof property == "string" &&
14273                     property.match(/\$\{\w+\}/)) {
14274                 propertyStyles[key] = true;
14275             }
14276         }
14277         return propertyStyles;
14278     },
14279     
14280     /**
14281      * APIMethod: addRules
14282      * Adds rules to this style.
14283      * 
14284      * Parameters:
14285      * rules - {Array(<OpenLayers.Rule>)}
14286      */
14287     addRules: function(rules) {
14288         Array.prototype.push.apply(this.rules, rules);
14289         this.propertyStyles = this.findPropertyStyles();
14290     },
14291     
14292     /**
14293      * APIMethod: setDefaultStyle
14294      * Sets the default style for this style object.
14295      * 
14296      * Parameters:
14297      * style - {Object} Hash of style properties
14298      */
14299     setDefaultStyle: function(style) {
14300         this.defaultStyle = style; 
14301         this.propertyStyles = this.findPropertyStyles();
14302     },
14303         
14304     /**
14305      * Method: getSymbolizerPrefix
14306      * Returns the correct symbolizer prefix according to the
14307      * geometry type of the passed geometry
14308      * 
14309      * Parameters:
14310      * geometry - {<OpenLayers.Geometry>}
14311      * 
14312      * Returns:
14313      * {String} key of the according symbolizer
14314      */
14315     getSymbolizerPrefix: function(geometry) {
14316         var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;
14317         for (var i=0, len=prefixes.length; i<len; i++) {
14318             if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {
14319                 return prefixes[i];
14320             }
14321         }
14322     },
14323     
14324     /**
14325      * APIMethod: clone
14326      * Clones this style.
14327      * 
14328      * Returns:
14329      * {<OpenLayers.Style>} Clone of this style.
14330      */
14331     clone: function() {
14332         var options = OpenLayers.Util.extend({}, this);
14333         // clone rules
14334         if(this.rules) {
14335             options.rules = [];
14336             for(var i=0, len=this.rules.length; i<len; ++i) {
14337                 options.rules.push(this.rules[i].clone());
14338             }
14339         }
14340         // clone context
14341         options.context = this.context && OpenLayers.Util.extend({}, this.context);
14342         //clone default style
14343         var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);
14344         return new OpenLayers.Style(defaultStyle, options);
14345     },
14346     
14347     CLASS_NAME: "OpenLayers.Style"
14348 });
14349
14350
14351 /**
14352  * Function: createLiteral
14353  * converts a style value holding a combination of PropertyName and Literal
14354  * into a Literal, taking the property values from the passed features.
14355  * 
14356  * Parameters:
14357  * value - {String} value to parse. If this string contains a construct like
14358  *         "foo ${bar}", then "foo " will be taken as literal, and "${bar}"
14359  *         will be replaced by the value of the "bar" attribute of the passed
14360  *         feature.
14361  * context - {Object} context to take attribute values from
14362  * feature - {<OpenLayers.Feature.Vector>} optional feature to pass to
14363  *           <OpenLayers.String.format> for evaluating functions in the
14364  *           context.
14365  * property - {String} optional, name of the property for which the literal is
14366  *            being created for evaluating functions in the context.
14367  * 
14368  * Returns:
14369  * {String} the parsed value. In the example of the value parameter above, the
14370  * result would be "foo valueOfBar", assuming that the passed feature has an
14371  * attribute named "bar" with the value "valueOfBar".
14372  */
14373 OpenLayers.Style.createLiteral = function(value, context, feature, property) {
14374     if (typeof value == "string" && value.indexOf("${") != -1) {
14375         value = OpenLayers.String.format(value, context, [feature, property]);
14376         value = (isNaN(value) || !value) ? value : parseFloat(value);
14377     }
14378     return value;
14379 };
14380     
14381 /**
14382  * Constant: OpenLayers.Style.SYMBOLIZER_PREFIXES
14383  * {Array} prefixes of the sld symbolizers. These are the
14384  * same as the main geometry types
14385  */
14386 OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text',
14387     'Raster'];
14388 /* ======================================================================
14389     OpenLayers/Filter.js
14390    ====================================================================== */
14391
14392 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
14393  * full list of contributors). Published under the 2-clause BSD license.
14394  * See license.txt in the OpenLayers distribution or repository for the
14395  * full text of the license. */
14396
14397
14398 /**
14399  * @requires OpenLayers/BaseTypes/Class.js
14400  * @requires OpenLayers/Util.js
14401  * @requires OpenLayers/Style.js
14402  */
14403
14404 /**
14405  * Class: OpenLayers.Filter
14406  * This class represents an OGC Filter.
14407  */
14408 OpenLayers.Filter = OpenLayers.Class({
14409     
14410     /** 
14411      * Constructor: OpenLayers.Filter
14412      * This class represents a generic filter.
14413      *
14414      * Parameters:
14415      * options - {Object} Optional object whose properties will be set on the
14416      *     instance.
14417      * 
14418      * Returns:
14419      * {<OpenLayers.Filter>}
14420      */
14421     initialize: function(options) {
14422         OpenLayers.Util.extend(this, options);
14423     },
14424
14425     /** 
14426      * APIMethod: destroy
14427      * Remove reference to anything added.
14428      */
14429     destroy: function() {
14430     },
14431
14432     /**
14433      * APIMethod: evaluate
14434      * Evaluates this filter in a specific context.  Instances or subclasses
14435      * are supposed to override this method.
14436      * 
14437      * Parameters:
14438      * context - {Object} Context to use in evaluating the filter.  If a vector
14439      *     feature is provided, the feature.attributes will be used as context.
14440      * 
14441      * Returns:
14442      * {Boolean} The filter applies.
14443      */
14444     evaluate: function(context) {
14445         return true;
14446     },
14447     
14448     /**
14449      * APIMethod: clone
14450      * Clones this filter. Should be implemented by subclasses.
14451      * 
14452      * Returns:
14453      * {<OpenLayers.Filter>} Clone of this filter.
14454      */
14455     clone: function() {
14456         return null;
14457     },
14458     
14459     /**
14460      * APIMethod: toString
14461      *
14462      * Returns:
14463      * {String} Include <OpenLayers.Format.CQL> in your build to get a CQL
14464      *     representation of the filter returned. Otherwise "[Object object]"
14465      *     will be returned.
14466      */
14467     toString: function() {
14468         var string;
14469         if (OpenLayers.Format && OpenLayers.Format.CQL) {
14470             string = OpenLayers.Format.CQL.prototype.write(this);
14471         } else {
14472             string = Object.prototype.toString.call(this);
14473         }
14474         return string;
14475     },
14476     
14477     CLASS_NAME: "OpenLayers.Filter"
14478 });
14479 /* ======================================================================
14480     OpenLayers/Strategy/Filter.js
14481    ====================================================================== */
14482
14483 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
14484  * full list of contributors). Published under the 2-clause BSD license.
14485  * See license.txt in the OpenLayers distribution or repository for the
14486  * full text of the license. */
14487
14488 /**
14489  * @requires OpenLayers/Strategy.js
14490  * @requires OpenLayers/Filter.js
14491  */
14492
14493 /**
14494  * Class: OpenLayers.Strategy.Filter
14495  * Strategy for limiting features that get added to a layer by 
14496  *     evaluating a filter.  The strategy maintains a cache of
14497  *     all features until removeFeatures is called on the layer.
14498  *
14499  * Inherits from:
14500  *  - <OpenLayers.Strategy>
14501  */
14502 OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, {
14503     
14504     /**
14505      * APIProperty: filter
14506      * {<OpenLayers.Filter>}  Filter for limiting features sent to the layer.
14507      *     Use the <setFilter> method to update this filter after construction.
14508      */
14509     filter: null,
14510     
14511     /**
14512      * Property: cache
14513      * {Array(<OpenLayers.Feature.Vector>)} List of currently cached
14514      *     features.
14515      */
14516     cache: null,
14517     
14518     /**
14519      * Property: caching
14520      * {Boolean} The filter is currently caching features.
14521      */
14522     caching: false,
14523     
14524     /**
14525      * Constructor: OpenLayers.Strategy.Filter
14526      * Create a new filter strategy.
14527      *
14528      * Parameters:
14529      * options - {Object} Optional object whose properties will be set on the
14530      *     instance.
14531      */
14532
14533     /**
14534      * APIMethod: activate
14535      * Activate the strategy.  Register any listeners, do appropriate setup.
14536      *     By default, this strategy automatically activates itself when a layer
14537      *     is added to a map.
14538      *
14539      * Returns:
14540      * {Boolean} True if the strategy was successfully activated or false if
14541      *      the strategy was already active.
14542      */
14543     activate: function() {
14544         var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);
14545         if (activated) {
14546             this.cache = [];
14547             this.layer.events.on({
14548                 "beforefeaturesadded": this.handleAdd,
14549                 "beforefeaturesremoved": this.handleRemove,
14550                 scope: this
14551             });
14552         }
14553         return activated;
14554     },
14555     
14556     /**
14557      * APIMethod: deactivate
14558      * Deactivate the strategy.  Clear the feature cache.
14559      *
14560      * Returns:
14561      * {Boolean} True if the strategy was successfully deactivated or false if
14562      *      the strategy was already inactive.
14563      */
14564     deactivate: function() {
14565         this.cache = null;
14566         if (this.layer && this.layer.events) {
14567             this.layer.events.un({
14568                 "beforefeaturesadded": this.handleAdd,
14569                 "beforefeaturesremoved": this.handleRemove,
14570                 scope: this
14571             });            
14572         }
14573         return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments);
14574     },
14575     
14576     /**
14577      * Method: handleAdd
14578      */
14579     handleAdd: function(event) {
14580         if (!this.caching && this.filter) {
14581             var features = event.features;
14582             event.features = [];
14583             var feature;
14584             for (var i=0, ii=features.length; i<ii; ++i) {
14585                 feature = features[i];
14586                 if (this.filter.evaluate(feature)) {
14587                     event.features.push(feature);
14588                 } else {
14589                     this.cache.push(feature);
14590                 }
14591             }
14592         }
14593     },
14594     
14595     /**
14596      * Method: handleRemove
14597      */
14598     handleRemove: function(event) {
14599         if (!this.caching) {
14600             this.cache = [];
14601         }
14602     },
14603
14604     /** 
14605      * APIMethod: setFilter
14606      * Update the filter for this strategy.  This will re-evaluate
14607      *     any features on the layer and in the cache.  Only features
14608      *     for which filter.evalute(feature) returns true will be
14609      *     added to the layer.  Others will be cached by the strategy.
14610      *
14611      * Parameters:
14612      * filter - {<OpenLayers.Filter>} A filter for evaluating features.
14613      */
14614     setFilter: function(filter) {
14615         this.filter = filter;
14616         var previousCache = this.cache;
14617         this.cache = [];
14618         // look through layer for features to remove from layer
14619         this.handleAdd({features: this.layer.features});
14620         // cache now contains features to remove from layer
14621         if (this.cache.length > 0) {
14622             this.caching = true;
14623             this.layer.removeFeatures(this.cache.slice());
14624             this.caching = false;
14625         }
14626         // now look through previous cache for features to add to layer
14627         if (previousCache.length > 0) {
14628             var event = {features: previousCache};
14629             this.handleAdd(event);
14630             if (event.features.length > 0) {
14631                 // event has features to add to layer
14632                 this.caching = true;
14633                 this.layer.addFeatures(event.features);
14634                 this.caching = false;
14635             }
14636         }
14637     },
14638
14639     CLASS_NAME: "OpenLayers.Strategy.Filter"
14640
14641 });
14642 /* ======================================================================
14643     OpenLayers/Tile.js
14644    ====================================================================== */
14645
14646 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
14647  * full list of contributors). Published under the 2-clause BSD license.
14648  * See license.txt in the OpenLayers distribution or repository for the
14649  * full text of the license. */
14650
14651
14652 /**
14653  * @requires OpenLayers/BaseTypes/Class.js
14654  * @requires OpenLayers/Util.js
14655  */
14656
14657 /**
14658  * Class: OpenLayers.Tile 
14659  * This is a class designed to designate a single tile, however
14660  *     it is explicitly designed to do relatively little. Tiles store 
14661  *     information about themselves -- such as the URL that they are related
14662  *     to, and their size - but do not add themselves to the layer div 
14663  *     automatically, for example. Create a new tile with the 
14664  *     <OpenLayers.Tile> constructor, or a subclass. 
14665  * 
14666  * TBD 3.0 - remove reference to url in above paragraph
14667  * 
14668  */
14669 OpenLayers.Tile = OpenLayers.Class({
14670     
14671     /**
14672      * APIProperty: events
14673      * {<OpenLayers.Events>} An events object that handles all 
14674      *     events on the tile.
14675      *
14676      * Register a listener for a particular event with the following syntax:
14677      * (code)
14678      * tile.events.register(type, obj, listener);
14679      * (end)
14680      *
14681      * Supported event types:
14682      * beforedraw - Triggered before the tile is drawn. Used to defer
14683      *     drawing to an animation queue. To defer drawing, listeners need
14684      *     to return false, which will abort drawing. The queue handler needs
14685      *     to call <draw>(true) to actually draw the tile.
14686      * loadstart - Triggered when tile loading starts.
14687      * loadend - Triggered when tile loading ends.
14688      * loaderror - Triggered before the loadend event (i.e. when the tile is
14689      *     still hidden) if the tile could not be loaded.
14690      * reload - Triggered when an already loading tile is reloaded.
14691      * unload - Triggered before a tile is unloaded.
14692      */
14693     events: null,
14694
14695     /**
14696      * APIProperty: eventListeners
14697      * {Object} If set as an option at construction, the eventListeners
14698      *     object will be registered with <OpenLayers.Events.on>.  Object
14699      *     structure must be a listeners object as shown in the example for
14700      *     the events.on method.
14701      *
14702      * This options can be set in the ``tileOptions`` option from
14703      * <OpenLayers.Layer.Grid>. For example, to be notified of the
14704      * ``loadend`` event of each tiles:
14705      * (code)
14706      * new OpenLayers.Layer.OSM('osm', 'http://tile.openstreetmap.org/${z}/${x}/${y}.png', {
14707      *     tileOptions: {
14708      *         eventListeners: {
14709      *             'loadend': function(evt) {
14710      *                 // do something on loadend
14711      *             }
14712      *         }
14713      *     }
14714      * });
14715      * (end)
14716      */
14717     eventListeners: null,
14718
14719     /**
14720      * Property: id 
14721      * {String} null
14722      */
14723     id: null,
14724     
14725     /** 
14726      * Property: layer 
14727      * {<OpenLayers.Layer>} layer the tile is attached to 
14728      */
14729     layer: null,
14730     
14731     /**
14732      * Property: url
14733      * {String} url of the request.
14734      *
14735      * TBD 3.0 
14736      * Deprecated. The base tile class does not need an url. This should be 
14737      * handled in subclasses. Does not belong here.
14738      */
14739     url: null,
14740
14741     /** 
14742      * APIProperty: bounds 
14743      * {<OpenLayers.Bounds>} null
14744      */
14745     bounds: null,
14746     
14747     /** 
14748      * Property: size 
14749      * {<OpenLayers.Size>} null
14750      */
14751     size: null,
14752     
14753     /** 
14754      * Property: position 
14755      * {<OpenLayers.Pixel>} Top Left pixel of the tile
14756      */    
14757     position: null,
14758     
14759     /**
14760      * Property: isLoading
14761      * {Boolean} Is the tile loading?
14762      */
14763     isLoading: false,
14764     
14765     /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor.
14766      *             there is no need for the base tile class to have a url.
14767      */
14768
14769     /** 
14770      * Constructor: OpenLayers.Tile
14771      * Constructor for a new <OpenLayers.Tile> instance.
14772      * 
14773      * Parameters:
14774      * layer - {<OpenLayers.Layer>} layer that the tile will go in.
14775      * position - {<OpenLayers.Pixel>}
14776      * bounds - {<OpenLayers.Bounds>}
14777      * url - {<String>}
14778      * size - {<OpenLayers.Size>}
14779      * options - {Object}
14780      */   
14781     initialize: function(layer, position, bounds, url, size, options) {
14782         this.layer = layer;
14783         this.position = position.clone();
14784         this.setBounds(bounds);
14785         this.url = url;
14786         if (size) {
14787             this.size = size.clone();
14788         }
14789
14790         //give the tile a unique id based on its BBOX.
14791         this.id = OpenLayers.Util.createUniqueID("Tile_");
14792
14793         OpenLayers.Util.extend(this, options);
14794
14795         this.events = new OpenLayers.Events(this);
14796         if (this.eventListeners instanceof Object) {
14797             this.events.on(this.eventListeners);
14798         }
14799     },
14800
14801     /**
14802      * Method: unload
14803      * Call immediately before destroying if you are listening to tile
14804      * events, so that counters are properly handled if tile is still
14805      * loading at destroy-time. Will only fire an event if the tile is
14806      * still loading.
14807      */
14808     unload: function() {
14809        if (this.isLoading) { 
14810            this.isLoading = false; 
14811            this.events.triggerEvent("unload"); 
14812        }
14813     },
14814     
14815     /** 
14816      * APIMethod: destroy
14817      * Nullify references to prevent circular references and memory leaks.
14818      */
14819     destroy:function() {
14820         this.layer  = null;
14821         this.bounds = null;
14822         this.size = null;
14823         this.position = null;
14824         
14825         if (this.eventListeners) {
14826             this.events.un(this.eventListeners);
14827         }
14828         this.events.destroy();
14829         this.eventListeners = null;
14830         this.events = null;
14831     },
14832     
14833     /**
14834      * Method: draw
14835      * Clear whatever is currently in the tile, then return whether or not 
14836      *     it should actually be re-drawn. This is an example implementation
14837      *     that can be overridden by subclasses. The minimum thing to do here
14838      *     is to call <clear> and return the result from <shouldDraw>.
14839      *
14840      * Parameters:
14841      * force - {Boolean} If true, the tile will not be cleared and no beforedraw
14842      *     event will be fired. This is used for drawing tiles asynchronously
14843      *     after drawing has been cancelled by returning false from a beforedraw
14844      *     listener.
14845      * 
14846      * Returns:
14847      * {Boolean} Whether or not the tile should actually be drawn. Returns null
14848      *     if a beforedraw listener returned false.
14849      */
14850     draw: function(force) {
14851         if (!force) {
14852             //clear tile's contents and mark as not drawn
14853             this.clear();
14854         }
14855         var draw = this.shouldDraw();
14856         if (draw && !force && this.events.triggerEvent("beforedraw") === false) {
14857             draw = null;
14858         }
14859         return draw;
14860     },
14861     
14862     /**
14863      * Method: shouldDraw
14864      * Return whether or not the tile should actually be (re-)drawn. The only
14865      * case where we *wouldn't* want to draw the tile is if the tile is outside
14866      * its layer's maxExtent
14867      * 
14868      * Returns:
14869      * {Boolean} Whether or not the tile should actually be drawn.
14870      */
14871     shouldDraw: function() {        
14872         var withinMaxExtent = false,
14873             maxExtent = this.layer.maxExtent;
14874         if (maxExtent) {
14875             var map = this.layer.map;
14876             var worldBounds = map.baseLayer.wrapDateLine && map.getMaxExtent();
14877             if (this.bounds.intersectsBounds(maxExtent, {inclusive: false, worldBounds: worldBounds})) {
14878                 withinMaxExtent = true;
14879             }
14880         }
14881         
14882         return withinMaxExtent || this.layer.displayOutsideMaxExtent;
14883     },
14884     
14885     /**
14886      * Method: setBounds
14887      * Sets the bounds on this instance
14888      *
14889      * Parameters:
14890      * bounds {<OpenLayers.Bounds>}
14891      */
14892     setBounds: function(bounds) {
14893         bounds = bounds.clone();
14894         if (this.layer.map.baseLayer.wrapDateLine) {
14895             var worldExtent = this.layer.map.getMaxExtent(),
14896                 tolerance = this.layer.map.getResolution();
14897             bounds = bounds.wrapDateLine(worldExtent, {
14898                 leftTolerance: tolerance,
14899                 rightTolerance: tolerance
14900             });
14901         }
14902         this.bounds = bounds;
14903     },
14904     
14905     /** 
14906      * Method: moveTo
14907      * Reposition the tile.
14908      *
14909      * Parameters:
14910      * bounds - {<OpenLayers.Bounds>}
14911      * position - {<OpenLayers.Pixel>}
14912      * redraw - {Boolean} Call draw method on tile after moving.
14913      *     Default is true
14914      */
14915     moveTo: function (bounds, position, redraw) {
14916         if (redraw == null) {
14917             redraw = true;
14918         }
14919
14920         this.setBounds(bounds);
14921         this.position = position.clone();
14922         if (redraw) {
14923             this.draw();
14924         }
14925     },
14926
14927     /** 
14928      * Method: clear
14929      * Clear the tile of any bounds/position-related data so that it can 
14930      *     be reused in a new location.
14931      */
14932     clear: function(draw) {
14933         // to be extended by subclasses
14934     },
14935     
14936     CLASS_NAME: "OpenLayers.Tile"
14937 });
14938 /* ======================================================================
14939     OpenLayers/Tile/Image.js
14940    ====================================================================== */
14941
14942 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
14943  * full list of contributors). Published under the 2-clause BSD license.
14944  * See license.txt in the OpenLayers distribution or repository for the
14945  * full text of the license. */
14946
14947
14948 /**
14949  * @requires OpenLayers/Tile.js
14950  * @requires OpenLayers/Animation.js
14951  * @requires OpenLayers/Util.js
14952  */
14953
14954 /**
14955  * Class: OpenLayers.Tile.Image
14956  * Instances of OpenLayers.Tile.Image are used to manage the image tiles
14957  * used by various layers.  Create a new image tile with the
14958  * <OpenLayers.Tile.Image> constructor.
14959  *
14960  * Inherits from:
14961  *  - <OpenLayers.Tile>
14962  */
14963 OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {
14964
14965     /**
14966      * APIProperty: events
14967      * {<OpenLayers.Events>} An events object that handles all 
14968      *     events on the tile.
14969      *
14970      * Register a listener for a particular event with the following syntax:
14971      * (code)
14972      * tile.events.register(type, obj, listener);
14973      * (end)
14974      *
14975      * Supported event types (in addition to the <OpenLayers.Tile> events):
14976      * beforeload - Triggered before an image is prepared for loading, when the
14977      *     url for the image is known already. Listeners may call <setImage> on
14978      *     the tile instance. If they do so, that image will be used and no new
14979      *     one will be created.
14980      */
14981
14982     /** 
14983      * APIProperty: url
14984      * {String} The URL of the image being requested. No default. Filled in by
14985      * layer.getURL() function. May be modified by loadstart listeners.
14986      */
14987     url: null,
14988     
14989     /** 
14990      * Property: imgDiv
14991      * {HTMLImageElement} The image for this tile.
14992      */
14993     imgDiv: null,
14994     
14995     /**
14996      * Property: frame
14997      * {DOMElement} The image element is appended to the frame.  Any gutter on
14998      * the image will be hidden behind the frame. If no gutter is set,
14999      * this will be null.
15000      */ 
15001     frame: null, 
15002
15003     /** 
15004      * Property: imageReloadAttempts
15005      * {Integer} Attempts to load the image.
15006      */
15007     imageReloadAttempts: null,
15008     
15009     /**
15010      * Property: layerAlphaHack
15011      * {Boolean} True if the png alpha hack needs to be applied on the layer's div.
15012      */
15013     layerAlphaHack: null,
15014     
15015     /**
15016      * Property: asyncRequestId
15017      * {Integer} ID of an request to see if request is still valid. This is a
15018      * number which increments by 1 for each asynchronous request.
15019      */
15020     asyncRequestId: null,
15021     
15022     /**
15023      * APIProperty: maxGetUrlLength
15024      * {Number} If set, requests that would result in GET urls with more
15025      * characters than the number provided will be made using form-encoded
15026      * HTTP POST. It is good practice to avoid urls that are longer than 2048
15027      * characters.
15028      *
15029      * Caution:
15030      * Older versions of Gecko based browsers (e.g. Firefox < 3.5) and most
15031      * Opera versions do not fully support this option. On all browsers,
15032      * transition effects are not supported if POST requests are used.
15033      */
15034     maxGetUrlLength: null,
15035
15036     /**
15037      * Property: canvasContext
15038      * {CanvasRenderingContext2D} A canvas context associated with
15039      * the tile image.
15040      */
15041     canvasContext: null,
15042     
15043     /**
15044      * APIProperty: crossOriginKeyword
15045      * The value of the crossorigin keyword to use when loading images. This is
15046      * only relevant when using <getCanvasContext> for tiles from remote
15047      * origins and should be set to either 'anonymous' or 'use-credentials'
15048      * for servers that send Access-Control-Allow-Origin headers with their
15049      * tiles.
15050      */
15051     crossOriginKeyword: null,
15052
15053     /** TBD 3.0 - reorder the parameters to the init function to remove 
15054      *             URL. the getUrl() function on the layer gets called on 
15055      *             each draw(), so no need to specify it here.
15056      */
15057
15058     /** 
15059      * Constructor: OpenLayers.Tile.Image
15060      * Constructor for a new <OpenLayers.Tile.Image> instance.
15061      * 
15062      * Parameters:
15063      * layer - {<OpenLayers.Layer>} layer that the tile will go in.
15064      * position - {<OpenLayers.Pixel>}
15065      * bounds - {<OpenLayers.Bounds>}
15066      * url - {<String>} Deprecated. Remove me in 3.0.
15067      * size - {<OpenLayers.Size>}
15068      * options - {Object}
15069      */   
15070     initialize: function(layer, position, bounds, url, size, options) {
15071         OpenLayers.Tile.prototype.initialize.apply(this, arguments);
15072
15073         this.url = url; //deprecated remove me
15074         
15075         this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();
15076
15077         if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) {
15078             // only create frame if it's needed
15079             this.frame = document.createElement("div");
15080             this.frame.style.position = "absolute";
15081             this.frame.style.overflow = "hidden";
15082         }
15083         if (this.maxGetUrlLength != null) {
15084             OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame);
15085         }
15086     },
15087     
15088     /** 
15089      * APIMethod: destroy
15090      * nullify references to prevent circular references and memory leaks
15091      */
15092     destroy: function() {
15093         if (this.imgDiv)  {
15094             this.clear();
15095             this.imgDiv = null;
15096             this.frame = null;
15097         }
15098         // don't handle async requests any more
15099         this.asyncRequestId = null;
15100         OpenLayers.Tile.prototype.destroy.apply(this, arguments);
15101     },
15102     
15103     /**
15104      * Method: draw
15105      * Check that a tile should be drawn, and draw it.
15106      * 
15107      * Returns:
15108      * {Boolean} Was a tile drawn? Or null if a beforedraw listener returned
15109      *     false.
15110      */
15111     draw: function() {
15112         var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments);
15113         if (shouldDraw) {
15114             // The layer's reproject option is deprecated.
15115             if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {
15116                 // getBoundsFromBaseLayer is defined in deprecated.js.
15117                 this.bounds = this.getBoundsFromBaseLayer(this.position);
15118             }
15119             if (this.isLoading) {
15120                 //if we're already loading, send 'reload' instead of 'loadstart'.
15121                 this._loadEvent = "reload";
15122             } else {
15123                 this.isLoading = true;
15124                 this._loadEvent = "loadstart";
15125             }
15126             this.renderTile();
15127             this.positionTile();
15128         } else if (shouldDraw === false) {
15129             this.unload();
15130         }
15131         return shouldDraw;
15132     },
15133     
15134     /**
15135      * Method: renderTile
15136      * Internal function to actually initialize the image tile,
15137      *     position it correctly, and set its url.
15138      */
15139     renderTile: function() {
15140         if (this.layer.async) {
15141             // Asynchronous image requests call the asynchronous getURL method
15142             // on the layer to fetch an image that covers 'this.bounds'.
15143             var id = this.asyncRequestId = (this.asyncRequestId || 0) + 1;
15144             this.layer.getURLasync(this.bounds, function(url) {
15145                 if (id == this.asyncRequestId) {
15146                     this.url = url;
15147                     this.initImage();
15148                 }
15149             }, this);
15150         } else {
15151             // synchronous image requests get the url immediately.
15152             this.url = this.layer.getURL(this.bounds);
15153             this.initImage();
15154         }
15155     },
15156
15157     /**
15158      * Method: positionTile
15159      * Using the properties currenty set on the layer, position the tile correctly.
15160      * This method is used both by the async and non-async versions of the Tile.Image
15161      * code.
15162      */
15163     positionTile: function() {
15164         var style = this.getTile().style,
15165             size = this.frame ? this.size :
15166                 this.layer.getImageSize(this.bounds),
15167             ratio = 1;
15168         if (this.layer instanceof OpenLayers.Layer.Grid) {
15169             ratio = this.layer.getServerResolution() / this.layer.map.getResolution();
15170         }
15171         style.left = this.position.x + "px";
15172         style.top = this.position.y + "px";
15173         style.width = Math.round(ratio * size.w) + "px";
15174         style.height = Math.round(ratio * size.h) + "px";
15175     },
15176
15177     /** 
15178      * Method: clear
15179      * Remove the tile from the DOM, clear it of any image related data so that
15180      * it can be reused in a new location.
15181      */
15182     clear: function() {
15183         OpenLayers.Tile.prototype.clear.apply(this, arguments);
15184         var img = this.imgDiv;
15185         if (img) {
15186             var tile = this.getTile();
15187             if (tile.parentNode === this.layer.div) {
15188                 this.layer.div.removeChild(tile);
15189             }
15190             this.setImgSrc();
15191             if (this.layerAlphaHack === true) {
15192                 img.style.filter = "";
15193             }
15194             OpenLayers.Element.removeClass(img, "olImageLoadError");
15195         }
15196         this.canvasContext = null;
15197     },
15198     
15199     /**
15200      * Method: getImage
15201      * Returns or creates and returns the tile image.
15202      */
15203     getImage: function() {
15204         if (!this.imgDiv) {
15205             this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false);
15206
15207             var style = this.imgDiv.style;
15208             if (this.frame) {
15209                 var left = 0, top = 0;
15210                 if (this.layer.gutter) {
15211                     left = this.layer.gutter / this.layer.tileSize.w * 100;
15212                     top = this.layer.gutter / this.layer.tileSize.h * 100;
15213                 }
15214                 style.left = -left + "%";
15215                 style.top = -top + "%";
15216                 style.width = (2 * left + 100) + "%";
15217                 style.height = (2 * top + 100) + "%";
15218             }
15219             style.visibility = "hidden";
15220             style.opacity = 0;
15221             if (this.layer.opacity < 1) {
15222                 style.filter = 'alpha(opacity=' +
15223                                (this.layer.opacity * 100) +
15224                                ')';
15225             }
15226             style.position = "absolute";
15227             if (this.layerAlphaHack) {
15228                 // move the image out of sight
15229                 style.paddingTop = style.height;
15230                 style.height = "0";
15231                 style.width = "100%";
15232             }
15233             if (this.frame) {
15234                 this.frame.appendChild(this.imgDiv);
15235             }
15236         }
15237
15238         return this.imgDiv;
15239     },
15240     
15241     /**
15242      * APIMethod: setImage
15243      * Sets the image element for this tile. This method should only be called
15244      * from beforeload listeners.
15245      *
15246      * Parameters
15247      * img - {HTMLImageElement} The image to use for this tile.
15248      */
15249     setImage: function(img) {
15250         this.imgDiv = img;
15251     },
15252
15253     /**
15254      * Method: initImage
15255      * Creates the content for the frame on the tile.
15256      */
15257     initImage: function() {
15258         if (!this.url && !this.imgDiv) {
15259             // fast path out - if there is no tile url and no previous image
15260             this.isLoading = false;
15261             return;
15262         }
15263         this.events.triggerEvent('beforeload');
15264         this.layer.div.appendChild(this.getTile());
15265         this.events.triggerEvent(this._loadEvent);
15266         var img = this.getImage();
15267         var src = img.getAttribute('src') || '';
15268         if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) {
15269             this._loadTimeout = window.setTimeout(
15270                 OpenLayers.Function.bind(this.onImageLoad, this), 0
15271             );
15272         } else {
15273             this.stopLoading();
15274             if (this.crossOriginKeyword) {
15275                 img.removeAttribute("crossorigin");
15276             }
15277             OpenLayers.Event.observe(img, "load",
15278                 OpenLayers.Function.bind(this.onImageLoad, this)
15279             );
15280             OpenLayers.Event.observe(img, "error",
15281                 OpenLayers.Function.bind(this.onImageError, this)
15282             );
15283             this.imageReloadAttempts = 0;
15284             this.setImgSrc(this.url);
15285         }
15286     },
15287     
15288     /**
15289      * Method: setImgSrc
15290      * Sets the source for the tile image
15291      *
15292      * Parameters:
15293      * url - {String} or undefined to hide the image
15294      */
15295     setImgSrc: function(url) {
15296         var img = this.imgDiv;
15297         if (url) {
15298             img.style.visibility = 'hidden';
15299             img.style.opacity = 0;
15300             // don't set crossOrigin if the url is a data URL
15301             if (this.crossOriginKeyword) {
15302                 if (url.substr(0, 5) !== 'data:') {
15303                     img.setAttribute("crossorigin", this.crossOriginKeyword);
15304                 } else {
15305                     img.removeAttribute("crossorigin");
15306                 }
15307             }
15308             img.src = url;
15309         } else {
15310             // Remove reference to the image, and leave it to the browser's
15311             // caching and garbage collection.
15312             this.stopLoading();
15313             this.imgDiv = null;
15314             if (img.parentNode) {
15315                 img.parentNode.removeChild(img);
15316             }
15317         }
15318     },
15319     
15320     /**
15321      * Method: getTile
15322      * Get the tile's markup.
15323      *
15324      * Returns:
15325      * {DOMElement} The tile's markup
15326      */
15327     getTile: function() {
15328         return this.frame ? this.frame : this.getImage();
15329     },
15330
15331     /**
15332      * Method: createBackBuffer
15333      * Create a backbuffer for this tile. A backbuffer isn't exactly a clone
15334      * of the tile's markup, because we want to avoid the reloading of the
15335      * image. So we clone the frame, and steal the image from the tile.
15336      *
15337      * Returns:
15338      * {DOMElement} The markup, or undefined if the tile has no image
15339      * or if it's currently loading.
15340      */
15341     createBackBuffer: function() {
15342         if (!this.imgDiv || this.isLoading) {
15343             return;
15344         }
15345         var backBuffer;
15346         if (this.frame) {
15347             backBuffer = this.frame.cloneNode(false);
15348             backBuffer.appendChild(this.imgDiv);
15349         } else {
15350             backBuffer = this.imgDiv;
15351         }
15352         this.imgDiv = null;
15353         return backBuffer;
15354     },
15355
15356     /**
15357      * Method: onImageLoad
15358      * Handler for the image onload event
15359      */
15360     onImageLoad: function() {
15361         var img = this.imgDiv;
15362         this.stopLoading();
15363         img.style.visibility = 'inherit';
15364         img.style.opacity = this.layer.opacity;
15365         this.isLoading = false;
15366         this.canvasContext = null;
15367         this.events.triggerEvent("loadend");
15368
15369         if (this.layerAlphaHack === true) {
15370             img.style.filter =
15371                 "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" +
15372                 img.src + "', sizingMethod='scale')";
15373         }
15374     },
15375     
15376     /**
15377      * Method: onImageError
15378      * Handler for the image onerror event
15379      */
15380     onImageError: function() {
15381         var img = this.imgDiv;
15382         if (img.src != null) {
15383             this.imageReloadAttempts++;
15384             if (this.imageReloadAttempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
15385                 this.setImgSrc(this.layer.getURL(this.bounds));
15386             } else {
15387                 OpenLayers.Element.addClass(img, "olImageLoadError");
15388                 this.events.triggerEvent("loaderror");
15389                 this.onImageLoad();
15390             }
15391         }
15392     },
15393     
15394     /**
15395      * Method: stopLoading
15396      * Stops a loading sequence so <onImageLoad> won't be executed.
15397      */
15398     stopLoading: function() {
15399         OpenLayers.Event.stopObservingElement(this.imgDiv);
15400         window.clearTimeout(this._loadTimeout);
15401         delete this._loadTimeout;
15402     },
15403
15404     /**
15405      * APIMethod: getCanvasContext
15406      * Returns a canvas context associated with the tile image (with
15407      * the image drawn on it).
15408      * Returns undefined if the browser does not support canvas, if
15409      * the tile has no image or if it's currently loading.
15410      *
15411      * The function returns a canvas context instance but the
15412      * underlying canvas is still available in the 'canvas' property:
15413      * (code)
15414      * var context = tile.getCanvasContext();
15415      * if (context) {
15416      *     var data = context.canvas.toDataURL('image/jpeg');
15417      * }
15418      * (end)
15419      *
15420      * Returns:
15421      * {Boolean}
15422      */
15423     getCanvasContext: function() {
15424         if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) {
15425             if (!this.canvasContext) {
15426                 var canvas = document.createElement("canvas");
15427                 canvas.width = this.size.w;
15428                 canvas.height = this.size.h;
15429                 this.canvasContext = canvas.getContext("2d");
15430                 this.canvasContext.drawImage(this.imgDiv, 0, 0);
15431             }
15432             return this.canvasContext;
15433         }
15434     },
15435
15436     CLASS_NAME: "OpenLayers.Tile.Image"
15437
15438 });
15439
15440 /** 
15441  * Constant: OpenLayers.Tile.Image.IMAGE
15442  * {HTMLImageElement} The image for a tile.
15443  */
15444 OpenLayers.Tile.Image.IMAGE = (function() {
15445     var img = new Image();
15446     img.className = "olTileImage";
15447     // avoid image gallery menu in IE6
15448     img.galleryImg = "no";
15449     return img;
15450 }());
15451
15452 /* ======================================================================
15453     OpenLayers/Layer/Image.js
15454    ====================================================================== */
15455
15456 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
15457  * full list of contributors). Published under the 2-clause BSD license.
15458  * See license.txt in the OpenLayers distribution or repository for the
15459  * full text of the license. */
15460  
15461 /**
15462  * @requires OpenLayers/Layer.js
15463  * @requires OpenLayers/Tile/Image.js
15464  */
15465
15466 /**
15467  * Class: OpenLayers.Layer.Image
15468  * Instances of OpenLayers.Layer.Image are used to display data from a web
15469  * accessible image as a map layer.  Create a new image layer with the
15470  * <OpenLayers.Layer.Image> constructor.
15471  *
15472  * Inherits from:
15473  *  - <OpenLayers.Layer>
15474  */
15475 OpenLayers.Layer.Image = OpenLayers.Class(OpenLayers.Layer, {
15476
15477     /**
15478      * Property: isBaseLayer
15479      * {Boolean} The layer is a base layer.  Default is true.  Set this property
15480      * in the layer options
15481      */
15482     isBaseLayer: true,
15483     
15484     /**
15485      * Property: url
15486      * {String} URL of the image to use
15487      */
15488     url: null,
15489
15490     /**
15491      * Property: extent
15492      * {<OpenLayers.Bounds>} The image bounds in map units.  This extent will
15493      *     also be used as the default maxExtent for the layer.  If you wish
15494      *     to have a maxExtent that is different than the image extent, set the
15495      *     maxExtent property of the options argument (as with any other layer).
15496      */
15497     extent: null,
15498     
15499     /**
15500      * Property: size
15501      * {<OpenLayers.Size>} The image size in pixels
15502      */
15503     size: null,
15504
15505     /**
15506      * Property: tile
15507      * {<OpenLayers.Tile.Image>}
15508      */
15509     tile: null,
15510
15511     /**
15512      * Property: aspectRatio
15513      * {Float} The ratio of height/width represented by a single pixel in the
15514      * graphic
15515      */
15516     aspectRatio: null,
15517
15518     /**
15519      * Constructor: OpenLayers.Layer.Image
15520      * Create a new image layer
15521      *
15522      * Parameters:
15523      * name - {String} A name for the layer.
15524      * url - {String} Relative or absolute path to the image
15525      * extent - {<OpenLayers.Bounds>} The extent represented by the image
15526      * size - {<OpenLayers.Size>} The size (in pixels) of the image
15527      * options - {Object} Hashtable of extra options to tag onto the layer
15528      */
15529     initialize: function(name, url, extent, size, options) {
15530         this.url = url;
15531         this.extent = extent;
15532         this.maxExtent = extent;
15533         this.size = size;
15534         OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);
15535
15536         this.aspectRatio = (this.extent.getHeight() / this.size.h) /
15537                            (this.extent.getWidth() / this.size.w);
15538     },    
15539
15540     /**
15541      * Method: destroy
15542      * Destroy this layer
15543      */
15544     destroy: function() {
15545         if (this.tile) {
15546             this.removeTileMonitoringHooks(this.tile);
15547             this.tile.destroy();
15548             this.tile = null;
15549         }
15550         OpenLayers.Layer.prototype.destroy.apply(this, arguments);
15551     },
15552     
15553     /**
15554      * Method: clone
15555      * Create a clone of this layer
15556      *
15557      * Paramters:
15558      * obj - {Object} An optional layer (is this ever used?)
15559      *
15560      * Returns:
15561      * {<OpenLayers.Layer.Image>} An exact copy of this layer
15562      */
15563     clone: function(obj) {
15564         
15565         if(obj == null) {
15566             obj = new OpenLayers.Layer.Image(this.name,
15567                                                this.url,
15568                                                this.extent,
15569                                                this.size,
15570                                                this.getOptions());
15571         }
15572
15573         //get all additions from superclasses
15574         obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
15575
15576         // copy/set any non-init, non-simple values here
15577
15578         return obj;
15579     },    
15580     
15581     /**
15582      * APIMethod: setMap
15583      * 
15584      * Parameters:
15585      * map - {<OpenLayers.Map>}
15586      */
15587     setMap: function(map) {
15588         /**
15589          * If nothing to do with resolutions has been set, assume a single
15590          * resolution determined by ratio*extent/size - if an image has a
15591          * pixel aspect ratio different than one (as calculated above), the
15592          * image will be stretched in one dimension only.
15593          */
15594         if( this.options.maxResolution == null ) {
15595             this.options.maxResolution = this.aspectRatio *
15596                                          this.extent.getWidth() /
15597                                          this.size.w;
15598         }
15599         OpenLayers.Layer.prototype.setMap.apply(this, arguments);
15600     },
15601
15602     /** 
15603      * Method: moveTo
15604      * Create the tile for the image or resize it for the new resolution
15605      * 
15606      * Parameters:
15607      * bounds - {<OpenLayers.Bounds>}
15608      * zoomChanged - {Boolean}
15609      * dragging - {Boolean}
15610      */
15611     moveTo:function(bounds, zoomChanged, dragging) {
15612         OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
15613
15614         var firstRendering = (this.tile == null);
15615
15616         if(zoomChanged || firstRendering) {
15617
15618             //determine new tile size
15619             this.setTileSize();
15620
15621             //determine new position (upper left corner of new bounds)
15622             var ulPx = this.map.getLayerPxFromLonLat({
15623                 lon: this.extent.left,
15624                 lat: this.extent.top
15625             });
15626
15627             if(firstRendering) {
15628                 //create the new tile
15629                 this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent, 
15630                                                       null, this.tileSize);
15631                 this.addTileMonitoringHooks(this.tile);
15632             } else {
15633                 //just resize the tile and set it's new position
15634                 this.tile.size = this.tileSize.clone();
15635                 this.tile.position = ulPx.clone();
15636             }
15637             this.tile.draw();
15638         }
15639     }, 
15640
15641     /**
15642      * Set the tile size based on the map size.
15643      */
15644     setTileSize: function() {
15645         var tileWidth = this.extent.getWidth() / this.map.getResolution();
15646         var tileHeight = this.extent.getHeight() / this.map.getResolution();
15647         this.tileSize = new OpenLayers.Size(tileWidth, tileHeight);
15648     },
15649
15650     /** 
15651      * Method: addTileMonitoringHooks
15652      * This function takes a tile as input and adds the appropriate hooks to 
15653      *     the tile so that the layer can keep track of the loading tiles.
15654      * 
15655      * Parameters: 
15656      * tile - {<OpenLayers.Tile>}
15657      */
15658     addTileMonitoringHooks: function(tile) {
15659         tile.onLoadStart = function() {
15660             this.events.triggerEvent("loadstart");
15661         };
15662         tile.events.register("loadstart", this, tile.onLoadStart);
15663       
15664         tile.onLoadEnd = function() {
15665             this.events.triggerEvent("loadend");
15666         };
15667         tile.events.register("loadend", this, tile.onLoadEnd);
15668         tile.events.register("unload", this, tile.onLoadEnd);
15669     },
15670
15671     /** 
15672      * Method: removeTileMonitoringHooks
15673      * This function takes a tile as input and removes the tile hooks 
15674      *     that were added in <addTileMonitoringHooks>.
15675      * 
15676      * Parameters: 
15677      * tile - {<OpenLayers.Tile>}
15678      */
15679     removeTileMonitoringHooks: function(tile) {
15680         tile.unload();
15681         tile.events.un({
15682             "loadstart": tile.onLoadStart,
15683             "loadend": tile.onLoadEnd,
15684             "unload": tile.onLoadEnd,
15685             scope: this
15686         });
15687     },
15688     
15689     /**
15690      * APIMethod: setUrl
15691      * 
15692      * Parameters:
15693      * newUrl - {String}
15694      */
15695     setUrl: function(newUrl) {
15696         this.url = newUrl;
15697         this.tile.draw();
15698     },
15699
15700     /** 
15701      * APIMethod: getURL
15702      * The url we return is always the same (the image itself never changes)
15703      *     so we can ignore the bounds parameter (it will always be the same, 
15704      *     anyways) 
15705      * 
15706      * Parameters:
15707      * bounds - {<OpenLayers.Bounds>}
15708      */
15709     getURL: function(bounds) {
15710         return this.url;
15711     },
15712
15713     CLASS_NAME: "OpenLayers.Layer.Image"
15714 });
15715 /* ======================================================================
15716     OpenLayers/Geometry.js
15717    ====================================================================== */
15718
15719 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
15720  * full list of contributors). Published under the 2-clause BSD license.
15721  * See license.txt in the OpenLayers distribution or repository for the
15722  * full text of the license. */
15723  
15724 /**
15725  * @requires OpenLayers/BaseTypes/Class.js
15726  */
15727
15728 /**
15729  * Class: OpenLayers.Geometry
15730  * A Geometry is a description of a geographic object.  Create an instance of
15731  * this class with the <OpenLayers.Geometry> constructor.  This is a base class,
15732  * typical geometry types are described by subclasses of this class.
15733  *
15734  * Note that if you use the <OpenLayers.Geometry.fromWKT> method, you must
15735  * explicitly include the OpenLayers.Format.WKT in your build.
15736  */
15737 OpenLayers.Geometry = OpenLayers.Class({
15738
15739     /**
15740      * Property: id
15741      * {String} A unique identifier for this geometry.
15742      */
15743     id: null,
15744
15745     /**
15746      * Property: parent
15747      * {<OpenLayers.Geometry>}This is set when a Geometry is added as component
15748      * of another geometry
15749      */
15750     parent: null,
15751
15752     /**
15753      * Property: bounds 
15754      * {<OpenLayers.Bounds>} The bounds of this geometry
15755      */
15756     bounds: null,
15757
15758     /**
15759      * Constructor: OpenLayers.Geometry
15760      * Creates a geometry object.  
15761      */
15762     initialize: function() {
15763         this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME+ "_");
15764     },
15765     
15766     /**
15767      * Method: destroy
15768      * Destroy this geometry.
15769      */
15770     destroy: function() {
15771         this.id = null;
15772         this.bounds = null;
15773     },
15774     
15775     /**
15776      * APIMethod: clone
15777      * Create a clone of this geometry.  Does not set any non-standard
15778      *     properties of the cloned geometry.
15779      * 
15780      * Returns:
15781      * {<OpenLayers.Geometry>} An exact clone of this geometry.
15782      */
15783     clone: function() {
15784         return new OpenLayers.Geometry();
15785     },
15786     
15787     /**
15788      * Method: setBounds
15789      * Set the bounds for this Geometry.
15790      * 
15791      * Parameters:
15792      * bounds - {<OpenLayers.Bounds>} 
15793      */
15794     setBounds: function(bounds) {
15795         if (bounds) {
15796             this.bounds = bounds.clone();
15797         }
15798     },
15799     
15800     /**
15801      * Method: clearBounds
15802      * Nullify this components bounds and that of its parent as well.
15803      */
15804     clearBounds: function() {
15805         this.bounds = null;
15806         if (this.parent) {
15807             this.parent.clearBounds();
15808         }    
15809     },
15810     
15811     /**
15812      * Method: extendBounds
15813      * Extend the existing bounds to include the new bounds. 
15814      * If geometry's bounds is not yet set, then set a new Bounds.
15815      * 
15816      * Parameters:
15817      * newBounds - {<OpenLayers.Bounds>} 
15818      */
15819     extendBounds: function(newBounds){
15820         var bounds = this.getBounds();
15821         if (!bounds) {
15822             this.setBounds(newBounds);
15823         } else {
15824             this.bounds.extend(newBounds);
15825         }
15826     },
15827     
15828     /**
15829      * APIMethod: getBounds
15830      * Get the bounds for this Geometry. If bounds is not set, it 
15831      * is calculated again, this makes queries faster.
15832      * 
15833      * Returns:
15834      * {<OpenLayers.Bounds>}
15835      */
15836     getBounds: function() {
15837         if (this.bounds == null) {
15838             this.calculateBounds();
15839         }
15840         return this.bounds;
15841     },
15842     
15843     /** 
15844      * APIMethod: calculateBounds
15845      * Recalculate the bounds for the geometry. 
15846      */
15847     calculateBounds: function() {
15848         //
15849         // This should be overridden by subclasses.
15850         //
15851     },
15852     
15853     /**
15854      * APIMethod: distanceTo
15855      * Calculate the closest distance between two geometries (on the x-y plane).
15856      *
15857      * Parameters:
15858      * geometry - {<OpenLayers.Geometry>} The target geometry.
15859      * options - {Object} Optional properties for configuring the distance
15860      *     calculation.
15861      *
15862      * Valid options depend on the specific geometry type.
15863      * 
15864      * Returns:
15865      * {Number | Object} The distance between this geometry and the target.
15866      *     If details is true, the return will be an object with distance,
15867      *     x0, y0, x1, and x2 properties.  The x0 and y0 properties represent
15868      *     the coordinates of the closest point on this geometry. The x1 and y1
15869      *     properties represent the coordinates of the closest point on the
15870      *     target geometry.
15871      */
15872     distanceTo: function(geometry, options) {
15873     },
15874     
15875     /**
15876      * APIMethod: getVertices
15877      * Return a list of all points in this geometry.
15878      *
15879      * Parameters:
15880      * nodes - {Boolean} For lines, only return vertices that are
15881      *     endpoints.  If false, for lines, only vertices that are not
15882      *     endpoints will be returned.  If not provided, all vertices will
15883      *     be returned.
15884      *
15885      * Returns:
15886      * {Array} A list of all vertices in the geometry.
15887      */
15888     getVertices: function(nodes) {
15889     },
15890
15891     /**
15892      * Method: atPoint
15893      * Note - This is only an approximation based on the bounds of the 
15894      * geometry.
15895      * 
15896      * Parameters:
15897      * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
15898      *     object with a 'lon' and 'lat' properties.
15899      * toleranceLon - {float} Optional tolerance in Geometric Coords
15900      * toleranceLat - {float} Optional tolerance in Geographic Coords
15901      * 
15902      * Returns:
15903      * {Boolean} Whether or not the geometry is at the specified location
15904      */
15905     atPoint: function(lonlat, toleranceLon, toleranceLat) {
15906         var atPoint = false;
15907         var bounds = this.getBounds();
15908         if ((bounds != null) && (lonlat != null)) {
15909
15910             var dX = (toleranceLon != null) ? toleranceLon : 0;
15911             var dY = (toleranceLat != null) ? toleranceLat : 0;
15912     
15913             var toleranceBounds = 
15914                 new OpenLayers.Bounds(this.bounds.left - dX,
15915                                       this.bounds.bottom - dY,
15916                                       this.bounds.right + dX,
15917                                       this.bounds.top + dY);
15918
15919             atPoint = toleranceBounds.containsLonLat(lonlat);
15920         }
15921         return atPoint;
15922     },
15923     
15924     /**
15925      * Method: getLength
15926      * Calculate the length of this geometry. This method is defined in
15927      * subclasses.
15928      * 
15929      * Returns:
15930      * {Float} The length of the collection by summing its parts
15931      */
15932     getLength: function() {
15933         //to be overridden by geometries that actually have a length
15934         //
15935         return 0.0;
15936     },
15937
15938     /**
15939      * Method: getArea
15940      * Calculate the area of this geometry. This method is defined in subclasses.
15941      * 
15942      * Returns:
15943      * {Float} The area of the collection by summing its parts
15944      */
15945     getArea: function() {
15946         //to be overridden by geometries that actually have an area
15947         //
15948         return 0.0;
15949     },
15950     
15951     /**
15952      * APIMethod: getCentroid
15953      * Calculate the centroid of this geometry. This method is defined in subclasses.
15954      *
15955      * Returns:
15956      * {<OpenLayers.Geometry.Point>} The centroid of the collection
15957      */
15958     getCentroid: function() {
15959         return null;
15960     },
15961
15962     /**
15963      * Method: toString
15964      * Returns a text representation of the geometry.  If the WKT format is
15965      *     included in a build, this will be the Well-Known Text 
15966      *     representation.
15967      *
15968      * Returns:
15969      * {String} String representation of this geometry.
15970      */
15971     toString: function() {
15972         var string;
15973         if (OpenLayers.Format && OpenLayers.Format.WKT) {
15974             string = OpenLayers.Format.WKT.prototype.write(
15975                 new OpenLayers.Feature.Vector(this)
15976             );
15977         } else {
15978             string = Object.prototype.toString.call(this);
15979         }
15980         return string;
15981     },
15982
15983     CLASS_NAME: "OpenLayers.Geometry"
15984 });
15985
15986 /**
15987  * Function: OpenLayers.Geometry.fromWKT
15988  * Generate a geometry given a Well-Known Text string.  For this method to
15989  *     work, you must include the OpenLayers.Format.WKT in your build 
15990  *     explicitly.
15991  *
15992  * Parameters:
15993  * wkt - {String} A string representing the geometry in Well-Known Text.
15994  *
15995  * Returns:
15996  * {<OpenLayers.Geometry>} A geometry of the appropriate class.
15997  */
15998 OpenLayers.Geometry.fromWKT = function(wkt) {
15999     var geom;
16000     if (OpenLayers.Format && OpenLayers.Format.WKT) {
16001         var format = OpenLayers.Geometry.fromWKT.format;
16002         if (!format) {
16003             format = new OpenLayers.Format.WKT();
16004             OpenLayers.Geometry.fromWKT.format = format;
16005         }
16006         var result = format.read(wkt);
16007         if (result instanceof OpenLayers.Feature.Vector) {
16008             geom = result.geometry;
16009         } else if (OpenLayers.Util.isArray(result)) {
16010             var len = result.length;
16011             var components = new Array(len);
16012             for (var i=0; i<len; ++i) {
16013                 components[i] = result[i].geometry;
16014             }
16015             geom = new OpenLayers.Geometry.Collection(components);
16016         }
16017     }
16018     return geom;
16019 };
16020     
16021 /**
16022  * Method: OpenLayers.Geometry.segmentsIntersect
16023  * Determine whether two line segments intersect.  Optionally calculates
16024  *     and returns the intersection point.  This function is optimized for
16025  *     cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1.  In those
16026  *     obvious cases where there is no intersection, the function should
16027  *     not be called.
16028  *
16029  * Parameters:
16030  * seg1 - {Object} Object representing a segment with properties x1, y1, x2,
16031  *     and y2.  The start point is represented by x1 and y1.  The end point
16032  *     is represented by x2 and y2.  Start and end are ordered so that x1 < x2.
16033  * seg2 - {Object} Object representing a segment with properties x1, y1, x2,
16034  *     and y2.  The start point is represented by x1 and y1.  The end point
16035  *     is represented by x2 and y2.  Start and end are ordered so that x1 < x2.
16036  * options - {Object} Optional properties for calculating the intersection.
16037  *
16038  * Valid options:
16039  * point - {Boolean} Return the intersection point.  If false, the actual
16040  *     intersection point will not be calculated.  If true and the segments
16041  *     intersect, the intersection point will be returned.  If true and
16042  *     the segments do not intersect, false will be returned.  If true and
16043  *     the segments are coincident, true will be returned.
16044  * tolerance - {Number} If a non-null value is provided, if the segments are
16045  *     within the tolerance distance, this will be considered an intersection.
16046  *     In addition, if the point option is true and the calculated intersection
16047  *     is within the tolerance distance of an end point, the endpoint will be
16048  *     returned instead of the calculated intersection.  Further, if the
16049  *     intersection is within the tolerance of endpoints on both segments, or
16050  *     if two segment endpoints are within the tolerance distance of eachother
16051  *     (but no intersection is otherwise calculated), an endpoint on the
16052  *     first segment provided will be returned.
16053  *
16054  * Returns:
16055  * {Boolean | <OpenLayers.Geometry.Point>}  The two segments intersect.
16056  *     If the point argument is true, the return will be the intersection
16057  *     point or false if none exists.  If point is true and the segments
16058  *     are coincident, return will be true (and the instersection is equal
16059  *     to the shorter segment).
16060  */
16061 OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) {
16062     var point = options && options.point;
16063     var tolerance = options && options.tolerance;
16064     var intersection = false;
16065     var x11_21 = seg1.x1 - seg2.x1;
16066     var y11_21 = seg1.y1 - seg2.y1;
16067     var x12_11 = seg1.x2 - seg1.x1;
16068     var y12_11 = seg1.y2 - seg1.y1;
16069     var y22_21 = seg2.y2 - seg2.y1;
16070     var x22_21 = seg2.x2 - seg2.x1;
16071     var d = (y22_21 * x12_11) - (x22_21 * y12_11);
16072     var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);
16073     var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);
16074     if(d == 0) {
16075         // parallel
16076         if(n1 == 0 && n2 == 0) {
16077             // coincident
16078             intersection = true;
16079         }
16080     } else {
16081         var along1 = n1 / d;
16082         var along2 = n2 / d;
16083         if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) {
16084             // intersect
16085             if(!point) {
16086                 intersection = true;
16087             } else {
16088                 // calculate the intersection point
16089                 var x = seg1.x1 + (along1 * x12_11);
16090                 var y = seg1.y1 + (along1 * y12_11);
16091                 intersection = new OpenLayers.Geometry.Point(x, y);
16092             }
16093         }
16094     }
16095     if(tolerance) {
16096         var dist;
16097         if(intersection) {
16098             if(point) {
16099                 var segs = [seg1, seg2];
16100                 var seg, x, y;
16101                 // check segment endpoints for proximity to intersection
16102                 // set intersection to first endpoint within the tolerance
16103                 outer: for(var i=0; i<2; ++i) {
16104                     seg = segs[i];
16105                     for(var j=1; j<3; ++j) {
16106                         x = seg["x" + j];
16107                         y = seg["y" + j];
16108                         dist = Math.sqrt(
16109                             Math.pow(x - intersection.x, 2) +
16110                             Math.pow(y - intersection.y, 2)
16111                         );
16112                         if(dist < tolerance) {
16113                             intersection.x = x;
16114                             intersection.y = y;
16115                             break outer;
16116                         }
16117                     }
16118                 }
16119                 
16120             }
16121         } else {
16122             // no calculated intersection, but segments could be within
16123             // the tolerance of one another
16124             var segs = [seg1, seg2];
16125             var source, target, x, y, p, result;
16126             // check segment endpoints for proximity to intersection
16127             // set intersection to first endpoint within the tolerance
16128             outer: for(var i=0; i<2; ++i) {
16129                 source = segs[i];
16130                 target = segs[(i+1)%2];
16131                 for(var j=1; j<3; ++j) {
16132                     p = {x: source["x"+j], y: source["y"+j]};
16133                     result = OpenLayers.Geometry.distanceToSegment(p, target);
16134                     if(result.distance < tolerance) {
16135                         if(point) {
16136                             intersection = new OpenLayers.Geometry.Point(p.x, p.y);
16137                         } else {
16138                             intersection = true;
16139                         }
16140                         break outer;
16141                     }
16142                 }
16143             }
16144         }
16145     }
16146     return intersection;
16147 };
16148
16149 /**
16150  * Function: OpenLayers.Geometry.distanceToSegment
16151  *
16152  * Parameters:
16153  * point - {Object} An object with x and y properties representing the
16154  *     point coordinates.
16155  * segment - {Object} An object with x1, y1, x2, and y2 properties
16156  *     representing endpoint coordinates.
16157  *
16158  * Returns:
16159  * {Object} An object with distance, along, x, and y properties.  The distance
16160  *     will be the shortest distance between the input point and segment.
16161  *     The x and y properties represent the coordinates along the segment
16162  *     where the shortest distance meets the segment. The along attribute
16163  *     describes how far between the two segment points the given point is.
16164  */
16165 OpenLayers.Geometry.distanceToSegment = function(point, segment) {
16166     var result = OpenLayers.Geometry.distanceSquaredToSegment(point, segment);
16167     result.distance = Math.sqrt(result.distance);
16168     return result;
16169 };
16170
16171 /**
16172  * Function: OpenLayers.Geometry.distanceSquaredToSegment
16173  *
16174  * Usually the distanceToSegment function should be used. This variant however
16175  * can be used for comparisons where the exact distance is not important.
16176  *
16177  * Parameters:
16178  * point - {Object} An object with x and y properties representing the
16179  *     point coordinates.
16180  * segment - {Object} An object with x1, y1, x2, and y2 properties
16181  *     representing endpoint coordinates.
16182  *
16183  * Returns:
16184  * {Object} An object with squared distance, along, x, and y properties.
16185  *     The distance will be the shortest distance between the input point and
16186  *     segment. The x and y properties represent the coordinates along the
16187  *     segment where the shortest distance meets the segment. The along
16188  *     attribute describes how far between the two segment points the given
16189  *     point is.
16190  */
16191 OpenLayers.Geometry.distanceSquaredToSegment = function(point, segment) {
16192     var x0 = point.x;
16193     var y0 = point.y;
16194     var x1 = segment.x1;
16195     var y1 = segment.y1;
16196     var x2 = segment.x2;
16197     var y2 = segment.y2;
16198     var dx = x2 - x1;
16199     var dy = y2 - y1;
16200     var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) /
16201                 (Math.pow(dx, 2) + Math.pow(dy, 2));
16202     var x, y;
16203     if(along <= 0.0) {
16204         x = x1;
16205         y = y1;
16206     } else if(along >= 1.0) {
16207         x = x2;
16208         y = y2;
16209     } else {
16210         x = x1 + along * dx;
16211         y = y1 + along * dy;
16212     }
16213     return {
16214         distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2),
16215         x: x, y: y,
16216         along: along
16217     };
16218 };
16219 /* ======================================================================
16220     OpenLayers/Geometry/Collection.js
16221    ====================================================================== */
16222
16223 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
16224  * full list of contributors). Published under the 2-clause BSD license.
16225  * See license.txt in the OpenLayers distribution or repository for the
16226  * full text of the license. */
16227
16228 /**
16229  * @requires OpenLayers/Geometry.js
16230  */
16231
16232 /**
16233  * Class: OpenLayers.Geometry.Collection
16234  * A Collection is exactly what it sounds like: A collection of different 
16235  * Geometries. These are stored in the local parameter <components> (which
16236  * can be passed as a parameter to the constructor). 
16237  * 
16238  * As new geometries are added to the collection, they are NOT cloned. 
16239  * When removing geometries, they need to be specified by reference (ie you 
16240  * have to pass in the *exact* geometry to be removed).
16241  * 
16242  * The <getArea> and <getLength> functions here merely iterate through
16243  * the components, summing their respective areas and lengths.
16244  *
16245  * Create a new instance with the <OpenLayers.Geometry.Collection> constructor.
16246  *
16247  * Inherits from:
16248  *  - <OpenLayers.Geometry> 
16249  */
16250 OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {
16251
16252     /**
16253      * APIProperty: components
16254      * {Array(<OpenLayers.Geometry>)} The component parts of this geometry
16255      */
16256     components: null,
16257     
16258     /**
16259      * Property: componentTypes
16260      * {Array(String)} An array of class names representing the types of
16261      * components that the collection can include.  A null value means the
16262      * component types are not restricted.
16263      */
16264     componentTypes: null,
16265
16266     /**
16267      * Constructor: OpenLayers.Geometry.Collection
16268      * Creates a Geometry Collection -- a list of geoms.
16269      *
16270      * Parameters: 
16271      * components - {Array(<OpenLayers.Geometry>)} Optional array of geometries
16272      *
16273      */
16274     initialize: function (components) {
16275         OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
16276         this.components = [];
16277         if (components != null) {
16278             this.addComponents(components);
16279         }
16280     },
16281
16282     /**
16283      * APIMethod: destroy
16284      * Destroy this geometry.
16285      */
16286     destroy: function () {
16287         this.components.length = 0;
16288         this.components = null;
16289         OpenLayers.Geometry.prototype.destroy.apply(this, arguments);
16290     },
16291
16292     /**
16293      * APIMethod: clone
16294      * Clone this geometry.
16295      *
16296      * Returns:
16297      * {<OpenLayers.Geometry.Collection>} An exact clone of this collection
16298      */
16299     clone: function() {
16300         var geometry = eval("new " + this.CLASS_NAME + "()");
16301         for(var i=0, len=this.components.length; i<len; i++) {
16302             geometry.addComponent(this.components[i].clone());
16303         }
16304         
16305         // catch any randomly tagged-on properties
16306         OpenLayers.Util.applyDefaults(geometry, this);
16307         
16308         return geometry;
16309     },
16310
16311     /**
16312      * Method: getComponentsString
16313      * Get a string representing the components for this collection
16314      * 
16315      * Returns:
16316      * {String} A string representation of the components of this geometry
16317      */
16318     getComponentsString: function(){
16319         var strings = [];
16320         for(var i=0, len=this.components.length; i<len; i++) {
16321             strings.push(this.components[i].toShortString()); 
16322         }
16323         return strings.join(",");
16324     },
16325
16326     /**
16327      * APIMethod: calculateBounds
16328      * Recalculate the bounds by iterating through the components and 
16329      * calling calling extendBounds() on each item.
16330      */
16331     calculateBounds: function() {
16332         this.bounds = null;
16333         var bounds = new OpenLayers.Bounds();
16334         var components = this.components;
16335         if (components) {
16336             for (var i=0, len=components.length; i<len; i++) {
16337                 bounds.extend(components[i].getBounds());
16338             }
16339         }
16340         // to preserve old behavior, we only set bounds if non-null
16341         // in the future, we could add bounds.isEmpty()
16342         if (bounds.left != null && bounds.bottom != null && 
16343             bounds.right != null && bounds.top != null) {
16344             this.setBounds(bounds);
16345         }
16346     },
16347
16348     /**
16349      * APIMethod: addComponents
16350      * Add components to this geometry.
16351      *
16352      * Parameters:
16353      * components - {Array(<OpenLayers.Geometry>)} An array of geometries to add
16354      */
16355     addComponents: function(components){
16356         if(!(OpenLayers.Util.isArray(components))) {
16357             components = [components];
16358         }
16359         for(var i=0, len=components.length; i<len; i++) {
16360             this.addComponent(components[i]);
16361         }
16362     },
16363
16364     /**
16365      * Method: addComponent
16366      * Add a new component (geometry) to the collection.  If this.componentTypes
16367      * is set, then the component class name must be in the componentTypes array.
16368      *
16369      * The bounds cache is reset.
16370      * 
16371      * Parameters:
16372      * component - {<OpenLayers.Geometry>} A geometry to add
16373      * index - {int} Optional index into the array to insert the component
16374      *
16375      * Returns:
16376      * {Boolean} The component geometry was successfully added
16377      */    
16378     addComponent: function(component, index) {
16379         var added = false;
16380         if(component) {
16381             if(this.componentTypes == null ||
16382                (OpenLayers.Util.indexOf(this.componentTypes,
16383                                         component.CLASS_NAME) > -1)) {
16384
16385                 if(index != null && (index < this.components.length)) {
16386                     var components1 = this.components.slice(0, index);
16387                     var components2 = this.components.slice(index, 
16388                                                            this.components.length);
16389                     components1.push(component);
16390                     this.components = components1.concat(components2);
16391                 } else {
16392                     this.components.push(component);
16393                 }
16394                 component.parent = this;
16395                 this.clearBounds();
16396                 added = true;
16397             }
16398         }
16399         return added;
16400     },
16401     
16402     /**
16403      * APIMethod: removeComponents
16404      * Remove components from this geometry.
16405      *
16406      * Parameters:
16407      * components - {Array(<OpenLayers.Geometry>)} The components to be removed
16408      *
16409      * Returns: 
16410      * {Boolean} A component was removed.
16411      */
16412     removeComponents: function(components) {
16413         var removed = false;
16414
16415         if(!(OpenLayers.Util.isArray(components))) {
16416             components = [components];
16417         }
16418         for(var i=components.length-1; i>=0; --i) {
16419             removed = this.removeComponent(components[i]) || removed;
16420         }
16421         return removed;
16422     },
16423     
16424     /**
16425      * Method: removeComponent
16426      * Remove a component from this geometry.
16427      *
16428      * Parameters:
16429      * component - {<OpenLayers.Geometry>} 
16430      *
16431      * Returns: 
16432      * {Boolean} The component was removed.
16433      */
16434     removeComponent: function(component) {
16435         
16436         OpenLayers.Util.removeItem(this.components, component);
16437         
16438         // clearBounds() so that it gets recalculated on the next call
16439         // to this.getBounds();
16440         this.clearBounds();
16441         return true;
16442     },
16443
16444     /**
16445      * APIMethod: getLength
16446      * Calculate the length of this geometry
16447      *
16448      * Returns:
16449      * {Float} The length of the geometry
16450      */
16451     getLength: function() {
16452         var length = 0.0;
16453         for (var i=0, len=this.components.length; i<len; i++) {
16454             length += this.components[i].getLength();
16455         }
16456         return length;
16457     },
16458     
16459     /**
16460      * APIMethod: getArea
16461      * Calculate the area of this geometry. Note how this function is overridden
16462      * in <OpenLayers.Geometry.Polygon>.
16463      *
16464      * Returns:
16465      * {Float} The area of the collection by summing its parts
16466      */
16467     getArea: function() {
16468         var area = 0.0;
16469         for (var i=0, len=this.components.length; i<len; i++) {
16470             area += this.components[i].getArea();
16471         }
16472         return area;
16473     },
16474
16475     /** 
16476      * APIMethod: getGeodesicArea
16477      * Calculate the approximate area of the polygon were it projected onto
16478      *     the earth.
16479      *
16480      * Parameters:
16481      * projection - {<OpenLayers.Projection>} The spatial reference system
16482      *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
16483      *     assumed.
16484      * 
16485      * Reference:
16486      * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
16487      *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
16488      *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
16489      *
16490      * Returns:
16491      * {float} The approximate geodesic area of the geometry in square meters.
16492      */
16493     getGeodesicArea: function(projection) {
16494         var area = 0.0;
16495         for(var i=0, len=this.components.length; i<len; i++) {
16496             area += this.components[i].getGeodesicArea(projection);
16497         }
16498         return area;
16499     },
16500     
16501     /**
16502      * APIMethod: getCentroid
16503      *
16504      * Compute the centroid for this geometry collection.
16505      *
16506      * Parameters:
16507      * weighted - {Boolean} Perform the getCentroid computation recursively,
16508      * returning an area weighted average of all geometries in this collection.
16509      *
16510      * Returns:
16511      * {<OpenLayers.Geometry.Point>} The centroid of the collection
16512      */
16513     getCentroid: function(weighted) {
16514         if (!weighted) {
16515             return this.components.length && this.components[0].getCentroid();
16516         }
16517         var len = this.components.length;
16518         if (!len) {
16519             return false;
16520         }
16521         
16522         var areas = [];
16523         var centroids = [];
16524         var areaSum = 0;
16525         var minArea = Number.MAX_VALUE;
16526         var component;
16527         for (var i=0; i<len; ++i) {
16528             component = this.components[i];
16529             var area = component.getArea();
16530             var centroid = component.getCentroid(true);
16531             if (isNaN(area) || isNaN(centroid.x) || isNaN(centroid.y)) {
16532                 continue;
16533             }
16534             areas.push(area);
16535             areaSum += area;
16536             minArea = (area < minArea && area > 0) ? area : minArea;
16537             centroids.push(centroid);
16538         }
16539         len = areas.length;
16540         if (areaSum === 0) {
16541             // all the components in this collection have 0 area
16542             // probably a collection of points -- weight all the points the same
16543             for (var i=0; i<len; ++i) {
16544                 areas[i] = 1;
16545             }
16546             areaSum = areas.length;
16547         } else {
16548             // normalize all the areas where the smallest area will get
16549             // a value of 1
16550             for (var i=0; i<len; ++i) {
16551                 areas[i] /= minArea;
16552             }
16553             areaSum /= minArea;
16554         }
16555         
16556         var xSum = 0, ySum = 0, centroid, area;
16557         for (var i=0; i<len; ++i) {
16558             centroid = centroids[i];
16559             area = areas[i];
16560             xSum += centroid.x * area;
16561             ySum += centroid.y * area;
16562         }
16563         
16564         return new OpenLayers.Geometry.Point(xSum/areaSum, ySum/areaSum);
16565     },
16566
16567     /**
16568      * APIMethod: getGeodesicLength
16569      * Calculate the approximate length of the geometry were it projected onto
16570      *     the earth.
16571      *
16572      * projection - {<OpenLayers.Projection>} The spatial reference system
16573      *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
16574      *     assumed.
16575      * 
16576      * Returns:
16577      * {Float} The appoximate geodesic length of the geometry in meters.
16578      */
16579     getGeodesicLength: function(projection) {
16580         var length = 0.0;
16581         for(var i=0, len=this.components.length; i<len; i++) {
16582             length += this.components[i].getGeodesicLength(projection);
16583         }
16584         return length;
16585     },
16586
16587     /**
16588      * APIMethod: move
16589      * Moves a geometry by the given displacement along positive x and y axes.
16590      *     This modifies the position of the geometry and clears the cached
16591      *     bounds.
16592      *
16593      * Parameters:
16594      * x - {Float} Distance to move geometry in positive x direction. 
16595      * y - {Float} Distance to move geometry in positive y direction.
16596      */
16597     move: function(x, y) {
16598         for(var i=0, len=this.components.length; i<len; i++) {
16599             this.components[i].move(x, y);
16600         }
16601     },
16602
16603     /**
16604      * APIMethod: rotate
16605      * Rotate a geometry around some origin
16606      *
16607      * Parameters:
16608      * angle - {Float} Rotation angle in degrees (measured counterclockwise
16609      *                 from the positive x-axis)
16610      * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
16611      */
16612     rotate: function(angle, origin) {
16613         for(var i=0, len=this.components.length; i<len; ++i) {
16614             this.components[i].rotate(angle, origin);
16615         }
16616     },
16617
16618     /**
16619      * APIMethod: resize
16620      * Resize a geometry relative to some origin.  Use this method to apply
16621      *     a uniform scaling to a geometry.
16622      *
16623      * Parameters:
16624      * scale - {Float} Factor by which to scale the geometry.  A scale of 2
16625      *                 doubles the size of the geometry in each dimension
16626      *                 (lines, for example, will be twice as long, and polygons
16627      *                 will have four times the area).
16628      * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
16629      * ratio - {Float} Optional x:y ratio for resizing.  Default ratio is 1.
16630      * 
16631      * Returns:
16632      * {<OpenLayers.Geometry>} - The current geometry. 
16633      */
16634     resize: function(scale, origin, ratio) {
16635         for(var i=0; i<this.components.length; ++i) {
16636             this.components[i].resize(scale, origin, ratio);
16637         }
16638         return this;
16639     },
16640
16641     /**
16642      * APIMethod: distanceTo
16643      * Calculate the closest distance between two geometries (on the x-y plane).
16644      *
16645      * Parameters:
16646      * geometry - {<OpenLayers.Geometry>} The target geometry.
16647      * options - {Object} Optional properties for configuring the distance
16648      *     calculation.
16649      *
16650      * Valid options:
16651      * details - {Boolean} Return details from the distance calculation.
16652      *     Default is false.
16653      * edge - {Boolean} Calculate the distance from this geometry to the
16654      *     nearest edge of the target geometry.  Default is true.  If true,
16655      *     calling distanceTo from a geometry that is wholly contained within
16656      *     the target will result in a non-zero distance.  If false, whenever
16657      *     geometries intersect, calling distanceTo will return 0.  If false,
16658      *     details cannot be returned.
16659      *
16660      * Returns:
16661      * {Number | Object} The distance between this geometry and the target.
16662      *     If details is true, the return will be an object with distance,
16663      *     x0, y0, x1, and y1 properties.  The x0 and y0 properties represent
16664      *     the coordinates of the closest point on this geometry. The x1 and y1
16665      *     properties represent the coordinates of the closest point on the
16666      *     target geometry.
16667      */
16668     distanceTo: function(geometry, options) {
16669         var edge = !(options && options.edge === false);
16670         var details = edge && options && options.details;
16671         var result, best, distance;
16672         var min = Number.POSITIVE_INFINITY;
16673         for(var i=0, len=this.components.length; i<len; ++i) {
16674             result = this.components[i].distanceTo(geometry, options);
16675             distance = details ? result.distance : result;
16676             if(distance < min) {
16677                 min = distance;
16678                 best = result;
16679                 if(min == 0) {
16680                     break;
16681                 }
16682             }
16683         }
16684         return best;
16685     },
16686
16687     /** 
16688      * APIMethod: equals
16689      * Determine whether another geometry is equivalent to this one.  Geometries
16690      *     are considered equivalent if all components have the same coordinates.
16691      * 
16692      * Parameters:
16693      * geometry - {<OpenLayers.Geometry>} The geometry to test. 
16694      *
16695      * Returns:
16696      * {Boolean} The supplied geometry is equivalent to this geometry.
16697      */
16698     equals: function(geometry) {
16699         var equivalent = true;
16700         if(!geometry || !geometry.CLASS_NAME ||
16701            (this.CLASS_NAME != geometry.CLASS_NAME)) {
16702             equivalent = false;
16703         } else if(!(OpenLayers.Util.isArray(geometry.components)) ||
16704                   (geometry.components.length != this.components.length)) {
16705             equivalent = false;
16706         } else {
16707             for(var i=0, len=this.components.length; i<len; ++i) {
16708                 if(!this.components[i].equals(geometry.components[i])) {
16709                     equivalent = false;
16710                     break;
16711                 }
16712             }
16713         }
16714         return equivalent;
16715     },
16716
16717     /**
16718      * APIMethod: transform
16719      * Reproject the components geometry from source to dest.
16720      * 
16721      * Parameters:
16722      * source - {<OpenLayers.Projection>} 
16723      * dest - {<OpenLayers.Projection>}
16724      * 
16725      * Returns:
16726      * {<OpenLayers.Geometry>} 
16727      */
16728     transform: function(source, dest) {
16729         if (source && dest) {
16730             for (var i=0, len=this.components.length; i<len; i++) {  
16731                 var component = this.components[i];
16732                 component.transform(source, dest);
16733             }
16734             this.bounds = null;
16735         }
16736         return this;
16737     },
16738
16739     /**
16740      * APIMethod: intersects
16741      * Determine if the input geometry intersects this one.
16742      *
16743      * Parameters:
16744      * geometry - {<OpenLayers.Geometry>} Any type of geometry.
16745      *
16746      * Returns:
16747      * {Boolean} The input geometry intersects this one.
16748      */
16749     intersects: function(geometry) {
16750         var intersect = false;
16751         for(var i=0, len=this.components.length; i<len; ++ i) {
16752             intersect = geometry.intersects(this.components[i]);
16753             if(intersect) {
16754                 break;
16755             }
16756         }
16757         return intersect;
16758     },
16759
16760     /**
16761      * APIMethod: getVertices
16762      * Return a list of all points in this geometry.
16763      *
16764      * Parameters:
16765      * nodes - {Boolean} For lines, only return vertices that are
16766      *     endpoints.  If false, for lines, only vertices that are not
16767      *     endpoints will be returned.  If not provided, all vertices will
16768      *     be returned.
16769      *
16770      * Returns:
16771      * {Array} A list of all vertices in the geometry.
16772      */
16773     getVertices: function(nodes) {
16774         var vertices = [];
16775         for(var i=0, len=this.components.length; i<len; ++i) {
16776             Array.prototype.push.apply(
16777                 vertices, this.components[i].getVertices(nodes)
16778             );
16779         }
16780         return vertices;
16781     },
16782
16783
16784     CLASS_NAME: "OpenLayers.Geometry.Collection"
16785 });
16786 /* ======================================================================
16787     OpenLayers/Geometry/Point.js
16788    ====================================================================== */
16789
16790 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
16791  * full list of contributors). Published under the 2-clause BSD license.
16792  * See license.txt in the OpenLayers distribution or repository for the
16793  * full text of the license. */
16794
16795 /**
16796  * @requires OpenLayers/Geometry.js
16797  */
16798
16799 /**
16800  * Class: OpenLayers.Geometry.Point
16801  * Point geometry class. 
16802  * 
16803  * Inherits from:
16804  *  - <OpenLayers.Geometry> 
16805  */
16806 OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {
16807
16808     /** 
16809      * APIProperty: x 
16810      * {float} 
16811      */
16812     x: null,
16813
16814     /** 
16815      * APIProperty: y 
16816      * {float} 
16817      */
16818     y: null,
16819
16820     /**
16821      * Constructor: OpenLayers.Geometry.Point
16822      * Construct a point geometry.
16823      *
16824      * Parameters:
16825      * x - {float} 
16826      * y - {float}
16827      * 
16828      */
16829     initialize: function(x, y) {
16830         OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
16831         
16832         this.x = parseFloat(x);
16833         this.y = parseFloat(y);
16834     },
16835
16836     /**
16837      * APIMethod: clone
16838      * 
16839      * Returns:
16840      * {<OpenLayers.Geometry.Point>} An exact clone of this OpenLayers.Geometry.Point
16841      */
16842     clone: function(obj) {
16843         if (obj == null) {
16844             obj = new OpenLayers.Geometry.Point(this.x, this.y);
16845         }
16846
16847         // catch any randomly tagged-on properties
16848         OpenLayers.Util.applyDefaults(obj, this);
16849
16850         return obj;
16851     },
16852
16853     /** 
16854      * Method: calculateBounds
16855      * Create a new Bounds based on the lon/lat
16856      */
16857     calculateBounds: function () {
16858         this.bounds = new OpenLayers.Bounds(this.x, this.y,
16859                                             this.x, this.y);
16860     },
16861
16862     /**
16863      * APIMethod: distanceTo
16864      * Calculate the closest distance between two geometries (on the x-y plane).
16865      *
16866      * Parameters:
16867      * geometry - {<OpenLayers.Geometry>} The target geometry.
16868      * options - {Object} Optional properties for configuring the distance
16869      *     calculation.
16870      *
16871      * Valid options:
16872      * details - {Boolean} Return details from the distance calculation.
16873      *     Default is false.
16874      * edge - {Boolean} Calculate the distance from this geometry to the
16875      *     nearest edge of the target geometry.  Default is true.  If true,
16876      *     calling distanceTo from a geometry that is wholly contained within
16877      *     the target will result in a non-zero distance.  If false, whenever
16878      *     geometries intersect, calling distanceTo will return 0.  If false,
16879      *     details cannot be returned.
16880      *
16881      * Returns:
16882      * {Number | Object} The distance between this geometry and the target.
16883      *     If details is true, the return will be an object with distance,
16884      *     x0, y0, x1, and x2 properties.  The x0 and y0 properties represent
16885      *     the coordinates of the closest point on this geometry. The x1 and y1
16886      *     properties represent the coordinates of the closest point on the
16887      *     target geometry.
16888      */
16889     distanceTo: function(geometry, options) {
16890         var edge = !(options && options.edge === false);
16891         var details = edge && options && options.details;
16892         var distance, x0, y0, x1, y1, result;
16893         if(geometry instanceof OpenLayers.Geometry.Point) {
16894             x0 = this.x;
16895             y0 = this.y;
16896             x1 = geometry.x;
16897             y1 = geometry.y;
16898             distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2));
16899             result = !details ?
16900                 distance : {x0: x0, y0: y0, x1: x1, y1: y1, distance: distance};
16901         } else {
16902             result = geometry.distanceTo(this, options);
16903             if(details) {
16904                 // switch coord order since this geom is target
16905                 result = {
16906                     x0: result.x1, y0: result.y1,
16907                     x1: result.x0, y1: result.y0,
16908                     distance: result.distance
16909                 };
16910             }
16911         }
16912         return result;
16913     },
16914     
16915     /** 
16916      * APIMethod: equals
16917      * Determine whether another geometry is equivalent to this one.  Geometries
16918      *     are considered equivalent if all components have the same coordinates.
16919      * 
16920      * Parameters:
16921      * geom - {<OpenLayers.Geometry.Point>} The geometry to test. 
16922      *
16923      * Returns:
16924      * {Boolean} The supplied geometry is equivalent to this geometry.
16925      */
16926     equals: function(geom) {
16927         var equals = false;
16928         if (geom != null) {
16929             equals = ((this.x == geom.x && this.y == geom.y) ||
16930                       (isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)));
16931         }
16932         return equals;
16933     },
16934     
16935     /**
16936      * Method: toShortString
16937      *
16938      * Returns:
16939      * {String} Shortened String representation of Point object. 
16940      *         (ex. <i>"5, 42"</i>)
16941      */
16942     toShortString: function() {
16943         return (this.x + ", " + this.y);
16944     },
16945     
16946     /**
16947      * APIMethod: move
16948      * Moves a geometry by the given displacement along positive x and y axes.
16949      *     This modifies the position of the geometry and clears the cached
16950      *     bounds.
16951      *
16952      * Parameters:
16953      * x - {Float} Distance to move geometry in positive x direction. 
16954      * y - {Float} Distance to move geometry in positive y direction.
16955      */
16956     move: function(x, y) {
16957         this.x = this.x + x;
16958         this.y = this.y + y;
16959         this.clearBounds();
16960     },
16961
16962     /**
16963      * APIMethod: rotate
16964      * Rotate a point around another.
16965      *
16966      * Parameters:
16967      * angle - {Float} Rotation angle in degrees (measured counterclockwise
16968      *                 from the positive x-axis)
16969      * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
16970      */
16971     rotate: function(angle, origin) {
16972         angle *= Math.PI / 180;
16973         var radius = this.distanceTo(origin);
16974         var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);
16975         this.x = origin.x + (radius * Math.cos(theta));
16976         this.y = origin.y + (radius * Math.sin(theta));
16977         this.clearBounds();
16978     },
16979     
16980     /**
16981      * APIMethod: getCentroid
16982      *
16983      * Returns:
16984      * {<OpenLayers.Geometry.Point>} The centroid of the collection
16985      */
16986     getCentroid: function() {
16987         return new OpenLayers.Geometry.Point(this.x, this.y);
16988     },
16989
16990     /**
16991      * APIMethod: resize
16992      * Resize a point relative to some origin.  For points, this has the effect
16993      *     of scaling a vector (from the origin to the point).  This method is
16994      *     more useful on geometry collection subclasses.
16995      *
16996      * Parameters:
16997      * scale - {Float} Ratio of the new distance from the origin to the old
16998      *                 distance from the origin.  A scale of 2 doubles the
16999      *                 distance between the point and origin.
17000      * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
17001      * ratio - {Float} Optional x:y ratio for resizing.  Default ratio is 1.
17002      * 
17003      * Returns:
17004      * {<OpenLayers.Geometry>} - The current geometry. 
17005      */
17006     resize: function(scale, origin, ratio) {
17007         ratio = (ratio == undefined) ? 1 : ratio;
17008         this.x = origin.x + (scale * ratio * (this.x - origin.x));
17009         this.y = origin.y + (scale * (this.y - origin.y));
17010         this.clearBounds();
17011         return this;
17012     },
17013     
17014     /**
17015      * APIMethod: intersects
17016      * Determine if the input geometry intersects this one.
17017      *
17018      * Parameters:
17019      * geometry - {<OpenLayers.Geometry>} Any type of geometry.
17020      *
17021      * Returns:
17022      * {Boolean} The input geometry intersects this one.
17023      */
17024     intersects: function(geometry) {
17025         var intersect = false;
17026         if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
17027             intersect = this.equals(geometry);
17028         } else {
17029             intersect = geometry.intersects(this);
17030         }
17031         return intersect;
17032     },
17033     
17034     /**
17035      * APIMethod: transform
17036      * Translate the x,y properties of the point from source to dest.
17037      * 
17038      * Parameters:
17039      * source - {<OpenLayers.Projection>} 
17040      * dest - {<OpenLayers.Projection>}
17041      * 
17042      * Returns:
17043      * {<OpenLayers.Geometry>} 
17044      */
17045     transform: function(source, dest) {
17046         if ((source && dest)) {
17047             OpenLayers.Projection.transform(
17048                 this, source, dest); 
17049             this.bounds = null;
17050         }       
17051         return this;
17052     },
17053
17054     /**
17055      * APIMethod: getVertices
17056      * Return a list of all points in this geometry.
17057      *
17058      * Parameters:
17059      * nodes - {Boolean} For lines, only return vertices that are
17060      *     endpoints.  If false, for lines, only vertices that are not
17061      *     endpoints will be returned.  If not provided, all vertices will
17062      *     be returned.
17063      *
17064      * Returns:
17065      * {Array} A list of all vertices in the geometry.
17066      */
17067     getVertices: function(nodes) {
17068         return [this];
17069     },
17070
17071     CLASS_NAME: "OpenLayers.Geometry.Point"
17072 });
17073 /* ======================================================================
17074     OpenLayers/Geometry/MultiPoint.js
17075    ====================================================================== */
17076
17077 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
17078  * full list of contributors). Published under the 2-clause BSD license.
17079  * See license.txt in the OpenLayers distribution or repository for the
17080  * full text of the license. */
17081
17082 /**
17083  * @requires OpenLayers/Geometry/Collection.js
17084  * @requires OpenLayers/Geometry/Point.js
17085  */
17086
17087 /**
17088  * Class: OpenLayers.Geometry.MultiPoint
17089  * MultiPoint is a collection of Points.  Create a new instance with the
17090  * <OpenLayers.Geometry.MultiPoint> constructor.
17091  *
17092  * Inherits from:
17093  *  - <OpenLayers.Geometry.Collection>
17094  *  - <OpenLayers.Geometry>
17095  */
17096 OpenLayers.Geometry.MultiPoint = OpenLayers.Class(
17097   OpenLayers.Geometry.Collection, {
17098
17099     /**
17100      * Property: componentTypes
17101      * {Array(String)} An array of class names representing the types of
17102      * components that the collection can include.  A null value means the
17103      * component types are not restricted.
17104      */
17105     componentTypes: ["OpenLayers.Geometry.Point"],
17106
17107     /**
17108      * Constructor: OpenLayers.Geometry.MultiPoint
17109      * Create a new MultiPoint Geometry
17110      *
17111      * Parameters:
17112      * components - {Array(<OpenLayers.Geometry.Point>)} 
17113      *
17114      * Returns:
17115      * {<OpenLayers.Geometry.MultiPoint>}
17116      */
17117
17118     /**
17119      * APIMethod: addPoint
17120      * Wrapper for <OpenLayers.Geometry.Collection.addComponent>
17121      *
17122      * Parameters:
17123      * point - {<OpenLayers.Geometry.Point>} Point to be added
17124      * index - {Integer} Optional index
17125      */
17126     addPoint: function(point, index) {
17127         this.addComponent(point, index);
17128     },
17129     
17130     /**
17131      * APIMethod: removePoint
17132      * Wrapper for <OpenLayers.Geometry.Collection.removeComponent>
17133      *
17134      * Parameters:
17135      * point - {<OpenLayers.Geometry.Point>} Point to be removed
17136      */
17137     removePoint: function(point){
17138         this.removeComponent(point);
17139     },
17140
17141     CLASS_NAME: "OpenLayers.Geometry.MultiPoint"
17142 });
17143 /* ======================================================================
17144     OpenLayers/Geometry/Curve.js
17145    ====================================================================== */
17146
17147 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
17148  * full list of contributors). Published under the 2-clause BSD license.
17149  * See license.txt in the OpenLayers distribution or repository for the
17150  * full text of the license. */
17151
17152 /**
17153  * @requires OpenLayers/Geometry/MultiPoint.js
17154  */
17155
17156 /**
17157  * Class: OpenLayers.Geometry.Curve
17158  * A Curve is a MultiPoint, whose points are assumed to be connected. To 
17159  * this end, we provide a "getLength()" function, which iterates through 
17160  * the points, summing the distances between them. 
17161  * 
17162  * Inherits: 
17163  *  - <OpenLayers.Geometry.MultiPoint>
17164  */
17165 OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, {
17166
17167     /**
17168      * Property: componentTypes
17169      * {Array(String)} An array of class names representing the types of 
17170      *                 components that the collection can include.  A null 
17171      *                 value means the component types are not restricted.
17172      */
17173     componentTypes: ["OpenLayers.Geometry.Point"],
17174
17175     /**
17176      * Constructor: OpenLayers.Geometry.Curve
17177      * 
17178      * Parameters:
17179      * point - {Array(<OpenLayers.Geometry.Point>)}
17180      */
17181     
17182     /**
17183      * APIMethod: getLength
17184      * 
17185      * Returns:
17186      * {Float} The length of the curve
17187      */
17188     getLength: function() {
17189         var length = 0.0;
17190         if ( this.components && (this.components.length > 1)) {
17191             for(var i=1, len=this.components.length; i<len; i++) {
17192                 length += this.components[i-1].distanceTo(this.components[i]);
17193             }
17194         }
17195         return length;
17196     },
17197
17198     /**
17199      * APIMethod: getGeodesicLength
17200      * Calculate the approximate length of the geometry were it projected onto
17201      *     the earth.
17202      *
17203      * projection - {<OpenLayers.Projection>} The spatial reference system
17204      *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
17205      *     assumed.
17206      * 
17207      * Returns:
17208      * {Float} The appoximate geodesic length of the geometry in meters.
17209      */
17210     getGeodesicLength: function(projection) {
17211         var geom = this;  // so we can work with a clone if needed
17212         if(projection) {
17213             var gg = new OpenLayers.Projection("EPSG:4326");
17214             if(!gg.equals(projection)) {
17215                 geom = this.clone().transform(projection, gg);
17216             }
17217         }
17218         var length = 0.0;
17219         if(geom.components && (geom.components.length > 1)) {
17220             var p1, p2;
17221             for(var i=1, len=geom.components.length; i<len; i++) {
17222                 p1 = geom.components[i-1];
17223                 p2 = geom.components[i];
17224                 // this returns km and requires lon/lat properties
17225                 length += OpenLayers.Util.distVincenty(
17226                     {lon: p1.x, lat: p1.y}, {lon: p2.x, lat: p2.y}
17227                 );
17228             }
17229         }
17230         // convert to m
17231         return length * 1000;
17232     },
17233
17234     CLASS_NAME: "OpenLayers.Geometry.Curve"
17235 });
17236 /* ======================================================================
17237     OpenLayers/Geometry/LineString.js
17238    ====================================================================== */
17239
17240 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
17241  * full list of contributors). Published under the 2-clause BSD license.
17242  * See license.txt in the OpenLayers distribution or repository for the
17243  * full text of the license. */
17244
17245 /**
17246  * @requires OpenLayers/Geometry/Curve.js
17247  */
17248
17249 /**
17250  * Class: OpenLayers.Geometry.LineString
17251  * A LineString is a Curve which, once two points have been added to it, can 
17252  * never be less than two points long.
17253  * 
17254  * Inherits from:
17255  *  - <OpenLayers.Geometry.Curve>
17256  */
17257 OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, {
17258
17259     /**
17260      * Constructor: OpenLayers.Geometry.LineString
17261      * Create a new LineString geometry
17262      *
17263      * Parameters:
17264      * points - {Array(<OpenLayers.Geometry.Point>)} An array of points used to
17265      *          generate the linestring
17266      *
17267      */
17268
17269     /**
17270      * APIMethod: removeComponent
17271      * Only allows removal of a point if there are three or more points in 
17272      * the linestring. (otherwise the result would be just a single point)
17273      *
17274      * Parameters: 
17275      * point - {<OpenLayers.Geometry.Point>} The point to be removed
17276      *
17277      * Returns: 
17278      * {Boolean} The component was removed.
17279      */
17280     removeComponent: function(point) {
17281         var removed = this.components && (this.components.length > 2);
17282         if (removed) {
17283             OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, 
17284                                                                   arguments);
17285         }
17286         return removed;
17287     },
17288     
17289     /**
17290      * APIMethod: intersects
17291      * Test for instersection between two geometries.  This is a cheapo
17292      *     implementation of the Bently-Ottmann algorigithm.  It doesn't
17293      *     really keep track of a sweep line data structure.  It is closer
17294      *     to the brute force method, except that segments are sorted and
17295      *     potential intersections are only calculated when bounding boxes
17296      *     intersect.
17297      *
17298      * Parameters:
17299      * geometry - {<OpenLayers.Geometry>}
17300      *
17301      * Returns:
17302      * {Boolean} The input geometry intersects this geometry.
17303      */
17304     intersects: function(geometry) {
17305         var intersect = false;
17306         var type = geometry.CLASS_NAME;
17307         if(type == "OpenLayers.Geometry.LineString" ||
17308            type == "OpenLayers.Geometry.LinearRing" ||
17309            type == "OpenLayers.Geometry.Point") {
17310             var segs1 = this.getSortedSegments();
17311             var segs2;
17312             if(type == "OpenLayers.Geometry.Point") {
17313                 segs2 = [{
17314                     x1: geometry.x, y1: geometry.y,
17315                     x2: geometry.x, y2: geometry.y
17316                 }];
17317             } else {
17318                 segs2 = geometry.getSortedSegments();
17319             }
17320             var seg1, seg1x1, seg1x2, seg1y1, seg1y2,
17321                 seg2, seg2y1, seg2y2;
17322             // sweep right
17323             outer: for(var i=0, len=segs1.length; i<len; ++i) {
17324                 seg1 = segs1[i];
17325                 seg1x1 = seg1.x1;
17326                 seg1x2 = seg1.x2;
17327                 seg1y1 = seg1.y1;
17328                 seg1y2 = seg1.y2;
17329                 inner: for(var j=0, jlen=segs2.length; j<jlen; ++j) {
17330                     seg2 = segs2[j];
17331                     if(seg2.x1 > seg1x2) {
17332                         // seg1 still left of seg2
17333                         break;
17334                     }
17335                     if(seg2.x2 < seg1x1) {
17336                         // seg2 still left of seg1
17337                         continue;
17338                     }
17339                     seg2y1 = seg2.y1;
17340                     seg2y2 = seg2.y2;
17341                     if(Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) {
17342                         // seg2 above seg1
17343                         continue;
17344                     }
17345                     if(Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) {
17346                         // seg2 below seg1
17347                         continue;
17348                     }
17349                     if(OpenLayers.Geometry.segmentsIntersect(seg1, seg2)) {
17350                         intersect = true;
17351                         break outer;
17352                     }
17353                 }
17354             }
17355         } else {
17356             intersect = geometry.intersects(this);
17357         }
17358         return intersect;
17359     },
17360     
17361     /**
17362      * Method: getSortedSegments
17363      *
17364      * Returns:
17365      * {Array} An array of segment objects.  Segment objects have properties
17366      *     x1, y1, x2, and y2.  The start point is represented by x1 and y1.
17367      *     The end point is represented by x2 and y2.  Start and end are
17368      *     ordered so that x1 < x2.
17369      */
17370     getSortedSegments: function() {
17371         var numSeg = this.components.length - 1;
17372         var segments = new Array(numSeg), point1, point2;
17373         for(var i=0; i<numSeg; ++i) {
17374             point1 = this.components[i];
17375             point2 = this.components[i + 1];
17376             if(point1.x < point2.x) {
17377                 segments[i] = {
17378                     x1: point1.x,
17379                     y1: point1.y,
17380                     x2: point2.x,
17381                     y2: point2.y
17382                 };
17383             } else {
17384                 segments[i] = {
17385                     x1: point2.x,
17386                     y1: point2.y,
17387                     x2: point1.x,
17388                     y2: point1.y
17389                 };
17390             }
17391         }
17392         // more efficient to define this somewhere static
17393         function byX1(seg1, seg2) {
17394             return seg1.x1 - seg2.x1;
17395         }
17396         return segments.sort(byX1);
17397     },
17398     
17399     /**
17400      * Method: splitWithSegment
17401      * Split this geometry with the given segment.
17402      *
17403      * Parameters:
17404      * seg - {Object} An object with x1, y1, x2, and y2 properties referencing
17405      *     segment endpoint coordinates.
17406      * options - {Object} Properties of this object will be used to determine
17407      *     how the split is conducted.
17408      *
17409      * Valid options:
17410      * edge - {Boolean} Allow splitting when only edges intersect.  Default is
17411      *     true.  If false, a vertex on the source segment must be within the
17412      *     tolerance distance of the intersection to be considered a split.
17413      * tolerance - {Number} If a non-null value is provided, intersections
17414      *     within the tolerance distance of one of the source segment's
17415      *     endpoints will be assumed to occur at the endpoint.
17416      *
17417      * Returns:
17418      * {Object} An object with *lines* and *points* properties.  If the given
17419      *     segment intersects this linestring, the lines array will reference
17420      *     geometries that result from the split.  The points array will contain
17421      *     all intersection points.  Intersection points are sorted along the
17422      *     segment (in order from x1,y1 to x2,y2).
17423      */
17424     splitWithSegment: function(seg, options) {
17425         var edge = !(options && options.edge === false);
17426         var tolerance = options && options.tolerance;
17427         var lines = [];
17428         var verts = this.getVertices();
17429         var points = [];
17430         var intersections = [];
17431         var split = false;
17432         var vert1, vert2, point;
17433         var node, vertex, target;
17434         var interOptions = {point: true, tolerance: tolerance};
17435         var result = null;
17436         for(var i=0, stop=verts.length-2; i<=stop; ++i) {
17437             vert1 = verts[i];
17438             points.push(vert1.clone());
17439             vert2 = verts[i+1];
17440             target = {x1: vert1.x, y1: vert1.y, x2: vert2.x, y2: vert2.y};
17441             point = OpenLayers.Geometry.segmentsIntersect(
17442                 seg, target, interOptions
17443             );
17444             if(point instanceof OpenLayers.Geometry.Point) {
17445                 if((point.x === seg.x1 && point.y === seg.y1) ||
17446                    (point.x === seg.x2 && point.y === seg.y2) ||
17447                    point.equals(vert1) || point.equals(vert2)) {
17448                     vertex = true;
17449                 } else {
17450                     vertex = false;
17451                 }
17452                 if(vertex || edge) {
17453                     // push intersections different than the previous
17454                     if(!point.equals(intersections[intersections.length-1])) {
17455                         intersections.push(point.clone());
17456                     }
17457                     if(i === 0) {
17458                         if(point.equals(vert1)) {
17459                             continue;
17460                         }
17461                     }
17462                     if(point.equals(vert2)) {
17463                         continue;
17464                     }
17465                     split = true;
17466                     if(!point.equals(vert1)) {
17467                         points.push(point);
17468                     }
17469                     lines.push(new OpenLayers.Geometry.LineString(points));
17470                     points = [point.clone()];
17471                 }
17472             }
17473         }
17474         if(split) {
17475             points.push(vert2.clone());
17476             lines.push(new OpenLayers.Geometry.LineString(points));
17477         }
17478         if(intersections.length > 0) {
17479             // sort intersections along segment
17480             var xDir = seg.x1 < seg.x2 ? 1 : -1;
17481             var yDir = seg.y1 < seg.y2 ? 1 : -1;
17482             result = {
17483                 lines: lines,
17484                 points: intersections.sort(function(p1, p2) {
17485                     return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y);
17486                 })
17487             };
17488         }
17489         return result;
17490     },
17491
17492     /**
17493      * Method: split
17494      * Use this geometry (the source) to attempt to split a target geometry.
17495      * 
17496      * Parameters:
17497      * target - {<OpenLayers.Geometry>} The target geometry.
17498      * options - {Object} Properties of this object will be used to determine
17499      *     how the split is conducted.
17500      *
17501      * Valid options:
17502      * mutual - {Boolean} Split the source geometry in addition to the target
17503      *     geometry.  Default is false.
17504      * edge - {Boolean} Allow splitting when only edges intersect.  Default is
17505      *     true.  If false, a vertex on the source must be within the tolerance
17506      *     distance of the intersection to be considered a split.
17507      * tolerance - {Number} If a non-null value is provided, intersections
17508      *     within the tolerance distance of an existing vertex on the source
17509      *     will be assumed to occur at the vertex.
17510      * 
17511      * Returns:
17512      * {Array} A list of geometries (of this same type as the target) that
17513      *     result from splitting the target with the source geometry.  The
17514      *     source and target geometry will remain unmodified.  If no split
17515      *     results, null will be returned.  If mutual is true and a split
17516      *     results, return will be an array of two arrays - the first will be
17517      *     all geometries that result from splitting the source geometry and
17518      *     the second will be all geometries that result from splitting the
17519      *     target geometry.
17520      */
17521     split: function(target, options) {
17522         var results = null;
17523         var mutual = options && options.mutual;
17524         var sourceSplit, targetSplit, sourceParts, targetParts;
17525         if(target instanceof OpenLayers.Geometry.LineString) {
17526             var verts = this.getVertices();
17527             var vert1, vert2, seg, splits, lines, point;
17528             var points = [];
17529             sourceParts = [];
17530             for(var i=0, stop=verts.length-2; i<=stop; ++i) {
17531                 vert1 = verts[i];
17532                 vert2 = verts[i+1];
17533                 seg = {
17534                     x1: vert1.x, y1: vert1.y,
17535                     x2: vert2.x, y2: vert2.y
17536                 };
17537                 targetParts = targetParts || [target];
17538                 if(mutual) {
17539                     points.push(vert1.clone());
17540                 }
17541                 for(var j=0; j<targetParts.length; ++j) {
17542                     splits = targetParts[j].splitWithSegment(seg, options);
17543                     if(splits) {
17544                         // splice in new features
17545                         lines = splits.lines;
17546                         if(lines.length > 0) {
17547                             lines.unshift(j, 1);
17548                             Array.prototype.splice.apply(targetParts, lines);
17549                             j += lines.length - 2;
17550                         }
17551                         if(mutual) {
17552                             for(var k=0, len=splits.points.length; k<len; ++k) {
17553                                 point = splits.points[k];
17554                                 if(!point.equals(vert1)) {
17555                                     points.push(point);
17556                                     sourceParts.push(new OpenLayers.Geometry.LineString(points));
17557                                     if(point.equals(vert2)) {
17558                                         points = [];
17559                                     } else {
17560                                         points = [point.clone()];
17561                                     }
17562                                 }
17563                             }
17564                         }
17565                     }
17566                 }
17567             }
17568             if(mutual && sourceParts.length > 0 && points.length > 0) {
17569                 points.push(vert2.clone());
17570                 sourceParts.push(new OpenLayers.Geometry.LineString(points));
17571             }
17572         } else {
17573             results = target.splitWith(this, options);
17574         }
17575         if(targetParts && targetParts.length > 1) {
17576             targetSplit = true;
17577         } else {
17578             targetParts = [];
17579         }
17580         if(sourceParts && sourceParts.length > 1) {
17581             sourceSplit = true;
17582         } else {
17583             sourceParts = [];
17584         }
17585         if(targetSplit || sourceSplit) {
17586             if(mutual) {
17587                 results = [sourceParts, targetParts];
17588             } else {
17589                 results = targetParts;
17590             }
17591         }
17592         return results;
17593     },
17594
17595     /**
17596      * Method: splitWith
17597      * Split this geometry (the target) with the given geometry (the source).
17598      *
17599      * Parameters:
17600      * geometry - {<OpenLayers.Geometry>} A geometry used to split this
17601      *     geometry (the source).
17602      * options - {Object} Properties of this object will be used to determine
17603      *     how the split is conducted.
17604      *
17605      * Valid options:
17606      * mutual - {Boolean} Split the source geometry in addition to the target
17607      *     geometry.  Default is false.
17608      * edge - {Boolean} Allow splitting when only edges intersect.  Default is
17609      *     true.  If false, a vertex on the source must be within the tolerance
17610      *     distance of the intersection to be considered a split.
17611      * tolerance - {Number} If a non-null value is provided, intersections
17612      *     within the tolerance distance of an existing vertex on the source
17613      *     will be assumed to occur at the vertex.
17614      * 
17615      * Returns:
17616      * {Array} A list of geometries (of this same type as the target) that
17617      *     result from splitting the target with the source geometry.  The
17618      *     source and target geometry will remain unmodified.  If no split
17619      *     results, null will be returned.  If mutual is true and a split
17620      *     results, return will be an array of two arrays - the first will be
17621      *     all geometries that result from splitting the source geometry and
17622      *     the second will be all geometries that result from splitting the
17623      *     target geometry.
17624      */
17625     splitWith: function(geometry, options) {
17626         return geometry.split(this, options);
17627
17628     },
17629
17630     /**
17631      * APIMethod: getVertices
17632      * Return a list of all points in this geometry.
17633      *
17634      * Parameters:
17635      * nodes - {Boolean} For lines, only return vertices that are
17636      *     endpoints.  If false, for lines, only vertices that are not
17637      *     endpoints will be returned.  If not provided, all vertices will
17638      *     be returned.
17639      *
17640      * Returns:
17641      * {Array} A list of all vertices in the geometry.
17642      */
17643     getVertices: function(nodes) {
17644         var vertices;
17645         if(nodes === true) {
17646             vertices = [
17647                 this.components[0],
17648                 this.components[this.components.length-1]
17649             ];
17650         } else if (nodes === false) {
17651             vertices = this.components.slice(1, this.components.length-1);
17652         } else {
17653             vertices = this.components.slice();
17654         }
17655         return vertices;
17656     },
17657
17658     /**
17659      * APIMethod: distanceTo
17660      * Calculate the closest distance between two geometries (on the x-y plane).
17661      *
17662      * Parameters:
17663      * geometry - {<OpenLayers.Geometry>} The target geometry.
17664      * options - {Object} Optional properties for configuring the distance
17665      *     calculation.
17666      *
17667      * Valid options:
17668      * details - {Boolean} Return details from the distance calculation.
17669      *     Default is false.
17670      * edge - {Boolean} Calculate the distance from this geometry to the
17671      *     nearest edge of the target geometry.  Default is true.  If true,
17672      *     calling distanceTo from a geometry that is wholly contained within
17673      *     the target will result in a non-zero distance.  If false, whenever
17674      *     geometries intersect, calling distanceTo will return 0.  If false,
17675      *     details cannot be returned.
17676      *
17677      * Returns:
17678      * {Number | Object} The distance between this geometry and the target.
17679      *     If details is true, the return will be an object with distance,
17680      *     x0, y0, x1, and x2 properties.  The x0 and y0 properties represent
17681      *     the coordinates of the closest point on this geometry. The x1 and y1
17682      *     properties represent the coordinates of the closest point on the
17683      *     target geometry.
17684      */
17685     distanceTo: function(geometry, options) {
17686         var edge = !(options && options.edge === false);
17687         var details = edge && options && options.details;
17688         var result, best = {};
17689         var min = Number.POSITIVE_INFINITY;
17690         if(geometry instanceof OpenLayers.Geometry.Point) {
17691             var segs = this.getSortedSegments();
17692             var x = geometry.x;
17693             var y = geometry.y;
17694             var seg;
17695             for(var i=0, len=segs.length; i<len; ++i) {
17696                 seg = segs[i];
17697                 result = OpenLayers.Geometry.distanceToSegment(geometry, seg);
17698                 if(result.distance < min) {
17699                     min = result.distance;
17700                     best = result;
17701                     if(min === 0) {
17702                         break;
17703                     }
17704                 } else {
17705                     // if distance increases and we cross y0 to the right of x0, no need to keep looking.
17706                     if(seg.x2 > x && ((y > seg.y1 && y < seg.y2) || (y < seg.y1 && y > seg.y2))) {
17707                         break;
17708                     }
17709                 }
17710             }
17711             if(details) {
17712                 best = {
17713                     distance: best.distance,
17714                     x0: best.x, y0: best.y,
17715                     x1: x, y1: y
17716                 };
17717             } else {
17718                 best = best.distance;
17719             }
17720         } else if(geometry instanceof OpenLayers.Geometry.LineString) { 
17721             var segs0 = this.getSortedSegments();
17722             var segs1 = geometry.getSortedSegments();
17723             var seg0, seg1, intersection, x0, y0;
17724             var len1 = segs1.length;
17725             var interOptions = {point: true};
17726             outer: for(var i=0, len=segs0.length; i<len; ++i) {
17727                 seg0 = segs0[i];
17728                 x0 = seg0.x1;
17729                 y0 = seg0.y1;
17730                 for(var j=0; j<len1; ++j) {
17731                     seg1 = segs1[j];
17732                     intersection = OpenLayers.Geometry.segmentsIntersect(seg0, seg1, interOptions);
17733                     if(intersection) {
17734                         min = 0;
17735                         best = {
17736                             distance: 0,
17737                             x0: intersection.x, y0: intersection.y,
17738                             x1: intersection.x, y1: intersection.y
17739                         };
17740                         break outer;
17741                     } else {
17742                         result = OpenLayers.Geometry.distanceToSegment({x: x0, y: y0}, seg1);
17743                         if(result.distance < min) {
17744                             min = result.distance;
17745                             best = {
17746                                 distance: min,
17747                                 x0: x0, y0: y0,
17748                                 x1: result.x, y1: result.y
17749                             };
17750                         }
17751                     }
17752                 }
17753             }
17754             if(!details) {
17755                 best = best.distance;
17756             }
17757             if(min !== 0) {
17758                 // check the final vertex in this line's sorted segments
17759                 if(seg0) {
17760                     result = geometry.distanceTo(
17761                         new OpenLayers.Geometry.Point(seg0.x2, seg0.y2),
17762                         options
17763                     );
17764                     var dist = details ? result.distance : result;
17765                     if(dist < min) {
17766                         if(details) {
17767                             best = {
17768                                 distance: min,
17769                                 x0: result.x1, y0: result.y1,
17770                                 x1: result.x0, y1: result.y0
17771                             };
17772                         } else {
17773                             best = dist;
17774                         }
17775                     }
17776                 }
17777             }
17778         } else {
17779             best = geometry.distanceTo(this, options);
17780             // swap since target comes from this line
17781             if(details) {
17782                 best = {
17783                     distance: best.distance,
17784                     x0: best.x1, y0: best.y1,
17785                     x1: best.x0, y1: best.y0
17786                 };
17787             }
17788         }
17789         return best;
17790     },
17791     
17792     /**
17793      * APIMethod: simplify
17794      * This function will return a simplified LineString.
17795      * Simplification is based on the Douglas-Peucker algorithm.
17796      *
17797      *
17798      * Parameters:
17799      * tolerance - {number} threshhold for simplification in map units
17800      *
17801      * Returns:
17802      * {OpenLayers.Geometry.LineString} the simplified LineString
17803      */
17804     simplify: function(tolerance){
17805         if (this && this !== null) {
17806             var points = this.getVertices();
17807             if (points.length < 3) {
17808                 return this;
17809             }
17810     
17811             var compareNumbers = function(a, b){
17812                 return (a-b);
17813             };
17814     
17815             /**
17816              * Private function doing the Douglas-Peucker reduction
17817              */
17818             var douglasPeuckerReduction = function(points, firstPoint, lastPoint, tolerance){
17819                 var maxDistance = 0;
17820                 var indexFarthest = 0;
17821     
17822                 for (var index = firstPoint, distance; index < lastPoint; index++) {
17823                     distance = perpendicularDistance(points[firstPoint], points[lastPoint], points[index]);
17824                     if (distance > maxDistance) {
17825                         maxDistance = distance;
17826                         indexFarthest = index;
17827                     }
17828                 }
17829     
17830                 if (maxDistance > tolerance && indexFarthest != firstPoint) {
17831                     //Add the largest point that exceeds the tolerance
17832                     pointIndexsToKeep.push(indexFarthest);
17833                     douglasPeuckerReduction(points, firstPoint, indexFarthest, tolerance);
17834                     douglasPeuckerReduction(points, indexFarthest, lastPoint, tolerance);
17835                 }
17836             };
17837     
17838             /**
17839              * Private function calculating the perpendicular distance
17840              * TODO: check whether OpenLayers.Geometry.LineString::distanceTo() is faster or slower
17841              */
17842             var perpendicularDistance = function(point1, point2, point){
17843                 //Area = |(1/2)(x1y2 + x2y3 + x3y1 - x2y1 - x3y2 - x1y3)|   *Area of triangle
17844                 //Base = v((x1-x2)²+(x1-x2)²)                               *Base of Triangle*
17845                 //Area = .5*Base*H                                          *Solve for height
17846                 //Height = Area/.5/Base
17847     
17848                 var area = Math.abs(0.5 * (point1.x * point2.y + point2.x * point.y + point.x * point1.y - point2.x * point1.y - point.x * point2.y - point1.x * point.y));
17849                 var bottom = Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2));
17850                 var height = area / bottom * 2;
17851     
17852                 return height;
17853             };
17854     
17855             var firstPoint = 0;
17856             var lastPoint = points.length - 1;
17857             var pointIndexsToKeep = [];
17858     
17859             //Add the first and last index to the keepers
17860             pointIndexsToKeep.push(firstPoint);
17861             pointIndexsToKeep.push(lastPoint);
17862     
17863             //The first and the last point cannot be the same
17864             while (points[firstPoint].equals(points[lastPoint])) {
17865                 lastPoint--;
17866                 //Addition: the first point not equal to first point in the LineString is kept as well
17867                 pointIndexsToKeep.push(lastPoint);
17868             }
17869     
17870             douglasPeuckerReduction(points, firstPoint, lastPoint, tolerance);
17871             var returnPoints = [];
17872             pointIndexsToKeep.sort(compareNumbers);
17873             for (var index = 0; index < pointIndexsToKeep.length; index++) {
17874                 returnPoints.push(points[pointIndexsToKeep[index]]);
17875             }
17876             return new OpenLayers.Geometry.LineString(returnPoints);
17877     
17878         }
17879         else {
17880             return this;
17881         }
17882     },
17883
17884     CLASS_NAME: "OpenLayers.Geometry.LineString"
17885 });
17886 /* ======================================================================
17887     OpenLayers/Geometry/LinearRing.js
17888    ====================================================================== */
17889
17890 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
17891  * full list of contributors). Published under the 2-clause BSD license.
17892  * See license.txt in the OpenLayers distribution or repository for the
17893  * full text of the license. */
17894
17895 /**
17896  * @requires OpenLayers/Geometry/LineString.js
17897  */
17898
17899 /**
17900  * Class: OpenLayers.Geometry.LinearRing
17901  * 
17902  * A Linear Ring is a special LineString which is closed. It closes itself 
17903  * automatically on every addPoint/removePoint by adding a copy of the first
17904  * point as the last point. 
17905  * 
17906  * Also, as it is the first in the line family to close itself, a getArea()
17907  * function is defined to calculate the enclosed area of the linearRing
17908  * 
17909  * Inherits:
17910  *  - <OpenLayers.Geometry.LineString>
17911  */
17912 OpenLayers.Geometry.LinearRing = OpenLayers.Class(
17913   OpenLayers.Geometry.LineString, {
17914
17915     /**
17916      * Property: componentTypes
17917      * {Array(String)} An array of class names representing the types of 
17918      *                 components that the collection can include.  A null 
17919      *                 value means the component types are not restricted.
17920      */
17921     componentTypes: ["OpenLayers.Geometry.Point"],
17922
17923     /**
17924      * Constructor: OpenLayers.Geometry.LinearRing
17925      * Linear rings are constructed with an array of points.  This array
17926      *     can represent a closed or open ring.  If the ring is open (the last
17927      *     point does not equal the first point), the constructor will close
17928      *     the ring.  If the ring is already closed (the last point does equal
17929      *     the first point), it will be left closed.
17930      * 
17931      * Parameters:
17932      * points - {Array(<OpenLayers.Geometry.Point>)} points
17933      */
17934
17935     /**
17936      * APIMethod: addComponent
17937      * Adds a point to geometry components.  If the point is to be added to
17938      *     the end of the components array and it is the same as the last point
17939      *     already in that array, the duplicate point is not added.  This has 
17940      *     the effect of closing the ring if it is not already closed, and 
17941      *     doing the right thing if it is already closed.  This behavior can 
17942      *     be overridden by calling the method with a non-null index as the 
17943      *     second argument.
17944      *
17945      * Parameters:
17946      * point - {<OpenLayers.Geometry.Point>}
17947      * index - {Integer} Index into the array to insert the component
17948      * 
17949      * Returns:
17950      * {Boolean} Was the Point successfully added?
17951      */
17952     addComponent: function(point, index) {
17953         var added = false;
17954
17955         //remove last point
17956         var lastPoint = this.components.pop();
17957
17958         // given an index, add the point
17959         // without an index only add non-duplicate points
17960         if(index != null || !point.equals(lastPoint)) {
17961             added = OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, 
17962                                                                     arguments);
17963         }
17964
17965         //append copy of first point
17966         var firstPoint = this.components[0];
17967         OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, 
17968                                                                 [firstPoint]);
17969         
17970         return added;
17971     },
17972     
17973     /**
17974      * APIMethod: removeComponent
17975      * Removes a point from geometry components.
17976      *
17977      * Parameters:
17978      * point - {<OpenLayers.Geometry.Point>}
17979      *
17980      * Returns: 
17981      * {Boolean} The component was removed.
17982      */
17983     removeComponent: function(point) {
17984         var removed = this.components && (this.components.length > 3);
17985         if (removed) {
17986             //remove last point
17987             this.components.pop();
17988             
17989             //remove our point
17990             OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, 
17991                                                                     arguments);
17992             //append copy of first point
17993             var firstPoint = this.components[0];
17994             OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, 
17995                                                                 [firstPoint]);
17996         }
17997         return removed;
17998     },
17999     
18000     /**
18001      * APIMethod: move
18002      * Moves a geometry by the given displacement along positive x and y axes.
18003      *     This modifies the position of the geometry and clears the cached
18004      *     bounds.
18005      *
18006      * Parameters:
18007      * x - {Float} Distance to move geometry in positive x direction. 
18008      * y - {Float} Distance to move geometry in positive y direction.
18009      */
18010     move: function(x, y) {
18011         for(var i = 0, len=this.components.length; i<len - 1; i++) {
18012             this.components[i].move(x, y);
18013         }
18014     },
18015
18016     /**
18017      * APIMethod: rotate
18018      * Rotate a geometry around some origin
18019      *
18020      * Parameters:
18021      * angle - {Float} Rotation angle in degrees (measured counterclockwise
18022      *                 from the positive x-axis)
18023      * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
18024      */
18025     rotate: function(angle, origin) {
18026         for(var i=0, len=this.components.length; i<len - 1; ++i) {
18027             this.components[i].rotate(angle, origin);
18028         }
18029     },
18030
18031     /**
18032      * APIMethod: resize
18033      * Resize a geometry relative to some origin.  Use this method to apply
18034      *     a uniform scaling to a geometry.
18035      *
18036      * Parameters:
18037      * scale - {Float} Factor by which to scale the geometry.  A scale of 2
18038      *                 doubles the size of the geometry in each dimension
18039      *                 (lines, for example, will be twice as long, and polygons
18040      *                 will have four times the area).
18041      * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
18042      * ratio - {Float} Optional x:y ratio for resizing.  Default ratio is 1.
18043      * 
18044      * Returns:
18045      * {<OpenLayers.Geometry>} - The current geometry. 
18046      */
18047     resize: function(scale, origin, ratio) {
18048         for(var i=0, len=this.components.length; i<len - 1; ++i) {
18049             this.components[i].resize(scale, origin, ratio);
18050         }
18051         return this;
18052     },
18053     
18054     /**
18055      * APIMethod: transform
18056      * Reproject the components geometry from source to dest.
18057      *
18058      * Parameters:
18059      * source - {<OpenLayers.Projection>}
18060      * dest - {<OpenLayers.Projection>}
18061      * 
18062      * Returns:
18063      * {<OpenLayers.Geometry>} 
18064      */
18065     transform: function(source, dest) {
18066         if (source && dest) {
18067             for (var i=0, len=this.components.length; i<len - 1; i++) {
18068                 var component = this.components[i];
18069                 component.transform(source, dest);
18070             }
18071             this.bounds = null;
18072         }
18073         return this;
18074     },
18075     
18076     /**
18077      * APIMethod: getCentroid
18078      *
18079      * Returns:
18080      * {<OpenLayers.Geometry.Point>} The centroid of the collection
18081      */
18082     getCentroid: function() {
18083         if (this.components) {
18084             var len = this.components.length;
18085             if (len > 0 && len <= 2) {
18086                 return this.components[0].clone();
18087             } else if (len > 2) {
18088                 var sumX = 0.0;
18089                 var sumY = 0.0;
18090                 var x0 = this.components[0].x;
18091                 var y0 = this.components[0].y;
18092                 var area = -1 * this.getArea();
18093                 if (area != 0) {
18094                     for (var i = 0; i < len - 1; i++) {
18095                         var b = this.components[i];
18096                         var c = this.components[i+1];
18097                         sumX += (b.x + c.x - 2 * x0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));
18098                         sumY += (b.y + c.y - 2 * y0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0));
18099                     }
18100                     var x = x0 + sumX / (6 * area);
18101                     var y = y0 + sumY / (6 * area);
18102                 } else {
18103                     for (var i = 0; i < len - 1; i++) {
18104                         sumX += this.components[i].x;
18105                         sumY += this.components[i].y;
18106                     }
18107                     var x = sumX / (len - 1);
18108                     var y = sumY / (len - 1);
18109                 }
18110                 return new OpenLayers.Geometry.Point(x, y);
18111             } else {
18112                 return null;
18113             }
18114         }
18115     },
18116
18117     /**
18118      * APIMethod: getArea
18119      * Note - The area is positive if the ring is oriented CW, otherwise
18120      *         it will be negative.
18121      * 
18122      * Returns:
18123      * {Float} The signed area for a ring.
18124      */
18125     getArea: function() {
18126         var area = 0.0;
18127         if ( this.components && (this.components.length > 2)) {
18128             var sum = 0.0;
18129             for (var i=0, len=this.components.length; i<len - 1; i++) {
18130                 var b = this.components[i];
18131                 var c = this.components[i+1];
18132                 sum += (b.x + c.x) * (c.y - b.y);
18133             }
18134             area = - sum / 2.0;
18135         }
18136         return area;
18137     },
18138     
18139     /**
18140      * APIMethod: getGeodesicArea
18141      * Calculate the approximate area of the polygon were it projected onto
18142      *     the earth.  Note that this area will be positive if ring is oriented
18143      *     clockwise, otherwise it will be negative.
18144      *
18145      * Parameters:
18146      * projection - {<OpenLayers.Projection>} The spatial reference system
18147      *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
18148      *     assumed.
18149      * 
18150      * Reference:
18151      * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
18152      *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
18153      *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
18154      *
18155      * Returns:
18156      * {float} The approximate signed geodesic area of the polygon in square
18157      *     meters.
18158      */
18159     getGeodesicArea: function(projection) {
18160         var ring = this;  // so we can work with a clone if needed
18161         if(projection) {
18162             var gg = new OpenLayers.Projection("EPSG:4326");
18163             if(!gg.equals(projection)) {
18164                 ring = this.clone().transform(projection, gg);
18165             }
18166         }
18167         var area = 0.0;
18168         var len = ring.components && ring.components.length;
18169         if(len > 2) {
18170             var p1, p2;
18171             for(var i=0; i<len-1; i++) {
18172                 p1 = ring.components[i];
18173                 p2 = ring.components[i+1];
18174                 area += OpenLayers.Util.rad(p2.x - p1.x) *
18175                         (2 + Math.sin(OpenLayers.Util.rad(p1.y)) +
18176                         Math.sin(OpenLayers.Util.rad(p2.y)));
18177             }
18178             area = area * 6378137.0 * 6378137.0 / 2.0;
18179         }
18180         return area;
18181     },
18182     
18183     /**
18184      * Method: containsPoint
18185      * Test if a point is inside a linear ring.  For the case where a point
18186      *     is coincident with a linear ring edge, returns 1.  Otherwise,
18187      *     returns boolean.
18188      *
18189      * Parameters:
18190      * point - {<OpenLayers.Geometry.Point>}
18191      *
18192      * Returns:
18193      * {Boolean | Number} The point is inside the linear ring.  Returns 1 if
18194      *     the point is coincident with an edge.  Returns boolean otherwise.
18195      */
18196     containsPoint: function(point) {
18197         var approx = OpenLayers.Number.limitSigDigs;
18198         var digs = 14;
18199         var px = approx(point.x, digs);
18200         var py = approx(point.y, digs);
18201         function getX(y, x1, y1, x2, y2) {
18202             return (y - y2) * ((x2 - x1) / (y2 - y1)) + x2;
18203         }
18204         var numSeg = this.components.length - 1;
18205         var start, end, x1, y1, x2, y2, cx, cy;
18206         var crosses = 0;
18207         for(var i=0; i<numSeg; ++i) {
18208             start = this.components[i];
18209             x1 = approx(start.x, digs);
18210             y1 = approx(start.y, digs);
18211             end = this.components[i + 1];
18212             x2 = approx(end.x, digs);
18213             y2 = approx(end.y, digs);
18214             
18215             /**
18216              * The following conditions enforce five edge-crossing rules:
18217              *    1. points coincident with edges are considered contained;
18218              *    2. an upward edge includes its starting endpoint, and
18219              *    excludes its final endpoint;
18220              *    3. a downward edge excludes its starting endpoint, and
18221              *    includes its final endpoint;
18222              *    4. horizontal edges are excluded; and
18223              *    5. the edge-ray intersection point must be strictly right
18224              *    of the point P.
18225              */
18226             if(y1 == y2) {
18227                 // horizontal edge
18228                 if(py == y1) {
18229                     // point on horizontal line
18230                     if(x1 <= x2 && (px >= x1 && px <= x2) || // right or vert
18231                        x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert
18232                         // point on edge
18233                         crosses = -1;
18234                         break;
18235                     }
18236                 }
18237                 // ignore other horizontal edges
18238                 continue;
18239             }
18240             cx = approx(getX(py, x1, y1, x2, y2), digs);
18241             if(cx == px) {
18242                 // point on line
18243                 if(y1 < y2 && (py >= y1 && py <= y2) || // upward
18244                    y1 > y2 && (py <= y1 && py >= y2)) { // downward
18245                     // point on edge
18246                     crosses = -1;
18247                     break;
18248                 }
18249             }
18250             if(cx <= px) {
18251                 // no crossing to the right
18252                 continue;
18253             }
18254             if(x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) {
18255                 // no crossing
18256                 continue;
18257             }
18258             if(y1 < y2 && (py >= y1 && py < y2) || // upward
18259                y1 > y2 && (py < y1 && py >= y2)) { // downward
18260                 ++crosses;
18261             }
18262         }
18263         var contained = (crosses == -1) ?
18264             // on edge
18265             1 :
18266             // even (out) or odd (in)
18267             !!(crosses & 1);
18268
18269         return contained;
18270     },
18271
18272     /**
18273      * APIMethod: intersects
18274      * Determine if the input geometry intersects this one.
18275      *
18276      * Parameters:
18277      * geometry - {<OpenLayers.Geometry>} Any type of geometry.
18278      *
18279      * Returns:
18280      * {Boolean} The input geometry intersects this one.
18281      */
18282     intersects: function(geometry) {
18283         var intersect = false;
18284         if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
18285             intersect = this.containsPoint(geometry);
18286         } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") {
18287             intersect = geometry.intersects(this);
18288         } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
18289             intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply(
18290                 this, [geometry]
18291             );
18292         } else {
18293             // check for component intersections
18294             for(var i=0, len=geometry.components.length; i<len; ++ i) {
18295                 intersect = geometry.components[i].intersects(this);
18296                 if(intersect) {
18297                     break;
18298                 }
18299             }
18300         }
18301         return intersect;
18302     },
18303
18304     /**
18305      * APIMethod: getVertices
18306      * Return a list of all points in this geometry.
18307      *
18308      * Parameters:
18309      * nodes - {Boolean} For lines, only return vertices that are
18310      *     endpoints.  If false, for lines, only vertices that are not
18311      *     endpoints will be returned.  If not provided, all vertices will
18312      *     be returned.
18313      *
18314      * Returns:
18315      * {Array} A list of all vertices in the geometry.
18316      */
18317     getVertices: function(nodes) {
18318         return (nodes === true) ? [] : this.components.slice(0, this.components.length-1);
18319     },
18320
18321     CLASS_NAME: "OpenLayers.Geometry.LinearRing"
18322 });
18323 /* ======================================================================
18324     OpenLayers/Layer/HTTPRequest.js
18325    ====================================================================== */
18326
18327 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
18328  * full list of contributors). Published under the 2-clause BSD license.
18329  * See license.txt in the OpenLayers distribution or repository for the
18330  * full text of the license. */
18331
18332
18333 /**
18334  * @requires OpenLayers/Layer.js
18335  */
18336
18337 /**
18338  * Class: OpenLayers.Layer.HTTPRequest
18339  * 
18340  * Inherits from: 
18341  *  - <OpenLayers.Layer>
18342  */
18343 OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {
18344
18345     /** 
18346      * Constant: URL_HASH_FACTOR
18347      * {Float} Used to hash URL param strings for multi-WMS server selection.
18348      *         Set to the Golden Ratio per Knuth's recommendation.
18349      */
18350     URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,
18351
18352     /** 
18353      * Property: url
18354      * {Array(String) or String} This is either an array of url strings or 
18355      *                           a single url string. 
18356      */
18357     url: null,
18358
18359     /** 
18360      * Property: params
18361      * {Object} Hashtable of key/value parameters
18362      */
18363     params: null,
18364     
18365     /** 
18366      * APIProperty: reproject
18367      * *Deprecated*. See http://docs.openlayers.org/library/spherical_mercator.html
18368      * for information on the replacement for this functionality. 
18369      * {Boolean} Whether layer should reproject itself based on base layer 
18370      *           locations. This allows reprojection onto commercial layers. 
18371      *           Default is false: Most layers can't reproject, but layers 
18372      *           which can create non-square geographic pixels can, like WMS.
18373      *           
18374      */
18375     reproject: false,
18376
18377     /**
18378      * Constructor: OpenLayers.Layer.HTTPRequest
18379      * 
18380      * Parameters:
18381      * name - {String}
18382      * url - {Array(String) or String}
18383      * params - {Object}
18384      * options - {Object} Hashtable of extra options to tag onto the layer
18385      */
18386     initialize: function(name, url, params, options) {
18387         OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);
18388         this.url = url;
18389         if (!this.params) {
18390             this.params = OpenLayers.Util.extend({}, params);
18391         }
18392     },
18393
18394     /**
18395      * APIMethod: destroy
18396      */
18397     destroy: function() {
18398         this.url = null;
18399         this.params = null;
18400         OpenLayers.Layer.prototype.destroy.apply(this, arguments); 
18401     },
18402     
18403     /**
18404      * APIMethod: clone
18405      * 
18406      * Parameters:
18407      * obj - {Object}
18408      * 
18409      * Returns:
18410      * {<OpenLayers.Layer.HTTPRequest>} An exact clone of this 
18411      *                                  <OpenLayers.Layer.HTTPRequest>
18412      */
18413     clone: function (obj) {
18414         
18415         if (obj == null) {
18416             obj = new OpenLayers.Layer.HTTPRequest(this.name,
18417                                                    this.url,
18418                                                    this.params,
18419                                                    this.getOptions());
18420         }
18421         
18422         //get all additions from superclasses
18423         obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
18424
18425         // copy/set any non-init, non-simple values here
18426         
18427         return obj;
18428     },
18429
18430     /** 
18431      * APIMethod: setUrl
18432      * 
18433      * Parameters:
18434      * newUrl - {String}
18435      */
18436     setUrl: function(newUrl) {
18437         this.url = newUrl;
18438     },
18439
18440     /**
18441      * APIMethod: mergeNewParams
18442      * 
18443      * Parameters:
18444      * newParams - {Object}
18445      *
18446      * Returns:
18447      * redrawn: {Boolean} whether the layer was actually redrawn.
18448      */
18449     mergeNewParams:function(newParams) {
18450         this.params = OpenLayers.Util.extend(this.params, newParams);
18451         var ret = this.redraw();
18452         if(this.map != null) {
18453             this.map.events.triggerEvent("changelayer", {
18454                 layer: this,
18455                 property: "params"
18456             });
18457         }
18458         return ret;
18459     },
18460
18461     /**
18462      * APIMethod: redraw
18463      * Redraws the layer.  Returns true if the layer was redrawn, false if not.
18464      *
18465      * Parameters:
18466      * force - {Boolean} Force redraw by adding random parameter.
18467      *
18468      * Returns:
18469      * {Boolean} The layer was redrawn.
18470      */
18471     redraw: function(force) { 
18472         if (force) {
18473             return this.mergeNewParams({"_olSalt": Math.random()});
18474         } else {
18475             return OpenLayers.Layer.prototype.redraw.apply(this, []);
18476         }
18477     },
18478     
18479     /**
18480      * Method: selectUrl
18481      * selectUrl() implements the standard floating-point multiplicative
18482      *     hash function described by Knuth, and hashes the contents of the 
18483      *     given param string into a float between 0 and 1. This float is then
18484      *     scaled to the size of the provided urls array, and used to select
18485      *     a URL.
18486      *
18487      * Parameters:
18488      * paramString - {String}
18489      * urls - {Array(String)}
18490      * 
18491      * Returns:
18492      * {String} An entry from the urls array, deterministically selected based
18493      *          on the paramString.
18494      */
18495     selectUrl: function(paramString, urls) {
18496         var product = 1;
18497         for (var i=0, len=paramString.length; i<len; i++) { 
18498             product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR; 
18499             product -= Math.floor(product); 
18500         }
18501         return urls[Math.floor(product * urls.length)];
18502     },
18503
18504     /** 
18505      * Method: getFullRequestString
18506      * Combine url with layer's params and these newParams. 
18507      *   
18508      *    does checking on the serverPath variable, allowing for cases when it 
18509      *     is supplied with trailing ? or &, as well as cases where not. 
18510      *
18511      *    return in formatted string like this:
18512      *        "server?key1=value1&key2=value2&key3=value3"
18513      * 
18514      * WARNING: The altUrl parameter is deprecated and will be removed in 3.0.
18515      *
18516      * Parameters:
18517      * newParams - {Object}
18518      * altUrl - {String} Use this as the url instead of the layer's url
18519      *   
18520      * Returns: 
18521      * {String}
18522      */
18523     getFullRequestString:function(newParams, altUrl) {
18524
18525         // if not altUrl passed in, use layer's url
18526         var url = altUrl || this.url;
18527         
18528         // create a new params hashtable with all the layer params and the 
18529         // new params together. then convert to string
18530         var allParams = OpenLayers.Util.extend({}, this.params);
18531         allParams = OpenLayers.Util.extend(allParams, newParams);
18532         var paramsString = OpenLayers.Util.getParameterString(allParams);
18533         
18534         // if url is not a string, it should be an array of strings, 
18535         // in which case we will deterministically select one of them in 
18536         // order to evenly distribute requests to different urls.
18537         //
18538         if (OpenLayers.Util.isArray(url)) {
18539             url = this.selectUrl(paramsString, url);
18540         }   
18541  
18542         // ignore parameters that are already in the url search string
18543         var urlParams = 
18544             OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));
18545         for(var key in allParams) {
18546             if(key.toUpperCase() in urlParams) {
18547                 delete allParams[key];
18548             }
18549         }
18550         paramsString = OpenLayers.Util.getParameterString(allParams);
18551         
18552         return OpenLayers.Util.urlAppend(url, paramsString);
18553     },
18554
18555     CLASS_NAME: "OpenLayers.Layer.HTTPRequest"
18556 });
18557 /* ======================================================================
18558     OpenLayers/Layer/Grid.js
18559    ====================================================================== */
18560
18561 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
18562  * full list of contributors). Published under the 2-clause BSD license.
18563  * See license.txt in the OpenLayers distribution or repository for the
18564  * full text of the license. */
18565
18566
18567 /**
18568  * @requires OpenLayers/Layer/HTTPRequest.js
18569  * @requires OpenLayers/Tile/Image.js
18570  */
18571
18572 /**
18573  * Class: OpenLayers.Layer.Grid
18574  * Base class for layers that use a lattice of tiles.  Create a new grid
18575  * layer with the <OpenLayers.Layer.Grid> constructor.
18576  *
18577  * Inherits from:
18578  *  - <OpenLayers.Layer.HTTPRequest>
18579  */
18580 OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
18581     
18582     /**
18583      * APIProperty: tileSize
18584      * {<OpenLayers.Size>}
18585      */
18586     tileSize: null,
18587
18588     /**
18589      * Property: tileOriginCorner
18590      * {String} If the <tileOrigin> property is not provided, the tile origin 
18591      *     will be derived from the layer's <maxExtent>.  The corner of the 
18592      *     <maxExtent> used is determined by this property.  Acceptable values
18593      *     are "tl" (top left), "tr" (top right), "bl" (bottom left), and "br"
18594      *     (bottom right).  Default is "bl".
18595      */
18596     tileOriginCorner: "bl",
18597     
18598     /**
18599      * APIProperty: tileOrigin
18600      * {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles.
18601      *     If provided, requests for tiles at all resolutions will be aligned
18602      *     with this location (no tiles shall overlap this location).  If
18603      *     not provided, the grid of tiles will be aligned with the layer's
18604      *     <maxExtent>.  Default is ``null``.
18605      */
18606     tileOrigin: null,
18607     
18608     /** APIProperty: tileOptions
18609      *  {Object} optional configuration options for <OpenLayers.Tile> instances
18610      *  created by this Layer, if supported by the tile class.
18611      */
18612     tileOptions: null,
18613
18614     /**
18615      * APIProperty: tileClass
18616      * {<OpenLayers.Tile>} The tile class to use for this layer.
18617      *     Defaults is OpenLayers.Tile.Image.
18618      */
18619     tileClass: OpenLayers.Tile.Image,
18620     
18621     /**
18622      * Property: grid
18623      * {Array(Array(<OpenLayers.Tile>))} This is an array of rows, each row is 
18624      *     an array of tiles.
18625      */
18626     grid: null,
18627
18628     /**
18629      * APIProperty: singleTile
18630      * {Boolean} Moves the layer into single-tile mode, meaning that one tile 
18631      *     will be loaded. The tile's size will be determined by the 'ratio'
18632      *     property. When the tile is dragged such that it does not cover the 
18633      *     entire viewport, it is reloaded.
18634      */
18635     singleTile: false,
18636
18637     /** APIProperty: ratio
18638      *  {Float} Used only when in single-tile mode, this specifies the 
18639      *          ratio of the size of the single tile to the size of the map.
18640      *          Default value is 1.5.
18641      */
18642     ratio: 1.5,
18643
18644     /**
18645      * APIProperty: buffer
18646      * {Integer} Used only when in gridded mode, this specifies the number of 
18647      *           extra rows and colums of tiles on each side which will
18648      *           surround the minimum grid tiles to cover the map.
18649      *           For very slow loading layers, a larger value may increase
18650      *           performance somewhat when dragging, but will increase bandwidth
18651      *           use significantly. 
18652      */
18653     buffer: 0,
18654
18655     /**
18656      * APIProperty: transitionEffect
18657      * {String} The transition effect to use when the map is zoomed.
18658      * Two posible values:
18659      *
18660      * "resize" - Existing tiles are resized on zoom to provide a visual
18661      *     effect of the zoom having taken place immediately.  As the
18662      *     new tiles become available, they are drawn on top of the
18663      *     resized tiles (this is the default setting).
18664      * "map-resize" - Existing tiles are resized on zoom and placed below the
18665      *     base layer.  New tiles for the base layer will cover existing tiles.
18666      *     This setting is recommended when having an overlay duplicated during
18667      *     the transition is undesirable (e.g. street labels or big transparent
18668      *     fills). 
18669      * null - No transition effect.
18670      *
18671      * Using "resize" on non-opaque layers can cause undesired visual
18672      * effects.  Set transitionEffect to null in this case.
18673      */
18674     transitionEffect: "resize",
18675
18676     /**
18677      * APIProperty: numLoadingTiles
18678      * {Integer} How many tiles are still loading?
18679      */
18680     numLoadingTiles: 0,
18681
18682     /**
18683      * Property: serverResolutions
18684      * {Array(Number}} This property is documented in subclasses as
18685      *     an API property.
18686      */
18687     serverResolutions: null,
18688
18689     /**
18690      * Property: loading
18691      * {Boolean} Indicates if tiles are being loaded.
18692      */
18693     loading: false,
18694     
18695     /**
18696      * Property: backBuffer
18697      * {DOMElement} The back buffer.
18698      */
18699     backBuffer: null,
18700
18701     /**
18702      * Property: gridResolution
18703      * {Number} The resolution of the current grid. Used for backbuffer and
18704      *     client zoom. This property is updated every time the grid is
18705      *     initialized.
18706      */
18707     gridResolution: null,
18708
18709     /**
18710      * Property: backBufferResolution
18711      * {Number} The resolution of the current back buffer. This property is
18712      *     updated each time a back buffer is created.
18713      */
18714     backBufferResolution: null,
18715
18716     /**
18717      * Property: backBufferLonLat
18718      * {Object} The top-left corner of the current back buffer. Includes lon
18719      *     and lat properties. This object is updated each time a back buffer
18720      *     is created.
18721      */
18722     backBufferLonLat: null,
18723
18724     /**
18725      * Property: backBufferTimerId
18726      * {Number} The id of the back buffer timer. This timer is used to
18727      *     delay the removal of the back buffer, thereby preventing
18728      *     flash effects caused by tile animation.
18729      */
18730     backBufferTimerId: null,
18731
18732     /**
18733      * APIProperty: removeBackBufferDelay
18734      * {Number} Delay for removing the backbuffer when all tiles have finished
18735      *     loading. Can be set to 0 when no css opacity transitions for the
18736      *     olTileImage class are used. Default is 0 for <singleTile> layers,
18737      *     2500 for tiled layers. See <className> for more information on
18738      *     tile animation.
18739      */
18740     removeBackBufferDelay: null,
18741
18742     /**
18743      * APIProperty: className
18744      * {String} Name of the class added to the layer div. If not set in the
18745      *     options passed to the constructor then className defaults to
18746      *     "olLayerGridSingleTile" for single tile layers (see <singleTile>),
18747      *     and "olLayerGrid" for non single tile layers.
18748      *
18749      * Note:
18750      *
18751      * The displaying of tiles is not animated by default for single tile
18752      *     layers - OpenLayers' default theme (style.css) includes this:
18753      * (code)
18754      * .olLayerGrid .olTileImage {
18755      *     -webkit-transition: opacity 0.2s linear;
18756      *     -moz-transition: opacity 0.2s linear;
18757      *     -o-transition: opacity 0.2s linear;
18758      *     transition: opacity 0.2s linear;
18759      *  }
18760      * (end)
18761      * To animate tile displaying for any grid layer the following
18762      *     CSS rule can be used:
18763      * (code)
18764      * .olTileImage {
18765      *     -webkit-transition: opacity 0.2s linear;
18766      *     -moz-transition: opacity 0.2s linear;
18767      *     -o-transition: opacity 0.2s linear;
18768      *     transition: opacity 0.2s linear;
18769      * }
18770      * (end)
18771      * In that case, to avoid flash effects, <removeBackBufferDelay>
18772      *     should not be zero.
18773      */
18774     className: null,
18775     
18776     /**
18777      * Register a listener for a particular event with the following syntax:
18778      * (code)
18779      * layer.events.register(type, obj, listener);
18780      * (end)
18781      *
18782      * Listeners will be called with a reference to an event object.  The
18783      *     properties of this event depends on exactly what happened.
18784      *
18785      * All event objects have at least the following properties:
18786      * object - {Object} A reference to layer.events.object.
18787      * element - {DOMElement} A reference to layer.events.element.
18788      *
18789      * Supported event types:
18790      * addtile - Triggered when a tile is added to this layer. Listeners receive
18791      *     an object as first argument, which has a tile property that
18792      *     references the tile that has been added.
18793      * tileloadstart - Triggered when a tile starts loading. Listeners receive
18794      *     an object as first argument, which has a tile property that
18795      *     references the tile that starts loading.
18796      * tileloaded - Triggered when each new tile is
18797      *     loaded, as a means of progress update to listeners.
18798      *     listeners can access 'numLoadingTiles' if they wish to keep
18799      *     track of the loading progress. Listeners are called with an object
18800      *     with a 'tile' property as first argument, making the loaded tile
18801      *     available to the listener, and an 'aborted' property, which will be
18802      *     true when loading was aborted and no tile data is available.
18803      * tileerror - Triggered before the tileloaded event (i.e. when the tile is
18804      *     still hidden) if a tile failed to load. Listeners receive an object
18805      *     as first argument, which has a tile property that references the
18806      *     tile that could not be loaded.
18807      * retile - Triggered when the layer recreates its tile grid.
18808      */
18809
18810     /**
18811      * Property: gridLayout
18812      * {Object} Object containing properties tilelon, tilelat, startcol,
18813      * startrow
18814      */
18815     gridLayout: null,
18816     
18817     /**
18818      * Property: rowSign
18819      * {Number} 1 for grids starting at the top, -1 for grids starting at the
18820      * bottom. This is used for several grid index and offset calculations.
18821      */
18822     rowSign: null,
18823
18824     /**
18825      * Property: transitionendEvents
18826      * {Array} Event names for transitionend
18827      */
18828     transitionendEvents: [
18829         'transitionend', 'webkitTransitionEnd', 'otransitionend',
18830         'oTransitionEnd'
18831     ],
18832
18833     /**
18834      * Constructor: OpenLayers.Layer.Grid
18835      * Create a new grid layer
18836      *
18837      * Parameters:
18838      * name - {String}
18839      * url - {String}
18840      * params - {Object}
18841      * options - {Object} Hashtable of extra options to tag onto the layer
18842      */
18843     initialize: function(name, url, params, options) {
18844         OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, 
18845                                                                 arguments);
18846         this.grid = [];
18847         this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this);
18848
18849         this.initProperties();
18850
18851         this.rowSign = this.tileOriginCorner.substr(0, 1) === "t" ? 1 : -1;
18852     },
18853
18854     /**
18855      * Method: initProperties
18856      * Set any properties that depend on the value of singleTile.
18857      * Currently sets removeBackBufferDelay and className
18858      */
18859     initProperties: function() {
18860         if (this.options.removeBackBufferDelay === undefined) {
18861             this.removeBackBufferDelay = this.singleTile ? 0 : 2500;
18862         }
18863
18864         if (this.options.className === undefined) {
18865             this.className = this.singleTile ? 'olLayerGridSingleTile' :
18866                                                'olLayerGrid';
18867         }
18868     },
18869
18870     /**
18871      * Method: setMap
18872      *
18873      * Parameters:
18874      * map - {<OpenLayers.Map>} The map.
18875      */
18876     setMap: function(map) {
18877         OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map);
18878         OpenLayers.Element.addClass(this.div, this.className);
18879     },
18880
18881     /**
18882      * Method: removeMap
18883      * Called when the layer is removed from the map.
18884      *
18885      * Parameters:
18886      * map - {<OpenLayers.Map>} The map.
18887      */
18888     removeMap: function(map) {
18889         this.removeBackBuffer();
18890     },
18891
18892     /**
18893      * APIMethod: destroy
18894      * Deconstruct the layer and clear the grid.
18895      */
18896     destroy: function() {
18897         this.removeBackBuffer();
18898         this.clearGrid();
18899
18900         this.grid = null;
18901         this.tileSize = null;
18902         OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments); 
18903     },
18904
18905     /**
18906      * APIMethod: mergeNewParams
18907      * Refetches tiles with new params merged, keeping a backbuffer. Each
18908      * loading new tile will have a css class of '.olTileReplacing'. If a
18909      * stylesheet applies a 'display: none' style to that class, any fade-in
18910      * transition will not apply, and backbuffers for each tile will be removed
18911      * as soon as the tile is loaded.
18912      * 
18913      * Parameters:
18914      * newParams - {Object}
18915      *
18916      * Returns:
18917      * redrawn: {Boolean} whether the layer was actually redrawn.
18918      */
18919
18920     /**
18921      * Method: clearGrid
18922      * Go through and remove all tiles from the grid, calling
18923      *    destroy() on each of them to kill circular references
18924      */
18925     clearGrid:function() {
18926         if (this.grid) {
18927             for(var iRow=0, len=this.grid.length; iRow<len; iRow++) {
18928                 var row = this.grid[iRow];
18929                 for(var iCol=0, clen=row.length; iCol<clen; iCol++) {
18930                     var tile = row[iCol];
18931                     this.destroyTile(tile);
18932                 }
18933             }
18934             this.grid = [];
18935             this.gridResolution = null;
18936             this.gridLayout = null;
18937         }
18938     },
18939
18940    /**
18941     * APIMethod: addOptions
18942     * 
18943     * Parameters:
18944     * newOptions - {Object}
18945     * reinitialize - {Boolean} If set to true, and if resolution options of the
18946     *     current baseLayer were changed, the map will be recentered to make
18947     *     sure that it is displayed with a valid resolution, and a
18948     *     changebaselayer event will be triggered.
18949     */
18950     addOptions: function (newOptions, reinitialize) {
18951         var singleTileChanged = newOptions.singleTile !== undefined && 
18952             newOptions.singleTile !== this.singleTile;
18953         OpenLayers.Layer.HTTPRequest.prototype.addOptions.apply(this, arguments);
18954         if (this.map && singleTileChanged) {
18955             this.initProperties();
18956             this.clearGrid();
18957             this.tileSize = this.options.tileSize;
18958             this.setTileSize();
18959             this.moveTo(null, true);
18960         }
18961     },
18962     
18963     /**
18964      * APIMethod: clone
18965      * Create a clone of this layer
18966      *
18967      * Parameters:
18968      * obj - {Object} Is this ever used?
18969      * 
18970      * Returns:
18971      * {<OpenLayers.Layer.Grid>} An exact clone of this OpenLayers.Layer.Grid
18972      */
18973     clone: function (obj) {
18974         
18975         if (obj == null) {
18976             obj = new OpenLayers.Layer.Grid(this.name,
18977                                             this.url,
18978                                             this.params,
18979                                             this.getOptions());
18980         }
18981
18982         //get all additions from superclasses
18983         obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);
18984
18985         // copy/set any non-init, non-simple values here
18986         if (this.tileSize != null) {
18987             obj.tileSize = this.tileSize.clone();
18988         }
18989         
18990         // we do not want to copy reference to grid, so we make a new array
18991         obj.grid = [];
18992         obj.gridResolution = null;
18993         // same for backbuffer
18994         obj.backBuffer = null;
18995         obj.backBufferTimerId = null;
18996         obj.loading = false;
18997         obj.numLoadingTiles = 0;
18998
18999         return obj;
19000     },    
19001
19002     /**
19003      * Method: moveTo
19004      * This function is called whenever the map is moved. All the moving
19005      * of actual 'tiles' is done by the map, but moveTo's role is to accept
19006      * a bounds and make sure the data that that bounds requires is pre-loaded.
19007      *
19008      * Parameters:
19009      * bounds - {<OpenLayers.Bounds>}
19010      * zoomChanged - {Boolean}
19011      * dragging - {Boolean}
19012      */
19013     moveTo:function(bounds, zoomChanged, dragging) {
19014
19015         OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);
19016
19017         bounds = bounds || this.map.getExtent();
19018
19019         if (bounds != null) {
19020              
19021             // if grid is empty or zoom has changed, we *must* re-tile
19022             var forceReTile = !this.grid.length || zoomChanged;
19023             
19024             // total bounds of the tiles
19025             var tilesBounds = this.getTilesBounds();            
19026
19027             // the new map resolution
19028             var resolution = this.map.getResolution();
19029
19030             // the server-supported resolution for the new map resolution
19031             var serverResolution = this.getServerResolution(resolution);
19032
19033             if (this.singleTile) {
19034                 
19035                 // We want to redraw whenever even the slightest part of the 
19036                 //  current bounds is not contained by our tile.
19037                 //  (thus, we do not specify partial -- its default is false)
19038
19039                 if ( forceReTile ||
19040                      (!dragging && !tilesBounds.containsBounds(bounds))) {
19041
19042                     // In single tile mode with no transition effect, we insert
19043                     // a non-scaled backbuffer when the layer is moved. But if
19044                     // a zoom occurs right after a move, i.e. before the new
19045                     // image is received, we need to remove the backbuffer, or
19046                     // an ill-positioned image will be visible during the zoom
19047                     // transition.
19048
19049                     if(zoomChanged && this.transitionEffect !== 'resize') {
19050                         this.removeBackBuffer();
19051                     }
19052
19053                     if(!zoomChanged || this.transitionEffect === 'resize') {
19054                         this.applyBackBuffer(resolution);
19055                     }
19056
19057                     this.initSingleTile(bounds);
19058                 }
19059             } else {
19060
19061                 // if the bounds have changed such that they are not even 
19062                 // *partially* contained by our tiles (e.g. when user has 
19063                 // programmatically panned to the other side of the earth on
19064                 // zoom level 18), then moveGriddedTiles could potentially have
19065                 // to run through thousands of cycles, so we want to reTile
19066                 // instead (thus, partial true).  
19067                 forceReTile = forceReTile ||
19068                     !tilesBounds.intersectsBounds(bounds, {
19069                         worldBounds: this.map.baseLayer.wrapDateLine &&
19070                             this.map.getMaxExtent()
19071                     });
19072
19073                 if(forceReTile) {
19074                     if(zoomChanged && (this.transitionEffect === 'resize' ||
19075                                           this.gridResolution === resolution)) {
19076                         this.applyBackBuffer(resolution);
19077                     }
19078                     this.initGriddedTiles(bounds);
19079                 } else {
19080                     this.moveGriddedTiles();
19081                 }
19082             }
19083         }
19084     },
19085
19086     /**
19087      * Method: getTileData
19088      * Given a map location, retrieve a tile and the pixel offset within that
19089      *     tile corresponding to the location.  If there is not an existing 
19090      *     tile in the grid that covers the given location, null will be 
19091      *     returned.
19092      *
19093      * Parameters:
19094      * loc - {<OpenLayers.LonLat>} map location
19095      *
19096      * Returns:
19097      * {Object} Object with the following properties: tile ({<OpenLayers.Tile>}),
19098      *     i ({Number} x-pixel offset from top left), and j ({Integer} y-pixel
19099      *     offset from top left).
19100      */
19101     getTileData: function(loc) {
19102         var data = null,
19103             x = loc.lon,
19104             y = loc.lat,
19105             numRows = this.grid.length;
19106
19107         if (this.map && numRows) {
19108             var res = this.map.getResolution(),
19109                 tileWidth = this.tileSize.w,
19110                 tileHeight = this.tileSize.h,
19111                 bounds = this.grid[0][0].bounds,
19112                 left = bounds.left,
19113                 top = bounds.top;
19114
19115             if (x < left) {
19116                 // deal with multiple worlds
19117                 if (this.map.baseLayer.wrapDateLine) {
19118                     var worldWidth = this.map.getMaxExtent().getWidth();
19119                     var worldsAway = Math.ceil((left - x) / worldWidth);
19120                     x += worldWidth * worldsAway;
19121                 }
19122             }
19123             // tile distance to location (fractional number of tiles);
19124             var dtx = (x - left) / (res * tileWidth);
19125             var dty = (top - y) / (res * tileHeight);
19126             // index of tile in grid
19127             var col = Math.floor(dtx);
19128             var row = Math.floor(dty);
19129             if (row >= 0 && row < numRows) {
19130                 var tile = this.grid[row][col];
19131                 if (tile) {
19132                     data = {
19133                         tile: tile,
19134                         // pixel index within tile
19135                         i: Math.floor((dtx - col) * tileWidth),
19136                         j: Math.floor((dty - row) * tileHeight)
19137                     };                    
19138                 }
19139             }
19140         }
19141         return data;
19142     },
19143     
19144     /**
19145      * Method: destroyTile
19146      *
19147      * Parameters:
19148      * tile - {<OpenLayers.Tile>}
19149      */
19150     destroyTile: function(tile) {
19151         this.removeTileMonitoringHooks(tile);
19152         tile.destroy();
19153     },
19154
19155     /**
19156      * Method: getServerResolution
19157      * Return the closest server-supported resolution.
19158      *
19159      * Parameters:
19160      * resolution - {Number} The base resolution. If undefined the
19161      *     map resolution is used.
19162      *
19163      * Returns:
19164      * {Number} The closest server resolution value.
19165      */
19166     getServerResolution: function(resolution) {
19167         var distance = Number.POSITIVE_INFINITY;
19168         resolution = resolution || this.map.getResolution();
19169         if(this.serverResolutions &&
19170            OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {
19171             var i, newDistance, newResolution, serverResolution;
19172             for(i=this.serverResolutions.length-1; i>= 0; i--) {
19173                 newResolution = this.serverResolutions[i];
19174                 newDistance = Math.abs(newResolution - resolution);
19175                 if (newDistance > distance) {
19176                     break;
19177                 }
19178                 distance = newDistance;
19179                 serverResolution = newResolution;
19180             }
19181             resolution = serverResolution;
19182         }
19183         return resolution;
19184     },
19185
19186     /**
19187      * Method: getServerZoom
19188      * Return the zoom value corresponding to the best matching server
19189      * resolution, taking into account <serverResolutions> and <zoomOffset>.
19190      *
19191      * Returns:
19192      * {Number} The closest server supported zoom. This is not the map zoom
19193      *     level, but an index of the server's resolutions array.
19194      */
19195     getServerZoom: function() {
19196         var resolution = this.getServerResolution();
19197         return this.serverResolutions ?
19198             OpenLayers.Util.indexOf(this.serverResolutions, resolution) :
19199             this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0);
19200     },
19201
19202     /**
19203      * Method: applyBackBuffer
19204      * Create, insert, scale and position a back buffer for the layer.
19205      *
19206      * Parameters:
19207      * resolution - {Number} The resolution to transition to.
19208      */
19209     applyBackBuffer: function(resolution) {
19210         if(this.backBufferTimerId !== null) {
19211             this.removeBackBuffer();
19212         }
19213         var backBuffer = this.backBuffer;
19214         if(!backBuffer) {
19215             backBuffer = this.createBackBuffer();
19216             if(!backBuffer) {
19217                 return;
19218             }
19219             if (resolution === this.gridResolution) {
19220                 this.div.insertBefore(backBuffer, this.div.firstChild);
19221             } else {
19222                 this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div);
19223             }
19224             this.backBuffer = backBuffer;
19225
19226             // set some information in the instance for subsequent
19227             // calls to applyBackBuffer where the same back buffer
19228             // is reused
19229             var topLeftTileBounds = this.grid[0][0].bounds;
19230             this.backBufferLonLat = {
19231                 lon: topLeftTileBounds.left,
19232                 lat: topLeftTileBounds.top
19233             };
19234             this.backBufferResolution = this.gridResolution;
19235         }
19236         
19237         var ratio = this.backBufferResolution / resolution;
19238
19239         // scale the tiles inside the back buffer
19240         var tiles = backBuffer.childNodes, tile;
19241         for (var i=tiles.length-1; i>=0; --i) {
19242             tile = tiles[i];
19243             tile.style.top = ((ratio * tile._i * tile._h) | 0) + 'px';
19244             tile.style.left = ((ratio * tile._j * tile._w) | 0) + 'px';
19245             tile.style.width = Math.round(ratio * tile._w) + 'px';
19246             tile.style.height = Math.round(ratio * tile._h) + 'px';
19247         }
19248
19249         // and position it (based on the grid's top-left corner)
19250         var position = this.getViewPortPxFromLonLat(
19251                 this.backBufferLonLat, resolution);
19252         var leftOffset = this.map.layerContainerOriginPx.x;
19253         var topOffset = this.map.layerContainerOriginPx.y;
19254         backBuffer.style.left = Math.round(position.x - leftOffset) + 'px';
19255         backBuffer.style.top = Math.round(position.y - topOffset) + 'px';
19256     },
19257
19258     /**
19259      * Method: createBackBuffer
19260      * Create a back buffer.
19261      *
19262      * Returns:
19263      * {DOMElement} The DOM element for the back buffer, undefined if the
19264      * grid isn't initialized yet.
19265      */
19266     createBackBuffer: function() {
19267         var backBuffer;
19268         if(this.grid.length > 0) {
19269             backBuffer = document.createElement('div');
19270             backBuffer.id = this.div.id + '_bb';
19271             backBuffer.className = 'olBackBuffer';
19272             backBuffer.style.position = 'absolute';
19273             var map = this.map;
19274             backBuffer.style.zIndex = this.transitionEffect === 'resize' ?
19275                     this.getZIndex() - 1 :
19276                     // 'map-resize':
19277                     map.Z_INDEX_BASE.BaseLayer -
19278                             (map.getNumLayers() - map.getLayerIndex(this));
19279             for(var i=0, lenI=this.grid.length; i<lenI; i++) {
19280                 for(var j=0, lenJ=this.grid[i].length; j<lenJ; j++) {
19281                     var tile = this.grid[i][j],
19282                         markup = this.grid[i][j].createBackBuffer();
19283                     if (markup) {
19284                         markup._i = i;
19285                         markup._j = j;
19286                         markup._w = tile.size.w;
19287                         markup._h = tile.size.h;
19288                         markup.id = tile.id + '_bb';
19289                         backBuffer.appendChild(markup);
19290                     }
19291                 }
19292             }
19293         }
19294         return backBuffer;
19295     },
19296
19297     /**
19298      * Method: removeBackBuffer
19299      * Remove back buffer from DOM.
19300      */
19301     removeBackBuffer: function() {
19302         if (this._transitionElement) {
19303             for (var i=this.transitionendEvents.length-1; i>=0; --i) {
19304                 OpenLayers.Event.stopObserving(this._transitionElement,
19305                     this.transitionendEvents[i], this._removeBackBuffer);
19306             }
19307             delete this._transitionElement;
19308         }
19309         if(this.backBuffer) {
19310             if (this.backBuffer.parentNode) {
19311                 this.backBuffer.parentNode.removeChild(this.backBuffer);
19312             }
19313             this.backBuffer = null;
19314             this.backBufferResolution = null;
19315             if(this.backBufferTimerId !== null) {
19316                 window.clearTimeout(this.backBufferTimerId);
19317                 this.backBufferTimerId = null;
19318             }
19319         }
19320     },
19321
19322     /**
19323      * Method: moveByPx
19324      * Move the layer based on pixel vector.
19325      *
19326      * Parameters:
19327      * dx - {Number}
19328      * dy - {Number}
19329      */
19330     moveByPx: function(dx, dy) {
19331         if (!this.singleTile) {
19332             this.moveGriddedTiles();
19333         }
19334     },
19335
19336     /**
19337      * APIMethod: setTileSize
19338      * Check if we are in singleTile mode and if so, set the size as a ratio
19339      *     of the map size (as specified by the layer's 'ratio' property).
19340      * 
19341      * Parameters:
19342      * size - {<OpenLayers.Size>}
19343      */
19344     setTileSize: function(size) { 
19345         if (this.singleTile) {
19346             size = this.map.getSize();
19347             size.h = parseInt(size.h * this.ratio, 10);
19348             size.w = parseInt(size.w * this.ratio, 10);
19349         } 
19350         OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]);
19351     },
19352
19353     /**
19354      * APIMethod: getTilesBounds
19355      * Return the bounds of the tile grid.
19356      *
19357      * Returns:
19358      * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the
19359      *     currently loaded tiles (including those partially or not at all seen 
19360      *     onscreen).
19361      */
19362     getTilesBounds: function() {    
19363         var bounds = null; 
19364         
19365         var length = this.grid.length;
19366         if (length) {
19367             var bottomLeftTileBounds = this.grid[length - 1][0].bounds,
19368                 width = this.grid[0].length * bottomLeftTileBounds.getWidth(),
19369                 height = this.grid.length * bottomLeftTileBounds.getHeight();
19370             
19371             bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left, 
19372                                            bottomLeftTileBounds.bottom,
19373                                            bottomLeftTileBounds.left + width, 
19374                                            bottomLeftTileBounds.bottom + height);
19375         }   
19376         return bounds;
19377     },
19378
19379     /**
19380      * Method: initSingleTile
19381      * 
19382      * Parameters: 
19383      * bounds - {<OpenLayers.Bounds>}
19384      */
19385     initSingleTile: function(bounds) {
19386         this.events.triggerEvent("retile");
19387
19388         //determine new tile bounds
19389         var center = bounds.getCenterLonLat();
19390         var tileWidth = bounds.getWidth() * this.ratio;
19391         var tileHeight = bounds.getHeight() * this.ratio;
19392                                        
19393         var tileBounds = 
19394             new OpenLayers.Bounds(center.lon - (tileWidth/2),
19395                                   center.lat - (tileHeight/2),
19396                                   center.lon + (tileWidth/2),
19397                                   center.lat + (tileHeight/2));
19398   
19399         var px = this.map.getLayerPxFromLonLat({
19400             lon: tileBounds.left,
19401             lat: tileBounds.top
19402         });
19403
19404         if (!this.grid.length) {
19405             this.grid[0] = [];
19406         }
19407
19408         var tile = this.grid[0][0];
19409         if (!tile) {
19410             tile = this.addTile(tileBounds, px);
19411             
19412             this.addTileMonitoringHooks(tile);
19413             tile.draw();
19414             this.grid[0][0] = tile;
19415         } else {
19416             tile.moveTo(tileBounds, px);
19417         }           
19418         
19419         //remove all but our single tile
19420         this.removeExcessTiles(1,1);
19421
19422         // store the resolution of the grid
19423         this.gridResolution = this.getServerResolution();
19424     },
19425
19426     /** 
19427      * Method: calculateGridLayout
19428      * Generate parameters for the grid layout.
19429      *
19430      * Parameters:
19431      * bounds - {<OpenLayers.Bound>|Object} OpenLayers.Bounds or an
19432      *     object with a 'left' and 'top' properties.
19433      * origin - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an
19434      *     object with a 'lon' and 'lat' properties.
19435      * resolution - {Number}
19436      *
19437      * Returns:
19438      * {Object} Object containing properties tilelon, tilelat, startcol,
19439      * startrow
19440      */
19441     calculateGridLayout: function(bounds, origin, resolution) {
19442         var tilelon = resolution * this.tileSize.w;
19443         var tilelat = resolution * this.tileSize.h;
19444         
19445         var offsetlon = bounds.left - origin.lon;
19446         var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
19447         
19448         var rowSign = this.rowSign;
19449
19450         var offsetlat = rowSign * (origin.lat - bounds.top + tilelat);  
19451         var tilerow = Math[~rowSign ? 'floor' : 'ceil'](offsetlat/tilelat) - this.buffer * rowSign;
19452         
19453         return { 
19454           tilelon: tilelon, tilelat: tilelat,
19455           startcol: tilecol, startrow: tilerow
19456         };
19457
19458     },
19459     
19460     /**
19461      * Method: getTileOrigin
19462      * Determine the origin for aligning the grid of tiles.  If a <tileOrigin>
19463      *     property is supplied, that will be returned.  Otherwise, the origin
19464      *     will be derived from the layer's <maxExtent> property.  In this case,
19465      *     the tile origin will be the corner of the <maxExtent> given by the 
19466      *     <tileOriginCorner> property.
19467      *
19468      * Returns:
19469      * {<OpenLayers.LonLat>} The tile origin.
19470      */
19471     getTileOrigin: function() {
19472         var origin = this.tileOrigin;
19473         if (!origin) {
19474             var extent = this.getMaxExtent();
19475             var edges = ({
19476                 "tl": ["left", "top"],
19477                 "tr": ["right", "top"],
19478                 "bl": ["left", "bottom"],
19479                 "br": ["right", "bottom"]
19480             })[this.tileOriginCorner];
19481             origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]);
19482         }
19483         return origin;
19484     },
19485
19486     /**
19487      * Method: getTileBoundsForGridIndex
19488      *
19489      * Parameters:
19490      * row - {Number} The row of the grid
19491      * col - {Number} The column of the grid
19492      *
19493      * Returns:
19494      * {<OpenLayers.Bounds>} The bounds for the tile at (row, col)
19495      */
19496     getTileBoundsForGridIndex: function(row, col) {
19497         var origin = this.getTileOrigin();
19498         var tileLayout = this.gridLayout;
19499         var tilelon = tileLayout.tilelon;
19500         var tilelat = tileLayout.tilelat;
19501         var startcol = tileLayout.startcol;
19502         var startrow = tileLayout.startrow;
19503         var rowSign = this.rowSign;
19504         return new OpenLayers.Bounds(
19505             origin.lon + (startcol + col) * tilelon,
19506             origin.lat - (startrow + row * rowSign) * tilelat * rowSign,
19507             origin.lon + (startcol + col + 1) * tilelon,
19508             origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign
19509         );
19510     },
19511
19512     /**
19513      * Method: initGriddedTiles
19514      * 
19515      * Parameters:
19516      * bounds - {<OpenLayers.Bounds>}
19517      */
19518     initGriddedTiles:function(bounds) {
19519         this.events.triggerEvent("retile");
19520
19521         // work out mininum number of rows and columns; this is the number of
19522         // tiles required to cover the viewport plus at least one for panning
19523
19524         var viewSize = this.map.getSize();
19525         
19526         var origin = this.getTileOrigin();
19527         var resolution = this.map.getResolution(),
19528             serverResolution = this.getServerResolution(),
19529             ratio = resolution / serverResolution,
19530             tileSize = {
19531                 w: this.tileSize.w / ratio,
19532                 h: this.tileSize.h / ratio
19533             };
19534
19535         var minRows = Math.ceil(viewSize.h/tileSize.h) + 
19536                       2 * this.buffer + 1;
19537         var minCols = Math.ceil(viewSize.w/tileSize.w) +
19538                       2 * this.buffer + 1;
19539
19540         var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution);
19541         this.gridLayout = tileLayout;
19542         
19543         var tilelon = tileLayout.tilelon;
19544         var tilelat = tileLayout.tilelat;
19545         
19546         var layerContainerDivLeft = this.map.layerContainerOriginPx.x;
19547         var layerContainerDivTop = this.map.layerContainerOriginPx.y;
19548
19549         var tileBounds = this.getTileBoundsForGridIndex(0, 0);
19550         var startPx = this.map.getViewPortPxFromLonLat(
19551             new OpenLayers.LonLat(tileBounds.left, tileBounds.top)
19552         );
19553         startPx.x = Math.round(startPx.x) - layerContainerDivLeft;
19554         startPx.y = Math.round(startPx.y) - layerContainerDivTop;
19555
19556         var tileData = [], center = this.map.getCenter();
19557
19558         var rowidx = 0;
19559         do {
19560             var row = this.grid[rowidx];
19561             if (!row) {
19562                 row = [];
19563                 this.grid.push(row);
19564             }
19565             
19566             var colidx = 0;
19567             do {
19568                 tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx);
19569                 var px = startPx.clone();
19570                 px.x = px.x + colidx * Math.round(tileSize.w);
19571                 px.y = px.y + rowidx * Math.round(tileSize.h);
19572                 var tile = row[colidx];
19573                 if (!tile) {
19574                     tile = this.addTile(tileBounds, px);
19575                     this.addTileMonitoringHooks(tile);
19576                     row.push(tile);
19577                 } else {
19578                     tile.moveTo(tileBounds, px, false);
19579                 }
19580                 var tileCenter = tileBounds.getCenterLonLat();
19581                 tileData.push({
19582                     tile: tile,
19583                     distance: Math.pow(tileCenter.lon - center.lon, 2) +
19584                         Math.pow(tileCenter.lat - center.lat, 2)
19585                 });
19586      
19587                 colidx += 1;
19588             } while ((tileBounds.right <= bounds.right + tilelon * this.buffer)
19589                      || colidx < minCols);
19590              
19591             rowidx += 1;
19592         } while((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer)
19593                 || rowidx < minRows);
19594         
19595         //shave off exceess rows and colums
19596         this.removeExcessTiles(rowidx, colidx);
19597
19598         var resolution = this.getServerResolution();
19599         // store the resolution of the grid
19600         this.gridResolution = resolution;
19601
19602         //now actually draw the tiles
19603         tileData.sort(function(a, b) {
19604             return a.distance - b.distance; 
19605         });
19606         for (var i=0, ii=tileData.length; i<ii; ++i) {
19607             tileData[i].tile.draw();
19608         }
19609     },
19610
19611     /**
19612      * Method: getMaxExtent
19613      * Get this layer's maximum extent. (Implemented as a getter for
19614      *     potential specific implementations in sub-classes.)
19615      *
19616      * Returns:
19617      * {<OpenLayers.Bounds>}
19618      */
19619     getMaxExtent: function() {
19620         return this.maxExtent;
19621     },
19622     
19623     /**
19624      * APIMethod: addTile
19625      * Create a tile, initialize it, and add it to the layer div. 
19626      *
19627      * Parameters
19628      * bounds - {<OpenLayers.Bounds>}
19629      * position - {<OpenLayers.Pixel>}
19630      *
19631      * Returns:
19632      * {<OpenLayers.Tile>} The added OpenLayers.Tile
19633      */
19634     addTile: function(bounds, position) {
19635         var tile = new this.tileClass(
19636             this, position, bounds, null, this.tileSize, this.tileOptions
19637         );
19638         this.events.triggerEvent("addtile", {tile: tile});
19639         return tile;
19640     },
19641     
19642     /** 
19643      * Method: addTileMonitoringHooks
19644      * This function takes a tile as input and adds the appropriate hooks to 
19645      *     the tile so that the layer can keep track of the loading tiles.
19646      * 
19647      * Parameters: 
19648      * tile - {<OpenLayers.Tile>}
19649      */
19650     addTileMonitoringHooks: function(tile) {
19651         
19652         var replacingCls = 'olTileReplacing';
19653
19654         tile.onLoadStart = function() {
19655             //if that was first tile then trigger a 'loadstart' on the layer
19656             if (this.loading === false) {
19657                 this.loading = true;
19658                 this.events.triggerEvent("loadstart");
19659             }
19660             this.events.triggerEvent("tileloadstart", {tile: tile});
19661             this.numLoadingTiles++;
19662             if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) {
19663                 OpenLayers.Element.addClass(tile.getTile(), replacingCls);
19664             }
19665         };
19666       
19667         tile.onLoadEnd = function(evt) {
19668             this.numLoadingTiles--;
19669             var aborted = evt.type === 'unload';
19670             this.events.triggerEvent("tileloaded", {
19671                 tile: tile,
19672                 aborted: aborted
19673             });
19674             if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) {
19675                 var tileDiv = tile.getTile();
19676                 if (OpenLayers.Element.getStyle(tileDiv, 'display') === 'none') {
19677                     var bufferTile = document.getElementById(tile.id + '_bb');
19678                     if (bufferTile) {
19679                         bufferTile.parentNode.removeChild(bufferTile);
19680                     }
19681                 }
19682                 OpenLayers.Element.removeClass(tileDiv, replacingCls);
19683             }
19684             //if that was the last tile, then trigger a 'loadend' on the layer
19685             if (this.numLoadingTiles === 0) {
19686                 if (this.backBuffer) {
19687                     if (this.backBuffer.childNodes.length === 0) {
19688                         // no tiles transitioning, remove immediately
19689                         this.removeBackBuffer();
19690                     } else {
19691                         // wait until transition has ended or delay has passed
19692                         this._transitionElement = aborted ?
19693                             this.div.lastChild : tile.imgDiv;
19694                         var transitionendEvents = this.transitionendEvents;
19695                         for (var i=transitionendEvents.length-1; i>=0; --i) {
19696                             OpenLayers.Event.observe(this._transitionElement,
19697                                 transitionendEvents[i],
19698                                 this._removeBackBuffer);
19699                         }
19700                         // the removal of the back buffer is delayed to prevent
19701                         // flash effects due to the animation of tile displaying
19702                         this.backBufferTimerId = window.setTimeout(
19703                             this._removeBackBuffer, this.removeBackBufferDelay
19704                         );
19705                     }
19706                 }
19707                 this.loading = false;
19708                 this.events.triggerEvent("loadend");
19709             }
19710         };
19711         
19712         tile.onLoadError = function() {
19713             this.events.triggerEvent("tileerror", {tile: tile});
19714         };
19715         
19716         tile.events.on({
19717             "loadstart": tile.onLoadStart,
19718             "loadend": tile.onLoadEnd,
19719             "unload": tile.onLoadEnd,
19720             "loaderror": tile.onLoadError,
19721             scope: this
19722         });
19723     },
19724
19725     /** 
19726      * Method: removeTileMonitoringHooks
19727      * This function takes a tile as input and removes the tile hooks 
19728      *     that were added in addTileMonitoringHooks()
19729      * 
19730      * Parameters: 
19731      * tile - {<OpenLayers.Tile>}
19732      */
19733     removeTileMonitoringHooks: function(tile) {
19734         tile.unload();
19735         tile.events.un({
19736             "loadstart": tile.onLoadStart,
19737             "loadend": tile.onLoadEnd,
19738             "unload": tile.onLoadEnd,
19739             "loaderror": tile.onLoadError,
19740             scope: this
19741         });
19742     },
19743     
19744     /**
19745      * Method: moveGriddedTiles
19746      */
19747     moveGriddedTiles: function() {
19748         var buffer = this.buffer + 1;
19749         while(true) {
19750             var tlTile = this.grid[0][0];
19751             var tlViewPort = {
19752                 x: tlTile.position.x +
19753                     this.map.layerContainerOriginPx.x,
19754                 y: tlTile.position.y +
19755                     this.map.layerContainerOriginPx.y
19756             };
19757             var ratio = this.getServerResolution() / this.map.getResolution();
19758             var tileSize = {
19759                 w: Math.round(this.tileSize.w * ratio),
19760                 h: Math.round(this.tileSize.h * ratio)
19761             };
19762             if (tlViewPort.x > -tileSize.w * (buffer - 1)) {
19763                 this.shiftColumn(true, tileSize);
19764             } else if (tlViewPort.x < -tileSize.w * buffer) {
19765                 this.shiftColumn(false, tileSize);
19766             } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {
19767                 this.shiftRow(true, tileSize);
19768             } else if (tlViewPort.y < -tileSize.h * buffer) {
19769                 this.shiftRow(false, tileSize);
19770             } else {
19771                 break;
19772             }
19773         }
19774     },
19775
19776     /**
19777      * Method: shiftRow
19778      * Shifty grid work
19779      *
19780      * Parameters:
19781      * prepend - {Boolean} if true, prepend to beginning.
19782      *                          if false, then append to end
19783      * tileSize - {Object} rendered tile size; object with w and h properties
19784      */
19785     shiftRow: function(prepend, tileSize) {
19786         var grid = this.grid;
19787         var rowIndex = prepend ? 0 : (grid.length - 1);
19788         var sign = prepend ? -1 : 1;
19789         var rowSign = this.rowSign;
19790         var tileLayout = this.gridLayout;
19791         tileLayout.startrow += sign * rowSign;
19792
19793         var modelRow = grid[rowIndex];
19794         var row = grid[prepend ? 'pop' : 'shift']();
19795         for (var i=0, len=row.length; i<len; i++) {
19796             var tile = row[i];
19797             var position = modelRow[i].position.clone();
19798             position.y += tileSize.h * sign;
19799             tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position);
19800         }
19801         grid[prepend ? 'unshift' : 'push'](row);
19802     },
19803
19804     /**
19805      * Method: shiftColumn
19806      * Shift grid work in the other dimension
19807      *
19808      * Parameters:
19809      * prepend - {Boolean} if true, prepend to beginning.
19810      *                          if false, then append to end
19811      * tileSize - {Object} rendered tile size; object with w and h properties
19812      */
19813     shiftColumn: function(prepend, tileSize) {
19814         var grid = this.grid;
19815         var colIndex = prepend ? 0 : (grid[0].length - 1);
19816         var sign = prepend ? -1 : 1;
19817         var tileLayout = this.gridLayout;
19818         tileLayout.startcol += sign;
19819
19820         for (var i=0, len=grid.length; i<len; i++) {
19821             var row = grid[i];
19822             var position = row[colIndex].position.clone();
19823             var tile = row[prepend ? 'pop' : 'shift']();            
19824             position.x += tileSize.w * sign;
19825             tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position);
19826             row[prepend ? 'unshift' : 'push'](tile);
19827         }
19828     },
19829
19830     /**
19831      * Method: removeExcessTiles
19832      * When the size of the map or the buffer changes, we may need to
19833      *     remove some excess rows and columns.
19834      * 
19835      * Parameters:
19836      * rows - {Integer} Maximum number of rows we want our grid to have.
19837      * columns - {Integer} Maximum number of columns we want our grid to have.
19838      */
19839     removeExcessTiles: function(rows, columns) {
19840         var i, l;
19841         
19842         // remove extra rows
19843         while (this.grid.length > rows) {
19844             var row = this.grid.pop();
19845             for (i=0, l=row.length; i<l; i++) {
19846                 var tile = row[i];
19847                 this.destroyTile(tile);
19848             }
19849         }
19850         
19851         // remove extra columns
19852         for (i=0, l=this.grid.length; i<l; i++) {
19853             while (this.grid[i].length > columns) {
19854                 var row = this.grid[i];
19855                 var tile = row.pop();
19856                 this.destroyTile(tile);
19857             }
19858         }
19859     },
19860
19861     /**
19862      * Method: onMapResize
19863      * For singleTile layers, this will set a new tile size according to the
19864      * dimensions of the map pane.
19865      */
19866     onMapResize: function() {
19867         if (this.singleTile) {
19868             this.clearGrid();
19869             this.setTileSize();
19870         }
19871     },
19872     
19873     /**
19874      * APIMethod: getTileBounds
19875      * Returns The tile bounds for a layer given a pixel location.
19876      *
19877      * Parameters:
19878      * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport.
19879      *
19880      * Returns:
19881      * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location.
19882      */
19883     getTileBounds: function(viewPortPx) {
19884         var maxExtent = this.maxExtent;
19885         var resolution = this.getResolution();
19886         var tileMapWidth = resolution * this.tileSize.w;
19887         var tileMapHeight = resolution * this.tileSize.h;
19888         var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);
19889         var tileLeft = maxExtent.left + (tileMapWidth *
19890                                          Math.floor((mapPoint.lon -
19891                                                      maxExtent.left) /
19892                                                     tileMapWidth));
19893         var tileBottom = maxExtent.bottom + (tileMapHeight *
19894                                              Math.floor((mapPoint.lat -
19895                                                          maxExtent.bottom) /
19896                                                         tileMapHeight));
19897         return new OpenLayers.Bounds(tileLeft, tileBottom,
19898                                      tileLeft + tileMapWidth,
19899                                      tileBottom + tileMapHeight);
19900     },
19901
19902     CLASS_NAME: "OpenLayers.Layer.Grid"
19903 });
19904 /* ======================================================================
19905     OpenLayers/Layer/XYZ.js
19906    ====================================================================== */
19907
19908 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
19909  * full list of contributors). Published under the 2-clause BSD license.
19910  * See license.txt in the OpenLayers distribution or repository for the
19911  * full text of the license. */
19912
19913 /**
19914  * @requires OpenLayers/Layer/Grid.js
19915  */
19916
19917 /** 
19918  * Class: OpenLayers.Layer.XYZ
19919  * The XYZ class is designed to make it easier for people who have tiles
19920  * arranged by a standard XYZ grid. 
19921  * 
19922  * Inherits from:
19923  *  - <OpenLayers.Layer.Grid>
19924  */
19925 OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {
19926     
19927     /**
19928      * APIProperty: isBaseLayer
19929      * Default is true, as this is designed to be a base tile source. 
19930      */
19931     isBaseLayer: true,
19932     
19933     /**
19934      * APIProperty: sphericalMercator
19935      * Whether the tile extents should be set to the defaults for 
19936      *    spherical mercator. Useful for things like OpenStreetMap.
19937      *    Default is false, except for the OSM subclass.
19938      */
19939     sphericalMercator: false,
19940
19941     /**
19942      * APIProperty: zoomOffset
19943      * {Number} If your cache has more zoom levels than you want to provide
19944      *     access to with this layer, supply a zoomOffset.  This zoom offset
19945      *     is added to the current map zoom level to determine the level
19946      *     for a requested tile.  For example, if you supply a zoomOffset
19947      *     of 3, when the map is at the zoom 0, tiles will be requested from
19948      *     level 3 of your cache.  Default is 0 (assumes cache level and map
19949      *     zoom are equivalent).  Using <zoomOffset> is an alternative to
19950      *     setting <serverResolutions> if you only want to expose a subset
19951      *     of the server resolutions.
19952      */
19953     zoomOffset: 0,
19954     
19955     /**
19956      * APIProperty: serverResolutions
19957      * {Array} A list of all resolutions available on the server.  Only set this
19958      *     property if the map resolutions differ from the server. This
19959      *     property serves two purposes. (a) <serverResolutions> can include
19960      *     resolutions that the server supports and that you don't want to
19961      *     provide with this layer; you can also look at <zoomOffset>, which is
19962      *     an alternative to <serverResolutions> for that specific purpose.
19963      *     (b) The map can work with resolutions that aren't supported by
19964      *     the server, i.e. that aren't in <serverResolutions>. When the
19965      *     map is displayed in such a resolution data for the closest
19966      *     server-supported resolution is loaded and the layer div is
19967      *     stretched as necessary.
19968      */
19969     serverResolutions: null,
19970
19971     /**
19972      * Constructor: OpenLayers.Layer.XYZ
19973      *
19974      * Parameters:
19975      * name - {String}
19976      * url - {String}
19977      * options - {Object} Hashtable of extra options to tag onto the layer
19978      */
19979     initialize: function(name, url, options) {
19980         if (options && options.sphericalMercator || this.sphericalMercator) {
19981             options = OpenLayers.Util.extend({
19982                 projection: "EPSG:900913",
19983                 numZoomLevels: 19
19984             }, options);
19985         }
19986         OpenLayers.Layer.Grid.prototype.initialize.apply(this, [
19987             name || this.name, url || this.url, {}, options
19988         ]);
19989     },
19990     
19991     /**
19992      * APIMethod: clone
19993      * Create a clone of this layer
19994      *
19995      * Parameters:
19996      * obj - {Object} Is this ever used?
19997      * 
19998      * Returns:
19999      * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ
20000      */
20001     clone: function (obj) {
20002         
20003         if (obj == null) {
20004             obj = new OpenLayers.Layer.XYZ(this.name,
20005                                             this.url,
20006                                             this.getOptions());
20007         }
20008
20009         //get all additions from superclasses
20010         obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
20011
20012         return obj;
20013     },    
20014
20015     /**
20016      * Method: getURL
20017      *
20018      * Parameters:
20019      * bounds - {<OpenLayers.Bounds>}
20020      *
20021      * Returns:
20022      * {String} A string with the layer's url and parameters and also the
20023      *          passed-in bounds and appropriate tile size specified as
20024      *          parameters
20025      */
20026     getURL: function (bounds) {
20027         var xyz = this.getXYZ(bounds);
20028         var url = this.url;
20029         if (OpenLayers.Util.isArray(url)) {
20030             var s = '' + xyz.x + xyz.y + xyz.z;
20031             url = this.selectUrl(s, url);
20032         }
20033         
20034         return OpenLayers.String.format(url, xyz);
20035     },
20036     
20037     /**
20038      * Method: getXYZ
20039      * Calculates x, y and z for the given bounds.
20040      *
20041      * Parameters:
20042      * bounds - {<OpenLayers.Bounds>}
20043      *
20044      * Returns:
20045      * {Object} - an object with x, y and z properties.
20046      */
20047     getXYZ: function(bounds) {
20048         var res = this.getServerResolution();
20049         var x = Math.round((bounds.left - this.maxExtent.left) /
20050             (res * this.tileSize.w));
20051         var y = Math.round((this.maxExtent.top - bounds.top) /
20052             (res * this.tileSize.h));
20053         var z = this.getServerZoom();
20054
20055         if (this.wrapDateLine) {
20056             var limit = Math.pow(2, z);
20057             x = ((x % limit) + limit) % limit;
20058         }
20059
20060         return {'x': x, 'y': y, 'z': z};
20061     },
20062     
20063     /* APIMethod: setMap
20064      * When the layer is added to a map, then we can fetch our origin 
20065      *    (if we don't have one.) 
20066      * 
20067      * Parameters:
20068      * map - {<OpenLayers.Map>}
20069      */
20070     setMap: function(map) {
20071         OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
20072         if (!this.tileOrigin) { 
20073             this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left,
20074                                                 this.maxExtent.bottom);
20075         }                                       
20076     },
20077
20078     CLASS_NAME: "OpenLayers.Layer.XYZ"
20079 });
20080 /* ======================================================================
20081     OpenLayers/Layer/OSM.js
20082    ====================================================================== */
20083
20084 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
20085  * full list of contributors). Published under the 2-clause BSD license.
20086  * See license.txt in the OpenLayers distribution or repository for the
20087  * full text of the license. */
20088
20089 /**
20090  * @requires OpenLayers/Layer/XYZ.js
20091  */
20092
20093 /**
20094  * Class: OpenLayers.Layer.OSM
20095  * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap
20096  *    hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use
20097  *    a different layer instead, you need to provide a different
20098  *    URL to the constructor. Here's an example for using OpenCycleMap:
20099  * 
20100  * (code)
20101  *     new OpenLayers.Layer.OSM("OpenCycleMap", 
20102  *       ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
20103  *        "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
20104  *        "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]); 
20105  * (end)
20106  *
20107  * Inherits from:
20108  *  - <OpenLayers.Layer.XYZ>
20109  */
20110 OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
20111
20112     /**
20113      * APIProperty: name
20114      * {String} The layer name. Defaults to "OpenStreetMap" if the first
20115      * argument to the constructor is null or undefined.
20116      */
20117     name: "OpenStreetMap",
20118
20119     /**
20120      * APIProperty: url
20121      * {String} The tileset URL scheme. Defaults to
20122      * : http://[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png
20123      * (the official OSM tileset) if the second argument to the constructor
20124      * is null or undefined. To use another tileset you can have something
20125      * like this:
20126      * (code)
20127      *     new OpenLayers.Layer.OSM("OpenCycleMap", 
20128      *       ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
20129      *        "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
20130      *        "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]); 
20131      * (end)
20132      */
20133     url: [
20134         'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png',
20135         'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png',
20136         'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png'
20137     ],
20138
20139     /**
20140      * Property: attribution
20141      * {String} The layer attribution.
20142      */
20143     attribution: "&copy; <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors",
20144
20145     /**
20146      * Property: sphericalMercator
20147      * {Boolean}
20148      */
20149     sphericalMercator: true,
20150
20151     /**
20152      * Property: wrapDateLine
20153      * {Boolean}
20154      */
20155     wrapDateLine: true,
20156
20157     /** APIProperty: tileOptions
20158      *  {Object} optional configuration options for <OpenLayers.Tile> instances
20159      *  created by this Layer. Default is
20160      *
20161      *  (code)
20162      *  {crossOriginKeyword: 'anonymous'}
20163      *  (end)
20164      *
20165      *  When using OSM tilesets other than the default ones, it may be
20166      *  necessary to set this to
20167      *
20168      *  (code)
20169      *  {crossOriginKeyword: null}
20170      *  (end)
20171      *
20172      *  if the server does not send Access-Control-Allow-Origin headers.
20173      */
20174     tileOptions: null,
20175
20176     /**
20177      * Constructor: OpenLayers.Layer.OSM
20178      *
20179      * Parameters:
20180      * name - {String} The layer name.
20181      * url - {String} The tileset URL scheme.
20182      * options - {Object} Configuration options for the layer. Any inherited
20183      *     layer option can be set in this object (e.g.
20184      *     <OpenLayers.Layer.Grid.buffer>).
20185      */
20186     initialize: function(name, url, options) {
20187         OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);
20188         this.tileOptions = OpenLayers.Util.extend({
20189             crossOriginKeyword: 'anonymous'
20190         }, this.options && this.options.tileOptions);
20191     },
20192
20193     /**
20194      * Method: clone
20195      */
20196     clone: function(obj) {
20197         if (obj == null) {
20198             obj = new OpenLayers.Layer.OSM(
20199                 this.name, this.url, this.getOptions());
20200         }
20201         obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
20202         return obj;
20203     },
20204
20205     CLASS_NAME: "OpenLayers.Layer.OSM"
20206 });
20207 /* ======================================================================
20208     OpenLayers/Renderer.js
20209    ====================================================================== */
20210
20211 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
20212  * full list of contributors). Published under the 2-clause BSD license.
20213  * See license.txt in the OpenLayers distribution or repository for the
20214  * full text of the license. */
20215
20216 /**
20217  * @requires OpenLayers/BaseTypes/Class.js
20218  */
20219
20220 /**
20221  * Class: OpenLayers.Renderer 
20222  * This is the base class for all renderers.
20223  *
20224  * This is based on a merger code written by Paul Spencer and Bertil Chapuis.
20225  * It is largely composed of virtual functions that are to be implemented
20226  * in technology-specific subclasses, but there is some generic code too.
20227  * 
20228  * The functions that *are* implemented here merely deal with the maintenance
20229  *  of the size and extent variables, as well as the cached 'resolution' 
20230  *  value. 
20231  * 
20232  * A note to the user that all subclasses should use getResolution() instead
20233  *  of directly accessing this.resolution in order to correctly use the 
20234  *  cacheing system.
20235  *
20236  */
20237 OpenLayers.Renderer = OpenLayers.Class({
20238
20239     /** 
20240      * Property: container
20241      * {DOMElement} 
20242      */
20243     container: null,
20244     
20245     /**
20246      * Property: root
20247      * {DOMElement}
20248      */
20249     root: null,
20250
20251     /** 
20252      * Property: extent
20253      * {<OpenLayers.Bounds>}
20254      */
20255     extent: null,
20256
20257     /**
20258      * Property: locked
20259      * {Boolean} If the renderer is currently in a state where many things
20260      *     are changing, the 'locked' property is set to true. This means 
20261      *     that renderers can expect at least one more drawFeature event to be
20262      *     called with the 'locked' property set to 'true': In some renderers,
20263      *     this might make sense to use as a 'only update local information'
20264      *     flag. 
20265      */  
20266     locked: false,
20267     
20268     /** 
20269      * Property: size
20270      * {<OpenLayers.Size>} 
20271      */
20272     size: null,
20273     
20274     /**
20275      * Property: resolution
20276      * {Float} cache of current map resolution
20277      */
20278     resolution: null,
20279     
20280     /**
20281      * Property: map  
20282      * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap()
20283      */
20284     map: null,
20285     
20286     /**
20287      * Property: featureDx
20288      * {Number} Feature offset in x direction. Will be calculated for and
20289      * applied to the current feature while rendering (see
20290      * <calculateFeatureDx>).
20291      */
20292     featureDx: 0,
20293     
20294     /**
20295      * Constructor: OpenLayers.Renderer 
20296      *
20297      * Parameters:
20298      * containerID - {<String>} 
20299      * options - {Object} options for this renderer. See sublcasses for
20300      *     supported options.
20301      */
20302     initialize: function(containerID, options) {
20303         this.container = OpenLayers.Util.getElement(containerID);
20304         OpenLayers.Util.extend(this, options);
20305     },
20306     
20307     /**
20308      * APIMethod: destroy
20309      */
20310     destroy: function() {
20311         this.container = null;
20312         this.extent = null;
20313         this.size =  null;
20314         this.resolution = null;
20315         this.map = null;
20316     },
20317
20318     /**
20319      * APIMethod: supported
20320      * This should be overridden by specific subclasses
20321      * 
20322      * Returns:
20323      * {Boolean} Whether or not the browser supports the renderer class
20324      */
20325     supported: function() {
20326         return false;
20327     },    
20328     
20329     /**
20330      * Method: setExtent
20331      * Set the visible part of the layer.
20332      *
20333      * Resolution has probably changed, so we nullify the resolution 
20334      * cache (this.resolution) -- this way it will be re-computed when 
20335      * next it is needed.
20336      * We nullify the resolution cache (this.resolution) if resolutionChanged
20337      * is set to true - this way it will be re-computed on the next
20338      * getResolution() request.
20339      *
20340      * Parameters:
20341      * extent - {<OpenLayers.Bounds>}
20342      * resolutionChanged - {Boolean}
20343      *
20344      * Returns:
20345      * {Boolean} true to notify the layer that the new extent does not exceed
20346      *     the coordinate range, and the features will not need to be redrawn.
20347      *     False otherwise.
20348      */
20349     setExtent: function(extent, resolutionChanged) {
20350         this.extent = extent.clone();
20351         if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
20352             var ratio = extent.getWidth() / this.map.getExtent().getWidth(),
20353                 extent = extent.scale(1 / ratio);
20354             this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio);
20355         }
20356         if (resolutionChanged) {
20357             this.resolution = null;
20358         }
20359         return true;
20360     },
20361     
20362     /**
20363      * Method: setSize
20364      * Sets the size of the drawing surface.
20365      * 
20366      * Resolution has probably changed, so we nullify the resolution 
20367      * cache (this.resolution) -- this way it will be re-computed when 
20368      * next it is needed.
20369      *
20370      * Parameters:
20371      * size - {<OpenLayers.Size>} 
20372      */
20373     setSize: function(size) {
20374         this.size = size.clone();
20375         this.resolution = null;
20376     },
20377     
20378     /** 
20379      * Method: getResolution
20380      * Uses cached copy of resolution if available to minimize computing
20381      * 
20382      * Returns:
20383      * {Float} The current map's resolution
20384      */
20385     getResolution: function() {
20386         this.resolution = this.resolution || this.map.getResolution();
20387         return this.resolution;
20388     },
20389     
20390     /**
20391      * Method: drawFeature
20392      * Draw the feature.  The optional style argument can be used
20393      * to override the feature's own style.  This method should only
20394      * be called from layer.drawFeature().
20395      *
20396      * Parameters:
20397      * feature - {<OpenLayers.Feature.Vector>} 
20398      * style - {<Object>}
20399      * 
20400      * Returns:
20401      * {Boolean} true if the feature has been drawn completely, false if not,
20402      *     undefined if the feature had no geometry
20403      */
20404     drawFeature: function(feature, style) {
20405         if(style == null) {
20406             style = feature.style;
20407         }
20408         if (feature.geometry) {
20409             var bounds = feature.geometry.getBounds();
20410             if(bounds) {
20411                 var worldBounds;
20412                 if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
20413                     worldBounds = this.map.getMaxExtent();
20414                 }
20415                 if (!bounds.intersectsBounds(this.extent, {worldBounds: worldBounds})) {
20416                     style = {display: "none"};
20417                 } else {
20418                     this.calculateFeatureDx(bounds, worldBounds);
20419                 }
20420                 var rendered = this.drawGeometry(feature.geometry, style, feature.id);
20421                 if(style.display != "none" && style.label && rendered !== false) {
20422
20423                     var location = feature.geometry.getCentroid(); 
20424                     if(style.labelXOffset || style.labelYOffset) {
20425                         var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;
20426                         var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;
20427                         var res = this.getResolution();
20428                         location.move(xOffset*res, yOffset*res);
20429                     }
20430                     this.drawText(feature.id, style, location);
20431                 } else {
20432                     this.removeText(feature.id);
20433                 }
20434                 return rendered;
20435             }
20436         }
20437     },
20438
20439     /**
20440      * Method: calculateFeatureDx
20441      * {Number} Calculates the feature offset in x direction. Looking at the
20442      * center of the feature bounds and the renderer extent, we calculate how
20443      * many world widths the two are away from each other. This distance is
20444      * used to shift the feature as close as possible to the center of the
20445      * current enderer extent, which ensures that the feature is visible in the
20446      * current viewport.
20447      *
20448      * Parameters:
20449      * bounds - {<OpenLayers.Bounds>} Bounds of the feature
20450      * worldBounds - {<OpenLayers.Bounds>} Bounds of the world
20451      */
20452     calculateFeatureDx: function(bounds, worldBounds) {
20453         this.featureDx = 0;
20454         if (worldBounds) {
20455             var worldWidth = worldBounds.getWidth(),
20456                 rendererCenterX = (this.extent.left + this.extent.right) / 2,
20457                 featureCenterX = (bounds.left + bounds.right) / 2,
20458                 worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);
20459             this.featureDx = worldsAway * worldWidth;
20460         }
20461     },
20462
20463     /** 
20464      * Method: drawGeometry
20465      * 
20466      * Draw a geometry.  This should only be called from the renderer itself.
20467      * Use layer.drawFeature() from outside the renderer.
20468      * virtual function
20469      *
20470      * Parameters:
20471      * geometry - {<OpenLayers.Geometry>} 
20472      * style - {Object} 
20473      * featureId - {<String>} 
20474      */
20475     drawGeometry: function(geometry, style, featureId) {},
20476         
20477     /**
20478      * Method: drawText
20479      * Function for drawing text labels.
20480      * This method is only called by the renderer itself.
20481      * 
20482      * Parameters: 
20483      * featureId - {String}
20484      * style -
20485      * location - {<OpenLayers.Geometry.Point>}
20486      */
20487     drawText: function(featureId, style, location) {},
20488
20489     /**
20490      * Method: removeText
20491      * Function for removing text labels.
20492      * This method is only called by the renderer itself.
20493      * 
20494      * Parameters: 
20495      * featureId - {String}
20496      */
20497     removeText: function(featureId) {},
20498     
20499     /**
20500      * Method: clear
20501      * Clear all vectors from the renderer.
20502      * virtual function.
20503      */    
20504     clear: function() {},
20505
20506     /**
20507      * Method: getFeatureIdFromEvent
20508      * Returns a feature id from an event on the renderer.  
20509      * How this happens is specific to the renderer.  This should be
20510      * called from layer.getFeatureFromEvent().
20511      * Virtual function.
20512      * 
20513      * Parameters:
20514      * evt - {<OpenLayers.Event>} 
20515      *
20516      * Returns:
20517      * {String} A feature id or undefined.
20518      */
20519     getFeatureIdFromEvent: function(evt) {},
20520     
20521     /**
20522      * Method: eraseFeatures 
20523      * This is called by the layer to erase features
20524      * 
20525      * Parameters:
20526      * features - {Array(<OpenLayers.Feature.Vector>)} 
20527      */
20528     eraseFeatures: function(features) {
20529         if(!(OpenLayers.Util.isArray(features))) {
20530             features = [features];
20531         }
20532         for(var i=0, len=features.length; i<len; ++i) {
20533             var feature = features[i];
20534             this.eraseGeometry(feature.geometry, feature.id);
20535             this.removeText(feature.id);
20536         }
20537     },
20538     
20539     /**
20540      * Method: eraseGeometry
20541      * Remove a geometry from the renderer (by id).
20542      * virtual function.
20543      * 
20544      * Parameters:
20545      * geometry - {<OpenLayers.Geometry>} 
20546      * featureId - {String}
20547      */
20548     eraseGeometry: function(geometry, featureId) {},
20549     
20550     /**
20551      * Method: moveRoot
20552      * moves this renderer's root to a (different) renderer.
20553      * To be implemented by subclasses that require a common renderer root for
20554      * feature selection.
20555      * 
20556      * Parameters:
20557      * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
20558      */
20559     moveRoot: function(renderer) {},
20560
20561     /**
20562      * Method: getRenderLayerId
20563      * Gets the layer that this renderer's output appears on. If moveRoot was
20564      * used, this will be different from the id of the layer containing the
20565      * features rendered by this renderer.
20566      * 
20567      * Returns:
20568      * {String} the id of the output layer.
20569      */
20570     getRenderLayerId: function() {
20571         return this.container.id;
20572     },
20573     
20574     /**
20575      * Method: applyDefaultSymbolizer
20576      * 
20577      * Parameters:
20578      * symbolizer - {Object}
20579      * 
20580      * Returns:
20581      * {Object}
20582      */
20583     applyDefaultSymbolizer: function(symbolizer) {
20584         var result = OpenLayers.Util.extend({},
20585             OpenLayers.Renderer.defaultSymbolizer);
20586         if(symbolizer.stroke === false) {
20587             delete result.strokeWidth;
20588             delete result.strokeColor;
20589         }
20590         if(symbolizer.fill === false) {
20591             delete result.fillColor;
20592         }
20593         OpenLayers.Util.extend(result, symbolizer);
20594         return result;
20595     },
20596
20597     CLASS_NAME: "OpenLayers.Renderer"
20598 });
20599
20600 /**
20601  * Constant: OpenLayers.Renderer.defaultSymbolizer
20602  * {Object} Properties from this symbolizer will be applied to symbolizers
20603  *     with missing properties. This can also be used to set a global
20604  *     symbolizer default in OpenLayers. To be SLD 1.x compliant, add the
20605  *     following code before rendering any vector features:
20606  * (code)
20607  * OpenLayers.Renderer.defaultSymbolizer = {
20608  *     fillColor: "#808080",
20609  *     fillOpacity: 1,
20610  *     strokeColor: "#000000",
20611  *     strokeOpacity: 1,
20612  *     strokeWidth: 1,
20613  *     pointRadius: 3,
20614  *     graphicName: "square"
20615  * };
20616  * (end)
20617  */
20618 OpenLayers.Renderer.defaultSymbolizer = {
20619     fillColor: "#000000",
20620     strokeColor: "#000000",
20621     strokeWidth: 2,
20622     fillOpacity: 1,
20623     strokeOpacity: 1,
20624     pointRadius: 0,
20625     labelAlign: 'cm'
20626 };
20627     
20628
20629
20630 /**
20631  * Constant: OpenLayers.Renderer.symbol
20632  * Coordinate arrays for well known (named) symbols.
20633  */
20634 OpenLayers.Renderer.symbol = {
20635     "star": [350,75, 379,161, 469,161, 397,215, 423,301, 350,250, 277,301,
20636             303,215, 231,161, 321,161, 350,75],
20637     "cross": [4,0, 6,0, 6,4, 10,4, 10,6, 6,6, 6,10, 4,10, 4,6, 0,6, 0,4, 4,4,
20638             4,0],
20639     "x": [0,0, 25,0, 50,35, 75,0, 100,0, 65,50, 100,100, 75,100, 50,65, 25,100, 0,100, 35,50, 0,0],
20640     "square": [0,0, 0,1, 1,1, 1,0, 0,0],
20641     "triangle": [0,10, 10,10, 5,0, 0,10]
20642 };
20643 /* ======================================================================
20644     OpenLayers/Renderer/Canvas.js
20645    ====================================================================== */
20646
20647 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
20648  * full list of contributors). Published under the 2-clause BSD license.
20649  * See license.txt in the OpenLayers distribution or repository for the
20650  * full text of the license. */
20651
20652 /**
20653  * @requires OpenLayers/Renderer.js
20654  */
20655
20656 /**
20657  * Class: OpenLayers.Renderer.Canvas 
20658  * A renderer based on the 2D 'canvas' drawing element.
20659  * 
20660  * Inherits:
20661  *  - <OpenLayers.Renderer>
20662  */
20663 OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
20664     
20665     /**
20666      * APIProperty: hitDetection
20667      * {Boolean} Allow for hit detection of features.  Default is true.
20668      */
20669     hitDetection: true,
20670     
20671     /**
20672      * Property: hitOverflow
20673      * {Number} The method for converting feature identifiers to color values
20674      *     supports 16777215 sequential values.  Two features cannot be 
20675      *     predictably detected if their identifiers differ by more than this
20676      *     value.  The hitOverflow allows for bigger numbers (but the 
20677      *     difference in values is still limited).
20678      */
20679     hitOverflow: 0,
20680
20681     /**
20682      * Property: canvas
20683      * {Canvas} The canvas context object.
20684      */
20685     canvas: null, 
20686     
20687     /**
20688      * Property: features
20689      * {Object} Internal object of feature/style pairs for use in redrawing the layer.
20690      */
20691     features: null,
20692     
20693     /**
20694      * Property: pendingRedraw
20695      * {Boolean} The renderer needs a redraw call to render features added while
20696      *     the renderer was locked.
20697      */
20698     pendingRedraw: false,
20699     
20700     /**
20701      * Property: cachedSymbolBounds
20702      * {Object} Internal cache of calculated symbol extents.
20703      */
20704     cachedSymbolBounds: {},
20705     
20706     /**
20707      * Constructor: OpenLayers.Renderer.Canvas
20708      *
20709      * Parameters:
20710      * containerID - {<String>}
20711      * options - {Object} Optional properties to be set on the renderer.
20712      */
20713     initialize: function(containerID, options) {
20714         OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
20715         this.root = document.createElement("canvas");
20716         this.container.appendChild(this.root);
20717         this.canvas = this.root.getContext("2d");
20718         this.features = {};
20719         if (this.hitDetection) {
20720             this.hitCanvas = document.createElement("canvas");
20721             this.hitContext = this.hitCanvas.getContext("2d");
20722         }
20723     },
20724     
20725     /**
20726      * Method: setExtent
20727      * Set the visible part of the layer.
20728      *
20729      * Parameters:
20730      * extent - {<OpenLayers.Bounds>}
20731      * resolutionChanged - {Boolean}
20732      *
20733      * Returns:
20734      * {Boolean} true to notify the layer that the new extent does not exceed
20735      *     the coordinate range, and the features will not need to be redrawn.
20736      *     False otherwise.
20737      */
20738     setExtent: function() {
20739         OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);
20740         // always redraw features
20741         return false;
20742     },
20743     
20744     /** 
20745      * Method: eraseGeometry
20746      * Erase a geometry from the renderer. Because the Canvas renderer has
20747      *     'memory' of the features that it has drawn, we have to remove the
20748      *     feature so it doesn't redraw.   
20749      * 
20750      * Parameters:
20751      * geometry - {<OpenLayers.Geometry>}
20752      * featureId - {String}
20753      */
20754     eraseGeometry: function(geometry, featureId) {
20755         this.eraseFeatures(this.features[featureId][0]);
20756     },
20757
20758     /**
20759      * APIMethod: supported
20760      * 
20761      * Returns:
20762      * {Boolean} Whether or not the browser supports the renderer class
20763      */
20764     supported: function() {
20765         return OpenLayers.CANVAS_SUPPORTED;
20766     },    
20767     
20768     /**
20769      * Method: setSize
20770      * Sets the size of the drawing surface.
20771      *
20772      * Once the size is updated, redraw the canvas.
20773      *
20774      * Parameters:
20775      * size - {<OpenLayers.Size>} 
20776      */
20777     setSize: function(size) {
20778         this.size = size.clone();
20779         var root = this.root;
20780         root.style.width = size.w + "px";
20781         root.style.height = size.h + "px";
20782         root.width = size.w;
20783         root.height = size.h;
20784         this.resolution = null;
20785         if (this.hitDetection) {
20786             var hitCanvas = this.hitCanvas;
20787             hitCanvas.style.width = size.w + "px";
20788             hitCanvas.style.height = size.h + "px";
20789             hitCanvas.width = size.w;
20790             hitCanvas.height = size.h;
20791         }
20792     },
20793     
20794     /**
20795      * Method: drawFeature
20796      * Draw the feature. Stores the feature in the features list,
20797      * then redraws the layer. 
20798      *
20799      * Parameters:
20800      * feature - {<OpenLayers.Feature.Vector>} 
20801      * style - {<Object>} 
20802      *
20803      * Returns:
20804      * {Boolean} The feature has been drawn completely.  If the feature has no
20805      *     geometry, undefined will be returned.  If the feature is not rendered
20806      *     for other reasons, false will be returned.
20807      */
20808     drawFeature: function(feature, style) {
20809         var rendered;
20810         if (feature.geometry) {
20811             style = this.applyDefaultSymbolizer(style || feature.style);
20812             // don't render if display none or feature outside extent
20813             var bounds = feature.geometry.getBounds();
20814
20815             var worldBounds;
20816             if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
20817                 worldBounds = this.map.getMaxExtent();
20818             }
20819
20820             var intersects = bounds && bounds.intersectsBounds(this.extent, {worldBounds: worldBounds});
20821
20822             rendered = (style.display !== "none") && !!bounds && intersects;
20823             if (rendered) {
20824                 // keep track of what we have rendered for redraw
20825                 this.features[feature.id] = [feature, style];
20826             }
20827             else {
20828                 // remove from features tracked for redraw
20829                 delete(this.features[feature.id]);
20830             }
20831             this.pendingRedraw = true;
20832         }
20833         if (this.pendingRedraw && !this.locked) {
20834             this.redraw();
20835             this.pendingRedraw = false;
20836         }
20837         return rendered;
20838     },
20839
20840     /** 
20841      * Method: drawGeometry
20842      * Used when looping (in redraw) over the features; draws
20843      * the canvas. 
20844      *
20845      * Parameters:
20846      * geometry - {<OpenLayers.Geometry>} 
20847      * style - {Object} 
20848      */
20849     drawGeometry: function(geometry, style, featureId) {
20850         var className = geometry.CLASS_NAME;
20851         if ((className == "OpenLayers.Geometry.Collection") ||
20852             (className == "OpenLayers.Geometry.MultiPoint") ||
20853             (className == "OpenLayers.Geometry.MultiLineString") ||
20854             (className == "OpenLayers.Geometry.MultiPolygon")) {
20855             for (var i = 0; i < geometry.components.length; i++) {
20856                 this.drawGeometry(geometry.components[i], style, featureId);
20857             }
20858             return;
20859         }
20860         switch (geometry.CLASS_NAME) {
20861             case "OpenLayers.Geometry.Point":
20862                 this.drawPoint(geometry, style, featureId);
20863                 break;
20864             case "OpenLayers.Geometry.LineString":
20865                 this.drawLineString(geometry, style, featureId);
20866                 break;
20867             case "OpenLayers.Geometry.LinearRing":
20868                 this.drawLinearRing(geometry, style, featureId);
20869                 break;
20870             case "OpenLayers.Geometry.Polygon":
20871                 this.drawPolygon(geometry, style, featureId);
20872                 break;
20873             default:
20874                 break;
20875         }
20876     },
20877
20878     /**
20879      * Method: drawExternalGraphic
20880      * Called to draw External graphics. 
20881      * 
20882      * Parameters: 
20883      * geometry - {<OpenLayers.Geometry>}
20884      * style    - {Object}
20885      * featureId - {String}
20886      */ 
20887     drawExternalGraphic: function(geometry, style, featureId) {
20888         var img = new Image();
20889
20890         var title = style.title || style.graphicTitle;        
20891         if (title) {
20892             img.title = title;           
20893         }
20894
20895         var width = style.graphicWidth || style.graphicHeight;
20896         var height = style.graphicHeight || style.graphicWidth;
20897         width = width ? width : style.pointRadius * 2;
20898         height = height ? height : style.pointRadius * 2;
20899         var xOffset = (style.graphicXOffset != undefined) ?
20900            style.graphicXOffset : -(0.5 * width);
20901         var yOffset = (style.graphicYOffset != undefined) ?
20902            style.graphicYOffset : -(0.5 * height);
20903
20904         var opacity = style.graphicOpacity || style.fillOpacity;
20905         
20906         var onLoad = function() {
20907             if(!this.features[featureId]) {
20908                 return;
20909             }
20910             var pt = this.getLocalXY(geometry);
20911             var p0 = pt[0];
20912             var p1 = pt[1];
20913             if(!isNaN(p0) && !isNaN(p1)) {
20914                 var x = (p0 + xOffset) | 0;
20915                 var y = (p1 + yOffset) | 0;
20916                 var canvas = this.canvas;
20917                 canvas.globalAlpha = opacity;
20918                 var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor ||
20919                     (OpenLayers.Renderer.Canvas.drawImageScaleFactor =
20920                         /android 2.1/.test(navigator.userAgent.toLowerCase()) ?
20921                             // 320 is the screen width of the G1 phone, for
20922                             // which drawImage works out of the box.
20923                             320 / window.screen.width : 1
20924                     );
20925                 canvas.drawImage(
20926                     img, x*factor, y*factor, width*factor, height*factor
20927                 );
20928                 if (this.hitDetection) {
20929                     this.setHitContextStyle("fill", featureId);
20930                     this.hitContext.fillRect(x, y, width, height);
20931                 }
20932             }
20933         };
20934
20935         img.onload = OpenLayers.Function.bind(onLoad, this);
20936         img.src = style.externalGraphic;
20937     },
20938
20939     /**
20940      * Method: drawNamedSymbol
20941      * Called to draw Well Known Graphic Symbol Name. 
20942      * This method is only called by the renderer itself.
20943      * 
20944      * Parameters: 
20945      * geometry - {<OpenLayers.Geometry>}
20946      * style    - {Object}
20947      * featureId - {String}
20948      */ 
20949     drawNamedSymbol: function(geometry, style, featureId) {
20950         var x, y, cx, cy, i, symbolBounds, scaling, angle;
20951         var unscaledStrokeWidth;
20952         var deg2rad = Math.PI / 180.0;
20953         
20954         var symbol = OpenLayers.Renderer.symbol[style.graphicName];
20955          
20956         if (!symbol) {
20957             throw new Error(style.graphicName + ' is not a valid symbol name');
20958         }
20959         
20960         if (!symbol.length || symbol.length < 2) return;
20961         
20962         var pt = this.getLocalXY(geometry);
20963         var p0 = pt[0];
20964         var p1 = pt[1];
20965        
20966         if (isNaN(p0) || isNaN(p1)) return;
20967         
20968         // Use rounded line caps
20969         this.canvas.lineCap = "round";
20970         this.canvas.lineJoin = "round";
20971         
20972         if (this.hitDetection) {
20973             this.hitContext.lineCap = "round";
20974             this.hitContext.lineJoin = "round";
20975         }
20976         
20977         // Scale and rotate symbols, using precalculated bounds whenever possible.
20978         if (style.graphicName in this.cachedSymbolBounds) {
20979             symbolBounds = this.cachedSymbolBounds[style.graphicName];
20980         } else {
20981             symbolBounds = new OpenLayers.Bounds();
20982             for(i = 0; i < symbol.length; i+=2) {
20983                 symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i+1]));
20984             }
20985             this.cachedSymbolBounds[style.graphicName] = symbolBounds;
20986         }
20987         
20988         // Push symbol scaling, translation and rotation onto the transformation stack in reverse order.
20989         // Don't forget to apply all canvas transformations to the hitContext canvas as well(!)
20990         this.canvas.save();
20991         if (this.hitDetection) { this.hitContext.save(); }
20992         
20993         // Step 3: place symbol at the desired location
20994         this.canvas.translate(p0,p1);
20995         if (this.hitDetection) { this.hitContext.translate(p0,p1); }
20996         
20997         // Step 2a. rotate the symbol if necessary
20998         angle = deg2rad * style.rotation; // will be NaN when style.rotation is undefined.
20999         if (!isNaN(angle)) {
21000             this.canvas.rotate(angle);
21001             if (this.hitDetection) { this.hitContext.rotate(angle); }
21002         }
21003                 
21004         // // Step 2: scale symbol such that pointRadius equals half the maximum symbol dimension.
21005         scaling = 2.0 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight());
21006         this.canvas.scale(scaling,scaling);
21007         if (this.hitDetection) { this.hitContext.scale(scaling,scaling); }
21008         
21009         // Step 1: center the symbol at the origin        
21010         cx = symbolBounds.getCenterLonLat().lon;
21011         cy = symbolBounds.getCenterLonLat().lat;
21012         this.canvas.translate(-cx,-cy);
21013         if (this.hitDetection) { this.hitContext.translate(-cx,-cy); }        
21014
21015         // Don't forget to scale stroke widths, because they are affected by canvas scale transformations as well(!)
21016         // Alternative: scale symbol coordinates manually, so stroke width scaling is not needed anymore.
21017         unscaledStrokeWidth = style.strokeWidth;
21018         style.strokeWidth = unscaledStrokeWidth / scaling;
21019             
21020         if (style.fill !== false) {
21021             this.setCanvasStyle("fill", style);
21022             this.canvas.beginPath();
21023             for (i=0; i<symbol.length; i=i+2) {
21024                 x = symbol[i];
21025                 y = symbol[i+1];
21026                 if (i == 0) this.canvas.moveTo(x,y);
21027                 this.canvas.lineTo(x,y);
21028             }
21029             this.canvas.closePath();
21030             this.canvas.fill();
21031
21032             if (this.hitDetection) {
21033                 this.setHitContextStyle("fill", featureId, style);
21034                 this.hitContext.beginPath();
21035                 for (i=0; i<symbol.length; i=i+2) {
21036                     x = symbol[i];
21037                     y = symbol[i+1];
21038                     if (i == 0) this.canvas.moveTo(x,y);
21039                     this.hitContext.lineTo(x,y);
21040                 }
21041                 this.hitContext.closePath();
21042                 this.hitContext.fill();
21043             }
21044         }  
21045         
21046         if (style.stroke !== false) {
21047             this.setCanvasStyle("stroke", style);
21048             this.canvas.beginPath();
21049             for (i=0; i<symbol.length; i=i+2) {
21050                 x = symbol[i];
21051                 y = symbol[i+1];
21052                 if (i == 0) this.canvas.moveTo(x,y);
21053                 this.canvas.lineTo(x,y);
21054             }
21055             this.canvas.closePath();
21056             this.canvas.stroke();
21057             
21058             
21059             if (this.hitDetection) {
21060                 this.setHitContextStyle("stroke", featureId, style, scaling);
21061                 this.hitContext.beginPath();
21062                 for (i=0; i<symbol.length; i=i+2) {
21063                     x = symbol[i];
21064                     y = symbol[i+1];
21065                     if (i == 0) this.hitContext.moveTo(x,y);
21066                     this.hitContext.lineTo(x,y);
21067                 }
21068                 this.hitContext.closePath();
21069                 this.hitContext.stroke();
21070             }
21071             
21072         }
21073         
21074         style.strokeWidth = unscaledStrokeWidth;
21075         this.canvas.restore();
21076         if (this.hitDetection) { this.hitContext.restore(); }
21077         this.setCanvasStyle("reset");  
21078     },
21079
21080     /**
21081      * Method: setCanvasStyle
21082      * Prepare the canvas for drawing by setting various global settings.
21083      *
21084      * Parameters:
21085      * type - {String} one of 'stroke', 'fill', or 'reset'
21086      * style - {Object} Symbolizer hash
21087      */
21088     setCanvasStyle: function(type, style) {
21089         if (type === "fill") {     
21090             this.canvas.globalAlpha = style['fillOpacity'];
21091             this.canvas.fillStyle = style['fillColor'];
21092         } else if (type === "stroke") {  
21093             this.canvas.globalAlpha = style['strokeOpacity'];
21094             this.canvas.strokeStyle = style['strokeColor'];
21095             this.canvas.lineWidth = style['strokeWidth'];
21096         } else {
21097             this.canvas.globalAlpha = 0;
21098             this.canvas.lineWidth = 1;
21099         }
21100     },
21101     
21102     /**
21103      * Method: featureIdToHex
21104      * Convert a feature ID string into an RGB hex string.
21105      *
21106      * Parameters:
21107      * featureId - {String} Feature id
21108      *
21109      * Returns:
21110      * {String} RGB hex string.
21111      */
21112     featureIdToHex: function(featureId) {
21113         var id = Number(featureId.split("_").pop()) + 1; // zero for no feature
21114         if (id >= 16777216) {
21115             this.hitOverflow = id - 16777215;
21116             id = id % 16777216 + 1;
21117         }
21118         var hex = "000000" + id.toString(16);
21119         var len = hex.length;
21120         hex = "#" + hex.substring(len-6, len);
21121         return hex;
21122     },
21123     
21124     /**
21125      * Method: setHitContextStyle
21126      * Prepare the hit canvas for drawing by setting various global settings.
21127      *
21128      * Parameters:
21129      * type - {String} one of 'stroke', 'fill', or 'reset'
21130      * featureId - {String} The feature id.
21131      * symbolizer - {<OpenLayers.Symbolizer>} The symbolizer.
21132      */
21133     setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) {
21134         var hex = this.featureIdToHex(featureId);
21135         if (type == "fill") {
21136             this.hitContext.globalAlpha = 1.0;
21137             this.hitContext.fillStyle = hex;
21138         } else if (type == "stroke") {  
21139             this.hitContext.globalAlpha = 1.0;
21140             this.hitContext.strokeStyle = hex;
21141             // bump up stroke width to deal with antialiasing. If strokeScaling is defined, we're rendering a symbol 
21142             // on a transformed canvas, so the antialias width bump has to scale as well.
21143             if (typeof strokeScaling === "undefined") {
21144                 this.hitContext.lineWidth = symbolizer.strokeWidth + 2;
21145             } else {
21146                 if (!isNaN(strokeScaling)) { this.hitContext.lineWidth = symbolizer.strokeWidth + 2.0 / strokeScaling; }
21147             }
21148         } else {
21149             this.hitContext.globalAlpha = 0;
21150             this.hitContext.lineWidth = 1;
21151         }
21152     },
21153
21154     /**
21155      * Method: drawPoint
21156      * This method is only called by the renderer itself.
21157      * 
21158      * Parameters: 
21159      * geometry - {<OpenLayers.Geometry>}
21160      * style    - {Object}
21161      * featureId - {String}
21162      */ 
21163     drawPoint: function(geometry, style, featureId) {
21164         if(style.graphic !== false) {
21165             if(style.externalGraphic) {
21166                 this.drawExternalGraphic(geometry, style, featureId);
21167             } else if (style.graphicName && (style.graphicName != "circle")) {
21168                 this.drawNamedSymbol(geometry, style, featureId);
21169             } else {
21170                 var pt = this.getLocalXY(geometry);
21171                 var p0 = pt[0];
21172                 var p1 = pt[1];
21173                 if(!isNaN(p0) && !isNaN(p1)) {
21174                     var twoPi = Math.PI*2;
21175                     var radius = style.pointRadius;
21176                     if(style.fill !== false) {
21177                         this.setCanvasStyle("fill", style);
21178                         this.canvas.beginPath();
21179                         this.canvas.arc(p0, p1, radius, 0, twoPi, true);
21180                         this.canvas.fill();
21181                         if (this.hitDetection) {
21182                             this.setHitContextStyle("fill", featureId, style);
21183                             this.hitContext.beginPath();
21184                             this.hitContext.arc(p0, p1, radius, 0, twoPi, true);
21185                             this.hitContext.fill();
21186                         }
21187                     }
21188
21189                     if(style.stroke !== false) {
21190                         this.setCanvasStyle("stroke", style);
21191                         this.canvas.beginPath();
21192                         this.canvas.arc(p0, p1, radius, 0, twoPi, true);
21193                         this.canvas.stroke();
21194                         if (this.hitDetection) {
21195                             this.setHitContextStyle("stroke", featureId, style);
21196                             this.hitContext.beginPath();
21197                             this.hitContext.arc(p0, p1, radius, 0, twoPi, true);
21198                             this.hitContext.stroke();
21199                         }
21200                         this.setCanvasStyle("reset");
21201                     }
21202                 }
21203             }
21204         }
21205     },
21206     
21207     /**
21208      * Method: drawLineString
21209      * This method is only called by the renderer itself.
21210      * 
21211      * Parameters: 
21212      * geometry - {<OpenLayers.Geometry>}
21213      * style    - {Object}
21214      * featureId - {String}
21215      */ 
21216     drawLineString: function(geometry, style, featureId) {
21217         style = OpenLayers.Util.applyDefaults({fill: false}, style);
21218         this.drawLinearRing(geometry, style, featureId);
21219     },    
21220     
21221     /**
21222      * Method: drawLinearRing
21223      * This method is only called by the renderer itself.
21224      * 
21225      * Parameters: 
21226      * geometry - {<OpenLayers.Geometry>}
21227      * style    - {Object}
21228      * featureId - {String}
21229      */ 
21230     drawLinearRing: function(geometry, style, featureId) {
21231         if (style.fill !== false) {
21232             this.setCanvasStyle("fill", style);
21233             this.renderPath(this.canvas, geometry, style, featureId, "fill");
21234             if (this.hitDetection) {
21235                 this.setHitContextStyle("fill", featureId, style);
21236                 this.renderPath(this.hitContext, geometry, style, featureId, "fill");
21237             }
21238         }
21239         if (style.stroke !== false) {
21240             this.setCanvasStyle("stroke", style);
21241             this.renderPath(this.canvas, geometry, style, featureId, "stroke");
21242             if (this.hitDetection) {
21243                 this.setHitContextStyle("stroke", featureId, style);
21244                 this.renderPath(this.hitContext, geometry, style, featureId, "stroke");
21245             }
21246         }
21247         this.setCanvasStyle("reset");
21248     },
21249     
21250     /**
21251      * Method: renderPath
21252      * Render a path with stroke and optional fill.
21253      */
21254     renderPath: function(context, geometry, style, featureId, type) {
21255         var components = geometry.components;
21256         var len = components.length;
21257         context.beginPath();
21258         var start = this.getLocalXY(components[0]);
21259         var x = start[0];
21260         var y = start[1];
21261         if (!isNaN(x) && !isNaN(y)) {
21262             context.moveTo(start[0], start[1]);
21263             for (var i=1; i<len; ++i) {
21264                 var pt = this.getLocalXY(components[i]);
21265                 context.lineTo(pt[0], pt[1]);
21266             }
21267             if (type === "fill") {
21268                 context.fill();
21269             } else {
21270                 context.stroke();
21271             }
21272         }
21273     },
21274     
21275     /**
21276      * Method: drawPolygon
21277      * This method is only called by the renderer itself.
21278      * 
21279      * Parameters: 
21280      * geometry - {<OpenLayers.Geometry>}
21281      * style    - {Object}
21282      * featureId - {String}
21283      */ 
21284     drawPolygon: function(geometry, style, featureId) {
21285         var components = geometry.components;
21286         var len = components.length;
21287         this.drawLinearRing(components[0], style, featureId);
21288         // erase inner rings
21289         for (var i=1; i<len; ++i) {
21290             /** 
21291              * Note that this is overly agressive.  Here we punch holes through 
21292              * all previously rendered features on the same canvas.  A better 
21293              * solution for polygons with interior rings would be to draw the 
21294              * polygon on a sketch canvas first.  We could erase all holes 
21295              * there and then copy the drawing to the layer canvas. 
21296              * TODO: http://trac.osgeo.org/openlayers/ticket/3130 
21297              */
21298             this.canvas.globalCompositeOperation = "destination-out";
21299             if (this.hitDetection) {
21300                 this.hitContext.globalCompositeOperation = "destination-out";
21301             }
21302             this.drawLinearRing(
21303                 components[i], 
21304                 OpenLayers.Util.applyDefaults({stroke: false, fillOpacity: 1.0}, style),
21305                 featureId
21306             );
21307             this.canvas.globalCompositeOperation = "source-over";
21308             if (this.hitDetection) {
21309                 this.hitContext.globalCompositeOperation = "source-over";
21310             }
21311             this.drawLinearRing(
21312                 components[i], 
21313                 OpenLayers.Util.applyDefaults({fill: false}, style),
21314                 featureId
21315             );
21316         }
21317     },
21318     
21319     /**
21320      * Method: drawText
21321      * This method is only called by the renderer itself.
21322      *
21323      * Parameters:
21324      * location - {<OpenLayers.Point>}
21325      * style    - {Object}
21326      */
21327     drawText: function(location, style) {
21328         var pt = this.getLocalXY(location);
21329
21330         this.setCanvasStyle("reset");
21331         this.canvas.fillStyle = style.fontColor;
21332         this.canvas.globalAlpha = style.fontOpacity || 1.0;
21333         var fontStyle = [style.fontStyle ? style.fontStyle : "normal",
21334                          "normal", // "font-variant" not supported
21335                          style.fontWeight ? style.fontWeight : "normal",
21336                          style.fontSize ? style.fontSize : "1em",
21337                          style.fontFamily ? style.fontFamily : "sans-serif"].join(" ");
21338         var labelRows = style.label.split('\n');
21339         var numRows = labelRows.length;
21340         if (this.canvas.fillText) {
21341             // HTML5
21342             this.canvas.font = fontStyle;
21343             this.canvas.textAlign =
21344                 OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] ||
21345                 "center";
21346             this.canvas.textBaseline =
21347                 OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] ||
21348                 "middle";
21349             var vfactor =
21350                 OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
21351             if (vfactor == null) {
21352                 vfactor = -.5;
21353             }
21354             var lineHeight =
21355                 this.canvas.measureText('Mg').height ||
21356                 this.canvas.measureText('xx').width;
21357             pt[1] += lineHeight*vfactor*(numRows-1);
21358             for (var i = 0; i < numRows; i++) {
21359                 if (style.labelOutlineWidth) {
21360                     this.canvas.save();
21361                     this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1.0;
21362                     this.canvas.strokeStyle = style.labelOutlineColor;
21363                     this.canvas.lineWidth = style.labelOutlineWidth;
21364                     this.canvas.strokeText(labelRows[i], pt[0], pt[1] + (lineHeight*i) + 1);
21365                     this.canvas.restore();
21366                 }
21367                 this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight*i));
21368             }
21369         } else if (this.canvas.mozDrawText) {
21370             // Mozilla pre-Gecko1.9.1 (<FF3.1)
21371             this.canvas.mozTextStyle = fontStyle;
21372             // No built-in text alignment, so we measure and adjust the position
21373             var hfactor =
21374                 OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];
21375             if (hfactor == null) {
21376                 hfactor = -.5;
21377             }
21378             var vfactor =
21379                 OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
21380             if (vfactor == null) {
21381                 vfactor = -.5;
21382             }
21383             var lineHeight = this.canvas.mozMeasureText('xx');
21384             pt[1] += lineHeight*(1 + (vfactor*numRows));
21385             for (var i = 0; i < numRows; i++) {
21386                 var x = pt[0] + (hfactor*this.canvas.mozMeasureText(labelRows[i]));
21387                 var y = pt[1] + (i*lineHeight);
21388                 this.canvas.translate(x, y);
21389                 this.canvas.mozDrawText(labelRows[i]);
21390                 this.canvas.translate(-x, -y);
21391             }
21392         }
21393         this.setCanvasStyle("reset");
21394     },
21395     
21396     /**
21397      * Method: getLocalXY
21398      * transform geographic xy into pixel xy
21399      *
21400      * Parameters: 
21401      * point - {<OpenLayers.Geometry.Point>}
21402      */
21403     getLocalXY: function(point) {
21404         var resolution = this.getResolution();
21405         var extent = this.extent;
21406         var x = ((point.x - this.featureDx) / resolution + (-extent.left / resolution));
21407         var y = ((extent.top / resolution) - point.y / resolution);
21408         return [x, y];
21409     },
21410
21411     /**
21412      * Method: clear
21413      * Clear all vectors from the renderer.
21414      */    
21415     clear: function() {
21416         var height = this.root.height;
21417         var width = this.root.width;
21418         this.canvas.clearRect(0, 0, width, height);
21419         this.features = {};
21420         if (this.hitDetection) {
21421             this.hitContext.clearRect(0, 0, width, height);
21422         }
21423     },
21424
21425     /**
21426      * Method: getFeatureIdFromEvent
21427      * Returns a feature id from an event on the renderer.  
21428      * 
21429      * Parameters:
21430      * evt - {<OpenLayers.Event>} 
21431      *
21432      * Returns:
21433      * {<OpenLayers.Feature.Vector} A feature or undefined.  This method returns a 
21434      *     feature instead of a feature id to avoid an unnecessary lookup on the
21435      *     layer.
21436      */
21437     getFeatureIdFromEvent: function(evt) {
21438         var featureId, feature;
21439         
21440         if (this.hitDetection && this.root.style.display !== "none") {
21441             // this dragging check should go in the feature handler
21442             if (!this.map.dragging) {
21443                 var xy = evt.xy;
21444                 var x = xy.x | 0;
21445                 var y = xy.y | 0;
21446                 var data = this.hitContext.getImageData(x, y, 1, 1).data;
21447                 if (data[3] === 255) { // antialiased
21448                     var id = data[2] + (256 * (data[1] + (256 * data[0])));
21449                     if (id) {
21450                         featureId = "OpenLayers_Feature_Vector_" + (id - 1 + this.hitOverflow);
21451                         try {
21452                             feature = this.features[featureId][0];
21453                         } catch(err) {
21454                             // Because of antialiasing on the canvas, when the hit location is at a point where the edge of
21455                             // one symbol intersects the interior of another symbol, a wrong hit color (and therefore id) results.
21456                             // todo: set Antialiasing = 'off' on the hitContext as soon as browsers allow it.
21457                         }
21458                     }
21459                 }
21460             }
21461         }
21462         return feature;
21463     },
21464     
21465     /**
21466      * Method: eraseFeatures 
21467      * This is called by the layer to erase features; removes the feature from
21468      *     the list, then redraws the layer.
21469      * 
21470      * Parameters:
21471      * features - {Array(<OpenLayers.Feature.Vector>)} 
21472      */
21473     eraseFeatures: function(features) {
21474         if(!(OpenLayers.Util.isArray(features))) {
21475             features = [features];
21476         }
21477         for(var i=0; i<features.length; ++i) {
21478             delete this.features[features[i].id];
21479         }
21480         this.redraw();
21481     },
21482
21483     /**
21484      * Method: redraw
21485      * The real 'meat' of the function: any time things have changed,
21486      *     redraw() can be called to loop over all the data and (you guessed
21487      *     it) redraw it.  Unlike Elements-based Renderers, we can't interact
21488      *     with things once they're drawn, to remove them, for example, so
21489      *     instead we have to just clear everything and draw from scratch.
21490      */
21491     redraw: function() {
21492         if (!this.locked) {
21493             var height = this.root.height;
21494             var width = this.root.width;
21495             this.canvas.clearRect(0, 0, width, height);
21496             if (this.hitDetection) {
21497                 this.hitContext.clearRect(0, 0, width, height);
21498             }
21499             var labelMap = [];
21500             var feature, geometry, style;
21501             var worldBounds = (this.map.baseLayer && this.map.baseLayer.wrapDateLine) && this.map.getMaxExtent();
21502             for (var id in this.features) {
21503                 if (!this.features.hasOwnProperty(id)) { continue; }
21504                 feature = this.features[id][0];
21505                 geometry = feature.geometry;
21506                 this.calculateFeatureDx(geometry.getBounds(), worldBounds);
21507                 style = this.features[id][1];
21508                 this.drawGeometry(geometry, style, feature.id);
21509                 if(style.label) {
21510                     labelMap.push([feature, style]);
21511                 }
21512             }
21513             var item;
21514             for (var i=0, len=labelMap.length; i<len; ++i) {
21515                 item = labelMap[i];
21516                 this.drawText(item[0].geometry.getCentroid(), item[1]);
21517             }
21518         }    
21519     },
21520
21521     CLASS_NAME: "OpenLayers.Renderer.Canvas"
21522 });
21523
21524 /**
21525  * Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN
21526  * {Object}
21527  */
21528 OpenLayers.Renderer.Canvas.LABEL_ALIGN = {
21529     "l": "left",
21530     "r": "right",
21531     "t": "top",
21532     "b": "bottom"
21533 };
21534
21535 /**
21536  * Constant: OpenLayers.Renderer.Canvas.LABEL_FACTOR
21537  * {Object}
21538  */
21539 OpenLayers.Renderer.Canvas.LABEL_FACTOR = {
21540     "l": 0,
21541     "r": -1,
21542     "t": 0,
21543     "b": -1
21544 };
21545
21546 /**
21547  * Constant: OpenLayers.Renderer.Canvas.drawImageScaleFactor
21548  * {Number} Scale factor to apply to the canvas drawImage arguments. This
21549  *     is always 1 except for Android 2.1 devices, to work around
21550  *     http://code.google.com/p/android/issues/detail?id=5141.
21551  */
21552 OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;
21553 /* ======================================================================
21554     OpenLayers/Layer/Bing.js
21555    ====================================================================== */
21556
21557 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
21558  * full list of contributors). Published under the 2-clause BSD license.
21559  * See license.txt in the OpenLayers distribution or repository for the
21560  * full text of the license. */
21561
21562 /**
21563  * @requires OpenLayers/Layer/XYZ.js
21564  */
21565
21566 /** 
21567  * Class: OpenLayers.Layer.Bing
21568  * Bing layer using direct tile access as provided by Bing Maps REST Services.
21569  * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more
21570  * information. Note: Terms of Service compliant use requires the map to be
21571  * configured with an <OpenLayers.Control.Attribution> control and the
21572  * attribution placed on or near the map.
21573  * 
21574  * Inherits from:
21575  *  - <OpenLayers.Layer.XYZ>
21576  */
21577 OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {
21578
21579     /**
21580      * Property: key
21581      * {String} API key for Bing maps, get your own key 
21582      *     at http://bingmapsportal.com/ .
21583      */
21584     key: null,
21585
21586     /**
21587      * Property: serverResolutions
21588      * {Array} the resolutions provided by the Bing servers.
21589      */
21590     serverResolutions: [
21591         156543.03390625, 78271.516953125, 39135.7584765625,
21592         19567.87923828125, 9783.939619140625, 4891.9698095703125,
21593         2445.9849047851562, 1222.9924523925781, 611.4962261962891,
21594         305.74811309814453, 152.87405654907226, 76.43702827453613,
21595         38.218514137268066, 19.109257068634033, 9.554628534317017,
21596         4.777314267158508, 2.388657133579254, 1.194328566789627,
21597         0.5971642833948135, 0.29858214169740677, 0.14929107084870338,
21598         0.07464553542435169
21599     ],
21600     
21601     /**
21602      * Property: attributionTemplate
21603      * {String}
21604      */
21605     attributionTemplate: '<span class="olBingAttribution ${type}">' +
21606          '<div><a target="_blank" href="http://www.bing.com/maps/">' +
21607          '<img src="${logo}" /></a></div>${copyrights}' +
21608          '<a style="white-space: nowrap" target="_blank" '+
21609          'href="http://www.microsoft.com/maps/product/terms.html">' +
21610          'Terms of Use</a></span>',
21611
21612     /**
21613      * Property: metadata
21614      * {Object} Metadata for this layer, as returned by the callback script
21615      */
21616     metadata: null,
21617
21618     /**
21619      * Property: protocolRegex
21620      * {RegExp} Regular expression to match and replace http: in bing urls
21621      */
21622     protocolRegex: /^http:/i,
21623     
21624     /**
21625      * APIProperty: type
21626      * {String} The layer identifier.  Any non-birdseye imageryType
21627      *     from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be
21628      *     used.  Default is "Road".
21629      */
21630     type: "Road",
21631     
21632     /**
21633      * APIProperty: culture
21634      * {String} The culture identifier.  See http://msdn.microsoft.com/en-us/library/ff701709.aspx
21635      * for the definition and the possible values.  Default is "en-US".
21636      */
21637     culture: "en-US",
21638     
21639     /**
21640      * APIProperty: metadataParams
21641      * {Object} Optional url parameters for the Get Imagery Metadata request
21642      * as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx
21643      */
21644     metadataParams: null,
21645
21646     /** APIProperty: tileOptions
21647      *  {Object} optional configuration options for <OpenLayers.Tile> instances
21648      *  created by this Layer. Default is
21649      *
21650      *  (code)
21651      *  {crossOriginKeyword: 'anonymous'}
21652      *  (end)
21653      */
21654     tileOptions: null,
21655
21656     /** APIProperty: protocol
21657      *  {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo
21658      *  Can be 'http:' 'https:' or ''
21659      *
21660      *  Warning: tiles may not be available under both HTTP and HTTPS protocols.
21661      *  Microsoft approved use of both HTTP and HTTPS urls for tiles. However
21662      *  this is undocumented and the Imagery Metadata API always returns HTTP
21663      *  urls.
21664      *
21665      *  Default is '', unless when executed from a file:/// uri, in which case
21666      *  it is 'http:'.
21667      */
21668     protocol: ~window.location.href.indexOf('http') ? '' : 'http:',
21669
21670     /**
21671      * Constructor: OpenLayers.Layer.Bing
21672      * Create a new Bing layer.
21673      *
21674      * Example:
21675      * (code)
21676      * var road = new OpenLayers.Layer.Bing({
21677      *     name: "My Bing Aerial Layer",
21678      *     type: "Aerial",
21679      *     key: "my-api-key-here",
21680      * });
21681      * (end)
21682      *
21683      * Parameters:
21684      * options - {Object} Configuration properties for the layer.
21685      *
21686      * Required configuration properties:
21687      * key - {String} Bing Maps API key for your application. Get one at
21688      *     http://bingmapsportal.com/.
21689      * type - {String} The layer identifier.  Any non-birdseye imageryType
21690      *     from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be
21691      *     used.
21692      *
21693      * Any other documented layer properties can be provided in the config object.
21694      */
21695     initialize: function(options) {
21696         options = OpenLayers.Util.applyDefaults({
21697             sphericalMercator: true
21698         }, options);
21699         var name = options.name || "Bing " + (options.type || this.type);
21700         
21701         var newArgs = [name, null, options];
21702         OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);
21703         this.tileOptions = OpenLayers.Util.extend({
21704             crossOriginKeyword: 'anonymous'
21705         }, this.options.tileOptions);
21706         this.loadMetadata(); 
21707     },
21708
21709     /**
21710      * Method: loadMetadata
21711      */
21712     loadMetadata: function() {
21713         this._callbackId = "_callback_" + this.id.replace(/\./g, "_");
21714         // link the processMetadata method to the global scope and bind it
21715         // to this instance
21716         window[this._callbackId] = OpenLayers.Function.bind(
21717             OpenLayers.Layer.Bing.processMetadata, this
21718         );
21719         var params = OpenLayers.Util.applyDefaults({
21720             key: this.key,
21721             jsonp: this._callbackId,
21722             include: "ImageryProviders"
21723         }, this.metadataParams);
21724         var url = this.protocol + "//dev.virtualearth.net/REST/v1/Imagery/Metadata/" +
21725             this.type + "?" + OpenLayers.Util.getParameterString(params);
21726         var script = document.createElement("script");
21727         script.type = "text/javascript";
21728         script.src = url;
21729         script.id = this._callbackId;
21730         document.getElementsByTagName("head")[0].appendChild(script);
21731     },
21732     
21733     /**
21734      * Method: initLayer
21735      *
21736      * Sets layer properties according to the metadata provided by the API
21737      */
21738     initLayer: function() {
21739         var res = this.metadata.resourceSets[0].resources[0];
21740         var url = res.imageUrl.replace("{quadkey}", "${quadkey}");
21741         url = url.replace("{culture}", this.culture);
21742         url = url.replace(this.protocolRegex, this.protocol);
21743         this.url = [];
21744         for (var i=0; i<res.imageUrlSubdomains.length; ++i) {
21745             this.url.push(url.replace("{subdomain}", res.imageUrlSubdomains[i]));
21746         }
21747         this.addOptions({
21748             maxResolution: Math.min(
21749                 this.serverResolutions[res.zoomMin],
21750                 this.maxResolution || Number.POSITIVE_INFINITY
21751             ),
21752             numZoomLevels: Math.min(
21753                 res.zoomMax + 1 - res.zoomMin, this.numZoomLevels
21754             )
21755         }, true);
21756         if (!this.isBaseLayer) {
21757             this.redraw();
21758         }
21759         this.updateAttribution();
21760     },
21761     
21762     /**
21763      * Method: getURL
21764      *
21765      * Paramters:
21766      * bounds - {<OpenLayers.Bounds>}
21767      */
21768     getURL: function(bounds) {
21769         if (!this.url) {
21770             return;
21771         }
21772         var xyz = this.getXYZ(bounds), x = xyz.x, y = xyz.y, z = xyz.z;
21773         var quadDigits = [];
21774         for (var i = z; i > 0; --i) {
21775             var digit = '0';
21776             var mask = 1 << (i - 1);
21777             if ((x & mask) != 0) {
21778                 digit++;
21779             }
21780             if ((y & mask) != 0) {
21781                 digit++;
21782                 digit++;
21783             }
21784             quadDigits.push(digit);
21785         }
21786         var quadKey = quadDigits.join("");
21787         var url = this.selectUrl('' + x + y + z, this.url);
21788
21789         return OpenLayers.String.format(url, {'quadkey': quadKey});
21790     },
21791     
21792     /**
21793      * Method: updateAttribution
21794      * Updates the attribution according to the requirements outlined in
21795      * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html
21796      */
21797     updateAttribution: function() {
21798         var metadata = this.metadata;
21799         if (!metadata.resourceSets || !this.map || !this.map.center) {
21800             return;
21801         }
21802         var res = metadata.resourceSets[0].resources[0];
21803         var extent = this.map.getExtent().transform(
21804             this.map.getProjectionObject(),
21805             new OpenLayers.Projection("EPSG:4326")
21806         );
21807         var providers = res.imageryProviders || [],
21808             zoom = OpenLayers.Util.indexOf(this.serverResolutions,
21809                                            this.getServerResolution()),
21810             copyrights = "", provider, i, ii, j, jj, bbox, coverage;
21811         for (i=0,ii=providers.length; i<ii; ++i) {
21812             provider = providers[i];
21813             for (j=0,jj=provider.coverageAreas.length; j<jj; ++j) {
21814                 coverage = provider.coverageAreas[j];
21815                 // axis order provided is Y,X
21816                 bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true);
21817                 if (extent.intersectsBounds(bbox) &&
21818                         zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {
21819                     copyrights += provider.attribution + " ";
21820                 }
21821             }
21822         }
21823         var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol);
21824         this.attribution = OpenLayers.String.format(this.attributionTemplate, {
21825             type: this.type.toLowerCase(),
21826             logo: logo,
21827             copyrights: copyrights
21828         });
21829         this.map && this.map.events.triggerEvent("changelayer", {
21830             layer: this,
21831             property: "attribution"
21832         });
21833     },
21834     
21835     /**
21836      * Method: setMap
21837      */
21838     setMap: function() {
21839         OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);
21840         this.map.events.register("moveend", this, this.updateAttribution);
21841     },
21842     
21843     /**
21844      * APIMethod: clone
21845      * 
21846      * Parameters:
21847      * obj - {Object}
21848      * 
21849      * Returns:
21850      * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing>
21851      */
21852     clone: function(obj) {
21853         if (obj == null) {
21854             obj = new OpenLayers.Layer.Bing(this.options);
21855         }
21856         //get all additions from superclasses
21857         obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
21858         // copy/set any non-init, non-simple values here
21859         return obj;
21860     },
21861     
21862     /**
21863      * Method: destroy
21864      */
21865     destroy: function() {
21866         this.map &&
21867             this.map.events.unregister("moveend", this, this.updateAttribution);
21868         OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments);
21869     },
21870     
21871     CLASS_NAME: "OpenLayers.Layer.Bing"
21872 });
21873
21874 /**
21875  * Function: OpenLayers.Layer.Bing.processMetadata
21876  * This function will be bound to an instance, linked to the global scope with
21877  * an id, and called by the JSONP script returned by the API.
21878  *
21879  * Parameters:
21880  * metadata - {Object} metadata as returned by the API
21881  */
21882 OpenLayers.Layer.Bing.processMetadata = function(metadata) {
21883     this.metadata = metadata;
21884     this.initLayer();
21885     var script = document.getElementById(this._callbackId);
21886     script.parentNode.removeChild(script);
21887     window[this._callbackId] = undefined; // cannot delete from window in IE
21888     delete this._callbackId;
21889 };
21890 /* ======================================================================
21891     OpenLayers/Handler.js
21892    ====================================================================== */
21893
21894 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
21895  * full list of contributors). Published under the 2-clause BSD license.
21896  * See license.txt in the OpenLayers distribution or repository for the
21897  * full text of the license. */
21898
21899 /**
21900  * @requires OpenLayers/BaseTypes/Class.js
21901  * @requires OpenLayers/Events.js
21902  */
21903
21904 /**
21905  * Class: OpenLayers.Handler
21906  * Base class to construct a higher-level handler for event sequences.  All
21907  *     handlers have activate and deactivate methods.  In addition, they have
21908  *     methods named like browser events.  When a handler is activated, any
21909  *     additional methods named like a browser event is registered as a
21910  *     listener for the corresponding event.  When a handler is deactivated,
21911  *     those same methods are unregistered as event listeners.
21912  *
21913  * Handlers also typically have a callbacks object with keys named like
21914  *     the abstracted events or event sequences that they are in charge of
21915  *     handling.  The controls that wrap handlers define the methods that
21916  *     correspond to these abstract events - so instead of listening for
21917  *     individual browser events, they only listen for the abstract events
21918  *     defined by the handler.
21919  *     
21920  * Handlers are created by controls, which ultimately have the responsibility
21921  *     of making changes to the the state of the application.  Handlers
21922  *     themselves may make temporary changes, but in general are expected to
21923  *     return the application in the same state that they found it.
21924  */
21925 OpenLayers.Handler = OpenLayers.Class({
21926
21927     /**
21928      * Property: id
21929      * {String}
21930      */
21931     id: null,
21932         
21933     /**
21934      * APIProperty: control
21935      * {<OpenLayers.Control>}. The control that initialized this handler.  The
21936      *     control is assumed to have a valid map property - that map is used
21937      *     in the handler's own setMap method.
21938      */
21939     control: null,
21940
21941     /**
21942      * Property: map
21943      * {<OpenLayers.Map>}
21944      */
21945     map: null,
21946
21947     /**
21948      * APIProperty: keyMask
21949      * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler
21950      *     constants to construct a keyMask.  The keyMask is used by
21951      *     <checkModifiers>.  If the keyMask matches the combination of keys
21952      *     down on an event, checkModifiers returns true.
21953      *
21954      * Example:
21955      * (code)
21956      *     // handler only responds if the Shift key is down
21957      *     handler.keyMask = OpenLayers.Handler.MOD_SHIFT;
21958      *
21959      *     // handler only responds if Ctrl-Shift is down
21960      *     handler.keyMask = OpenLayers.Handler.MOD_SHIFT |
21961      *                       OpenLayers.Handler.MOD_CTRL;
21962      * (end)
21963      */
21964     keyMask: null,
21965
21966     /**
21967      * Property: active
21968      * {Boolean}
21969      */
21970     active: false,
21971     
21972     /**
21973      * Property: evt
21974      * {Event} This property references the last event handled by the handler.
21975      *     Note that this property is not part of the stable API.  Use of the
21976      *     evt property should be restricted to controls in the library
21977      *     or other applications that are willing to update with changes to
21978      *     the OpenLayers code.
21979      */
21980     evt: null,
21981     
21982     /**
21983      * Property: touch
21984      * {Boolean} Indicates the support of touch events. When touch events are 
21985      *     started touch will be true and all mouse related listeners will do 
21986      *     nothing.
21987      */
21988     touch: false,
21989
21990     /**
21991      * Constructor: OpenLayers.Handler
21992      * Construct a handler.
21993      *
21994      * Parameters:
21995      * control - {<OpenLayers.Control>} The control that initialized this
21996      *     handler.  The control is assumed to have a valid map property; that
21997      *     map is used in the handler's own setMap method.  If a map property
21998      *     is present in the options argument it will be used instead.
21999      * callbacks - {Object} An object whose properties correspond to abstracted
22000      *     events or sequences of browser events.  The values for these
22001      *     properties are functions defined by the control that get called by
22002      *     the handler.
22003      * options - {Object} An optional object whose properties will be set on
22004      *     the handler.
22005      */
22006     initialize: function(control, callbacks, options) {
22007         OpenLayers.Util.extend(this, options);
22008         this.control = control;
22009         this.callbacks = callbacks;
22010
22011         var map = this.map || control.map;
22012         if (map) {
22013             this.setMap(map); 
22014         }
22015         
22016         this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
22017     },
22018     
22019     /**
22020      * Method: setMap
22021      */
22022     setMap: function (map) {
22023         this.map = map;
22024     },
22025
22026     /**
22027      * Method: checkModifiers
22028      * Check the keyMask on the handler.  If no <keyMask> is set, this always
22029      *     returns true.  If a <keyMask> is set and it matches the combination
22030      *     of keys down on an event, this returns true.
22031      *
22032      * Returns:
22033      * {Boolean} The keyMask matches the keys down on an event.
22034      */
22035     checkModifiers: function (evt) {
22036         if(this.keyMask == null) {
22037             return true;
22038         }
22039         /* calculate the keyboard modifier mask for this event */
22040         var keyModifiers =
22041             (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |
22042             (evt.ctrlKey  ? OpenLayers.Handler.MOD_CTRL  : 0) |
22043             (evt.altKey   ? OpenLayers.Handler.MOD_ALT   : 0) |
22044             (evt.metaKey  ? OpenLayers.Handler.MOD_META  : 0);
22045     
22046         /* if it differs from the handler object's key mask,
22047            bail out of the event handler */
22048         return (keyModifiers == this.keyMask);
22049     },
22050
22051     /**
22052      * APIMethod: activate
22053      * Turn on the handler.  Returns false if the handler was already active.
22054      * 
22055      * Returns: 
22056      * {Boolean} The handler was activated.
22057      */
22058     activate: function() {
22059         if(this.active) {
22060             return false;
22061         }
22062         // register for event handlers defined on this class.
22063         var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
22064         for (var i=0, len=events.length; i<len; i++) {
22065             if (this[events[i]]) {
22066                 this.register(events[i], this[events[i]]); 
22067             }
22068         } 
22069         this.active = true;
22070         return true;
22071     },
22072     
22073     /**
22074      * APIMethod: deactivate
22075      * Turn off the handler.  Returns false if the handler was already inactive.
22076      * 
22077      * Returns:
22078      * {Boolean} The handler was deactivated.
22079      */
22080     deactivate: function() {
22081         if(!this.active) {
22082             return false;
22083         }
22084         // unregister event handlers defined on this class.
22085         var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
22086         for (var i=0, len=events.length; i<len; i++) {
22087             if (this[events[i]]) {
22088                 this.unregister(events[i], this[events[i]]); 
22089             }
22090         } 
22091         this.touch = false;
22092         this.active = false;
22093         return true;
22094     },
22095
22096     /**
22097      * Method: startTouch
22098      * Start touch events, this method must be called by subclasses in 
22099      *     "touchstart" method. When touch events are started <touch> will be
22100      *     true and all mouse related listeners will do nothing.
22101      */
22102     startTouch: function() {
22103         if (!this.touch) {
22104             this.touch = true;
22105             var events = [
22106                 "mousedown", "mouseup", "mousemove", "click", "dblclick",
22107                 "mouseout"
22108             ];
22109             for (var i=0, len=events.length; i<len; i++) {
22110                 if (this[events[i]]) {
22111                     this.unregister(events[i], this[events[i]]); 
22112                 }
22113             } 
22114         }
22115     },
22116
22117     /**
22118     * Method: callback
22119     * Trigger the control's named callback with the given arguments
22120     *
22121     * Parameters:
22122     * name - {String} The key for the callback that is one of the properties
22123     *     of the handler's callbacks object.
22124     * args - {Array(*)} An array of arguments (any type) with which to call 
22125     *     the callback (defined by the control).
22126     */
22127     callback: function (name, args) {
22128         if (name && this.callbacks[name]) {
22129             this.callbacks[name].apply(this.control, args);
22130         }
22131     },
22132
22133     /**
22134     * Method: register
22135     * register an event on the map
22136     */
22137     register: function (name, method) {
22138         // TODO: deal with registerPriority in 3.0
22139         this.map.events.registerPriority(name, this, method);
22140         this.map.events.registerPriority(name, this, this.setEvent);
22141     },
22142
22143     /**
22144     * Method: unregister
22145     * unregister an event from the map
22146     */
22147     unregister: function (name, method) {
22148         this.map.events.unregister(name, this, method);   
22149         this.map.events.unregister(name, this, this.setEvent);
22150     },
22151     
22152     /**
22153      * Method: setEvent
22154      * With each registered browser event, the handler sets its own evt
22155      *     property.  This property can be accessed by controls if needed
22156      *     to get more information about the event that the handler is
22157      *     processing.
22158      *
22159      * This allows modifier keys on the event to be checked (alt, shift, ctrl,
22160      *     and meta cannot be checked with the keyboard handler).  For a
22161      *     control to determine which modifier keys are associated with the
22162      *     event that a handler is currently processing, it should access
22163      *     (code)handler.evt.altKey || handler.evt.shiftKey ||
22164      *     handler.evt.ctrlKey || handler.evt.metaKey(end).
22165      *
22166      * Parameters:
22167      * evt - {Event} The browser event.
22168      */
22169     setEvent: function(evt) {
22170         this.evt = evt;
22171         return true;
22172     },
22173
22174     /**
22175      * Method: destroy
22176      * Deconstruct the handler.
22177      */
22178     destroy: function () {
22179         // unregister event listeners
22180         this.deactivate();
22181         // eliminate circular references
22182         this.control = this.map = null;        
22183     },
22184
22185     CLASS_NAME: "OpenLayers.Handler"
22186 });
22187
22188 /**
22189  * Constant: OpenLayers.Handler.MOD_NONE
22190  * If set as the <keyMask>, <checkModifiers> returns false if any key is down.
22191  */
22192 OpenLayers.Handler.MOD_NONE  = 0;
22193
22194 /**
22195  * Constant: OpenLayers.Handler.MOD_SHIFT
22196  * If set as the <keyMask>, <checkModifiers> returns false if Shift is down.
22197  */
22198 OpenLayers.Handler.MOD_SHIFT = 1;
22199
22200 /**
22201  * Constant: OpenLayers.Handler.MOD_CTRL
22202  * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.
22203  */
22204 OpenLayers.Handler.MOD_CTRL  = 2;
22205
22206 /**
22207  * Constant: OpenLayers.Handler.MOD_ALT
22208  * If set as the <keyMask>, <checkModifiers> returns false if Alt is down.
22209  */
22210 OpenLayers.Handler.MOD_ALT   = 4;
22211
22212 /**
22213  * Constant: OpenLayers.Handler.MOD_META
22214  * If set as the <keyMask>, <checkModifiers> returns false if Cmd is down.
22215  */
22216 OpenLayers.Handler.MOD_META  = 8;
22217
22218
22219 /* ======================================================================
22220     OpenLayers/Handler/MouseWheel.js
22221    ====================================================================== */
22222
22223 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22224  * full list of contributors). Published under the 2-clause BSD license.
22225  * See license.txt in the OpenLayers distribution or repository for the
22226  * full text of the license. */
22227
22228 /**
22229  * @requires OpenLayers/Handler.js
22230  */
22231
22232 /**
22233  * Class: OpenLayers.Handler.MouseWheel
22234  * Handler for wheel up/down events.
22235  * 
22236  * Inherits from:
22237  *  - <OpenLayers.Handler>
22238  */
22239 OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {
22240     /** 
22241      * Property: wheelListener 
22242      * {function} 
22243      */
22244     wheelListener: null,
22245
22246     /**
22247      * Property: interval
22248      * {Integer} In order to increase server performance, an interval (in 
22249      *     milliseconds) can be set to reduce the number of up/down events 
22250      *     called. If set, a new up/down event will not be set until the 
22251      *     interval has passed. 
22252      *     Defaults to 0, meaning no interval. 
22253      */
22254     interval: 0,
22255     
22256     /**
22257      * Property: maxDelta
22258      * {Integer} Maximum delta to collect before breaking from the current
22259      *    interval. In cumulative mode, this also limits the maximum delta
22260      *    returned from the handler. Default is Number.POSITIVE_INFINITY.
22261      */
22262     maxDelta: Number.POSITIVE_INFINITY,
22263     
22264     /**
22265      * Property: delta
22266      * {Integer} When interval is set, delta collects the mousewheel z-deltas
22267      *     of the events that occur within the interval.
22268      *      See also the cumulative option
22269      */
22270     delta: 0,
22271     
22272     /**
22273      * Property: cumulative
22274      * {Boolean} When interval is set: true to collect all the mousewheel 
22275      *     z-deltas, false to only record the delta direction (positive or
22276      *     negative)
22277      */
22278     cumulative: true,
22279     
22280     /**
22281      * Constructor: OpenLayers.Handler.MouseWheel
22282      *
22283      * Parameters:
22284      * control - {<OpenLayers.Control>} 
22285      * callbacks - {Object} An object containing a single function to be
22286      *                          called when the drag operation is finished.
22287      *                          The callback should expect to recieve a single
22288      *                          argument, the point geometry.
22289      * options - {Object} 
22290      */
22291     initialize: function(control, callbacks, options) {
22292         OpenLayers.Handler.prototype.initialize.apply(this, arguments);
22293         this.wheelListener = OpenLayers.Function.bindAsEventListener(
22294             this.onWheelEvent, this
22295         );
22296     },
22297
22298     /**
22299      * Method: destroy
22300      */    
22301     destroy: function() {
22302         OpenLayers.Handler.prototype.destroy.apply(this, arguments);
22303         this.wheelListener = null;
22304     },
22305
22306     /**
22307      *  Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
22308      */
22309
22310     /** 
22311      * Method: onWheelEvent
22312      * Catch the wheel event and handle it xbrowserly
22313      * 
22314      * Parameters:
22315      * e - {Event} 
22316      */
22317     onWheelEvent: function(e){
22318         
22319         // make sure we have a map and check keyboard modifiers
22320         if (!this.map || !this.checkModifiers(e)) {
22321             return;
22322         }
22323         
22324         // Ride up the element's DOM hierarchy to determine if it or any of 
22325         //  its ancestors was: 
22326         //   * specifically marked as scrollable (CSS overflow property)
22327         //   * one of our layer divs or a div marked as scrollable
22328         //     ('olScrollable' CSS class)
22329         //   * the map div
22330         //
22331         var overScrollableDiv = false;
22332         var allowScroll = false;
22333         var overMapDiv = false;
22334         
22335         var elem = OpenLayers.Event.element(e);
22336         while((elem != null) && !overMapDiv && !overScrollableDiv) {
22337
22338             if (!overScrollableDiv) {
22339                 try {
22340                     var overflow;
22341                     if (elem.currentStyle) {
22342                         overflow = elem.currentStyle["overflow"];
22343                     } else {
22344                         var style = 
22345                             document.defaultView.getComputedStyle(elem, null);
22346                         overflow = style.getPropertyValue("overflow");
22347                     }
22348                     overScrollableDiv = ( overflow && 
22349                         (overflow == "auto") || (overflow == "scroll") );
22350                 } catch(err) {
22351                     //sometimes when scrolling in a popup, this causes 
22352                     // obscure browser error
22353                 }
22354             }
22355
22356             if (!allowScroll) {
22357                 allowScroll = OpenLayers.Element.hasClass(elem, 'olScrollable');
22358                 if (!allowScroll) {
22359                     for (var i = 0, len = this.map.layers.length; i < len; i++) {
22360                         // Are we in the layer div? Note that we have two cases
22361                         // here: one is to catch EventPane layers, which have a
22362                         // pane above the layer (layer.pane)
22363                         var layer = this.map.layers[i];
22364                         if (elem == layer.div || elem == layer.pane) {
22365                             allowScroll = true;
22366                             break;
22367                         }
22368                     }
22369                 }
22370             }
22371             overMapDiv = (elem == this.map.div);
22372
22373             elem = elem.parentNode;
22374         }
22375         
22376         // Logic below is the following:
22377         //
22378         // If we are over a scrollable div or not over the map div:
22379         //  * do nothing (let the browser handle scrolling)
22380         //
22381         //    otherwise 
22382         // 
22383         //    If we are over the layer div or a 'olScrollable' div:
22384         //     * zoom/in out
22385         //     then
22386         //     * kill event (so as not to also scroll the page after zooming)
22387         //
22388         //       otherwise
22389         //
22390         //       Kill the event (dont scroll the page if we wheel over the 
22391         //        layerswitcher or the pan/zoom control)
22392         //
22393         if (!overScrollableDiv && overMapDiv) {
22394             if (allowScroll) {
22395                 var delta = 0;
22396                 
22397                 if (e.wheelDelta) {
22398                     delta = e.wheelDelta;
22399                     if (delta % 160 === 0) {
22400                         // opera have steps of 160 instead of 120
22401                         delta = delta * 0.75;
22402                     }
22403                     delta = delta / 120;
22404                 } else if (e.detail) {
22405                     // detail in Firefox on OS X is 1/3 of Windows
22406                     // so force delta 1 / -1
22407                     delta = - (e.detail / Math.abs(e.detail));
22408                 }
22409                 this.delta += delta;
22410
22411                 window.clearTimeout(this._timeoutId);
22412                 if(this.interval && Math.abs(this.delta) < this.maxDelta) {
22413                     // store e because window.event might change during delay
22414                     var evt = OpenLayers.Util.extend({}, e);
22415                     this._timeoutId = window.setTimeout(
22416                         OpenLayers.Function.bind(function(){
22417                             this.wheelZoom(evt);
22418                         }, this),
22419                         this.interval
22420                     );
22421                 } else {
22422                     this.wheelZoom(e);
22423                 }
22424             }
22425             OpenLayers.Event.stop(e);
22426         }
22427     },
22428
22429     /**
22430      * Method: wheelZoom
22431      * Given the wheel event, we carry out the appropriate zooming in or out,
22432      *     based on the 'wheelDelta' or 'detail' property of the event.
22433      * 
22434      * Parameters:
22435      * e - {Event}
22436      */
22437     wheelZoom: function(e) {
22438         var delta = this.delta;
22439         this.delta = 0;
22440         
22441         if (delta) {
22442             e.xy = this.map.events.getMousePosition(e);
22443             if (delta < 0) {
22444                 this.callback("down",
22445                     [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]);
22446             } else {
22447                 this.callback("up",
22448                     [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]);
22449             }
22450         }
22451     },
22452     
22453     /**
22454      * Method: activate 
22455      */
22456     activate: function (evt) {
22457         if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
22458             //register mousewheel events specifically on the window and document
22459             var wheelListener = this.wheelListener;
22460             OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener);
22461             OpenLayers.Event.observe(window, "mousewheel", wheelListener);
22462             OpenLayers.Event.observe(document, "mousewheel", wheelListener);
22463             return true;
22464         } else {
22465             return false;
22466         }
22467     },
22468
22469     /**
22470      * Method: deactivate 
22471      */
22472     deactivate: function (evt) {
22473         if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
22474             // unregister mousewheel events specifically on the window and document
22475             var wheelListener = this.wheelListener;
22476             OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener);
22477             OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener);
22478             OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener);
22479             return true;
22480         } else {
22481             return false;
22482         }
22483     },
22484
22485     CLASS_NAME: "OpenLayers.Handler.MouseWheel"
22486 });
22487 /* ======================================================================
22488     OpenLayers/Geometry/MultiLineString.js
22489    ====================================================================== */
22490
22491 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22492  * full list of contributors). Published under the 2-clause BSD license.
22493  * See license.txt in the OpenLayers distribution or repository for the
22494  * full text of the license. */
22495
22496 /**
22497  * @requires OpenLayers/Geometry/Collection.js
22498  * @requires OpenLayers/Geometry/LineString.js
22499  */
22500
22501 /**
22502  * Class: OpenLayers.Geometry.MultiLineString
22503  * A MultiLineString is a geometry with multiple <OpenLayers.Geometry.LineString>
22504  * components.
22505  * 
22506  * Inherits from:
22507  *  - <OpenLayers.Geometry.Collection>
22508  *  - <OpenLayers.Geometry> 
22509  */
22510 OpenLayers.Geometry.MultiLineString = OpenLayers.Class(
22511   OpenLayers.Geometry.Collection, {
22512
22513     /**
22514      * Property: componentTypes
22515      * {Array(String)} An array of class names representing the types of
22516      * components that the collection can include.  A null value means the
22517      * component types are not restricted.
22518      */
22519     componentTypes: ["OpenLayers.Geometry.LineString"],
22520
22521     /**
22522      * Constructor: OpenLayers.Geometry.MultiLineString
22523      * Constructor for a MultiLineString Geometry.
22524      *
22525      * Parameters: 
22526      * components - {Array(<OpenLayers.Geometry.LineString>)} 
22527      *
22528      */
22529     
22530     /**
22531      * Method: split
22532      * Use this geometry (the source) to attempt to split a target geometry.
22533      * 
22534      * Parameters:
22535      * geometry - {<OpenLayers.Geometry>} The target geometry.
22536      * options - {Object} Properties of this object will be used to determine
22537      *     how the split is conducted.
22538      *
22539      * Valid options:
22540      * mutual - {Boolean} Split the source geometry in addition to the target
22541      *     geometry.  Default is false.
22542      * edge - {Boolean} Allow splitting when only edges intersect.  Default is
22543      *     true.  If false, a vertex on the source must be within the tolerance
22544      *     distance of the intersection to be considered a split.
22545      * tolerance - {Number} If a non-null value is provided, intersections
22546      *     within the tolerance distance of an existing vertex on the source
22547      *     will be assumed to occur at the vertex.
22548      * 
22549      * Returns:
22550      * {Array} A list of geometries (of this same type as the target) that
22551      *     result from splitting the target with the source geometry.  The
22552      *     source and target geometry will remain unmodified.  If no split
22553      *     results, null will be returned.  If mutual is true and a split
22554      *     results, return will be an array of two arrays - the first will be
22555      *     all geometries that result from splitting the source geometry and
22556      *     the second will be all geometries that result from splitting the
22557      *     target geometry.
22558      */
22559     split: function(geometry, options) {
22560         var results = null;
22561         var mutual = options && options.mutual;
22562         var splits, sourceLine, sourceLines, sourceSplit, targetSplit;
22563         var sourceParts = [];
22564         var targetParts = [geometry];
22565         for(var i=0, len=this.components.length; i<len; ++i) {
22566             sourceLine = this.components[i];
22567             sourceSplit = false;
22568             for(var j=0; j < targetParts.length; ++j) { 
22569                 splits = sourceLine.split(targetParts[j], options);
22570                 if(splits) {
22571                     if(mutual) {
22572                         sourceLines = splits[0];
22573                         for(var k=0, klen=sourceLines.length; k<klen; ++k) {
22574                             if(k===0 && sourceParts.length) {
22575                                 sourceParts[sourceParts.length-1].addComponent(
22576                                     sourceLines[k]
22577                                 );
22578                             } else {
22579                                 sourceParts.push(
22580                                     new OpenLayers.Geometry.MultiLineString([
22581                                         sourceLines[k]
22582                                     ])
22583                                 );
22584                             }
22585                         }
22586                         sourceSplit = true;
22587                         splits = splits[1];
22588                     }
22589                     if(splits.length) {
22590                         // splice in new target parts
22591                         splits.unshift(j, 1);
22592                         Array.prototype.splice.apply(targetParts, splits);
22593                         break;
22594                     }
22595                 }
22596             }
22597             if(!sourceSplit) {
22598                 // source line was not hit
22599                 if(sourceParts.length) {
22600                     // add line to existing multi
22601                     sourceParts[sourceParts.length-1].addComponent(
22602                         sourceLine.clone()
22603                     );
22604                 } else {
22605                     // create a fresh multi
22606                     sourceParts = [
22607                         new OpenLayers.Geometry.MultiLineString(
22608                             sourceLine.clone()
22609                         )
22610                     ];
22611                 }
22612             }
22613         }
22614         if(sourceParts && sourceParts.length > 1) {
22615             sourceSplit = true;
22616         } else {
22617             sourceParts = [];
22618         }
22619         if(targetParts && targetParts.length > 1) {
22620             targetSplit = true;
22621         } else {
22622             targetParts = [];
22623         }
22624         if(sourceSplit || targetSplit) {
22625             if(mutual) {
22626                 results = [sourceParts, targetParts];
22627             } else {
22628                 results = targetParts;
22629             }
22630         }
22631         return results;
22632     },
22633     
22634     /**
22635      * Method: splitWith
22636      * Split this geometry (the target) with the given geometry (the source).
22637      *
22638      * Parameters:
22639      * geometry - {<OpenLayers.Geometry>} A geometry used to split this
22640      *     geometry (the source).
22641      * options - {Object} Properties of this object will be used to determine
22642      *     how the split is conducted.
22643      *
22644      * Valid options:
22645      * mutual - {Boolean} Split the source geometry in addition to the target
22646      *     geometry.  Default is false.
22647      * edge - {Boolean} Allow splitting when only edges intersect.  Default is
22648      *     true.  If false, a vertex on the source must be within the tolerance
22649      *     distance of the intersection to be considered a split.
22650      * tolerance - {Number} If a non-null value is provided, intersections
22651      *     within the tolerance distance of an existing vertex on the source
22652      *     will be assumed to occur at the vertex.
22653      * 
22654      * Returns:
22655      * {Array} A list of geometries (of this same type as the target) that
22656      *     result from splitting the target with the source geometry.  The
22657      *     source and target geometry will remain unmodified.  If no split
22658      *     results, null will be returned.  If mutual is true and a split
22659      *     results, return will be an array of two arrays - the first will be
22660      *     all geometries that result from splitting the source geometry and
22661      *     the second will be all geometries that result from splitting the
22662      *     target geometry.
22663      */
22664     splitWith: function(geometry, options) {
22665         var results = null;
22666         var mutual = options && options.mutual;
22667         var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;
22668         if(geometry instanceof OpenLayers.Geometry.LineString) {
22669             targetParts = [];
22670             sourceParts = [geometry];
22671             for(var i=0, len=this.components.length; i<len; ++i) {
22672                 targetSplit = false;
22673                 targetLine = this.components[i];
22674                 for(var j=0; j<sourceParts.length; ++j) {
22675                     splits = sourceParts[j].split(targetLine, options);
22676                     if(splits) {
22677                         if(mutual) {
22678                             sourceLines = splits[0];
22679                             if(sourceLines.length) {
22680                                 // splice in new source parts
22681                                 sourceLines.unshift(j, 1);
22682                                 Array.prototype.splice.apply(sourceParts, sourceLines);
22683                                 j += sourceLines.length - 2;
22684                             }
22685                             splits = splits[1];
22686                             if(splits.length === 0) {
22687                                 splits = [targetLine.clone()];
22688                             }
22689                         }
22690                         for(var k=0, klen=splits.length; k<klen; ++k) {
22691                             if(k===0 && targetParts.length) {
22692                                 targetParts[targetParts.length-1].addComponent(
22693                                     splits[k]
22694                                 );
22695                             } else {
22696                                 targetParts.push(
22697                                     new OpenLayers.Geometry.MultiLineString([
22698                                         splits[k]
22699                                     ])
22700                                 );
22701                             }
22702                         }
22703                         targetSplit = true;                    
22704                     }
22705                 }
22706                 if(!targetSplit) {
22707                     // target component was not hit
22708                     if(targetParts.length) {
22709                         // add it to any existing multi-line
22710                         targetParts[targetParts.length-1].addComponent(
22711                             targetLine.clone()
22712                         );
22713                     } else {
22714                         // or start with a fresh multi-line
22715                         targetParts = [
22716                             new OpenLayers.Geometry.MultiLineString([
22717                                 targetLine.clone()
22718                             ])
22719                         ];
22720                     }
22721                     
22722                 }
22723             }
22724         } else {
22725             results = geometry.split(this);
22726         }
22727         if(sourceParts && sourceParts.length > 1) {
22728             sourceSplit = true;
22729         } else {
22730             sourceParts = [];
22731         }
22732         if(targetParts && targetParts.length > 1) {
22733             targetSplit = true;
22734         } else {
22735             targetParts = [];
22736         }
22737         if(sourceSplit || targetSplit) {
22738             if(mutual) {
22739                 results = [sourceParts, targetParts];
22740             } else {
22741                 results = targetParts;
22742             }
22743         }
22744         return results;
22745     },
22746
22747     CLASS_NAME: "OpenLayers.Geometry.MultiLineString"
22748 });
22749 /* ======================================================================
22750     OpenLayers/Format.js
22751    ====================================================================== */
22752
22753 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22754  * full list of contributors). Published under the 2-clause BSD license.
22755  * See license.txt in the OpenLayers distribution or repository for the
22756  * full text of the license. */
22757
22758 /**
22759  * @requires OpenLayers/BaseTypes/Class.js
22760  * @requires OpenLayers/Util.js
22761  */
22762
22763 /**
22764  * Class: OpenLayers.Format
22765  * Base class for format reading/writing a variety of formats.  Subclasses
22766  *     of OpenLayers.Format are expected to have read and write methods.
22767  */
22768 OpenLayers.Format = OpenLayers.Class({
22769     
22770     /**
22771      * Property: options
22772      * {Object} A reference to options passed to the constructor.
22773      */
22774     options: null,
22775     
22776     /**
22777      * APIProperty: externalProjection
22778      * {<OpenLayers.Projection>} When passed a externalProjection and
22779      *     internalProjection, the format will reproject the geometries it
22780      *     reads or writes. The externalProjection is the projection used by
22781      *     the content which is passed into read or which comes out of write.
22782      *     In order to reproject, a projection transformation function for the
22783      *     specified projections must be available. This support may be 
22784      *     provided via proj4js or via a custom transformation function. See
22785      *     {<OpenLayers.Projection.addTransform>} for more information on
22786      *     custom transformations.
22787      */
22788     externalProjection: null,
22789
22790     /**
22791      * APIProperty: internalProjection
22792      * {<OpenLayers.Projection>} When passed a externalProjection and
22793      *     internalProjection, the format will reproject the geometries it
22794      *     reads or writes. The internalProjection is the projection used by
22795      *     the geometries which are returned by read or which are passed into
22796      *     write.  In order to reproject, a projection transformation function
22797      *     for the specified projections must be available. This support may be
22798      *     provided via proj4js or via a custom transformation function. See
22799      *     {<OpenLayers.Projection.addTransform>} for more information on
22800      *     custom transformations.
22801      */
22802     internalProjection: null,
22803
22804     /**
22805      * APIProperty: data
22806      * {Object} When <keepData> is true, this is the parsed string sent to
22807      *     <read>.
22808      */
22809     data: null,
22810
22811     /**
22812      * APIProperty: keepData
22813      * {Object} Maintain a reference (<data>) to the most recently read data.
22814      *     Default is false.
22815      */
22816     keepData: false,
22817
22818     /**
22819      * Constructor: OpenLayers.Format
22820      * Instances of this class are not useful.  See one of the subclasses.
22821      *
22822      * Parameters:
22823      * options - {Object} An optional object with properties to set on the
22824      *           format
22825      *
22826      * Valid options:
22827      * keepData - {Boolean} If true, upon <read>, the data property will be
22828      *     set to the parsed object (e.g. the json or xml object).
22829      *
22830      * Returns:
22831      * An instance of OpenLayers.Format
22832      */
22833     initialize: function(options) {
22834         OpenLayers.Util.extend(this, options);
22835         this.options = options;
22836     },
22837     
22838     /**
22839      * APIMethod: destroy
22840      * Clean up.
22841      */
22842     destroy: function() {
22843     },
22844
22845     /**
22846      * Method: read
22847      * Read data from a string, and return an object whose type depends on the
22848      * subclass. 
22849      * 
22850      * Parameters:
22851      * data - {string} Data to read/parse.
22852      *
22853      * Returns:
22854      * Depends on the subclass
22855      */
22856     read: function(data) {
22857         throw new Error('Read not implemented.');
22858     },
22859     
22860     /**
22861      * Method: write
22862      * Accept an object, and return a string. 
22863      *
22864      * Parameters:
22865      * object - {Object} Object to be serialized
22866      *
22867      * Returns:
22868      * {String} A string representation of the object.
22869      */
22870     write: function(object) {
22871         throw new Error('Write not implemented.');
22872     },
22873
22874     CLASS_NAME: "OpenLayers.Format"
22875 });     
22876 /* ======================================================================
22877     OpenLayers/Format/XML.js
22878    ====================================================================== */
22879
22880 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22881  * full list of contributors). Published under the 2-clause BSD license.
22882  * See license.txt in the OpenLayers distribution or repository for the
22883  * full text of the license. */
22884
22885 /**
22886  * @requires OpenLayers/Format.js
22887  */
22888
22889 /**
22890  * Class: OpenLayers.Format.XML
22891  * Read and write XML.  For cross-browser XML generation, use methods on an
22892  *     instance of the XML format class instead of on <code>document<end>.
22893  *     The DOM creation and traversing methods exposed here all mimic the
22894  *     W3C XML DOM methods.  Create a new parser with the
22895  *     <OpenLayers.Format.XML> constructor.
22896  *
22897  * Inherits from:
22898  *  - <OpenLayers.Format>
22899  */
22900 OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
22901     
22902     /**
22903      * Property: namespaces
22904      * {Object} Mapping of namespace aliases to namespace URIs.  Properties
22905      *     of this object should not be set individually.  Read-only.  All
22906      *     XML subclasses should have their own namespaces object.  Use
22907      *     <setNamespace> to add or set a namespace alias after construction.
22908      */
22909     namespaces: null,
22910     
22911     /**
22912      * Property: namespaceAlias
22913      * {Object} Mapping of namespace URI to namespace alias.  This object
22914      *     is read-only.  Use <setNamespace> to add or set a namespace alias.
22915      */
22916     namespaceAlias: null,
22917     
22918     /**
22919      * Property: defaultPrefix
22920      * {String} The default namespace alias for creating element nodes.
22921      */
22922     defaultPrefix: null,
22923     
22924     /**
22925      * Property: readers
22926      * Contains public functions, grouped by namespace prefix, that will
22927      *     be applied when a namespaced node is found matching the function
22928      *     name.  The function will be applied in the scope of this parser
22929      *     with two arguments: the node being read and a context object passed
22930      *     from the parent.
22931      */
22932     readers: {},
22933     
22934     /**
22935      * Property: writers
22936      * As a compliment to the <readers> property, this structure contains public
22937      *     writing functions grouped by namespace alias and named like the
22938      *     node names they produce.
22939      */
22940     writers: {},
22941
22942     /**
22943      * Property: xmldom
22944      * {XMLDom} If this browser uses ActiveX, this will be set to a XMLDOM
22945      *     object.  It is not intended to be a browser sniffing property.
22946      *     Instead, the xmldom property is used instead of <code>document<end>
22947      *     where namespaced node creation methods are not supported. In all
22948      *     other browsers, this remains null.
22949      */
22950     xmldom: null,
22951
22952     /**
22953      * Constructor: OpenLayers.Format.XML
22954      * Construct an XML parser.  The parser is used to read and write XML.
22955      *     Reading XML from a string returns a DOM element.  Writing XML from
22956      *     a DOM element returns a string.
22957      *
22958      * Parameters:
22959      * options - {Object} Optional object whose properties will be set on
22960      *     the object.
22961      */
22962     initialize: function(options) {
22963         if(window.ActiveXObject) {
22964             this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
22965         }
22966         OpenLayers.Format.prototype.initialize.apply(this, [options]);
22967         // clone the namespace object and set all namespace aliases
22968         this.namespaces = OpenLayers.Util.extend({}, this.namespaces);
22969         this.namespaceAlias = {};
22970         for(var alias in this.namespaces) {
22971             this.namespaceAlias[this.namespaces[alias]] = alias;
22972         }
22973     },
22974     
22975     /**
22976      * APIMethod: destroy
22977      * Clean up.
22978      */
22979     destroy: function() {
22980         this.xmldom = null;
22981         OpenLayers.Format.prototype.destroy.apply(this, arguments);
22982     },
22983     
22984     /**
22985      * Method: setNamespace
22986      * Set a namespace alias and URI for the format.
22987      *
22988      * Parameters:
22989      * alias - {String} The namespace alias (prefix).
22990      * uri - {String} The namespace URI.
22991      */
22992     setNamespace: function(alias, uri) {
22993         this.namespaces[alias] = uri;
22994         this.namespaceAlias[uri] = alias;
22995     },
22996
22997     /**
22998      * APIMethod: read
22999      * Deserialize a XML string and return a DOM node.
23000      *
23001      * Parameters:
23002      * text - {String} A XML string
23003      
23004      * Returns:
23005      * {DOMElement} A DOM node
23006      */
23007     read: function(text) {
23008         var index = text.indexOf('<');
23009         if(index > 0) {
23010             text = text.substring(index);
23011         }
23012         var node = OpenLayers.Util.Try(
23013             OpenLayers.Function.bind((
23014                 function() {
23015                     var xmldom;
23016                     /**
23017                      * Since we want to be able to call this method on the prototype
23018                      * itself, this.xmldom may not exist even if in IE.
23019                      */
23020                     if(window.ActiveXObject && !this.xmldom) {
23021                         xmldom = new ActiveXObject("Microsoft.XMLDOM");
23022                     } else {
23023                         xmldom = this.xmldom;
23024                         
23025                     }
23026                     xmldom.loadXML(text);
23027                     return xmldom;
23028                 }
23029             ), this),
23030             function() {
23031                 return new DOMParser().parseFromString(text, 'text/xml');
23032             },
23033             function() {
23034                 var req = new XMLHttpRequest();
23035                 req.open("GET", "data:" + "text/xml" +
23036                          ";charset=utf-8," + encodeURIComponent(text), false);
23037                 if(req.overrideMimeType) {
23038                     req.overrideMimeType("text/xml");
23039                 }
23040                 req.send(null);
23041                 return req.responseXML;
23042             }
23043         );
23044
23045         if(this.keepData) {
23046             this.data = node;
23047         }
23048
23049         return node;
23050     },
23051
23052     /**
23053      * APIMethod: write
23054      * Serialize a DOM node into a XML string.
23055      * 
23056      * Parameters:
23057      * node - {DOMElement} A DOM node.
23058      *
23059      * Returns:
23060      * {String} The XML string representation of the input node.
23061      */
23062     write: function(node) {
23063         var data;
23064         if(this.xmldom) {
23065             data = node.xml;
23066         } else {
23067             var serializer = new XMLSerializer();
23068             if (node.nodeType == 1) {
23069                 // Add nodes to a document before serializing. Everything else
23070                 // is serialized as is. This may need more work. See #1218 .
23071                 var doc = document.implementation.createDocument("", "", null);
23072                 if (doc.importNode) {
23073                     node = doc.importNode(node, true);
23074                 }
23075                 doc.appendChild(node);
23076                 data = serializer.serializeToString(doc);
23077             } else {
23078                 data = serializer.serializeToString(node);
23079             }
23080         }
23081         return data;
23082     },
23083
23084     /**
23085      * APIMethod: createElementNS
23086      * Create a new element with namespace.  This node can be appended to
23087      *     another node with the standard node.appendChild method.  For
23088      *     cross-browser support, this method must be used instead of
23089      *     document.createElementNS.
23090      *
23091      * Parameters:
23092      * uri - {String} Namespace URI for the element.
23093      * name - {String} The qualified name of the element (prefix:localname).
23094      * 
23095      * Returns:
23096      * {Element} A DOM element with namespace.
23097      */
23098     createElementNS: function(uri, name) {
23099         var element;
23100         if(this.xmldom) {
23101             if(typeof uri == "string") {
23102                 element = this.xmldom.createNode(1, name, uri);
23103             } else {
23104                 element = this.xmldom.createNode(1, name, "");
23105             }
23106         } else {
23107             element = document.createElementNS(uri, name);
23108         }
23109         return element;
23110     },
23111
23112     /**
23113      * APIMethod: createDocumentFragment
23114      * Create a document fragment node that can be appended to another node
23115      *     created by createElementNS.  This will call 
23116      *     document.createDocumentFragment outside of IE.  In IE, the ActiveX
23117      *     object's createDocumentFragment method is used.
23118      *
23119      * Returns:
23120      * {Element} A document fragment.
23121      */
23122     createDocumentFragment: function() {
23123         var element;
23124         if (this.xmldom) {
23125             element = this.xmldom.createDocumentFragment();
23126         } else {
23127             element = document.createDocumentFragment();
23128         }
23129         return element;
23130     },
23131
23132     /**
23133      * APIMethod: createTextNode
23134      * Create a text node.  This node can be appended to another node with
23135      *     the standard node.appendChild method.  For cross-browser support,
23136      *     this method must be used instead of document.createTextNode.
23137      * 
23138      * Parameters:
23139      * text - {String} The text of the node.
23140      * 
23141      * Returns: 
23142      * {DOMElement} A DOM text node.
23143      */
23144     createTextNode: function(text) {
23145         var node;
23146         if (typeof text !== "string") {
23147             text = String(text);
23148         }
23149         if(this.xmldom) {
23150             node = this.xmldom.createTextNode(text);
23151         } else {
23152             node = document.createTextNode(text);
23153         }
23154         return node;
23155     },
23156
23157     /**
23158      * APIMethod: getElementsByTagNameNS
23159      * Get a list of elements on a node given the namespace URI and local name.
23160      *     To return all nodes in a given namespace, use '*' for the name
23161      *     argument.  To return all nodes of a given (local) name, regardless
23162      *     of namespace, use '*' for the uri argument.
23163      * 
23164      * Parameters:
23165      * node - {Element} Node on which to search for other nodes.
23166      * uri - {String} Namespace URI.
23167      * name - {String} Local name of the tag (without the prefix).
23168      * 
23169      * Returns:
23170      * {NodeList} A node list or array of elements.
23171      */
23172     getElementsByTagNameNS: function(node, uri, name) {
23173         var elements = [];
23174         if(node.getElementsByTagNameNS) {
23175             elements = node.getElementsByTagNameNS(uri, name);
23176         } else {
23177             // brute force method
23178             var allNodes = node.getElementsByTagName("*");
23179             var potentialNode, fullName;
23180             for(var i=0, len=allNodes.length; i<len; ++i) {
23181                 potentialNode = allNodes[i];
23182                 fullName = (potentialNode.prefix) ?
23183                            (potentialNode.prefix + ":" + name) : name;
23184                 if((name == "*") || (fullName == potentialNode.nodeName)) {
23185                     if((uri == "*") || (uri == potentialNode.namespaceURI)) {
23186                         elements.push(potentialNode);
23187                     }
23188                 }
23189             }
23190         }
23191         return elements;
23192     },
23193
23194     /**
23195      * APIMethod: getAttributeNodeNS
23196      * Get an attribute node given the namespace URI and local name.
23197      * 
23198      * Parameters:
23199      * node - {Element} Node on which to search for attribute nodes.
23200      * uri - {String} Namespace URI.
23201      * name - {String} Local name of the attribute (without the prefix).
23202      * 
23203      * Returns:
23204      * {DOMElement} An attribute node or null if none found.
23205      */
23206     getAttributeNodeNS: function(node, uri, name) {
23207         var attributeNode = null;
23208         if(node.getAttributeNodeNS) {
23209             attributeNode = node.getAttributeNodeNS(uri, name);
23210         } else {
23211             var attributes = node.attributes;
23212             var potentialNode, fullName;
23213             for(var i=0, len=attributes.length; i<len; ++i) {
23214                 potentialNode = attributes[i];
23215                 if(potentialNode.namespaceURI == uri) {
23216                     fullName = (potentialNode.prefix) ?
23217                                (potentialNode.prefix + ":" + name) : name;
23218                     if(fullName == potentialNode.nodeName) {
23219                         attributeNode = potentialNode;
23220                         break;
23221                     }
23222                 }
23223             }
23224         }
23225         return attributeNode;
23226     },
23227
23228     /**
23229      * APIMethod: getAttributeNS
23230      * Get an attribute value given the namespace URI and local name.
23231      * 
23232      * Parameters:
23233      * node - {Element} Node on which to search for an attribute.
23234      * uri - {String} Namespace URI.
23235      * name - {String} Local name of the attribute (without the prefix).
23236      * 
23237      * Returns:
23238      * {String} An attribute value or and empty string if none found.
23239      */
23240     getAttributeNS: function(node, uri, name) {
23241         var attributeValue = "";
23242         if(node.getAttributeNS) {
23243             attributeValue = node.getAttributeNS(uri, name) || "";
23244         } else {
23245             var attributeNode = this.getAttributeNodeNS(node, uri, name);
23246             if(attributeNode) {
23247                 attributeValue = attributeNode.nodeValue;
23248             }
23249         }
23250         return attributeValue;
23251     },
23252     
23253     /**
23254      * APIMethod: getChildValue
23255      * Get the textual value of the node if it exists, or return an
23256      *     optional default string.  Returns an empty string if no first child
23257      *     exists and no default value is supplied.
23258      *
23259      * Parameters:
23260      * node - {DOMElement} The element used to look for a first child value.
23261      * def - {String} Optional string to return in the event that no
23262      *     first child value exists.
23263      *
23264      * Returns:
23265      * {String} The value of the first child of the given node.
23266      */
23267     getChildValue: function(node, def) {
23268         var value = def || "";
23269         if(node) {
23270             for(var child=node.firstChild; child; child=child.nextSibling) {
23271                 switch(child.nodeType) {
23272                     case 3: // text node
23273                     case 4: // cdata section
23274                         value += child.nodeValue;
23275                 }
23276             }
23277         }
23278         return value;
23279     },
23280
23281     /**
23282      * APIMethod: isSimpleContent
23283      * Test if the given node has only simple content (i.e. no child element
23284      *     nodes).
23285      *
23286      * Parameters:
23287      * node - {DOMElement} An element node.
23288      *
23289      * Returns:
23290      * {Boolean} The node has no child element nodes (nodes of type 1). 
23291      */
23292     isSimpleContent: function(node) {
23293         var simple = true;
23294         for(var child=node.firstChild; child; child=child.nextSibling) {
23295             if(child.nodeType === 1) {
23296                 simple = false;
23297                 break;
23298             }
23299         }
23300         return simple;
23301     },
23302     
23303     /**
23304      * APIMethod: contentType
23305      * Determine the content type for a given node.
23306      *
23307      * Parameters:
23308      * node - {DOMElement}
23309      *
23310      * Returns:
23311      * {Integer} One of OpenLayers.Format.XML.CONTENT_TYPE.{EMPTY,SIMPLE,COMPLEX,MIXED}
23312      *     if the node has no, simple, complex, or mixed content.
23313      */
23314     contentType: function(node) {
23315         var simple = false,
23316             complex = false;
23317             
23318         var type = OpenLayers.Format.XML.CONTENT_TYPE.EMPTY;
23319
23320         for(var child=node.firstChild; child; child=child.nextSibling) {
23321             switch(child.nodeType) {
23322                 case 1: // element
23323                     complex = true;
23324                     break;
23325                 case 8: // comment
23326                     break;
23327                 default:
23328                     simple = true;
23329             }
23330             if(complex && simple) {
23331                 break;
23332             }
23333         }
23334         
23335         if(complex && simple) {
23336             type = OpenLayers.Format.XML.CONTENT_TYPE.MIXED;
23337         } else if(complex) {
23338             return OpenLayers.Format.XML.CONTENT_TYPE.COMPLEX;
23339         } else if(simple) {
23340             return OpenLayers.Format.XML.CONTENT_TYPE.SIMPLE;
23341         }
23342         return type;
23343     },
23344
23345     /**
23346      * APIMethod: hasAttributeNS
23347      * Determine whether a node has a particular attribute matching the given
23348      *     name and namespace.
23349      * 
23350      * Parameters:
23351      * node - {Element} Node on which to search for an attribute.
23352      * uri - {String} Namespace URI.
23353      * name - {String} Local name of the attribute (without the prefix).
23354      * 
23355      * Returns:
23356      * {Boolean} The node has an attribute matching the name and namespace.
23357      */
23358     hasAttributeNS: function(node, uri, name) {
23359         var found = false;
23360         if(node.hasAttributeNS) {
23361             found = node.hasAttributeNS(uri, name);
23362         } else {
23363             found = !!this.getAttributeNodeNS(node, uri, name);
23364         }
23365         return found;
23366     },
23367     
23368     /**
23369      * APIMethod: setAttributeNS
23370      * Adds a new attribute or changes the value of an attribute with the given
23371      *     namespace and name.
23372      *
23373      * Parameters:
23374      * node - {Element} Element node on which to set the attribute.
23375      * uri - {String} Namespace URI for the attribute.
23376      * name - {String} Qualified name (prefix:localname) for the attribute.
23377      * value - {String} Attribute value.
23378      */
23379     setAttributeNS: function(node, uri, name, value) {
23380         if(node.setAttributeNS) {
23381             node.setAttributeNS(uri, name, value);
23382         } else {
23383             if(this.xmldom) {
23384                 if(uri) {
23385                     var attribute = node.ownerDocument.createNode(
23386                         2, name, uri
23387                     );
23388                     attribute.nodeValue = value;
23389                     node.setAttributeNode(attribute);
23390                 } else {
23391                     node.setAttribute(name, value);
23392                 }
23393             } else {
23394                 throw "setAttributeNS not implemented";
23395             }
23396         }
23397     },
23398
23399     /**
23400      * Method: createElementNSPlus
23401      * Shorthand for creating namespaced elements with optional attributes and
23402      *     child text nodes.
23403      *
23404      * Parameters:
23405      * name - {String} The qualified node name.
23406      * options - {Object} Optional object for node configuration.
23407      *
23408      * Valid options:
23409      * uri - {String} Optional namespace uri for the element - supply a prefix
23410      *     instead if the namespace uri is a property of the format's namespace
23411      *     object.
23412      * attributes - {Object} Optional attributes to be set using the
23413      *     <setAttributes> method.
23414      * value - {String} Optional text to be appended as a text node.
23415      *
23416      * Returns:
23417      * {Element} An element node.
23418      */
23419     createElementNSPlus: function(name, options) {
23420         options = options || {};
23421         // order of prefix preference
23422         // 1. in the uri option
23423         // 2. in the prefix option
23424         // 3. in the qualified name
23425         // 4. from the defaultPrefix
23426         var uri = options.uri || this.namespaces[options.prefix];
23427         if(!uri) {
23428             var loc = name.indexOf(":");
23429             uri = this.namespaces[name.substring(0, loc)];
23430         }
23431         if(!uri) {
23432             uri = this.namespaces[this.defaultPrefix];
23433         }
23434         var node = this.createElementNS(uri, name);
23435         if(options.attributes) {
23436             this.setAttributes(node, options.attributes);
23437         }
23438         var value = options.value;
23439         if(value != null) {
23440             node.appendChild(this.createTextNode(value));
23441         }
23442         return node;
23443     },
23444     
23445     /**
23446      * Method: setAttributes
23447      * Set multiple attributes given key value pairs from an object.
23448      *
23449      * Parameters:
23450      * node - {Element} An element node.
23451      * obj - {Object || Array} An object whose properties represent attribute
23452      *     names and values represent attribute values.  If an attribute name
23453      *     is a qualified name ("prefix:local"), the prefix will be looked up
23454      *     in the parsers {namespaces} object.  If the prefix is found,
23455      *     setAttributeNS will be used instead of setAttribute.
23456      */
23457     setAttributes: function(node, obj) {
23458         var value, uri;
23459         for(var name in obj) {
23460             if(obj[name] != null && obj[name].toString) {
23461                 value = obj[name].toString();
23462                 // check for qualified attribute name ("prefix:local")
23463                 uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null;
23464                 this.setAttributeNS(node, uri, name, value);
23465             }
23466         }
23467     },
23468
23469     /**
23470      * Method: readNode
23471      * Shorthand for applying one of the named readers given the node
23472      *     namespace and local name.  Readers take two args (node, obj) and
23473      *     generally extend or modify the second.
23474      *
23475      * Parameters:
23476      * node - {DOMElement} The node to be read (required).
23477      * obj - {Object} The object to be modified (optional).
23478      *
23479      * Returns:
23480      * {Object} The input object, modified (or a new one if none was provided).
23481      */
23482     readNode: function(node, obj) {
23483         if(!obj) {
23484             obj = {};
23485         }
23486         var group = this.readers[node.namespaceURI ? this.namespaceAlias[node.namespaceURI]: this.defaultPrefix];
23487         if(group) {
23488             var local = node.localName || node.nodeName.split(":").pop();
23489             var reader = group[local] || group["*"];
23490             if(reader) {
23491                 reader.apply(this, [node, obj]);
23492             }
23493         }
23494         return obj;
23495     },
23496
23497     /**
23498      * Method: readChildNodes
23499      * Shorthand for applying the named readers to all children of a node.
23500      *     For each child of type 1 (element), <readSelf> is called.
23501      *
23502      * Parameters:
23503      * node - {DOMElement} The node to be read (required).
23504      * obj - {Object} The object to be modified (optional).
23505      *
23506      * Returns:
23507      * {Object} The input object, modified.
23508      */
23509     readChildNodes: function(node, obj) {
23510         if(!obj) {
23511             obj = {};
23512         }
23513         var children = node.childNodes;
23514         var child;
23515         for(var i=0, len=children.length; i<len; ++i) {
23516             child = children[i];
23517             if(child.nodeType == 1) {
23518                 this.readNode(child, obj);
23519             }
23520         }
23521         return obj;
23522     },
23523
23524     /**
23525      * Method: writeNode
23526      * Shorthand for applying one of the named writers and appending the
23527      *     results to a node.  If a qualified name is not provided for the
23528      *     second argument (and a local name is used instead), the namespace
23529      *     of the parent node will be assumed.
23530      *
23531      * Parameters:
23532      * name - {String} The name of a node to generate.  If a qualified name
23533      *     (e.g. "pre:Name") is used, the namespace prefix is assumed to be
23534      *     in the <writers> group.  If a local name is used (e.g. "Name") then
23535      *     the namespace of the parent is assumed.  If a local name is used
23536      *     and no parent is supplied, then the default namespace is assumed.
23537      * obj - {Object} Structure containing data for the writer.
23538      * parent - {DOMElement} Result will be appended to this node.  If no parent
23539      *     is supplied, the node will not be appended to anything.
23540      *
23541      * Returns:
23542      * {DOMElement} The child node.
23543      */
23544     writeNode: function(name, obj, parent) {
23545         var prefix, local;
23546         var split = name.indexOf(":");
23547         if(split > 0) {
23548             prefix = name.substring(0, split);
23549             local = name.substring(split + 1);
23550         } else {
23551             if(parent) {
23552                 prefix = this.namespaceAlias[parent.namespaceURI];
23553             } else {
23554                 prefix = this.defaultPrefix;
23555             }
23556             local = name;
23557         }
23558         var child = this.writers[prefix][local].apply(this, [obj]);
23559         if(parent) {
23560             parent.appendChild(child);
23561         }
23562         return child;
23563     },
23564
23565     /**
23566      * APIMethod: getChildEl
23567      * Get the first child element.  Optionally only return the first child
23568      *     if it matches the given name and namespace URI.
23569      *
23570      * Parameters:
23571      * node - {DOMElement} The parent node.
23572      * name - {String} Optional node name (local) to search for.
23573      * uri - {String} Optional namespace URI to search for.
23574      *
23575      * Returns:
23576      * {DOMElement} The first child.  Returns null if no element is found, if
23577      *     something significant besides an element is found, or if the element
23578      *     found does not match the optional name and uri.
23579      */
23580     getChildEl: function(node, name, uri) {
23581         return node && this.getThisOrNextEl(node.firstChild, name, uri);
23582     },
23583     
23584     /**
23585      * APIMethod: getNextEl
23586      * Get the next sibling element.  Optionally get the first sibling only
23587      *     if it matches the given local name and namespace URI.
23588      *
23589      * Parameters:
23590      * node - {DOMElement} The node.
23591      * name - {String} Optional local name of the sibling to search for.
23592      * uri - {String} Optional namespace URI of the sibling to search for.
23593      *
23594      * Returns:
23595      * {DOMElement} The next sibling element.  Returns null if no element is
23596      *     found, something significant besides an element is found, or the
23597      *     found element does not match the optional name and uri.
23598      */
23599     getNextEl: function(node, name, uri) {
23600         return node && this.getThisOrNextEl(node.nextSibling, name, uri);
23601     },
23602     
23603     /**
23604      * Method: getThisOrNextEl
23605      * Return this node or the next element node.  Optionally get the first
23606      *     sibling with the given local name or namespace URI.
23607      *
23608      * Parameters:
23609      * node - {DOMElement} The node.
23610      * name - {String} Optional local name of the sibling to search for.
23611      * uri - {String} Optional namespace URI of the sibling to search for.
23612      *
23613      * Returns:
23614      * {DOMElement} The next sibling element.  Returns null if no element is
23615      *     found, something significant besides an element is found, or the
23616      *     found element does not match the query.
23617      */
23618     getThisOrNextEl: function(node, name, uri) {
23619         outer: for(var sibling=node; sibling; sibling=sibling.nextSibling) {
23620             switch(sibling.nodeType) {
23621                 case 1: // Element
23622                     if((!name || name === (sibling.localName || sibling.nodeName.split(":").pop())) &&
23623                        (!uri || uri === sibling.namespaceURI)) {
23624                         // matches
23625                         break outer;
23626                     }
23627                     sibling = null;
23628                     break outer;
23629                 case 3: // Text
23630                     if(/^\s*$/.test(sibling.nodeValue)) {
23631                         break;
23632                     }
23633                 case 4: // CDATA
23634                 case 6: // ENTITY_NODE
23635                 case 12: // NOTATION_NODE
23636                 case 10: // DOCUMENT_TYPE_NODE
23637                 case 11: // DOCUMENT_FRAGMENT_NODE
23638                     sibling = null;
23639                     break outer;
23640             } // ignore comments and processing instructions
23641         }
23642         return sibling || null;
23643     },
23644     
23645     /**
23646      * APIMethod: lookupNamespaceURI
23647      * Takes a prefix and returns the namespace URI associated with it on the given
23648      *     node if found (and null if not). Supplying null for the prefix will
23649      *     return the default namespace.
23650      *
23651      * For browsers that support it, this calls the native lookupNamesapceURI
23652      *     function.  In other browsers, this is an implementation of
23653      *     http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
23654      *
23655      * For browsers that don't support the attribute.ownerElement property, this
23656      *     method cannot be called on attribute nodes.
23657      *     
23658      * Parameters:
23659      * node - {DOMElement} The node from which to start looking.
23660      * prefix - {String} The prefix to lookup or null to lookup the default namespace.
23661      * 
23662      * Returns:
23663      * {String} The namespace URI for the given prefix.  Returns null if the prefix
23664      *     cannot be found or the node is the wrong type.
23665      */
23666     lookupNamespaceURI: function(node, prefix) {
23667         var uri = null;
23668         if(node) {
23669             if(node.lookupNamespaceURI) {
23670                 uri = node.lookupNamespaceURI(prefix);
23671             } else {
23672                 outer: switch(node.nodeType) {
23673                     case 1: // ELEMENT_NODE
23674                         if(node.namespaceURI !== null && node.prefix === prefix) {
23675                             uri = node.namespaceURI;
23676                             break outer;
23677                         }
23678                         var len = node.attributes.length;
23679                         if(len) {
23680                             var attr;
23681                             for(var i=0; i<len; ++i) {
23682                                 attr = node.attributes[i];
23683                                 if(attr.prefix === "xmlns" && attr.name === "xmlns:" + prefix) {
23684                                     uri = attr.value || null;
23685                                     break outer;
23686                                 } else if(attr.name === "xmlns" && prefix === null) {
23687                                     uri = attr.value || null;
23688                                     break outer;
23689                                 }
23690                             }
23691                         }
23692                         uri = this.lookupNamespaceURI(node.parentNode, prefix);
23693                         break outer;
23694                     case 2: // ATTRIBUTE_NODE
23695                         uri = this.lookupNamespaceURI(node.ownerElement, prefix);
23696                         break outer;
23697                     case 9: // DOCUMENT_NODE
23698                         uri = this.lookupNamespaceURI(node.documentElement, prefix);
23699                         break outer;
23700                     case 6: // ENTITY_NODE
23701                     case 12: // NOTATION_NODE
23702                     case 10: // DOCUMENT_TYPE_NODE
23703                     case 11: // DOCUMENT_FRAGMENT_NODE
23704                         break outer;
23705                     default: 
23706                         // TEXT_NODE (3), CDATA_SECTION_NODE (4), ENTITY_REFERENCE_NODE (5),
23707                         // PROCESSING_INSTRUCTION_NODE (7), COMMENT_NODE (8)
23708                         uri =  this.lookupNamespaceURI(node.parentNode, prefix);
23709                         break outer;
23710                 }
23711             }
23712         }
23713         return uri;
23714     },
23715     
23716     /**
23717      * Method: getXMLDoc
23718      * Get an XML document for nodes that are not supported in HTML (e.g.
23719      * createCDATASection). On IE, this will either return an existing or
23720      * create a new <xmldom> on the instance. On other browsers, this will
23721      * either return an existing or create a new shared document (see
23722      * <OpenLayers.Format.XML.document>).
23723      *
23724      * Returns:
23725      * {XMLDocument}
23726      */
23727     getXMLDoc: function() {
23728         if (!OpenLayers.Format.XML.document && !this.xmldom) {
23729             if (document.implementation && document.implementation.createDocument) {
23730                 OpenLayers.Format.XML.document =
23731                     document.implementation.createDocument("", "", null);
23732             } else if (!this.xmldom && window.ActiveXObject) {
23733                 this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
23734             }
23735         }
23736         return OpenLayers.Format.XML.document || this.xmldom;
23737     },
23738
23739     CLASS_NAME: "OpenLayers.Format.XML" 
23740
23741 });     
23742
23743 OpenLayers.Format.XML.CONTENT_TYPE = {EMPTY: 0, SIMPLE: 1, COMPLEX: 2, MIXED: 3};
23744
23745 /**
23746  * APIFunction: OpenLayers.Format.XML.lookupNamespaceURI
23747  * Takes a prefix and returns the namespace URI associated with it on the given
23748  *     node if found (and null if not). Supplying null for the prefix will
23749  *     return the default namespace.
23750  *
23751  * For browsers that support it, this calls the native lookupNamesapceURI
23752  *     function.  In other browsers, this is an implementation of
23753  *     http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
23754  *
23755  * For browsers that don't support the attribute.ownerElement property, this
23756  *     method cannot be called on attribute nodes.
23757  *     
23758  * Parameters:
23759  * node - {DOMElement} The node from which to start looking.
23760  * prefix - {String} The prefix to lookup or null to lookup the default namespace.
23761  * 
23762  * Returns:
23763  * {String} The namespace URI for the given prefix.  Returns null if the prefix
23764  *     cannot be found or the node is the wrong type.
23765  */
23766 OpenLayers.Format.XML.lookupNamespaceURI = OpenLayers.Function.bind(
23767     OpenLayers.Format.XML.prototype.lookupNamespaceURI,
23768     OpenLayers.Format.XML.prototype
23769 );
23770
23771 /**
23772  * Property: OpenLayers.Format.XML.document
23773  * {XMLDocument} XML document to reuse for creating non-HTML compliant nodes,
23774  * like document.createCDATASection.
23775  */
23776 OpenLayers.Format.XML.document = null;
23777 /* ======================================================================
23778     OpenLayers/Format/OGCExceptionReport.js
23779    ====================================================================== */
23780
23781 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
23782  * full list of contributors). Published under the 2-clause BSD license.
23783  * See license.txt in the OpenLayers distribution or repository for the
23784  * full text of the license. */
23785
23786 /**
23787  * @requires OpenLayers/Format/XML.js
23788  */
23789
23790 /**
23791  * Class: OpenLayers.Format.OGCExceptionReport
23792  * Class to read exception reports for various OGC services and versions.
23793  *
23794  * Inherits from:
23795  *  - <OpenLayers.Format.XML>
23796  */
23797 OpenLayers.Format.OGCExceptionReport = OpenLayers.Class(OpenLayers.Format.XML, {
23798
23799     /**
23800      * Property: namespaces
23801      * {Object} Mapping of namespace aliases to namespace URIs.
23802      */
23803     namespaces: {
23804         ogc: "http://www.opengis.net/ogc"
23805     },
23806
23807     /**
23808      * Property: regExes
23809      * Compiled regular expressions for manipulating strings.
23810      */
23811     regExes: {
23812         trimSpace: (/^\s*|\s*$/g),
23813         removeSpace: (/\s*/g),
23814         splitSpace: (/\s+/),
23815         trimComma: (/\s*,\s*/g)
23816     },
23817
23818     /**
23819      * Property: defaultPrefix
23820      */
23821     defaultPrefix: "ogc",
23822
23823     /**
23824      * Constructor: OpenLayers.Format.OGCExceptionReport
23825      * Create a new parser for OGC exception reports.
23826      *
23827      * Parameters:
23828      * options - {Object} An optional object whose properties will be set on
23829      *     this instance.
23830      */
23831
23832     /**
23833      * APIMethod: read
23834      * Read OGC exception report data from a string, and return an object with
23835      * information about the exceptions.
23836      *
23837      * Parameters:
23838      * data - {String} or {DOMElement} data to read/parse.
23839      *
23840      * Returns:
23841      * {Object} Information about the exceptions that occurred.
23842      */
23843     read: function(data) {
23844         var result;
23845         if(typeof data == "string") {
23846             data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
23847         }
23848         var root = data.documentElement;
23849         var exceptionInfo = {exceptionReport: null}; 
23850         if (root) {
23851             this.readChildNodes(data, exceptionInfo);
23852             if (exceptionInfo.exceptionReport === null) {
23853                 // fall-back to OWSCommon since this is a common output format for exceptions
23854                 // we cannot easily use the ows readers directly since they differ for 1.0 and 1.1
23855                 exceptionInfo = new OpenLayers.Format.OWSCommon().read(data);
23856             }
23857         }
23858         return exceptionInfo;
23859     },
23860
23861     /**
23862      * Property: readers
23863      * Contains public functions, grouped by namespace prefix, that will
23864      *     be applied when a namespaced node is found matching the function
23865      *     name.  The function will be applied in the scope of this parser
23866      *     with two arguments: the node being read and a context object passed
23867      *     from the parent.
23868      */
23869     readers: {
23870         "ogc": {
23871             "ServiceExceptionReport": function(node, obj) {
23872                 obj.exceptionReport = {exceptions: []};
23873                 this.readChildNodes(node, obj.exceptionReport);
23874             },
23875             "ServiceException": function(node, exceptionReport) {
23876                 var exception = {
23877                     code: node.getAttribute("code"),
23878                     locator: node.getAttribute("locator"),
23879                     text: this.getChildValue(node)
23880                 };
23881                 exceptionReport.exceptions.push(exception);
23882             }
23883         }
23884     },
23885     
23886     CLASS_NAME: "OpenLayers.Format.OGCExceptionReport"
23887     
23888 });
23889 /* ======================================================================
23890     OpenLayers/Format/XML/VersionedOGC.js
23891    ====================================================================== */
23892
23893 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
23894  * full list of contributors). Published under the 2-clause BSD license.
23895  * See license.txt in the OpenLayers distribution or repository for the
23896  * full text of the license. */
23897
23898 /**
23899  * @requires OpenLayers/Format/XML.js
23900  * @requires OpenLayers/Format/OGCExceptionReport.js
23901  */
23902
23903 /**
23904  * Class: OpenLayers.Format.XML.VersionedOGC
23905  * Base class for versioned formats, i.e. a format which supports multiple
23906  * versions.
23907  *
23908  * To enable checking if parsing succeeded, you will need to define a property
23909  * called errorProperty on the parser you want to check. The parser will then
23910  * check the returned object to see if that property is present. If it is, it
23911  * assumes the parsing was successful. If it is not present (or is null), it will
23912  * pass the document through an OGCExceptionReport parser.
23913  * 
23914  * If errorProperty is undefined for the parser, this error checking mechanism
23915  * will be disabled.
23916  *
23917  *
23918  * 
23919  * Inherits from:
23920  *  - <OpenLayers.Format.XML>
23921  */
23922 OpenLayers.Format.XML.VersionedOGC = OpenLayers.Class(OpenLayers.Format.XML, {
23923     
23924     /**
23925      * APIProperty: defaultVersion
23926      * {String} Version number to assume if none found.
23927      */
23928     defaultVersion: null,
23929     
23930     /**
23931      * APIProperty: version
23932      * {String} Specify a version string if one is known.
23933      */
23934     version: null,
23935
23936     /**
23937      * APIProperty: profile
23938      * {String} If provided, use a custom profile.
23939      */
23940     profile: null,
23941
23942     /**
23943      * APIProperty: allowFallback
23944      * {Boolean} If a profiled parser cannot be found for the returned version,
23945      * use a non-profiled parser as the fallback. Application code using this
23946      * should take into account that the return object structure might be
23947      * missing the specifics of the profile. Defaults to false.
23948      */
23949     allowFallback: false,
23950
23951     /**
23952      * Property: name
23953      * {String} The name of this parser, this is the part of the CLASS_NAME
23954      * except for "OpenLayers.Format."
23955      */
23956     name: null,
23957
23958     /**
23959      * APIProperty: stringifyOutput
23960      * {Boolean} If true, write will return a string otherwise a DOMElement.
23961      * Default is false.
23962      */
23963     stringifyOutput: false,
23964
23965     /**
23966      * Property: parser
23967      * {Object} Instance of the versioned parser.  Cached for multiple read and
23968      *     write calls of the same version.
23969      */
23970     parser: null,
23971
23972     /**
23973      * Constructor: OpenLayers.Format.XML.VersionedOGC.
23974      * Constructor.
23975      *
23976      * Parameters:
23977      * options - {Object} Optional object whose properties will be set on
23978      *     the object.
23979      */
23980     initialize: function(options) {
23981         OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
23982         var className = this.CLASS_NAME;
23983         this.name = className.substring(className.lastIndexOf(".")+1);
23984     },
23985
23986     /**
23987      * Method: getVersion
23988      * Returns the version to use. Subclasses can override this function
23989      * if a different version detection is needed.
23990      *
23991      * Parameters:
23992      * root - {DOMElement}
23993      * options - {Object} Optional configuration object.
23994      *
23995      * Returns:
23996      * {String} The version to use.
23997      */
23998     getVersion: function(root, options) {
23999         var version;
24000         // read
24001         if (root) {
24002             version = this.version;
24003             if(!version) {
24004                 version = root.getAttribute("version");
24005                 if(!version) {
24006                     version = this.defaultVersion;
24007                 }
24008             }
24009         } else { // write
24010             version = (options && options.version) || 
24011                 this.version || this.defaultVersion;
24012         }
24013         return version;
24014     },
24015
24016     /**
24017      * Method: getParser
24018      * Get an instance of the cached parser if available, otherwise create one.
24019      *
24020      * Parameters:
24021      * version - {String}
24022      *
24023      * Returns:
24024      * {<OpenLayers.Format>}
24025      */
24026     getParser: function(version) {
24027         version = version || this.defaultVersion;
24028         var profile = this.profile ? "_" + this.profile : "";
24029         if(!this.parser || this.parser.VERSION != version) {
24030             var format = OpenLayers.Format[this.name][
24031                 "v" + version.replace(/\./g, "_") + profile
24032             ];
24033             if(!format) {
24034                 if (profile !== "" && this.allowFallback) {
24035                     // fallback to the non-profiled version of the parser
24036                     profile = "";
24037                     format = OpenLayers.Format[this.name][
24038                         "v" + version.replace(/\./g, "_")
24039                     ];
24040                 }
24041                 if (!format) {
24042                     throw "Can't find a " + this.name + " parser for version " +
24043                           version + profile;
24044                 }
24045             }
24046             this.parser = new format(this.options);
24047         }
24048         return this.parser;
24049     },
24050
24051     /**
24052      * APIMethod: write
24053      * Write a document.
24054      *
24055      * Parameters:
24056      * obj - {Object} An object representing the document.
24057      * options - {Object} Optional configuration object.
24058      *
24059      * Returns:
24060      * {String} The document as a string
24061      */
24062     write: function(obj, options) {
24063         var version = this.getVersion(null, options);
24064         this.parser = this.getParser(version);
24065         var root = this.parser.write(obj, options);
24066         if (this.stringifyOutput === false) {
24067             return root;
24068         } else {
24069             return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
24070         }
24071     },
24072
24073     /**
24074      * APIMethod: read
24075      * Read a doc and return an object representing the document.
24076      *
24077      * Parameters:
24078      * data - {String | DOMElement} Data to read.
24079      * options - {Object} Options for the reader.
24080      *
24081      * Returns:
24082      * {Object} An object representing the document.
24083      */
24084     read: function(data, options) {
24085         if(typeof data == "string") {
24086             data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
24087         }
24088         var root = data.documentElement;
24089         var version = this.getVersion(root);
24090         this.parser = this.getParser(version);          // Select the parser
24091         var obj = this.parser.read(data, options);      // Parse the data
24092
24093         var errorProperty = this.parser.errorProperty || null;
24094         if (errorProperty !== null && obj[errorProperty] === undefined) {
24095             // an error must have happened, so parse it and report back
24096             var format = new OpenLayers.Format.OGCExceptionReport();
24097             obj.error = format.read(data);
24098         }
24099         obj.version = version;
24100         return obj;
24101     },
24102
24103     CLASS_NAME: "OpenLayers.Format.XML.VersionedOGC"
24104 });
24105 /* ======================================================================
24106     OpenLayers/Filter/FeatureId.js
24107    ====================================================================== */
24108
24109 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24110  * full list of contributors). Published under the 2-clause BSD license.
24111  * See license.txt in the OpenLayers distribution or repository for the
24112  * full text of the license. */
24113
24114
24115 /**
24116  * @requires OpenLayers/Filter.js
24117  */
24118
24119 /**
24120  * Class: OpenLayers.Filter.FeatureId
24121  * This class represents a ogc:FeatureId Filter, as being used for rule-based SLD
24122  * styling
24123  * 
24124  * Inherits from:
24125  * - <OpenLayers.Filter>
24126  */
24127 OpenLayers.Filter.FeatureId = OpenLayers.Class(OpenLayers.Filter, {
24128
24129     /** 
24130      * APIProperty: fids
24131      * {Array(String)} Feature Ids to evaluate this rule against. 
24132      *     To be passed inside the params object.
24133      */
24134     fids: null,
24135     
24136     /** 
24137      * Property: type
24138      * {String} Type to identify this filter.
24139      */
24140     type: "FID",
24141     
24142     /** 
24143      * Constructor: OpenLayers.Filter.FeatureId
24144      * Creates an ogc:FeatureId rule.
24145      *
24146      * Parameters:
24147      * options - {Object} An optional object with properties to set on the
24148      *           rule
24149      * 
24150      * Returns:
24151      * {<OpenLayers.Filter.FeatureId>}
24152      */
24153     initialize: function(options) {
24154         this.fids = [];
24155         OpenLayers.Filter.prototype.initialize.apply(this, [options]);
24156     },
24157
24158     /**
24159      * APIMethod: evaluate
24160      * evaluates this rule for a specific feature
24161      * 
24162      * Parameters:
24163      * feature - {<OpenLayers.Feature>} feature to apply the rule to.
24164      *           For vector features, the check is run against the fid,
24165      *           for plain features against the id.
24166      * 
24167      * Returns:
24168      * {Boolean} true if the rule applies, false if it does not
24169      */
24170     evaluate: function(feature) {
24171         for (var i=0, len=this.fids.length; i<len; i++) {
24172             var fid = feature.fid || feature.id;
24173             if (fid == this.fids[i]) {
24174                 return true;
24175             }
24176         }
24177         return false;
24178     },
24179     
24180     /**
24181      * APIMethod: clone
24182      * Clones this filter.
24183      * 
24184      * Returns:
24185      * {<OpenLayers.Filter.FeatureId>} Clone of this filter.
24186      */
24187     clone: function() {
24188         var filter = new OpenLayers.Filter.FeatureId();
24189         OpenLayers.Util.extend(filter, this);
24190         filter.fids = this.fids.slice();
24191         return filter;
24192     },
24193     
24194     CLASS_NAME: "OpenLayers.Filter.FeatureId"
24195 });
24196 /* ======================================================================
24197     OpenLayers/Filter/Logical.js
24198    ====================================================================== */
24199
24200 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24201  * full list of contributors). Published under the 2-clause BSD license.
24202  * See license.txt in the OpenLayers distribution or repository for the
24203  * full text of the license. */
24204
24205
24206 /**
24207  * @requires OpenLayers/Filter.js
24208  */
24209
24210 /**
24211  * Class: OpenLayers.Filter.Logical
24212  * This class represents ogc:And, ogc:Or and ogc:Not rules.
24213  * 
24214  * Inherits from:
24215  * - <OpenLayers.Filter>
24216  */
24217 OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {
24218
24219     /**
24220      * APIProperty: filters
24221      * {Array(<OpenLayers.Filter>)} Child filters for this filter.
24222      */
24223     filters: null, 
24224      
24225     /**
24226      * APIProperty: type
24227      * {String} type of logical operator. Available types are:
24228      * - OpenLayers.Filter.Logical.AND = "&&";
24229      * - OpenLayers.Filter.Logical.OR  = "||";
24230      * - OpenLayers.Filter.Logical.NOT = "!";
24231      */
24232     type: null,
24233
24234     /** 
24235      * Constructor: OpenLayers.Filter.Logical
24236      * Creates a logical filter (And, Or, Not).
24237      *
24238      * Parameters:
24239      * options - {Object} An optional object with properties to set on the
24240      *     filter.
24241      * 
24242      * Returns:
24243      * {<OpenLayers.Filter.Logical>}
24244      */
24245     initialize: function(options) {
24246         this.filters = [];
24247         OpenLayers.Filter.prototype.initialize.apply(this, [options]);
24248     },
24249     
24250     /** 
24251      * APIMethod: destroy
24252      * Remove reference to child filters.
24253      */
24254     destroy: function() {
24255         this.filters = null;
24256         OpenLayers.Filter.prototype.destroy.apply(this);
24257     },
24258
24259     /**
24260      * APIMethod: evaluate
24261      * Evaluates this filter in a specific context.
24262      * 
24263      * Parameters:
24264      * context - {Object} Context to use in evaluating the filter.  A vector
24265      *     feature may also be provided to evaluate feature attributes in 
24266      *     comparison filters or geometries in spatial filters.
24267      * 
24268      * Returns:
24269      * {Boolean} The filter applies.
24270      */
24271     evaluate: function(context) {
24272         var i, len;
24273         switch(this.type) {
24274             case OpenLayers.Filter.Logical.AND:
24275                 for (i=0, len=this.filters.length; i<len; i++) {
24276                     if (this.filters[i].evaluate(context) == false) {
24277                         return false;
24278                     }
24279                 }
24280                 return true;
24281                 
24282             case OpenLayers.Filter.Logical.OR:
24283                 for (i=0, len=this.filters.length; i<len; i++) {
24284                     if (this.filters[i].evaluate(context) == true) {
24285                         return true;
24286                     }
24287                 }
24288                 return false;
24289             
24290             case OpenLayers.Filter.Logical.NOT:
24291                 return (!this.filters[0].evaluate(context));
24292         }
24293         return undefined;
24294     },
24295     
24296     /**
24297      * APIMethod: clone
24298      * Clones this filter.
24299      * 
24300      * Returns:
24301      * {<OpenLayers.Filter.Logical>} Clone of this filter.
24302      */
24303     clone: function() {
24304         var filters = [];        
24305         for(var i=0, len=this.filters.length; i<len; ++i) {
24306             filters.push(this.filters[i].clone());
24307         }
24308         return new OpenLayers.Filter.Logical({
24309             type: this.type,
24310             filters: filters
24311         });
24312     },
24313     
24314     CLASS_NAME: "OpenLayers.Filter.Logical"
24315 });
24316
24317
24318 OpenLayers.Filter.Logical.AND = "&&";
24319 OpenLayers.Filter.Logical.OR  = "||";
24320 OpenLayers.Filter.Logical.NOT = "!";
24321 /* ======================================================================
24322     OpenLayers/Filter/Comparison.js
24323    ====================================================================== */
24324
24325 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24326  * full list of contributors). Published under the 2-clause BSD license.
24327  * See license.txt in the OpenLayers distribution or repository for the
24328  * full text of the license. */
24329
24330 /**
24331  * @requires OpenLayers/Filter.js
24332  */
24333
24334 /**
24335  * Class: OpenLayers.Filter.Comparison
24336  * This class represents a comparison filter.
24337  * 
24338  * Inherits from:
24339  * - <OpenLayers.Filter>
24340  */
24341 OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {
24342
24343     /**
24344      * APIProperty: type
24345      * {String} type: type of the comparison. This is one of
24346      * - OpenLayers.Filter.Comparison.EQUAL_TO                 = "==";
24347      * - OpenLayers.Filter.Comparison.NOT_EQUAL_TO             = "!=";
24348      * - OpenLayers.Filter.Comparison.LESS_THAN                = "<";
24349      * - OpenLayers.Filter.Comparison.GREATER_THAN             = ">";
24350      * - OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO    = "<=";
24351      * - OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
24352      * - OpenLayers.Filter.Comparison.BETWEEN                  = "..";
24353      * - OpenLayers.Filter.Comparison.LIKE                     = "~";
24354      * - OpenLayers.Filter.Comparison.IS_NULL                  = "NULL";
24355      */
24356     type: null,
24357     
24358     /**
24359      * APIProperty: property
24360      * {String}
24361      * name of the context property to compare
24362      */
24363     property: null,
24364     
24365     /**
24366      * APIProperty: value
24367      * {Number} or {String}
24368      * comparison value for binary comparisons. In the case of a String, this
24369      * can be a combination of text and propertyNames in the form
24370      * "literal ${propertyName}"
24371      */
24372     value: null,
24373     
24374     /**
24375      * Property: matchCase
24376      * {Boolean} Force case sensitive searches for EQUAL_TO and NOT_EQUAL_TO
24377      *     comparisons.  The Filter Encoding 1.1 specification added a matchCase
24378      *     attribute to ogc:PropertyIsEqualTo and ogc:PropertyIsNotEqualTo
24379      *     elements.  This property will be serialized with those elements only
24380      *     if using the v1.1.0 filter format. However, when evaluating filters
24381      *     here, the matchCase property will always be respected (for EQUAL_TO
24382      *     and NOT_EQUAL_TO).  Default is true. 
24383      */
24384     matchCase: true,
24385     
24386     /**
24387      * APIProperty: lowerBoundary
24388      * {Number} or {String}
24389      * lower boundary for between comparisons. In the case of a String, this
24390      * can be a combination of text and propertyNames in the form
24391      * "literal ${propertyName}"
24392      */
24393     lowerBoundary: null,
24394     
24395     /**
24396      * APIProperty: upperBoundary
24397      * {Number} or {String}
24398      * upper boundary for between comparisons. In the case of a String, this
24399      * can be a combination of text and propertyNames in the form
24400      * "literal ${propertyName}"
24401      */
24402     upperBoundary: null,
24403
24404     /** 
24405      * Constructor: OpenLayers.Filter.Comparison
24406      * Creates a comparison rule.
24407      *
24408      * Parameters:
24409      * options - {Object} An optional object with properties to set on the
24410      *           rule
24411      * 
24412      * Returns:
24413      * {<OpenLayers.Filter.Comparison>}
24414      */
24415     initialize: function(options) {
24416         OpenLayers.Filter.prototype.initialize.apply(this, [options]);
24417         // since matchCase on PropertyIsLike is not schema compliant, we only
24418         // want to use this if explicitly asked for
24419         if (this.type === OpenLayers.Filter.Comparison.LIKE 
24420             && options.matchCase === undefined) {
24421                 this.matchCase = null;
24422         }
24423     },
24424
24425     /**
24426      * APIMethod: evaluate
24427      * Evaluates this filter in a specific context.
24428      * 
24429      * Parameters:
24430      * context - {Object} Context to use in evaluating the filter.  If a vector
24431      *     feature is provided, the feature.attributes will be used as context.
24432      * 
24433      * Returns:
24434      * {Boolean} The filter applies.
24435      */
24436     evaluate: function(context) {
24437         if (context instanceof OpenLayers.Feature.Vector) {
24438             context = context.attributes;
24439         }
24440         var result = false;
24441         var got = context[this.property];
24442         var exp;
24443         switch(this.type) {
24444             case OpenLayers.Filter.Comparison.EQUAL_TO:
24445                 exp = this.value;
24446                 if(!this.matchCase &&
24447                    typeof got == "string" && typeof exp == "string") {
24448                     result = (got.toUpperCase() == exp.toUpperCase());
24449                 } else {
24450                     result = (got == exp);
24451                 }
24452                 break;
24453             case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:
24454                 exp = this.value;
24455                 if(!this.matchCase &&
24456                    typeof got == "string" && typeof exp == "string") {
24457                     result = (got.toUpperCase() != exp.toUpperCase());
24458                 } else {
24459                     result = (got != exp);
24460                 }
24461                 break;
24462             case OpenLayers.Filter.Comparison.LESS_THAN:
24463                 result = got < this.value;
24464                 break;
24465             case OpenLayers.Filter.Comparison.GREATER_THAN:
24466                 result = got > this.value;
24467                 break;
24468             case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:
24469                 result = got <= this.value;
24470                 break;
24471             case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:
24472                 result = got >= this.value;
24473                 break;
24474             case OpenLayers.Filter.Comparison.BETWEEN:
24475                 result = (got >= this.lowerBoundary) &&
24476                     (got <= this.upperBoundary);
24477                 break;
24478             case OpenLayers.Filter.Comparison.LIKE:
24479                 var regexp = new RegExp(this.value, "gi");
24480                 result = regexp.test(got);
24481                 break;
24482             case OpenLayers.Filter.Comparison.IS_NULL:
24483                 result = (got === null);
24484                 break;
24485         }
24486         return result;
24487     },
24488     
24489     /**
24490      * APIMethod: value2regex
24491      * Converts the value of this rule into a regular expression string,
24492      * according to the wildcard characters specified. This method has to
24493      * be called after instantiation of this class, if the value is not a
24494      * regular expression already.
24495      * 
24496      * Parameters:
24497      * wildCard   - {Char} wildcard character in the above value, default
24498      *              is "*"
24499      * singleChar - {Char} single-character wildcard in the above value
24500      *              default is "."
24501      * escapeChar - {Char} escape character in the above value, default is
24502      *              "!"
24503      * 
24504      * Returns:
24505      * {String} regular expression string
24506      */
24507     value2regex: function(wildCard, singleChar, escapeChar) {
24508         if (wildCard == ".") {
24509             throw new Error("'.' is an unsupported wildCard character for " +
24510                             "OpenLayers.Filter.Comparison");
24511         }
24512         
24513
24514         // set UMN MapServer defaults for unspecified parameters
24515         wildCard = wildCard ? wildCard : "*";
24516         singleChar = singleChar ? singleChar : ".";
24517         escapeChar = escapeChar ? escapeChar : "!";
24518         
24519         this.value = this.value.replace(
24520                 new RegExp("\\"+escapeChar+"(.|$)", "g"), "\\$1");
24521         this.value = this.value.replace(
24522                 new RegExp("\\"+singleChar, "g"), ".");
24523         this.value = this.value.replace(
24524                 new RegExp("\\"+wildCard, "g"), ".*");
24525         this.value = this.value.replace(
24526                 new RegExp("\\\\.\\*", "g"), "\\"+wildCard);
24527         this.value = this.value.replace(
24528                 new RegExp("\\\\\\.", "g"), "\\"+singleChar);
24529         
24530         return this.value;
24531     },
24532     
24533     /**
24534      * Method: regex2value
24535      * Convert the value of this rule from a regular expression string into an
24536      *     ogc literal string using a wildCard of *, a singleChar of ., and an
24537      *     escape of !.  Leaves the <value> property unmodified.
24538      * 
24539      * Returns:
24540      * {String} A string value.
24541      */
24542     regex2value: function() {
24543         
24544         var value = this.value;
24545         
24546         // replace ! with !!
24547         value = value.replace(/!/g, "!!");
24548
24549         // replace \. with !. (watching out for \\.)
24550         value = value.replace(/(\\)?\\\./g, function($0, $1) {
24551             return $1 ? $0 : "!.";
24552         });
24553         
24554         // replace \* with #* (watching out for \\*)
24555         value = value.replace(/(\\)?\\\*/g, function($0, $1) {
24556             return $1 ? $0 : "!*";
24557         });
24558         
24559         // replace \\ with \
24560         value = value.replace(/\\\\/g, "\\");
24561
24562         // convert .* to * (the sequence #.* is not allowed)
24563         value = value.replace(/\.\*/g, "*");
24564         
24565         return value;
24566     },
24567     
24568     /**
24569      * APIMethod: clone
24570      * Clones this filter.
24571      * 
24572      * Returns:
24573      * {<OpenLayers.Filter.Comparison>} Clone of this filter.
24574      */
24575     clone: function() {
24576         return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this);
24577     },
24578     
24579     CLASS_NAME: "OpenLayers.Filter.Comparison"
24580 });
24581
24582
24583 OpenLayers.Filter.Comparison.EQUAL_TO                 = "==";
24584 OpenLayers.Filter.Comparison.NOT_EQUAL_TO             = "!=";
24585 OpenLayers.Filter.Comparison.LESS_THAN                = "<";
24586 OpenLayers.Filter.Comparison.GREATER_THAN             = ">";
24587 OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO    = "<=";
24588 OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
24589 OpenLayers.Filter.Comparison.BETWEEN                  = "..";
24590 OpenLayers.Filter.Comparison.LIKE                     = "~";
24591 OpenLayers.Filter.Comparison.IS_NULL                  = "NULL";
24592 /* ======================================================================
24593     OpenLayers/Format/Filter.js
24594    ====================================================================== */
24595
24596 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24597  * full list of contributors). Published under the 2-clause BSD license.
24598  * See license.txt in the OpenLayers distribution or repository for the
24599  * full text of the license. */
24600
24601 /**
24602  * @requires OpenLayers/Format/XML/VersionedOGC.js
24603  * @requires OpenLayers/Filter/FeatureId.js
24604  * @requires OpenLayers/Filter/Logical.js
24605  * @requires OpenLayers/Filter/Comparison.js
24606  */
24607
24608 /**
24609  * Class: OpenLayers.Format.Filter
24610  * Read/Write ogc:Filter. Create a new instance with the <OpenLayers.Format.Filter>
24611  *     constructor.
24612  * 
24613  * Inherits from:
24614  *  - <OpenLayers.Format.XML.VersionedOGC>
24615  */
24616 OpenLayers.Format.Filter = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
24617     
24618     /**
24619      * APIProperty: defaultVersion
24620      * {String} Version number to assume if none found.  Default is "1.0.0".
24621      */
24622     defaultVersion: "1.0.0",
24623     
24624     /**
24625      * APIMethod: write
24626      * Write an ogc:Filter given a filter object.
24627      *
24628      * Parameters:
24629      * filter - {<OpenLayers.Filter>} An filter.
24630      * options - {Object} Optional configuration object.
24631      *
24632      * Returns:
24633      * {Elment} An ogc:Filter element node.
24634      */
24635     
24636     /**
24637      * APIMethod: read
24638      * Read and Filter doc and return an object representing the Filter.
24639      *
24640      * Parameters:
24641      * data - {String | DOMElement} Data to read.
24642      *
24643      * Returns:
24644      * {<OpenLayers.Filter>} A filter object.
24645      */
24646
24647     CLASS_NAME: "OpenLayers.Format.Filter" 
24648 });
24649 /* ======================================================================
24650     OpenLayers/Format/WFST.js
24651    ====================================================================== */
24652
24653 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24654  * full list of contributors). Published under the 2-clause BSD license.
24655  * See license.txt in the OpenLayers distribution or repository for the
24656  * full text of the license. */
24657
24658 /**
24659  * @requires OpenLayers/Format.js
24660  */
24661
24662 /**
24663  * Function: OpenLayers.Format.WFST
24664  * Used to create a versioned WFS protocol.  Default version is 1.0.0.
24665  *
24666  * Returns:
24667  * {<OpenLayers.Format>} A WFST format of the given version.
24668  */
24669 OpenLayers.Format.WFST = function(options) {
24670     options = OpenLayers.Util.applyDefaults(
24671         options, OpenLayers.Format.WFST.DEFAULTS
24672     );
24673     var cls = OpenLayers.Format.WFST["v"+options.version.replace(/\./g, "_")];
24674     if(!cls) {
24675         throw "Unsupported WFST version: " + options.version;
24676     }
24677     return new cls(options);
24678 };
24679
24680 /**
24681  * Constant: OpenLayers.Format.WFST.DEFAULTS
24682  * {Object} Default properties for the WFST format.
24683  */
24684 OpenLayers.Format.WFST.DEFAULTS = {
24685     "version": "1.0.0"
24686 };
24687 /* ======================================================================
24688     OpenLayers/Filter/Spatial.js
24689    ====================================================================== */
24690
24691 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24692  * full list of contributors). Published under the 2-clause BSD license.
24693  * See license.txt in the OpenLayers distribution or repository for the
24694  * full text of the license. */
24695
24696 /**
24697  * @requires OpenLayers/Filter.js
24698  */
24699
24700 /**
24701  * Class: OpenLayers.Filter.Spatial
24702  * This class represents a spatial filter.
24703  * Currently implemented: BBOX, DWithin and Intersects
24704  * 
24705  * Inherits from:
24706  * - <OpenLayers.Filter>
24707  */
24708 OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, {
24709
24710     /**
24711      * APIProperty: type
24712      * {String} Type of spatial filter.
24713      *
24714      * The type should be one of:
24715      * - OpenLayers.Filter.Spatial.BBOX
24716      * - OpenLayers.Filter.Spatial.INTERSECTS
24717      * - OpenLayers.Filter.Spatial.DWITHIN
24718      * - OpenLayers.Filter.Spatial.WITHIN
24719      * - OpenLayers.Filter.Spatial.CONTAINS
24720      */
24721     type: null,
24722     
24723     /**
24724      * APIProperty: property
24725      * {String} Name of the context property to compare.
24726      */
24727     property: null,
24728     
24729     /**
24730      * APIProperty: value
24731      * {<OpenLayers.Bounds> || <OpenLayers.Geometry>} The bounds or geometry
24732      *     to be used by the filter.  Use bounds for BBOX filters and geometry
24733      *     for INTERSECTS or DWITHIN filters.
24734      */
24735     value: null,
24736
24737     /**
24738      * APIProperty: distance
24739      * {Number} The distance to use in a DWithin spatial filter.
24740      */
24741     distance: null,
24742
24743     /**
24744      * APIProperty: distanceUnits
24745      * {String} The units to use for the distance, e.g. 'm'.
24746      */
24747     distanceUnits: null,
24748     
24749     /** 
24750      * Constructor: OpenLayers.Filter.Spatial
24751      * Creates a spatial filter.
24752      *
24753      * Parameters:
24754      * options - {Object} An optional object with properties to set on the
24755      *     filter.
24756      * 
24757      * Returns:
24758      * {<OpenLayers.Filter.Spatial>}
24759      */
24760
24761    /**
24762     * Method: evaluate
24763     * Evaluates this filter for a specific feature.
24764     * 
24765     * Parameters:
24766     * feature - {<OpenLayers.Feature.Vector>} feature to apply the filter to.
24767     * 
24768     * Returns:
24769     * {Boolean} The feature meets filter criteria.
24770     */
24771     evaluate: function(feature) {
24772         var intersect = false;
24773         switch(this.type) {
24774             case OpenLayers.Filter.Spatial.BBOX:
24775             case OpenLayers.Filter.Spatial.INTERSECTS:
24776                 if(feature.geometry) {
24777                     var geom = this.value;
24778                     if(this.value.CLASS_NAME == "OpenLayers.Bounds") {
24779                         geom = this.value.toGeometry();
24780                     }
24781                     if(feature.geometry.intersects(geom)) {
24782                         intersect = true;
24783                     }
24784                 }
24785                 break;
24786             default:
24787                 throw new Error('evaluate is not implemented for this filter type.');
24788         }
24789         return intersect;
24790     },
24791
24792     /**
24793      * APIMethod: clone
24794      * Clones this filter.
24795      * 
24796      * Returns:
24797      * {<OpenLayers.Filter.Spatial>} Clone of this filter.
24798      */
24799     clone: function() {
24800         var options = OpenLayers.Util.applyDefaults({
24801             value: this.value && this.value.clone && this.value.clone()
24802         }, this);
24803         return new OpenLayers.Filter.Spatial(options);
24804     },
24805     CLASS_NAME: "OpenLayers.Filter.Spatial"
24806 });
24807
24808 OpenLayers.Filter.Spatial.BBOX = "BBOX";
24809 OpenLayers.Filter.Spatial.INTERSECTS = "INTERSECTS";
24810 OpenLayers.Filter.Spatial.DWITHIN = "DWITHIN";
24811 OpenLayers.Filter.Spatial.WITHIN = "WITHIN";
24812 OpenLayers.Filter.Spatial.CONTAINS = "CONTAINS";
24813 /* ======================================================================
24814     OpenLayers/Format/WFST/v1.js
24815    ====================================================================== */
24816
24817 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24818  * full list of contributors). Published under the 2-clause BSD license.
24819  * See license.txt in the OpenLayers distribution or repository for the
24820  * full text of the license. */
24821
24822 /**
24823  * @requires OpenLayers/Format/XML.js
24824  * @requires OpenLayers/Format/WFST.js
24825  * @requires OpenLayers/Filter/Spatial.js
24826  * @requires OpenLayers/Filter/FeatureId.js
24827  */
24828
24829 /**
24830  * Class: OpenLayers.Format.WFST.v1
24831  * Superclass for WFST parsers.
24832  *
24833  * Inherits from:
24834  *  - <OpenLayers.Format.XML>
24835  */
24836 OpenLayers.Format.WFST.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
24837     
24838     /**
24839      * Property: namespaces
24840      * {Object} Mapping of namespace aliases to namespace URIs.
24841      */
24842     namespaces: {
24843         xlink: "http://www.w3.org/1999/xlink",
24844         xsi: "http://www.w3.org/2001/XMLSchema-instance",
24845         wfs: "http://www.opengis.net/wfs",
24846         gml: "http://www.opengis.net/gml",
24847         ogc: "http://www.opengis.net/ogc",
24848         ows: "http://www.opengis.net/ows"
24849     },
24850     
24851     /**
24852      * Property: defaultPrefix
24853      */
24854     defaultPrefix: "wfs",
24855
24856     /**
24857      * Property: version
24858      * {String} WFS version number.
24859      */
24860     version: null,
24861
24862     /**
24863      * Property: schemaLocation
24864      * {String} Schema location for a particular minor version.
24865      */
24866     schemaLocations: null,
24867     
24868     /**
24869      * APIProperty: srsName
24870      * {String} URI for spatial reference system.
24871      */
24872     srsName: null,
24873
24874     /**
24875      * APIProperty: extractAttributes
24876      * {Boolean} Extract attributes from GML.  Default is true.
24877      */
24878     extractAttributes: true,
24879     
24880     /**
24881      * APIProperty: xy
24882      * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
24883      * Changing is not recommended, a new Format should be instantiated.
24884      */ 
24885     xy: true,
24886
24887     /**
24888      * Property: stateName
24889      * {Object} Maps feature states to node names.
24890      */
24891     stateName: null,
24892     
24893     /**
24894      * Constructor: OpenLayers.Format.WFST.v1
24895      * Instances of this class are not created directly.  Use the
24896      *     <OpenLayers.Format.WFST.v1_0_0> or <OpenLayers.Format.WFST.v1_1_0>
24897      *     constructor instead.
24898      *
24899      * Parameters:
24900      * options - {Object} An optional object whose properties will be set on
24901      *     this instance.
24902      */
24903     initialize: function(options) {
24904         // set state name mapping
24905         this.stateName = {};
24906         this.stateName[OpenLayers.State.INSERT] = "wfs:Insert";
24907         this.stateName[OpenLayers.State.UPDATE] = "wfs:Update";
24908         this.stateName[OpenLayers.State.DELETE] = "wfs:Delete";
24909         OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
24910     },
24911     
24912     /**
24913      * Method: getSrsName
24914      */
24915     getSrsName: function(feature, options) {
24916         var srsName = options && options.srsName;
24917         if(!srsName) {
24918             if(feature && feature.layer) {
24919                 srsName = feature.layer.projection.getCode();
24920             } else {
24921                 srsName = this.srsName;
24922             }
24923         }
24924         return srsName;
24925     },
24926
24927     /**
24928      * APIMethod: read
24929      * Parse the response from a transaction.  Because WFS is split into
24930      *     Transaction requests (create, update, and delete) and GetFeature
24931      *     requests (read), this method handles parsing of both types of
24932      *     responses.
24933      *
24934      * Parameters:
24935      * data - {String | Document} The WFST document to read
24936      * options - {Object} Options for the reader
24937      *
24938      * Valid options properties:
24939      * output - {String} either "features" or "object". The default is
24940      *     "features", which means that the method will return an array of
24941      *     features. If set to "object", an object with a "features" property
24942      *     and other properties read by the parser will be returned.
24943      *
24944      * Returns:
24945      * {Array | Object} Output depending on the output option.
24946      */
24947     read: function(data, options) {
24948         options = options || {};
24949         OpenLayers.Util.applyDefaults(options, {
24950             output: "features"
24951         });
24952         
24953         if(typeof data == "string") { 
24954             data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
24955         }
24956         if(data && data.nodeType == 9) {
24957             data = data.documentElement;
24958         }
24959         var obj = {};
24960         if(data) {
24961             this.readNode(data, obj, true);
24962         }
24963         if(obj.features && options.output === "features") {
24964             obj = obj.features;
24965         }
24966         return obj;
24967     },
24968     
24969     /**
24970      * Property: readers
24971      * Contains public functions, grouped by namespace prefix, that will
24972      *     be applied when a namespaced node is found matching the function
24973      *     name.  The function will be applied in the scope of this parser
24974      *     with two arguments: the node being read and a context object passed
24975      *     from the parent.
24976      */
24977     readers: {
24978         "wfs": {
24979             "FeatureCollection": function(node, obj) {
24980                 obj.features = [];
24981                 this.readChildNodes(node, obj);
24982             }
24983         }
24984     },
24985     
24986     /**
24987      * Method: write
24988      * Given an array of features, write a WFS transaction.  This assumes
24989      *     the features have a state property that determines the operation
24990      *     type - insert, update, or delete.
24991      *
24992      * Parameters:
24993      * features - {Array(<OpenLayers.Feature.Vector>)} A list of features. See
24994      *     below for a more detailed description of the influence of the
24995      *     feature's *modified* property.
24996      * options - {Object}
24997      *
24998      * feature.modified rules:
24999      * If a feature has a modified property set, the following checks will be
25000      * made before a feature's geometry or attribute is included in an Update
25001      * transaction:
25002      * - *modified* is not set at all: The geometry and all attributes will be
25003      *     included.
25004      * - *modified.geometry* is set (null or a geometry): The geometry will be
25005      *     included. If *modified.attributes* is not set, all attributes will
25006      *     be included.
25007      * - *modified.attributes* is set: Only the attributes set (i.e. to null or
25008      *     a value) in *modified.attributes* will be included. 
25009      *     If *modified.geometry* is not set, the geometry will not be included.
25010      *
25011      * Valid options include:
25012      * - *multi* {Boolean} If set to true, geometries will be casted to
25013      *   Multi geometries before writing.
25014      *
25015      * Returns:
25016      * {String} A serialized WFS transaction.
25017      */
25018     write: function(features, options) {
25019         var node = this.writeNode("wfs:Transaction", {
25020             features:features,
25021             options: options
25022         });
25023         var value = this.schemaLocationAttr();
25024         if(value) {
25025             this.setAttributeNS(
25026                 node, this.namespaces["xsi"], "xsi:schemaLocation",  value
25027             );
25028         }
25029         return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
25030     },
25031     
25032     /**
25033      * Property: writers
25034      * As a compliment to the readers property, this structure contains public
25035      *     writing functions grouped by namespace alias and named like the
25036      *     node names they produce.
25037      */
25038     writers: {
25039         "wfs": {
25040             "GetFeature": function(options) {
25041                 var node = this.createElementNSPlus("wfs:GetFeature", {
25042                     attributes: {
25043                         service: "WFS",
25044                         version: this.version,
25045                         handle: options && options.handle,
25046                         outputFormat: options && options.outputFormat,
25047                         maxFeatures: options && options.maxFeatures,
25048                         "xsi:schemaLocation": this.schemaLocationAttr(options)
25049                     }
25050                 });
25051                 if (typeof this.featureType == "string") {
25052                     this.writeNode("Query", options, node);
25053                 } else {
25054                     for (var i=0,len = this.featureType.length; i<len; i++) { 
25055                         options.featureType = this.featureType[i]; 
25056                         this.writeNode("Query", options, node); 
25057                     } 
25058                 }
25059                 return node;
25060             },
25061             "Transaction": function(obj) {
25062                 obj = obj || {};
25063                 var options = obj.options || {};
25064                 var node = this.createElementNSPlus("wfs:Transaction", {
25065                     attributes: {
25066                         service: "WFS",
25067                         version: this.version,
25068                         handle: options.handle
25069                     }
25070                 });
25071                 var i, len;
25072                 var features = obj.features;
25073                 if(features) {
25074                     // temporarily re-assigning geometry types
25075                     if (options.multi === true) {
25076                         OpenLayers.Util.extend(this.geometryTypes, {
25077                             "OpenLayers.Geometry.Point": "MultiPoint",
25078                             "OpenLayers.Geometry.LineString": (this.multiCurve === true) ? "MultiCurve": "MultiLineString",
25079                             "OpenLayers.Geometry.Polygon": (this.multiSurface === true) ? "MultiSurface" : "MultiPolygon"
25080                         });
25081                     }
25082                     var name, feature;
25083                     for(i=0, len=features.length; i<len; ++i) {
25084                         feature = features[i];
25085                         name = this.stateName[feature.state];
25086                         if(name) {
25087                             this.writeNode(name, {
25088                                 feature: feature, 
25089                                 options: options
25090                             }, node);
25091                         }
25092                     }
25093                     // switch back to original geometry types assignment
25094                     if (options.multi === true) {
25095                         this.setGeometryTypes();
25096                     }
25097                 }
25098                 if (options.nativeElements) {
25099                     for (i=0, len=options.nativeElements.length; i<len; ++i) {
25100                         this.writeNode("wfs:Native", 
25101                             options.nativeElements[i], node);
25102                     }
25103                 }
25104                 return node;
25105             },
25106             "Native": function(nativeElement) {
25107                 var node = this.createElementNSPlus("wfs:Native", {
25108                     attributes: {
25109                         vendorId: nativeElement.vendorId,
25110                         safeToIgnore: nativeElement.safeToIgnore
25111                     },
25112                     value: nativeElement.value
25113                 });
25114                 return node;
25115             },
25116             "Insert": function(obj) {
25117                 var feature = obj.feature;
25118                 var options = obj.options;
25119                 var node = this.createElementNSPlus("wfs:Insert", {
25120                     attributes: {
25121                         handle: options && options.handle
25122                     }
25123                 });
25124                 this.srsName = this.getSrsName(feature);
25125                 this.writeNode("feature:_typeName", feature, node);
25126                 return node;
25127             },
25128             "Update": function(obj) {
25129                 var feature = obj.feature;
25130                 var options = obj.options;
25131                 var node = this.createElementNSPlus("wfs:Update", {
25132                     attributes: {
25133                         handle: options && options.handle,
25134                         typeName: (this.featureNS ? this.featurePrefix + ":" : "") +
25135                             this.featureType
25136                     }
25137                 });
25138                 if(this.featureNS) {
25139                     node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
25140                 }
25141                 
25142                 // add in geometry
25143                 var modified = feature.modified;
25144                 if (this.geometryName !== null && (!modified || modified.geometry !== undefined)) {
25145                     this.srsName = this.getSrsName(feature);
25146                     this.writeNode(
25147                         "Property", {name: this.geometryName, value: feature.geometry}, node
25148                     );
25149                 }
25150         
25151                 // add in attributes
25152                 for(var key in feature.attributes) {
25153                     if(feature.attributes[key] !== undefined &&
25154                                 (!modified || !modified.attributes ||
25155                                 (modified.attributes && modified.attributes[key] !== undefined))) {
25156                         this.writeNode(
25157                             "Property", {name: key, value: feature.attributes[key]}, node
25158                         );
25159                     }
25160                 }
25161                 
25162                 // add feature id filter
25163                 this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({
25164                     fids: [feature.fid]
25165                 }), node);
25166         
25167                 return node;
25168             },
25169             "Property": function(obj) {
25170                 var node = this.createElementNSPlus("wfs:Property");
25171                 this.writeNode("Name", obj.name, node);
25172                 if(obj.value !== null) {
25173                     this.writeNode("Value", obj.value, node);
25174                 }
25175                 return node;
25176             },
25177             "Name": function(name) {
25178                 return this.createElementNSPlus("wfs:Name", {value: name});
25179             },
25180             "Value": function(obj) {
25181                 var node;
25182                 if(obj instanceof OpenLayers.Geometry) {
25183                     node = this.createElementNSPlus("wfs:Value");
25184                     var geom = this.writeNode("feature:_geometry", obj).firstChild;
25185                     node.appendChild(geom);
25186                 } else {
25187                     node = this.createElementNSPlus("wfs:Value", {value: obj});                
25188                 }
25189                 return node;
25190             },
25191             "Delete": function(obj) {
25192                 var feature = obj.feature;
25193                 var options = obj.options;
25194                 var node = this.createElementNSPlus("wfs:Delete", {
25195                     attributes: {
25196                         handle: options && options.handle,
25197                         typeName: (this.featureNS ? this.featurePrefix + ":" : "") +
25198                             this.featureType
25199                     }
25200                 });
25201                 if(this.featureNS) {
25202                     node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
25203                 }
25204                 this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({
25205                     fids: [feature.fid]
25206                 }), node);
25207                 return node;
25208             }
25209         }
25210     },
25211
25212     /**
25213      * Method: schemaLocationAttr
25214      * Generate the xsi:schemaLocation attribute value.
25215      *
25216      * Returns:
25217      * {String} The xsi:schemaLocation attribute or undefined if none.
25218      */
25219     schemaLocationAttr: function(options) {
25220         options = OpenLayers.Util.extend({
25221             featurePrefix: this.featurePrefix,
25222             schema: this.schema
25223         }, options);
25224         var schemaLocations = OpenLayers.Util.extend({}, this.schemaLocations);
25225         if(options.schema) {
25226             schemaLocations[options.featurePrefix] = options.schema;
25227         }
25228         var parts = [];
25229         var uri;
25230         for(var key in schemaLocations) {
25231             uri = this.namespaces[key];
25232             if(uri) {
25233                 parts.push(uri + " " + schemaLocations[key]);
25234             }
25235         }
25236         var value = parts.join(" ") || undefined;
25237         return value;
25238     },
25239     
25240     /**
25241      * Method: setFilterProperty
25242      * Set the property of each spatial filter.
25243      *
25244      * Parameters:
25245      * filter - {<OpenLayers.Filter>}
25246      */
25247     setFilterProperty: function(filter) {
25248         if(filter.filters) {
25249             for(var i=0, len=filter.filters.length; i<len; ++i) {
25250                 OpenLayers.Format.WFST.v1.prototype.setFilterProperty.call(this, filter.filters[i]);
25251             }
25252         } else {
25253             if(filter instanceof OpenLayers.Filter.Spatial && !filter.property) {
25254                 // got a spatial filter without property, so set it
25255                 filter.property = this.geometryName;
25256             }
25257         }
25258     },
25259
25260     CLASS_NAME: "OpenLayers.Format.WFST.v1" 
25261
25262 });
25263 /* ======================================================================
25264     OpenLayers/Geometry/Polygon.js
25265    ====================================================================== */
25266
25267 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
25268  * full list of contributors). Published under the 2-clause BSD license.
25269  * See license.txt in the OpenLayers distribution or repository for the
25270  * full text of the license. */
25271
25272 /**
25273  * @requires OpenLayers/Geometry/Collection.js
25274  * @requires OpenLayers/Geometry/LinearRing.js
25275  */
25276
25277 /**
25278  * Class: OpenLayers.Geometry.Polygon 
25279  * Polygon is a collection of Geometry.LinearRings. 
25280  * 
25281  * Inherits from:
25282  *  - <OpenLayers.Geometry.Collection> 
25283  *  - <OpenLayers.Geometry> 
25284  */
25285 OpenLayers.Geometry.Polygon = OpenLayers.Class(
25286   OpenLayers.Geometry.Collection, {
25287
25288     /**
25289      * Property: componentTypes
25290      * {Array(String)} An array of class names representing the types of
25291      * components that the collection can include.  A null value means the
25292      * component types are not restricted.
25293      */
25294     componentTypes: ["OpenLayers.Geometry.LinearRing"],
25295
25296     /**
25297      * Constructor: OpenLayers.Geometry.Polygon
25298      * Constructor for a Polygon geometry. 
25299      * The first ring (this.component[0])is the outer bounds of the polygon and 
25300      * all subsequent rings (this.component[1-n]) are internal holes.
25301      *
25302      *
25303      * Parameters:
25304      * components - {Array(<OpenLayers.Geometry.LinearRing>)} 
25305      */
25306
25307     /** 
25308      * APIMethod: getArea
25309      * Calculated by subtracting the areas of the internal holes from the 
25310      *   area of the outer hole.
25311      * 
25312      * Returns:
25313      * {float} The area of the geometry
25314      */
25315     getArea: function() {
25316         var area = 0.0;
25317         if ( this.components && (this.components.length > 0)) {
25318             area += Math.abs(this.components[0].getArea());
25319             for (var i=1, len=this.components.length; i<len; i++) {
25320                 area -= Math.abs(this.components[i].getArea());
25321             }
25322         }
25323         return area;
25324     },
25325
25326     /** 
25327      * APIMethod: getGeodesicArea
25328      * Calculate the approximate area of the polygon were it projected onto
25329      *     the earth.
25330      *
25331      * Parameters:
25332      * projection - {<OpenLayers.Projection>} The spatial reference system
25333      *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
25334      *     assumed.
25335      * 
25336      * Reference:
25337      * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
25338      *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
25339      *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
25340      *
25341      * Returns:
25342      * {float} The approximate geodesic area of the polygon in square meters.
25343      */
25344     getGeodesicArea: function(projection) {
25345         var area = 0.0;
25346         if(this.components && (this.components.length > 0)) {
25347             area += Math.abs(this.components[0].getGeodesicArea(projection));
25348             for(var i=1, len=this.components.length; i<len; i++) {
25349                 area -= Math.abs(this.components[i].getGeodesicArea(projection));
25350             }
25351         }
25352         return area;
25353     },
25354
25355     /**
25356      * Method: containsPoint
25357      * Test if a point is inside a polygon.  Points on a polygon edge are
25358      *     considered inside.
25359      *
25360      * Parameters:
25361      * point - {<OpenLayers.Geometry.Point>}
25362      *
25363      * Returns:
25364      * {Boolean | Number} The point is inside the polygon.  Returns 1 if the
25365      *     point is on an edge.  Returns boolean otherwise.
25366      */
25367     containsPoint: function(point) {
25368         var numRings = this.components.length;
25369         var contained = false;
25370         if(numRings > 0) {
25371             // check exterior ring - 1 means on edge, boolean otherwise
25372             contained = this.components[0].containsPoint(point);
25373             if(contained !== 1) {
25374                 if(contained && numRings > 1) {
25375                     // check interior rings
25376                     var hole;
25377                     for(var i=1; i<numRings; ++i) {
25378                         hole = this.components[i].containsPoint(point);
25379                         if(hole) {
25380                             if(hole === 1) {
25381                                 // on edge
25382                                 contained = 1;
25383                             } else {
25384                                 // in hole
25385                                 contained = false;
25386                             }                            
25387                             break;
25388                         }
25389                     }
25390                 }
25391             }
25392         }
25393         return contained;
25394     },
25395
25396     /**
25397      * APIMethod: intersects
25398      * Determine if the input geometry intersects this one.
25399      *
25400      * Parameters:
25401      * geometry - {<OpenLayers.Geometry>} Any type of geometry.
25402      *
25403      * Returns:
25404      * {Boolean} The input geometry intersects this one.
25405      */
25406     intersects: function(geometry) {
25407         var intersect = false;
25408         var i, len;
25409         if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
25410             intersect = this.containsPoint(geometry);
25411         } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" ||
25412                   geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
25413             // check if rings/linestrings intersect
25414             for(i=0, len=this.components.length; i<len; ++i) {
25415                 intersect = geometry.intersects(this.components[i]);
25416                 if(intersect) {
25417                     break;
25418                 }
25419             }
25420             if(!intersect) {
25421                 // check if this poly contains points of the ring/linestring
25422                 for(i=0, len=geometry.components.length; i<len; ++i) {
25423                     intersect = this.containsPoint(geometry.components[i]);
25424                     if(intersect) {
25425                         break;
25426                     }
25427                 }
25428             }
25429         } else {
25430             for(i=0, len=geometry.components.length; i<len; ++ i) {
25431                 intersect = this.intersects(geometry.components[i]);
25432                 if(intersect) {
25433                     break;
25434                 }
25435             }
25436         }
25437         // check case where this poly is wholly contained by another
25438         if(!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
25439             // exterior ring points will be contained in the other geometry
25440             var ring = this.components[0];
25441             for(i=0, len=ring.components.length; i<len; ++i) {
25442                 intersect = geometry.containsPoint(ring.components[i]);
25443                 if(intersect) {
25444                     break;
25445                 }
25446             }
25447         }
25448         return intersect;
25449     },
25450
25451     /**
25452      * APIMethod: distanceTo
25453      * Calculate the closest distance between two geometries (on the x-y plane).
25454      *
25455      * Parameters:
25456      * geometry - {<OpenLayers.Geometry>} The target geometry.
25457      * options - {Object} Optional properties for configuring the distance
25458      *     calculation.
25459      *
25460      * Valid options:
25461      * details - {Boolean} Return details from the distance calculation.
25462      *     Default is false.
25463      * edge - {Boolean} Calculate the distance from this geometry to the
25464      *     nearest edge of the target geometry.  Default is true.  If true,
25465      *     calling distanceTo from a geometry that is wholly contained within
25466      *     the target will result in a non-zero distance.  If false, whenever
25467      *     geometries intersect, calling distanceTo will return 0.  If false,
25468      *     details cannot be returned.
25469      *
25470      * Returns:
25471      * {Number | Object} The distance between this geometry and the target.
25472      *     If details is true, the return will be an object with distance,
25473      *     x0, y0, x1, and y1 properties.  The x0 and y0 properties represent
25474      *     the coordinates of the closest point on this geometry. The x1 and y1
25475      *     properties represent the coordinates of the closest point on the
25476      *     target geometry.
25477      */
25478     distanceTo: function(geometry, options) {
25479         var edge = !(options && options.edge === false);
25480         var result;
25481         // this is the case where we might not be looking for distance to edge
25482         if(!edge && this.intersects(geometry)) {
25483             result = 0;
25484         } else {
25485             result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply(
25486                 this, [geometry, options]
25487             );
25488         }
25489         return result;
25490     },
25491
25492     CLASS_NAME: "OpenLayers.Geometry.Polygon"
25493 });
25494
25495 /**
25496  * APIMethod: createRegularPolygon
25497  * Create a regular polygon around a radius. Useful for creating circles 
25498  * and the like.
25499  *
25500  * Parameters:
25501  * origin - {<OpenLayers.Geometry.Point>} center of polygon.
25502  * radius - {Float} distance to vertex, in map units.
25503  * sides - {Integer} Number of sides. 20 approximates a circle.
25504  * rotation - {Float} original angle of rotation, in degrees.
25505  */
25506 OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {  
25507     var angle = Math.PI * ((1/sides) - (1/2));
25508     if(rotation) {
25509         angle += (rotation / 180) * Math.PI;
25510     }
25511     var rotatedAngle, x, y;
25512     var points = [];
25513     for(var i=0; i<sides; ++i) {
25514         rotatedAngle = angle + (i * 2 * Math.PI / sides);
25515         x = origin.x + (radius * Math.cos(rotatedAngle));
25516         y = origin.y + (radius * Math.sin(rotatedAngle));
25517         points.push(new OpenLayers.Geometry.Point(x, y));
25518     }
25519     var ring = new OpenLayers.Geometry.LinearRing(points);
25520     return new OpenLayers.Geometry.Polygon([ring]);
25521 };
25522 /* ======================================================================
25523     OpenLayers/Geometry/MultiPolygon.js
25524    ====================================================================== */
25525
25526 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
25527  * full list of contributors). Published under the 2-clause BSD license.
25528  * See license.txt in the OpenLayers distribution or repository for the
25529  * full text of the license. */
25530
25531 /**
25532  * @requires OpenLayers/Geometry/Collection.js
25533  * @requires OpenLayers/Geometry/Polygon.js
25534  */
25535
25536 /**
25537  * Class: OpenLayers.Geometry.MultiPolygon
25538  * MultiPolygon is a geometry with multiple <OpenLayers.Geometry.Polygon>
25539  * components.  Create a new instance with the <OpenLayers.Geometry.MultiPolygon>
25540  * constructor.
25541  * 
25542  * Inherits from:
25543  *  - <OpenLayers.Geometry.Collection>
25544  */
25545 OpenLayers.Geometry.MultiPolygon = OpenLayers.Class(
25546   OpenLayers.Geometry.Collection, {
25547
25548     /**
25549      * Property: componentTypes
25550      * {Array(String)} An array of class names representing the types of
25551      * components that the collection can include.  A null value means the
25552      * component types are not restricted.
25553      */
25554     componentTypes: ["OpenLayers.Geometry.Polygon"],
25555
25556     /**
25557      * Constructor: OpenLayers.Geometry.MultiPolygon
25558      * Create a new MultiPolygon geometry
25559      *
25560      * Parameters:
25561      * components - {Array(<OpenLayers.Geometry.Polygon>)} An array of polygons
25562      *              used to generate the MultiPolygon
25563      *
25564      */
25565
25566     CLASS_NAME: "OpenLayers.Geometry.MultiPolygon"
25567 });
25568 /* ======================================================================
25569     OpenLayers/Format/GML.js
25570    ====================================================================== */
25571
25572 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
25573  * full list of contributors). Published under the 2-clause BSD license.
25574  * See license.txt in the OpenLayers distribution or repository for the
25575  * full text of the license. */
25576
25577 /**
25578  * @requires OpenLayers/Format/XML.js
25579  * @requires OpenLayers/Feature/Vector.js
25580  * @requires OpenLayers/Geometry/Point.js
25581  * @requires OpenLayers/Geometry/MultiPoint.js
25582  * @requires OpenLayers/Geometry/LineString.js
25583  * @requires OpenLayers/Geometry/MultiLineString.js
25584  * @requires OpenLayers/Geometry/Polygon.js
25585  * @requires OpenLayers/Geometry/MultiPolygon.js
25586  */
25587
25588 /**
25589  * Class: OpenLayers.Format.GML
25590  * Read/Write GML. Create a new instance with the <OpenLayers.Format.GML>
25591  *     constructor.  Supports the GML simple features profile.
25592  * 
25593  * Inherits from:
25594  *  - <OpenLayers.Format.XML>
25595  */
25596 OpenLayers.Format.GML = OpenLayers.Class(OpenLayers.Format.XML, {
25597     
25598     /**
25599      * APIProperty: featureNS
25600      * {String} Namespace used for feature attributes.  Default is
25601      *     "http://mapserver.gis.umn.edu/mapserver".
25602      */
25603     featureNS: "http://mapserver.gis.umn.edu/mapserver",
25604     
25605     /**
25606      * APIProperty: featurePrefix
25607      * {String} Namespace alias (or prefix) for feature nodes.  Default is
25608      *     "feature".
25609      */
25610     featurePrefix: "feature",
25611     
25612     /**
25613      * APIProperty: featureName
25614      * {String} Element name for features. Default is "featureMember".
25615      */
25616     featureName: "featureMember", 
25617     
25618     /**
25619      * APIProperty: layerName
25620      * {String} Name of data layer. Default is "features".
25621      */
25622     layerName: "features",
25623     
25624     /**
25625      * APIProperty: geometryName
25626      * {String} Name of geometry element.  Defaults to "geometry".
25627      */
25628     geometryName: "geometry",
25629     
25630     /** 
25631      * APIProperty: collectionName
25632      * {String} Name of featureCollection element.
25633      */
25634     collectionName: "FeatureCollection",
25635     
25636     /**
25637      * APIProperty: gmlns
25638      * {String} GML Namespace.
25639      */
25640     gmlns: "http://www.opengis.net/gml",
25641
25642     /**
25643      * APIProperty: extractAttributes
25644      * {Boolean} Extract attributes from GML.
25645      */
25646     extractAttributes: true,
25647     
25648     /**
25649      * APIProperty: xy
25650      * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
25651      * Changing is not recommended, a new Format should be instantiated.
25652      */ 
25653     xy: true,
25654     
25655     /**
25656      * Constructor: OpenLayers.Format.GML
25657      * Create a new parser for GML.
25658      *
25659      * Parameters:
25660      * options - {Object} An optional object whose properties will be set on
25661      *     this instance.
25662      */
25663     initialize: function(options) {
25664         // compile regular expressions once instead of every time they are used
25665         this.regExes = {
25666             trimSpace: (/^\s*|\s*$/g),
25667             removeSpace: (/\s*/g),
25668             splitSpace: (/\s+/),
25669             trimComma: (/\s*,\s*/g)
25670         };
25671         OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
25672     },
25673
25674     /**
25675      * APIMethod: read
25676      * Read data from a string, and return a list of features. 
25677      * 
25678      * Parameters:
25679      * data - {String} or {DOMElement} data to read/parse.
25680      *
25681      * Returns:
25682      * {Array(<OpenLayers.Feature.Vector>)} An array of features.
25683      */
25684     read: function(data) {
25685         if(typeof data == "string") { 
25686             data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
25687         }
25688         var featureNodes = this.getElementsByTagNameNS(data.documentElement,
25689                                                        this.gmlns,
25690                                                        this.featureName);
25691         var features = [];
25692         for(var i=0; i<featureNodes.length; i++) {
25693             var feature = this.parseFeature(featureNodes[i]);
25694             if(feature) {
25695                 features.push(feature);
25696             }
25697         }
25698         return features;
25699     },
25700     
25701     /**
25702      * Method: parseFeature
25703      * This function is the core of the GML parsing code in OpenLayers.
25704      *    It creates the geometries that are then attached to the returned
25705      *    feature, and calls parseAttributes() to get attribute data out.
25706      *    
25707      * Parameters:
25708      * node - {DOMElement} A GML feature node. 
25709      */
25710     parseFeature: function(node) {
25711         // only accept one geometry per feature - look for highest "order"
25712         var order = ["MultiPolygon", "Polygon",
25713                      "MultiLineString", "LineString",
25714                      "MultiPoint", "Point", "Envelope"];
25715         // FIXME: In case we parse a feature with no geometry, but boundedBy an Envelope,
25716         // this code creates a geometry derived from the Envelope. This is not correct.
25717         var type, nodeList, geometry, parser;
25718         for(var i=0; i<order.length; ++i) {
25719             type = order[i];
25720             nodeList = this.getElementsByTagNameNS(node, this.gmlns, type);
25721             if(nodeList.length > 0) {
25722                 // only deal with first geometry of this type
25723                 parser = this.parseGeometry[type.toLowerCase()];
25724                 if(parser) {
25725                     geometry = parser.apply(this, [nodeList[0]]);
25726                     if (this.internalProjection && this.externalProjection) {
25727                         geometry.transform(this.externalProjection, 
25728                                            this.internalProjection); 
25729                     }                       
25730                 } else {
25731                     throw new TypeError("Unsupported geometry type: " + type);
25732                 }
25733                 // stop looking for different geometry types
25734                 break;
25735             }
25736         }
25737
25738         var bounds;
25739         var boxNodes = this.getElementsByTagNameNS(node, this.gmlns, "Box");
25740         for(i=0; i<boxNodes.length; ++i) {
25741             var boxNode = boxNodes[i];
25742             var box = this.parseGeometry["box"].apply(this, [boxNode]);
25743             var parentNode = boxNode.parentNode;
25744             var parentName = parentNode.localName ||
25745                              parentNode.nodeName.split(":").pop();
25746             if(parentName === "boundedBy") {
25747                 bounds = box;
25748             } else {
25749                 geometry = box.toGeometry();
25750             }
25751         }
25752         
25753         // construct feature (optionally with attributes)
25754         var attributes;
25755         if(this.extractAttributes) {
25756             attributes = this.parseAttributes(node);
25757         }
25758         var feature = new OpenLayers.Feature.Vector(geometry, attributes);
25759         feature.bounds = bounds;
25760         
25761         feature.gml = {
25762             featureType: node.firstChild.nodeName.split(":")[1],
25763             featureNS: node.firstChild.namespaceURI,
25764             featureNSPrefix: node.firstChild.prefix
25765         };
25766                 
25767         // assign fid - this can come from a "fid" or "id" attribute
25768         var childNode = node.firstChild;
25769         var fid;
25770         while(childNode) {
25771             if(childNode.nodeType == 1) {
25772                 fid = childNode.getAttribute("fid") ||
25773                       childNode.getAttribute("id");
25774                 if(fid) {
25775                     break;
25776                 }
25777             }
25778             childNode = childNode.nextSibling;
25779         }
25780         feature.fid = fid;
25781         return feature;
25782     },
25783     
25784     /**
25785      * Property: parseGeometry
25786      * Properties of this object are the functions that parse geometries based
25787      *     on their type.
25788      */
25789     parseGeometry: {
25790         
25791         /**
25792          * Method: parseGeometry.point
25793          * Given a GML node representing a point geometry, create an OpenLayers
25794          *     point geometry.
25795          *
25796          * Parameters:
25797          * node - {DOMElement} A GML node.
25798          *
25799          * Returns:
25800          * {<OpenLayers.Geometry.Point>} A point geometry.
25801          */
25802         point: function(node) {
25803             /**
25804              * Three coordinate variations to consider:
25805              * 1) <gml:pos>x y z</gml:pos>
25806              * 2) <gml:coordinates>x, y, z</gml:coordinates>
25807              * 3) <gml:coord><gml:X>x</gml:X><gml:Y>y</gml:Y></gml:coord>
25808              */
25809             var nodeList, coordString;
25810             var coords = [];
25811
25812             // look for <gml:pos>
25813             var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "pos");
25814             if(nodeList.length > 0) {
25815                 coordString = nodeList[0].firstChild.nodeValue;
25816                 coordString = coordString.replace(this.regExes.trimSpace, "");
25817                 coords = coordString.split(this.regExes.splitSpace);
25818             }
25819
25820             // look for <gml:coordinates>
25821             if(coords.length == 0) {
25822                 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
25823                                                        "coordinates");
25824                 if(nodeList.length > 0) {
25825                     coordString = nodeList[0].firstChild.nodeValue;
25826                     coordString = coordString.replace(this.regExes.removeSpace,
25827                                                       "");
25828                     coords = coordString.split(",");
25829                 }
25830             }
25831
25832             // look for <gml:coord>
25833             if(coords.length == 0) {
25834                 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
25835                                                        "coord");
25836                 if(nodeList.length > 0) {
25837                     var xList = this.getElementsByTagNameNS(nodeList[0],
25838                                                             this.gmlns, "X");
25839                     var yList = this.getElementsByTagNameNS(nodeList[0],
25840                                                             this.gmlns, "Y");
25841                     if(xList.length > 0 && yList.length > 0) {
25842                         coords = [xList[0].firstChild.nodeValue,
25843                                   yList[0].firstChild.nodeValue];
25844                     }
25845                 }
25846             }
25847                 
25848             // preserve third dimension
25849             if(coords.length == 2) {
25850                 coords[2] = null;
25851             }
25852             
25853             if (this.xy) {
25854                 return new OpenLayers.Geometry.Point(coords[0], coords[1],
25855                                                  coords[2]);
25856             }
25857             else{
25858                 return new OpenLayers.Geometry.Point(coords[1], coords[0],
25859                                                  coords[2]);
25860             }
25861         },
25862         
25863         /**
25864          * Method: parseGeometry.multipoint
25865          * Given a GML node representing a multipoint geometry, create an
25866          *     OpenLayers multipoint geometry.
25867          *
25868          * Parameters:
25869          * node - {DOMElement} A GML node.
25870          *
25871          * Returns:
25872          * {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
25873          */
25874         multipoint: function(node) {
25875             var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
25876                                                        "Point");
25877             var components = [];
25878             if(nodeList.length > 0) {
25879                 var point;
25880                 for(var i=0; i<nodeList.length; ++i) {
25881                     point = this.parseGeometry.point.apply(this, [nodeList[i]]);
25882                     if(point) {
25883                         components.push(point);
25884                     }
25885                 }
25886             }
25887             return new OpenLayers.Geometry.MultiPoint(components);
25888         },
25889         
25890         /**
25891          * Method: parseGeometry.linestring
25892          * Given a GML node representing a linestring geometry, create an
25893          *     OpenLayers linestring geometry.
25894          *
25895          * Parameters:
25896          * node - {DOMElement} A GML node.
25897          *
25898          * Returns:
25899          * {<OpenLayers.Geometry.LineString>} A linestring geometry.
25900          */
25901         linestring: function(node, ring) {
25902             /**
25903              * Two coordinate variations to consider:
25904              * 1) <gml:posList dimension="d">x0 y0 z0 x1 y1 z1</gml:posList>
25905              * 2) <gml:coordinates>x0, y0, z0 x1, y1, z1</gml:coordinates>
25906              */
25907             var nodeList, coordString;
25908             var coords = [];
25909             var points = [];
25910
25911             // look for <gml:posList>
25912             nodeList = this.getElementsByTagNameNS(node, this.gmlns, "posList");
25913             if(nodeList.length > 0) {
25914                 coordString = this.getChildValue(nodeList[0]);
25915                 coordString = coordString.replace(this.regExes.trimSpace, "");
25916                 coords = coordString.split(this.regExes.splitSpace);
25917                 var dim = parseInt(nodeList[0].getAttribute("dimension"));
25918                 var j, x, y, z;
25919                 for(var i=0; i<coords.length/dim; ++i) {
25920                     j = i * dim;
25921                     x = coords[j];
25922                     y = coords[j+1];
25923                     z = (dim == 2) ? null : coords[j+2];
25924                     if (this.xy) {
25925                         points.push(new OpenLayers.Geometry.Point(x, y, z));
25926                     } else {
25927                         points.push(new OpenLayers.Geometry.Point(y, x, z));
25928                     }
25929                 }
25930             }
25931
25932             // look for <gml:coordinates>
25933             if(coords.length == 0) {
25934                 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
25935                                                        "coordinates");
25936                 if(nodeList.length > 0) {
25937                     coordString = this.getChildValue(nodeList[0]);
25938                     coordString = coordString.replace(this.regExes.trimSpace,
25939                                                       "");
25940                     coordString = coordString.replace(this.regExes.trimComma,
25941                                                       ",");
25942                     var pointList = coordString.split(this.regExes.splitSpace);
25943                     for(var i=0; i<pointList.length; ++i) {
25944                         coords = pointList[i].split(",");
25945                         if(coords.length == 2) {
25946                             coords[2] = null;
25947                         }
25948                         if (this.xy) {
25949                             points.push(new OpenLayers.Geometry.Point(coords[0],
25950                                                                   coords[1],
25951                                                                   coords[2]));
25952                         } else {
25953                             points.push(new OpenLayers.Geometry.Point(coords[1],
25954                                                                   coords[0],
25955                                                                   coords[2]));
25956                         }
25957                     }
25958                 }
25959             }
25960
25961             var line = null;
25962             if(points.length != 0) {
25963                 if(ring) {
25964                     line = new OpenLayers.Geometry.LinearRing(points);
25965                 } else {
25966                     line = new OpenLayers.Geometry.LineString(points);
25967                 }
25968             }
25969             return line;
25970         },
25971         
25972         /**
25973          * Method: parseGeometry.multilinestring
25974          * Given a GML node representing a multilinestring geometry, create an
25975          *     OpenLayers multilinestring geometry.
25976          *
25977          * Parameters:
25978          * node - {DOMElement} A GML node.
25979          *
25980          * Returns:
25981          * {<OpenLayers.Geometry.MultiLineString>} A multilinestring geometry.
25982          */
25983         multilinestring: function(node) {
25984             var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
25985                                                        "LineString");
25986             var components = [];
25987             if(nodeList.length > 0) {
25988                 var line;
25989                 for(var i=0; i<nodeList.length; ++i) {
25990                     line = this.parseGeometry.linestring.apply(this,
25991                                                                [nodeList[i]]);
25992                     if(line) {
25993                         components.push(line);
25994                     }
25995                 }
25996             }
25997             return new OpenLayers.Geometry.MultiLineString(components);
25998         },
25999         
26000         /**
26001          * Method: parseGeometry.polygon
26002          * Given a GML node representing a polygon geometry, create an
26003          *     OpenLayers polygon geometry.
26004          *
26005          * Parameters:
26006          * node - {DOMElement} A GML node.
26007          *
26008          * Returns:
26009          * {<OpenLayers.Geometry.Polygon>} A polygon geometry.
26010          */
26011         polygon: function(node) {
26012             var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
26013                                                        "LinearRing");
26014             var components = [];
26015             if(nodeList.length > 0) {
26016                 // this assumes exterior ring first, inner rings after
26017                 var ring;
26018                 for(var i=0; i<nodeList.length; ++i) {
26019                     ring = this.parseGeometry.linestring.apply(this,
26020                                                         [nodeList[i], true]);
26021                     if(ring) {
26022                         components.push(ring);
26023                     }
26024                 }
26025             }
26026             return new OpenLayers.Geometry.Polygon(components);
26027         },
26028         
26029         /**
26030          * Method: parseGeometry.multipolygon
26031          * Given a GML node representing a multipolygon geometry, create an
26032          *     OpenLayers multipolygon geometry.
26033          *
26034          * Parameters:
26035          * node - {DOMElement} A GML node.
26036          *
26037          * Returns:
26038          * {<OpenLayers.Geometry.MultiPolygon>} A multipolygon geometry.
26039          */
26040         multipolygon: function(node) {
26041             var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
26042                                                        "Polygon");
26043             var components = [];
26044             if(nodeList.length > 0) {
26045                 var polygon;
26046                 for(var i=0; i<nodeList.length; ++i) {
26047                     polygon = this.parseGeometry.polygon.apply(this,
26048                                                                [nodeList[i]]);
26049                     if(polygon) {
26050                         components.push(polygon);
26051                     }
26052                 }
26053             }
26054             return new OpenLayers.Geometry.MultiPolygon(components);
26055         },
26056         
26057         envelope: function(node) {
26058             var components = [];
26059             var coordString;
26060             var envelope;
26061             
26062             var lpoint = this.getElementsByTagNameNS(node, this.gmlns, "lowerCorner");
26063             if (lpoint.length > 0) {
26064                 var coords = [];
26065                 
26066                 if(lpoint.length > 0) {
26067                     coordString = lpoint[0].firstChild.nodeValue;
26068                     coordString = coordString.replace(this.regExes.trimSpace, "");
26069                     coords = coordString.split(this.regExes.splitSpace);
26070                 }
26071                 
26072                 if(coords.length == 2) {
26073                     coords[2] = null;
26074                 }
26075                 if (this.xy) {
26076                     var lowerPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
26077                 } else {
26078                     var lowerPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
26079                 }
26080             }
26081             
26082             var upoint = this.getElementsByTagNameNS(node, this.gmlns, "upperCorner");
26083             if (upoint.length > 0) {
26084                 var coords = [];
26085                 
26086                 if(upoint.length > 0) {
26087                     coordString = upoint[0].firstChild.nodeValue;
26088                     coordString = coordString.replace(this.regExes.trimSpace, "");
26089                     coords = coordString.split(this.regExes.splitSpace);
26090                 }
26091                 
26092                 if(coords.length == 2) {
26093                     coords[2] = null;
26094                 }
26095                 if (this.xy) {
26096                     var upperPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
26097                 } else {
26098                     var upperPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
26099                 }
26100             }
26101             
26102             if (lowerPoint && upperPoint) {
26103                 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
26104                 components.push(new OpenLayers.Geometry.Point(upperPoint.x, lowerPoint.y));
26105                 components.push(new OpenLayers.Geometry.Point(upperPoint.x, upperPoint.y));
26106                 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, upperPoint.y));
26107                 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
26108                 
26109                 var ring = new OpenLayers.Geometry.LinearRing(components);
26110                 envelope = new OpenLayers.Geometry.Polygon([ring]);
26111             }
26112             return envelope; 
26113         },
26114
26115         /**
26116          * Method: parseGeometry.box
26117          * Given a GML node representing a box geometry, create an
26118          *     OpenLayers.Bounds.
26119          *
26120          * Parameters:
26121          * node - {DOMElement} A GML node.
26122          *
26123          * Returns:
26124          * {<OpenLayers.Bounds>} A bounds representing the box.
26125          */
26126         box: function(node) {
26127             var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
26128                                                    "coordinates");
26129             var coordString;
26130             var coords, beginPoint = null, endPoint = null;
26131             if (nodeList.length > 0) {
26132                 coordString = nodeList[0].firstChild.nodeValue;
26133                 coords = coordString.split(" ");
26134                 if (coords.length == 2) {
26135                     beginPoint = coords[0].split(",");
26136                     endPoint = coords[1].split(",");
26137                 }
26138             }
26139             if (beginPoint !== null && endPoint !== null) {
26140                 return new OpenLayers.Bounds(parseFloat(beginPoint[0]),
26141                     parseFloat(beginPoint[1]),
26142                     parseFloat(endPoint[0]),
26143                     parseFloat(endPoint[1]) );
26144             }
26145         }
26146         
26147     },
26148     
26149     /**
26150      * Method: parseAttributes
26151      *
26152      * Parameters:
26153      * node - {DOMElement}
26154      *
26155      * Returns:
26156      * {Object} An attributes object.
26157      */
26158     parseAttributes: function(node) {
26159         var attributes = {};
26160         // assume attributes are children of the first type 1 child
26161         var childNode = node.firstChild;
26162         var children, i, child, grandchildren, grandchild, name, value;
26163         while(childNode) {
26164             if(childNode.nodeType == 1) {
26165                 // attributes are type 1 children with one type 3 child
26166                 children = childNode.childNodes;
26167                 for(i=0; i<children.length; ++i) {
26168                     child = children[i];
26169                     if(child.nodeType == 1) {
26170                         grandchildren = child.childNodes;
26171                         if(grandchildren.length == 1) {
26172                             grandchild = grandchildren[0];
26173                             if(grandchild.nodeType == 3 ||
26174                                grandchild.nodeType == 4) {
26175                                 name = (child.prefix) ?
26176                                         child.nodeName.split(":")[1] :
26177                                         child.nodeName;
26178                                 value = grandchild.nodeValue.replace(
26179                                                 this.regExes.trimSpace, "");
26180                                 attributes[name] = value;
26181                             }
26182                         } else {
26183                             // If child has no childNodes (grandchildren),
26184                             // set an attribute with null value.
26185                             // e.g. <prefix:fieldname/> becomes
26186                             // {fieldname: null}
26187                             attributes[child.nodeName.split(":").pop()] = null;
26188                         }
26189                     }
26190                 }
26191                 break;
26192             }
26193             childNode = childNode.nextSibling;
26194         }
26195         return attributes;
26196     },
26197     
26198     /**
26199      * APIMethod: write
26200      * Generate a GML document string given a list of features. 
26201      * 
26202      * Parameters:
26203      * features - {Array(<OpenLayers.Feature.Vector>)} List of features to
26204      *     serialize into a string.
26205      *
26206      * Returns:
26207      * {String} A string representing the GML document.
26208      */
26209     write: function(features) {
26210         if(!(OpenLayers.Util.isArray(features))) {
26211             features = [features];
26212         }
26213         var gml = this.createElementNS("http://www.opengis.net/wfs",
26214                                        "wfs:" + this.collectionName);
26215         for(var i=0; i<features.length; i++) {
26216             gml.appendChild(this.createFeatureXML(features[i]));
26217         }
26218         return OpenLayers.Format.XML.prototype.write.apply(this, [gml]);
26219     },
26220
26221     /** 
26222      * Method: createFeatureXML
26223      * Accept an OpenLayers.Feature.Vector, and build a GML node for it.
26224      *
26225      * Parameters:
26226      * feature - {<OpenLayers.Feature.Vector>} The feature to be built as GML.
26227      *
26228      * Returns:
26229      * {DOMElement} A node reprensting the feature in GML.
26230      */
26231     createFeatureXML: function(feature) {
26232         var geometry = feature.geometry;
26233         var geometryNode = this.buildGeometryNode(geometry);
26234         var geomContainer = this.createElementNS(this.featureNS,
26235                                                  this.featurePrefix + ":" +
26236                                                  this.geometryName);
26237         geomContainer.appendChild(geometryNode);
26238         var featureNode = this.createElementNS(this.gmlns,
26239                                                "gml:" + this.featureName);
26240         var featureContainer = this.createElementNS(this.featureNS,
26241                                                     this.featurePrefix + ":" +
26242                                                     this.layerName);
26243         var fid = feature.fid || feature.id;
26244         featureContainer.setAttribute("fid", fid);
26245         featureContainer.appendChild(geomContainer);
26246         for(var attr in feature.attributes) {
26247             var attrText = this.createTextNode(feature.attributes[attr]); 
26248             var nodename = attr.substring(attr.lastIndexOf(":") + 1);
26249             var attrContainer = this.createElementNS(this.featureNS,
26250                                                      this.featurePrefix + ":" +
26251                                                      nodename);
26252             attrContainer.appendChild(attrText);
26253             featureContainer.appendChild(attrContainer);
26254         }    
26255         featureNode.appendChild(featureContainer);
26256         return featureNode;
26257     },
26258     
26259     /**
26260      * APIMethod: buildGeometryNode
26261      */
26262     buildGeometryNode: function(geometry) {
26263         if (this.externalProjection && this.internalProjection) {
26264             geometry = geometry.clone();
26265             geometry.transform(this.internalProjection, 
26266                                this.externalProjection);
26267         }    
26268         var className = geometry.CLASS_NAME;
26269         var type = className.substring(className.lastIndexOf(".") + 1);
26270         var builder = this.buildGeometry[type.toLowerCase()];
26271         return builder.apply(this, [geometry]);
26272     },
26273
26274     /**
26275      * Property: buildGeometry
26276      * Object containing methods to do the actual geometry node building
26277      *     based on geometry type.
26278      */
26279     buildGeometry: {
26280         // TBD retrieve the srs from layer
26281         // srsName is non-standard, so not including it until it's right.
26282         // gml.setAttribute("srsName",
26283         //                  "http://www.opengis.net/gml/srs/epsg.xml#4326");
26284
26285         /**
26286          * Method: buildGeometry.point
26287          * Given an OpenLayers point geometry, create a GML point.
26288          *
26289          * Parameters:
26290          * geometry - {<OpenLayers.Geometry.Point>} A point geometry.
26291          *
26292          * Returns:
26293          * {DOMElement} A GML point node.
26294          */
26295         point: function(geometry) {
26296             var gml = this.createElementNS(this.gmlns, "gml:Point");
26297             gml.appendChild(this.buildCoordinatesNode(geometry));
26298             return gml;
26299         },
26300         
26301         /**
26302          * Method: buildGeometry.multipoint
26303          * Given an OpenLayers multipoint geometry, create a GML multipoint.
26304          *
26305          * Parameters:
26306          * geometry - {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
26307          *
26308          * Returns:
26309          * {DOMElement} A GML multipoint node.
26310          */
26311         multipoint: function(geometry) {
26312             var gml = this.createElementNS(this.gmlns, "gml:MultiPoint");
26313             var points = geometry.components;
26314             var pointMember, pointGeom;
26315             for(var i=0; i<points.length; i++) { 
26316                 pointMember = this.createElementNS(this.gmlns,
26317                                                    "gml:pointMember");
26318                 pointGeom = this.buildGeometry.point.apply(this,
26319                                                                [points[i]]);
26320                 pointMember.appendChild(pointGeom);
26321                 gml.appendChild(pointMember);
26322             }
26323             return gml;            
26324         },
26325         
26326         /**
26327          * Method: buildGeometry.linestring
26328          * Given an OpenLayers linestring geometry, create a GML linestring.
26329          *
26330          * Parameters:
26331          * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry.
26332          *
26333          * Returns:
26334          * {DOMElement} A GML linestring node.
26335          */
26336         linestring: function(geometry) {
26337             var gml = this.createElementNS(this.gmlns, "gml:LineString");
26338             gml.appendChild(this.buildCoordinatesNode(geometry));
26339             return gml;
26340         },
26341         
26342         /**
26343          * Method: buildGeometry.multilinestring
26344          * Given an OpenLayers multilinestring geometry, create a GML
26345          *     multilinestring.
26346          *
26347          * Parameters:
26348          * geometry - {<OpenLayers.Geometry.MultiLineString>} A multilinestring
26349          *     geometry.
26350          *
26351          * Returns:
26352          * {DOMElement} A GML multilinestring node.
26353          */
26354         multilinestring: function(geometry) {
26355             var gml = this.createElementNS(this.gmlns, "gml:MultiLineString");
26356             var lines = geometry.components;
26357             var lineMember, lineGeom;
26358             for(var i=0; i<lines.length; ++i) {
26359                 lineMember = this.createElementNS(this.gmlns,
26360                                                   "gml:lineStringMember");
26361                 lineGeom = this.buildGeometry.linestring.apply(this,
26362                                                                    [lines[i]]);
26363                 lineMember.appendChild(lineGeom);
26364                 gml.appendChild(lineMember);
26365             }
26366             return gml;
26367         },
26368         
26369         /**
26370          * Method: buildGeometry.linearring
26371          * Given an OpenLayers linearring geometry, create a GML linearring.
26372          *
26373          * Parameters:
26374          * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry.
26375          *
26376          * Returns:
26377          * {DOMElement} A GML linearring node.
26378          */
26379         linearring: function(geometry) {
26380             var gml = this.createElementNS(this.gmlns, "gml:LinearRing");
26381             gml.appendChild(this.buildCoordinatesNode(geometry));
26382             return gml;
26383         },
26384         
26385         /**
26386          * Method: buildGeometry.polygon
26387          * Given an OpenLayers polygon geometry, create a GML polygon.
26388          *
26389          * Parameters:
26390          * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry.
26391          *
26392          * Returns:
26393          * {DOMElement} A GML polygon node.
26394          */
26395         polygon: function(geometry) {
26396             var gml = this.createElementNS(this.gmlns, "gml:Polygon");
26397             var rings = geometry.components;
26398             var ringMember, ringGeom, type;
26399             for(var i=0; i<rings.length; ++i) {
26400                 type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
26401                 ringMember = this.createElementNS(this.gmlns,
26402                                                   "gml:" + type);
26403                 ringGeom = this.buildGeometry.linearring.apply(this,
26404                                                                    [rings[i]]);
26405                 ringMember.appendChild(ringGeom);
26406                 gml.appendChild(ringMember);
26407             }
26408             return gml;
26409         },
26410         
26411         /**
26412          * Method: buildGeometry.multipolygon
26413          * Given an OpenLayers multipolygon geometry, create a GML multipolygon.
26414          *
26415          * Parameters:
26416          * geometry - {<OpenLayers.Geometry.MultiPolygon>} A multipolygon
26417          *     geometry.
26418          *
26419          * Returns:
26420          * {DOMElement} A GML multipolygon node.
26421          */
26422         multipolygon: function(geometry) {
26423             var gml = this.createElementNS(this.gmlns, "gml:MultiPolygon");
26424             var polys = geometry.components;
26425             var polyMember, polyGeom;
26426             for(var i=0; i<polys.length; ++i) {
26427                 polyMember = this.createElementNS(this.gmlns,
26428                                                   "gml:polygonMember");
26429                 polyGeom = this.buildGeometry.polygon.apply(this,
26430                                                                 [polys[i]]);
26431                 polyMember.appendChild(polyGeom);
26432                 gml.appendChild(polyMember);
26433             }
26434             return gml;
26435
26436         },
26437  
26438         /**
26439          * Method: buildGeometry.bounds
26440          * Given an OpenLayers bounds, create a GML box.
26441          *
26442          * Parameters:
26443          * bounds - {<OpenLayers.Geometry.Bounds>} A bounds object.
26444          *
26445          * Returns:
26446          * {DOMElement} A GML box node.
26447          */
26448         bounds: function(bounds) {
26449             var gml = this.createElementNS(this.gmlns, "gml:Box");
26450             gml.appendChild(this.buildCoordinatesNode(bounds));
26451             return gml;
26452         }
26453     },
26454
26455     /**
26456      * Method: buildCoordinates
26457      * builds the coordinates XmlNode
26458      * (code)
26459      * <gml:coordinates decimal="." cs="," ts=" ">...</gml:coordinates>
26460      * (end)
26461      *
26462      * Parameters: 
26463      * geometry - {<OpenLayers.Geometry>} 
26464      *
26465      * Returns:
26466      * {XmlNode} created xmlNode
26467      */
26468     buildCoordinatesNode: function(geometry) {
26469         var coordinatesNode = this.createElementNS(this.gmlns,
26470                                                    "gml:coordinates");
26471         coordinatesNode.setAttribute("decimal", ".");
26472         coordinatesNode.setAttribute("cs", ",");
26473         coordinatesNode.setAttribute("ts", " ");
26474
26475         var parts = [];
26476
26477         if(geometry instanceof OpenLayers.Bounds){
26478             parts.push(geometry.left + "," + geometry.bottom);
26479             parts.push(geometry.right + "," + geometry.top);
26480         } else {
26481             var points = (geometry.components) ? geometry.components : [geometry];
26482             for(var i=0; i<points.length; i++) {
26483                 parts.push(points[i].x + "," + points[i].y);                
26484             }            
26485         }
26486
26487         var txtNode = this.createTextNode(parts.join(" "));
26488         coordinatesNode.appendChild(txtNode);
26489         
26490         return coordinatesNode;
26491     },
26492
26493     CLASS_NAME: "OpenLayers.Format.GML" 
26494 });
26495 /* ======================================================================
26496     OpenLayers/Format/GML/Base.js
26497    ====================================================================== */
26498
26499 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
26500  * full list of contributors). Published under the 2-clause BSD license.
26501  * See license.txt in the OpenLayers distribution or repository for the
26502  * full text of the license. */
26503
26504 /**
26505  * @requires OpenLayers/Format/XML.js
26506  * @requires OpenLayers/Format/GML.js
26507  */
26508
26509 /**
26510  * Though required in the full build, if the GML format is excluded, we set
26511  * the namespace here.
26512  */
26513 if(!OpenLayers.Format.GML) {
26514     OpenLayers.Format.GML = {};
26515 }
26516
26517 /**
26518  * Class: OpenLayers.Format.GML.Base
26519  * Superclass for GML parsers.
26520  *
26521  * Inherits from:
26522  *  - <OpenLayers.Format.XML>
26523  */
26524 OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, {
26525     
26526     /**
26527      * Property: namespaces
26528      * {Object} Mapping of namespace aliases to namespace URIs.
26529      */
26530     namespaces: {
26531         gml: "http://www.opengis.net/gml",
26532         xlink: "http://www.w3.org/1999/xlink",
26533         xsi: "http://www.w3.org/2001/XMLSchema-instance",
26534         wfs: "http://www.opengis.net/wfs" // this is a convenience for reading wfs:FeatureCollection
26535     },
26536     
26537     /**
26538      * Property: defaultPrefix
26539      */
26540     defaultPrefix: "gml",
26541
26542     /**
26543      * Property: schemaLocation
26544      * {String} Schema location for a particular minor version.
26545      */
26546     schemaLocation: null,
26547     
26548     /**
26549      * APIProperty: featureType
26550      * {Array(String) or String} The local (without prefix) feature typeName(s).
26551      */
26552     featureType: null,
26553     
26554     /**
26555      * APIProperty: featureNS
26556      * {String} The feature namespace.  Must be set in the options at
26557      *     construction.
26558      */
26559     featureNS: null,
26560
26561     /**
26562      * APIProperty: geometry
26563      * {String} Name of geometry element.  Defaults to "geometry". If null, it
26564      * will be set on <read> when the first geometry is parsed.
26565      */
26566     geometryName: "geometry",
26567
26568     /**
26569      * APIProperty: extractAttributes
26570      * {Boolean} Extract attributes from GML.  Default is true.
26571      */
26572     extractAttributes: true,
26573     
26574     /**
26575      * APIProperty: srsName
26576      * {String} URI for spatial reference system.  This is optional for
26577      *     single part geometries and mandatory for collections and multis.
26578      *     If set, the srsName attribute will be written for all geometries.
26579      *     Default is null.
26580      */
26581     srsName: null,
26582
26583     /**
26584      * APIProperty: xy
26585      * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
26586      * Changing is not recommended, a new Format should be instantiated.
26587      */ 
26588     xy: true,
26589
26590     /**
26591      * Property: geometryTypes
26592      * {Object} Maps OpenLayers geometry class names to GML element names.
26593      *     Use <setGeometryTypes> before accessing this property.
26594      */
26595     geometryTypes: null,
26596
26597     /**
26598      * Property: singleFeatureType
26599      * {Boolean} True if there is only 1 featureType, and not an array
26600      *     of featuretypes.
26601      */
26602     singleFeatureType: null,
26603     
26604     /**
26605      * Property: autoConfig
26606      * {Boolean} Indicates if the format was configured without a <featureNS>,
26607      * but auto-configured <featureNS> and <featureType> during read.
26608      * Subclasses making use of <featureType> auto-configuration should make
26609      * the first call to the <readNode> method (usually in the read method)
26610      * with true as 3rd argument, so the auto-configured featureType can be
26611      * reset and the format can be reused for subsequent reads with data from
26612      * different featureTypes. Set to false after read if you want to keep the
26613      * auto-configured values.
26614      */
26615
26616     /**
26617      * Property: regExes
26618      * Compiled regular expressions for manipulating strings.
26619      */
26620     regExes: {
26621         trimSpace: (/^\s*|\s*$/g),
26622         removeSpace: (/\s*/g),
26623         splitSpace: (/\s+/),
26624         trimComma: (/\s*,\s*/g),
26625         featureMember: (/^(.*:)?featureMembers?$/)
26626     },
26627
26628     /**
26629      * Constructor: OpenLayers.Format.GML.Base
26630      * Instances of this class are not created directly.  Use the
26631      *     <OpenLayers.Format.GML.v2> or <OpenLayers.Format.GML.v3> constructor
26632      *     instead.
26633      *
26634      * Parameters:
26635      * options - {Object} An optional object whose properties will be set on
26636      *     this instance.
26637      *
26638      * Valid options properties:
26639      * featureType - {Array(String) or String} Local (without prefix) feature 
26640      *     typeName(s) (required for write).
26641      * featureNS - {String} Feature namespace (required for write).
26642      * geometryName - {String} Geometry element name (required for write).
26643      */
26644     initialize: function(options) {
26645         OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
26646         this.setGeometryTypes();
26647         if(options && options.featureNS) {
26648             this.setNamespace("feature", options.featureNS);
26649         }
26650         this.singleFeatureType = !options || (typeof options.featureType === "string");
26651     },
26652     
26653     /**
26654      * Method: read
26655      *
26656      * Parameters:
26657      * data - {DOMElement} A gml:featureMember element, a gml:featureMembers
26658      *     element, or an element containing either of the above at any level.
26659      *
26660      * Returns:
26661      * {Array(<OpenLayers.Feature.Vector>)} An array of features.
26662      */
26663     read: function(data) {
26664         if(typeof data == "string") { 
26665             data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
26666         }
26667         if(data && data.nodeType == 9) {
26668             data = data.documentElement;
26669         }
26670         var features = [];
26671         this.readNode(data, {features: features}, true);
26672         if(features.length == 0) {
26673             // look for gml:featureMember elements
26674             var elements = this.getElementsByTagNameNS(
26675                 data, this.namespaces.gml, "featureMember"
26676             );
26677             if(elements.length) {
26678                 for(var i=0, len=elements.length; i<len; ++i) {
26679                     this.readNode(elements[i], {features: features}, true);
26680                 }
26681             } else {
26682                 // look for gml:featureMembers elements (this is v3, but does no harm here)
26683                 var elements = this.getElementsByTagNameNS(
26684                     data, this.namespaces.gml, "featureMembers"
26685                 );
26686                 if(elements.length) {
26687                     // there can be only one
26688                     this.readNode(elements[0], {features: features}, true);
26689                 }
26690             }
26691         }
26692         return features;
26693     },
26694     
26695     /**
26696      * Method: readNode
26697      * Shorthand for applying one of the named readers given the node
26698      *     namespace and local name.  Readers take two args (node, obj) and
26699      *     generally extend or modify the second.
26700      *
26701      * Parameters:
26702      * node - {DOMElement} The node to be read (required).
26703      * obj - {Object} The object to be modified (optional).
26704      * first - {Boolean} Should be set to true for the first node read. This
26705      *     is usually the readNode call in the read method. Without this being
26706      *     set, auto-configured properties will stick on subsequent reads.
26707      *
26708      * Returns:
26709      * {Object} The input object, modified (or a new one if none was provided).
26710      */
26711     readNode: function(node, obj, first) {
26712         // on subsequent calls of format.read(), we want to reset auto-
26713         // configured properties and auto-configure again.
26714         if (first === true && this.autoConfig === true) {
26715             this.featureType = null;
26716             delete this.namespaceAlias[this.featureNS];
26717             delete this.namespaces["feature"];
26718             this.featureNS = null;
26719         }
26720         // featureType auto-configuration
26721         if (!this.featureNS && (!(node.prefix in this.namespaces) &&
26722                 node.parentNode.namespaceURI == this.namespaces["gml"] &&
26723                 this.regExes.featureMember.test(node.parentNode.nodeName))) {
26724             this.featureType = node.nodeName.split(":").pop();
26725             this.setNamespace("feature", node.namespaceURI);
26726             this.featureNS = node.namespaceURI;
26727             this.autoConfig = true;
26728         }
26729         return OpenLayers.Format.XML.prototype.readNode.apply(this, [node, obj]);
26730     },
26731     
26732     /**
26733      * Property: readers
26734      * Contains public functions, grouped by namespace prefix, that will
26735      *     be applied when a namespaced node is found matching the function
26736      *     name.  The function will be applied in the scope of this parser
26737      *     with two arguments: the node being read and a context object passed
26738      *     from the parent.
26739      */
26740     readers: {
26741         "gml": {
26742             "_inherit": function(node, obj, container) {
26743                 // To be implemented by version specific parsers
26744             },
26745             "featureMember": function(node, obj) {
26746                 this.readChildNodes(node, obj);
26747             },
26748             "featureMembers": function(node, obj) {
26749                 this.readChildNodes(node, obj);                
26750             },
26751             "name": function(node, obj) {
26752                 obj.name = this.getChildValue(node);
26753             },
26754             "boundedBy": function(node, obj) {
26755                 var container = {};
26756                 this.readChildNodes(node, container);
26757                 if(container.components && container.components.length > 0) {
26758                     obj.bounds = container.components[0];
26759                 }
26760             },
26761             "Point": function(node, container) {
26762                 var obj = {points: []};
26763                 this.readChildNodes(node, obj);
26764                 if(!container.components) {
26765                     container.components = [];
26766                 }
26767                 container.components.push(obj.points[0]);
26768             },
26769             "coordinates": function(node, obj) {
26770                 var str = this.getChildValue(node).replace(
26771                     this.regExes.trimSpace, ""
26772                 );
26773                 str = str.replace(this.regExes.trimComma, ",");
26774                 var pointList = str.split(this.regExes.splitSpace);
26775                 var coords;
26776                 var numPoints = pointList.length;
26777                 var points = new Array(numPoints);
26778                 for(var i=0; i<numPoints; ++i) {
26779                     coords = pointList[i].split(",");
26780                     if (this.xy) {
26781                         points[i] = new OpenLayers.Geometry.Point(
26782                             coords[0], coords[1], coords[2]
26783                         );
26784                     } else {
26785                         points[i] = new OpenLayers.Geometry.Point(
26786                             coords[1], coords[0], coords[2]
26787                         );
26788                     }
26789                 }
26790                 obj.points = points;
26791             },
26792             "coord": function(node, obj) {
26793                 var coord = {};
26794                 this.readChildNodes(node, coord);
26795                 if(!obj.points) {
26796                     obj.points = [];
26797                 }
26798                 obj.points.push(new OpenLayers.Geometry.Point(
26799                     coord.x, coord.y, coord.z
26800                 ));
26801             },
26802             "X": function(node, coord) {
26803                 coord.x = this.getChildValue(node);
26804             },
26805             "Y": function(node, coord) {
26806                 coord.y = this.getChildValue(node);
26807             },
26808             "Z": function(node, coord) {
26809                 coord.z = this.getChildValue(node);
26810             },
26811             "MultiPoint": function(node, container) {
26812                 var obj = {components: []};
26813                 this.readers.gml._inherit.apply(this, [node, obj, container]);
26814                 this.readChildNodes(node, obj);
26815                 container.components = [
26816                     new OpenLayers.Geometry.MultiPoint(obj.components)
26817                 ];
26818             },
26819             "pointMember": function(node, obj) {
26820                 this.readChildNodes(node, obj);
26821             },
26822             "LineString": function(node, container) {
26823                 var obj = {};
26824                 this.readers.gml._inherit.apply(this, [node, obj, container]);
26825                 this.readChildNodes(node, obj);
26826                 if(!container.components) {
26827                     container.components = [];
26828                 }
26829                 container.components.push(
26830                     new OpenLayers.Geometry.LineString(obj.points)
26831                 );
26832             },
26833             "MultiLineString": function(node, container) {
26834                 var obj = {components: []};
26835                 this.readers.gml._inherit.apply(this, [node, obj, container]);
26836                 this.readChildNodes(node, obj);
26837                 container.components = [
26838                     new OpenLayers.Geometry.MultiLineString(obj.components)
26839                 ];
26840             },
26841             "lineStringMember": function(node, obj) {
26842                 this.readChildNodes(node, obj);
26843             },
26844             "Polygon": function(node, container) {
26845                 var obj = {outer: null, inner: []};
26846                 this.readers.gml._inherit.apply(this, [node, obj, container]);
26847                 this.readChildNodes(node, obj);
26848                 obj.inner.unshift(obj.outer);
26849                 if(!container.components) {
26850                     container.components = [];
26851                 }
26852                 container.components.push(
26853                     new OpenLayers.Geometry.Polygon(obj.inner)
26854                 );
26855             },
26856             "LinearRing": function(node, obj) {
26857                 var container = {};
26858                 this.readers.gml._inherit.apply(this, [node, container]);
26859                 this.readChildNodes(node, container);
26860                 obj.components = [new OpenLayers.Geometry.LinearRing(
26861                     container.points
26862                 )];
26863             },
26864             "MultiPolygon": function(node, container) {
26865                 var obj = {components: []};
26866                 this.readers.gml._inherit.apply(this, [node, obj, container]);
26867                 this.readChildNodes(node, obj);
26868                 container.components = [
26869                     new OpenLayers.Geometry.MultiPolygon(obj.components)
26870                 ];
26871             },
26872             "polygonMember": function(node, obj) {
26873                 this.readChildNodes(node, obj);
26874             },
26875             "GeometryCollection": function(node, container) {
26876                 var obj = {components: []};
26877                 this.readers.gml._inherit.apply(this, [node, obj, container]);
26878                 this.readChildNodes(node, obj);
26879                 container.components = [
26880                     new OpenLayers.Geometry.Collection(obj.components)
26881                 ];
26882             },
26883             "geometryMember": function(node, obj) {
26884                 this.readChildNodes(node, obj);
26885             }
26886         },
26887         "feature": {
26888             "*": function(node, obj) {
26889                 // The node can either be named like the featureType, or it
26890                 // can be a child of the feature:featureType.  Children can be
26891                 // geometry or attributes.
26892                 var name;
26893                 var local = node.localName || node.nodeName.split(":").pop();
26894                 // Since an attribute can have the same name as the feature type
26895                 // we only want to read the node as a feature if the parent
26896                 // node can have feature nodes as children.  In this case, the
26897                 // obj.features property is set.
26898                 if (obj.features) {
26899                     if (!this.singleFeatureType &&
26900                         (OpenLayers.Util.indexOf(this.featureType, local) !== -1)) {
26901                         name = "_typeName";
26902                     } else if(local === this.featureType) {
26903                         name = "_typeName";
26904                     }
26905                 } else {
26906                     // Assume attribute elements have one child node and that the child
26907                     // is a text node.  Otherwise assume it is a geometry node.
26908                     if(node.childNodes.length == 0 ||
26909                        (node.childNodes.length == 1 && node.firstChild.nodeType == 3)) {
26910                         if(this.extractAttributes) {
26911                             name = "_attribute";
26912                         }
26913                     } else {
26914                         name = "_geometry";
26915                     }
26916                 }
26917                 if(name) {
26918                     this.readers.feature[name].apply(this, [node, obj]);
26919                 }
26920             },
26921             "_typeName": function(node, obj) {
26922                 var container = {components: [], attributes: {}};
26923                 this.readChildNodes(node, container);
26924                 // look for common gml namespaced elements
26925                 if(container.name) {
26926                     container.attributes.name = container.name;
26927                 }
26928                 var feature = new OpenLayers.Feature.Vector(
26929                     container.components[0], container.attributes
26930                 );
26931                 if (!this.singleFeatureType) {
26932                     feature.type = node.nodeName.split(":").pop();
26933                     feature.namespace = node.namespaceURI;
26934                 }
26935                 var fid = node.getAttribute("fid") ||
26936                     this.getAttributeNS(node, this.namespaces["gml"], "id");
26937                 if(fid) {
26938                     feature.fid = fid;
26939                 }
26940                 if(this.internalProjection && this.externalProjection &&
26941                    feature.geometry) {
26942                     feature.geometry.transform(
26943                         this.externalProjection, this.internalProjection
26944                     );
26945                 }
26946                 if(container.bounds) {
26947                     feature.bounds = container.bounds;
26948                 }
26949                 obj.features.push(feature);
26950             },
26951             "_geometry": function(node, obj) {
26952                 if (!this.geometryName) {
26953                     this.geometryName = node.nodeName.split(":").pop();
26954                 }
26955                 this.readChildNodes(node, obj);
26956             },
26957             "_attribute": function(node, obj) {
26958                 var local = node.localName || node.nodeName.split(":").pop();
26959                 var value = this.getChildValue(node);
26960                 obj.attributes[local] = value;
26961             }
26962         },
26963         "wfs": {
26964             "FeatureCollection": function(node, obj) {
26965                 this.readChildNodes(node, obj);
26966             }
26967         }
26968     },
26969     
26970     /**
26971      * Method: write
26972      *
26973      * Parameters:
26974      * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
26975      *     An array of features or a single feature.
26976      *
26977      * Returns:
26978      * {String} Given an array of features, a doc with a gml:featureMembers
26979      *     element will be returned.  Given a single feature, a doc with a
26980      *     gml:featureMember element will be returned.
26981      */
26982     write: function(features) {
26983         var name;
26984         if(OpenLayers.Util.isArray(features)) {
26985             name = "featureMembers";
26986         } else {
26987             name = "featureMember";
26988         }
26989         var root = this.writeNode("gml:" + name, features);
26990         this.setAttributeNS(
26991             root, this.namespaces["xsi"],
26992             "xsi:schemaLocation", this.schemaLocation
26993         );
26994
26995         return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
26996     },
26997     
26998     /**
26999      * Property: writers
27000      * As a compliment to the readers property, this structure contains public
27001      *     writing functions grouped by namespace alias and named like the
27002      *     node names they produce.
27003      */
27004     writers: {
27005         "gml": {
27006             "featureMember": function(feature) {
27007                 var node = this.createElementNSPlus("gml:featureMember");
27008                 this.writeNode("feature:_typeName", feature, node);
27009                 return node;
27010             },
27011             "MultiPoint": function(geometry) {
27012                 var node = this.createElementNSPlus("gml:MultiPoint");
27013                 var components = geometry.components || [geometry];
27014                 for(var i=0, ii=components.length; i<ii; ++i) {
27015                     this.writeNode("pointMember", components[i], node);
27016                 }
27017                 return node;
27018             },
27019             "pointMember": function(geometry) {
27020                 var node = this.createElementNSPlus("gml:pointMember");
27021                 this.writeNode("Point", geometry, node);
27022                 return node;
27023             },
27024             "MultiLineString": function(geometry) {
27025                 var node = this.createElementNSPlus("gml:MultiLineString");
27026                 var components = geometry.components || [geometry];
27027                 for(var i=0, ii=components.length; i<ii; ++i) {
27028                     this.writeNode("lineStringMember", components[i], node);
27029                 }
27030                 return node;
27031             },
27032             "lineStringMember": function(geometry) {
27033                 var node = this.createElementNSPlus("gml:lineStringMember");
27034                 this.writeNode("LineString", geometry, node);
27035                 return node;
27036             },
27037             "MultiPolygon": function(geometry) {
27038                 var node = this.createElementNSPlus("gml:MultiPolygon");
27039                 var components = geometry.components || [geometry];
27040                 for(var i=0, ii=components.length; i<ii; ++i) {
27041                     this.writeNode(
27042                         "polygonMember", components[i], node
27043                     );
27044                 }
27045                 return node;
27046             },
27047             "polygonMember": function(geometry) {
27048                 var node = this.createElementNSPlus("gml:polygonMember");
27049                 this.writeNode("Polygon", geometry, node);
27050                 return node;
27051             },
27052             "GeometryCollection": function(geometry) {
27053                 var node = this.createElementNSPlus("gml:GeometryCollection");
27054                 for(var i=0, len=geometry.components.length; i<len; ++i) {
27055                     this.writeNode("geometryMember", geometry.components[i], node);
27056                 }
27057                 return node;
27058             },
27059             "geometryMember": function(geometry) {
27060                 var node = this.createElementNSPlus("gml:geometryMember");
27061                 var child = this.writeNode("feature:_geometry", geometry);
27062                 node.appendChild(child.firstChild);
27063                 return node;
27064             }
27065         },
27066         "feature": {
27067             "_typeName": function(feature) {
27068                 var node = this.createElementNSPlus("feature:" + this.featureType, {
27069                     attributes: {fid: feature.fid}
27070                 });
27071                 if(feature.geometry) {
27072                     this.writeNode("feature:_geometry", feature.geometry, node);
27073                 }
27074                 for(var name in feature.attributes) {
27075                     var value = feature.attributes[name];
27076                     if(value != null) {
27077                         this.writeNode(
27078                             "feature:_attribute",
27079                             {name: name, value: value}, node
27080                         );
27081                     }
27082                 }
27083                 return node;
27084             },
27085             "_geometry": function(geometry) {
27086                 if(this.externalProjection && this.internalProjection) {
27087                     geometry = geometry.clone().transform(
27088                         this.internalProjection, this.externalProjection
27089                     );
27090                 }    
27091                 var node = this.createElementNSPlus(
27092                     "feature:" + this.geometryName
27093                 );
27094                 var type = this.geometryTypes[geometry.CLASS_NAME];
27095                 var child = this.writeNode("gml:" + type, geometry, node);
27096                 if(this.srsName) {
27097                     child.setAttribute("srsName", this.srsName);
27098                 }
27099                 return node;
27100             },
27101             "_attribute": function(obj) {
27102                 return this.createElementNSPlus("feature:" + obj.name, {
27103                     value: obj.value
27104                 });
27105             }
27106         },
27107         "wfs": {
27108             "FeatureCollection": function(features) {
27109                 /**
27110                  * This is only here because GML2 only describes abstract
27111                  * feature collections.  Typically, you would not be using
27112                  * the GML format to write wfs elements.  This just provides
27113                  * some way to write out lists of features.  GML3 defines the
27114                  * featureMembers element, so that is used by default instead.
27115                  */
27116                 var node = this.createElementNSPlus("wfs:FeatureCollection");
27117                 for(var i=0, len=features.length; i<len; ++i) {
27118                     this.writeNode("gml:featureMember", features[i], node);
27119                 }
27120                 return node;
27121             }
27122         }
27123     },
27124     
27125     /**
27126      * Method: setGeometryTypes
27127      * Sets the <geometryTypes> mapping.
27128      */
27129     setGeometryTypes: function() {
27130         this.geometryTypes = {
27131             "OpenLayers.Geometry.Point": "Point",
27132             "OpenLayers.Geometry.MultiPoint": "MultiPoint",
27133             "OpenLayers.Geometry.LineString": "LineString",
27134             "OpenLayers.Geometry.MultiLineString": "MultiLineString",
27135             "OpenLayers.Geometry.Polygon": "Polygon",
27136             "OpenLayers.Geometry.MultiPolygon": "MultiPolygon",
27137             "OpenLayers.Geometry.Collection": "GeometryCollection"
27138         };
27139     },
27140
27141     CLASS_NAME: "OpenLayers.Format.GML.Base" 
27142
27143 });
27144 /* ======================================================================
27145     OpenLayers/Format/GML/v2.js
27146    ====================================================================== */
27147
27148 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27149  * full list of contributors). Published under the 2-clause BSD license.
27150  * See license.txt in the OpenLayers distribution or repository for the
27151  * full text of the license. */
27152
27153 /**
27154  * @requires OpenLayers/Format/GML/Base.js
27155  */
27156
27157 /**
27158  * Class: OpenLayers.Format.GML.v2
27159  * Parses GML version 2.
27160  *
27161  * Inherits from:
27162  *  - <OpenLayers.Format.GML.Base>
27163  */
27164 OpenLayers.Format.GML.v2 = OpenLayers.Class(OpenLayers.Format.GML.Base, {
27165     
27166     /**
27167      * Property: schemaLocation
27168      * {String} Schema location for a particular minor version.
27169      */
27170     schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd",
27171
27172     /**
27173      * Constructor: OpenLayers.Format.GML.v2
27174      * Create a parser for GML v2.
27175      *
27176      * Parameters:
27177      * options - {Object} An optional object whose properties will be set on
27178      *     this instance.
27179      *
27180      * Valid options properties:
27181      * featureType - {String} Local (without prefix) feature typeName (required).
27182      * featureNS - {String} Feature namespace (required).
27183      * geometryName - {String} Geometry element name.
27184      */
27185     initialize: function(options) {
27186         OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]);
27187     },
27188
27189     /**
27190      * Property: readers
27191      * Contains public functions, grouped by namespace prefix, that will
27192      *     be applied when a namespaced node is found matching the function
27193      *     name.  The function will be applied in the scope of this parser
27194      *     with two arguments: the node being read and a context object passed
27195      *     from the parent.
27196      */
27197     readers: {
27198         "gml": OpenLayers.Util.applyDefaults({
27199             "outerBoundaryIs": function(node, container) {
27200                 var obj = {};
27201                 this.readChildNodes(node, obj);
27202                 container.outer = obj.components[0];
27203             },
27204             "innerBoundaryIs": function(node, container) {
27205                 var obj = {};
27206                 this.readChildNodes(node, obj);
27207                 container.inner.push(obj.components[0]);
27208             },
27209             "Box": function(node, container) {
27210                 var obj = {};
27211                 this.readChildNodes(node, obj);
27212                 if(!container.components) {
27213                     container.components = [];
27214                 }
27215                 var min = obj.points[0];
27216                 var max = obj.points[1];
27217                 container.components.push(
27218                     new OpenLayers.Bounds(min.x, min.y, max.x, max.y)
27219                 );
27220             }
27221         }, OpenLayers.Format.GML.Base.prototype.readers["gml"]),
27222         "feature": OpenLayers.Format.GML.Base.prototype.readers["feature"],
27223         "wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"]
27224     },
27225
27226     /**
27227      * Method: write
27228      *
27229      * Parameters:
27230      * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
27231      *     An array of features or a single feature.
27232      *
27233      * Returns:
27234      * {String} Given an array of features, a doc with a gml:featureMembers
27235      *     element will be returned.  Given a single feature, a doc with a
27236      *     gml:featureMember element will be returned.
27237      */
27238     write: function(features) {
27239         var name;
27240         if(OpenLayers.Util.isArray(features)) {
27241             // GML2 only has abstract feature collections
27242             // wfs provides a feature collection from a well-known schema
27243             name = "wfs:FeatureCollection";
27244         } else {
27245             name = "gml:featureMember";
27246         }
27247         var root = this.writeNode(name, features);
27248         this.setAttributeNS(
27249             root, this.namespaces["xsi"],
27250             "xsi:schemaLocation", this.schemaLocation
27251         );
27252
27253         return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
27254     },
27255
27256     /**
27257      * Property: writers
27258      * As a compliment to the readers property, this structure contains public
27259      *     writing functions grouped by namespace alias and named like the
27260      *     node names they produce.
27261      */
27262     writers: {
27263         "gml": OpenLayers.Util.applyDefaults({
27264             "Point": function(geometry) {
27265                 var node = this.createElementNSPlus("gml:Point");
27266                 this.writeNode("coordinates", [geometry], node);
27267                 return node;
27268             },
27269             "coordinates": function(points) {
27270                 var numPoints = points.length;
27271                 var parts = new Array(numPoints);
27272                 var point;
27273                 for(var i=0; i<numPoints; ++i) {
27274                     point = points[i];
27275                     if(this.xy) {
27276                         parts[i] = point.x + "," + point.y;
27277                     } else {
27278                         parts[i] = point.y + "," + point.x;
27279                     }
27280                     if(point.z != undefined) { // allow null or undefined
27281                         parts[i] += "," + point.z;
27282                     }
27283                 }
27284                 return this.createElementNSPlus("gml:coordinates", {
27285                     attributes: {
27286                         decimal: ".", cs: ",", ts: " "
27287                     },
27288                     value: (numPoints == 1) ? parts[0] : parts.join(" ")
27289                 });
27290             },
27291             "LineString": function(geometry) {
27292                 var node = this.createElementNSPlus("gml:LineString");
27293                 this.writeNode("coordinates", geometry.components, node);
27294                 return node;
27295             },
27296             "Polygon": function(geometry) {
27297                 var node = this.createElementNSPlus("gml:Polygon");
27298                 this.writeNode("outerBoundaryIs", geometry.components[0], node);
27299                 for(var i=1; i<geometry.components.length; ++i) {
27300                     this.writeNode(
27301                         "innerBoundaryIs", geometry.components[i], node
27302                     );
27303                 }
27304                 return node;
27305             },
27306             "outerBoundaryIs": function(ring) {
27307                 var node = this.createElementNSPlus("gml:outerBoundaryIs");
27308                 this.writeNode("LinearRing", ring, node);
27309                 return node;
27310             },
27311             "innerBoundaryIs": function(ring) {
27312                 var node = this.createElementNSPlus("gml:innerBoundaryIs");
27313                 this.writeNode("LinearRing", ring, node);
27314                 return node;
27315             },
27316             "LinearRing": function(ring) {
27317                 var node = this.createElementNSPlus("gml:LinearRing");
27318                 this.writeNode("coordinates", ring.components, node);
27319                 return node;
27320             },
27321             "Box": function(bounds) {
27322                 var node = this.createElementNSPlus("gml:Box");
27323                 this.writeNode("coordinates", [
27324                     {x: bounds.left, y: bounds.bottom},
27325                     {x: bounds.right, y: bounds.top}
27326                 ], node);
27327                 // srsName attribute is optional for gml:Box
27328                 if(this.srsName) {
27329                     node.setAttribute("srsName", this.srsName);
27330                 }
27331                 return node;
27332             }
27333         }, OpenLayers.Format.GML.Base.prototype.writers["gml"]),
27334         "feature": OpenLayers.Format.GML.Base.prototype.writers["feature"],
27335         "wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"]
27336     },
27337     
27338     CLASS_NAME: "OpenLayers.Format.GML.v2" 
27339
27340 });
27341 /* ======================================================================
27342     OpenLayers/Filter/Function.js
27343    ====================================================================== */
27344
27345 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27346  * full list of contributors). Published under the 2-clause BSD license.
27347  * See license.txt in the OpenLayers distribution or repository for the
27348  * full text of the license. */
27349
27350 /**
27351  * @requires OpenLayers/Filter.js
27352  */
27353
27354 /**
27355  * Class: OpenLayers.Filter.Function
27356  * This class represents a filter function.
27357  * We are using this class for creation of complex 
27358  * filters that can contain filter functions as values.
27359  * Nesting function as other functions parameter is supported.
27360  * 
27361  * Inherits from:
27362  * - <OpenLayers.Filter>
27363  */
27364 OpenLayers.Filter.Function = OpenLayers.Class(OpenLayers.Filter, {
27365
27366     /**
27367      * APIProperty: name
27368      * {String} Name of the function.
27369      */
27370     name: null,
27371     
27372     /**
27373      * APIProperty: params
27374      * {Array(<OpenLayers.Filter.Function> || String || Number)} Function parameters
27375      * For now support only other Functions, String or Number
27376      */
27377     params: null,  
27378     
27379     /** 
27380      * Constructor: OpenLayers.Filter.Function
27381      * Creates a filter function.
27382      *
27383      * Parameters:
27384      * options - {Object} An optional object with properties to set on the
27385      *     function.
27386      * 
27387      * Returns:
27388      * {<OpenLayers.Filter.Function>}
27389      */
27390
27391     CLASS_NAME: "OpenLayers.Filter.Function"
27392 });
27393
27394 /* ======================================================================
27395     OpenLayers/BaseTypes/Date.js
27396    ====================================================================== */
27397
27398 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27399  * full list of contributors). Published under the 2-clause BSD license.
27400  * See license.txt in the OpenLayers distribution or repository for the
27401  * full text of the license. */
27402
27403 /**
27404  * @requires OpenLayers/SingleFile.js
27405  */
27406
27407 /**
27408  * Namespace: OpenLayers.Date
27409  * Contains implementations of Date.parse and date.toISOString that match the
27410  *     ECMAScript 5 specification for parsing RFC 3339 dates.
27411  *     http://tools.ietf.org/html/rfc3339
27412  */
27413 OpenLayers.Date = {
27414
27415     /** 
27416      * APIProperty: dateRegEx
27417      * The regex to be used for validating dates. You can provide your own
27418      * regex for instance for adding support for years before BC. Default
27419      * value is: /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/
27420      */
27421     dateRegEx: /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/,
27422
27423     /**
27424      * APIMethod: toISOString
27425      * Generates a string representing a date.  The format of the string follows
27426      *     the profile of ISO 8601 for date and time on the Internet (see
27427      *     http://tools.ietf.org/html/rfc3339).  If the toISOString method is
27428      *     available on the Date prototype, that is used.  The toISOString
27429      *     method for Date instances is defined in ECMA-262.
27430      *
27431      * Parameters:
27432      * date - {Date} A date object.
27433      *
27434      * Returns:
27435      * {String} A string representing the date (e.g.
27436      *     "2010-08-07T16:58:23.123Z").  If the date does not have a valid time
27437      *     (i.e. isNaN(date.getTime())) this method returns the string "Invalid
27438      *     Date".  The ECMA standard says the toISOString method should throw
27439      *     RangeError in this case, but Firefox returns a string instead.  For
27440      *     best results, use isNaN(date.getTime()) to determine date validity
27441      *     before generating date strings.
27442      */
27443     toISOString: (function() {
27444         if ("toISOString" in Date.prototype) {
27445             return function(date) {
27446                 return date.toISOString();
27447             };
27448         } else {
27449             return function(date) {
27450                 var str;
27451                 if (isNaN(date.getTime())) {
27452                     // ECMA-262 says throw RangeError, Firefox returns
27453                     // "Invalid Date"
27454                     str = "Invalid Date";
27455                 } else {
27456                     str =
27457                         date.getUTCFullYear() + "-" +
27458                         OpenLayers.Number.zeroPad(date.getUTCMonth() + 1, 2) + "-" +
27459                         OpenLayers.Number.zeroPad(date.getUTCDate(), 2) + "T" +
27460                         OpenLayers.Number.zeroPad(date.getUTCHours(), 2) + ":" +
27461                         OpenLayers.Number.zeroPad(date.getUTCMinutes(), 2) + ":" +
27462                         OpenLayers.Number.zeroPad(date.getUTCSeconds(), 2) + "." +
27463                         OpenLayers.Number.zeroPad(date.getUTCMilliseconds(), 3) + "Z";
27464                 }
27465                 return str;
27466             };
27467         }
27468
27469     })(),
27470
27471     /**
27472      * APIMethod: parse
27473      * Generate a date object from a string.  The format for the string follows
27474      *     the profile of ISO 8601 for date and time on the Internet (see
27475      *     http://tools.ietf.org/html/rfc3339).  We don't call the native
27476      *     Date.parse because of inconsistency between implmentations.  In
27477      *     Chrome, calling Date.parse with a string that doesn't contain any
27478      *     indication of the timezone (e.g. "2011"), the date is interpreted
27479      *     in local time.  On Firefox, the assumption is UTC.
27480      *
27481      * Parameters:
27482      * str - {String} A string representing the date (e.g.
27483      *     "2010", "2010-08", "2010-08-07", "2010-08-07T16:58:23.123Z",
27484      *     "2010-08-07T11:58:23.123-06").
27485      *
27486      * Returns:
27487      * {Date} A date object.  If the string could not be parsed, an invalid
27488      *     date is returned (i.e. isNaN(date.getTime())).
27489      */
27490     parse: function(str) {
27491         var date;
27492         var match = str.match(this.dateRegEx);
27493         if (match && (match[1] || match[7])) { // must have at least year or time
27494             var year = parseInt(match[1], 10) || 0;
27495             var month = (parseInt(match[2], 10) - 1) || 0;
27496             var day = parseInt(match[3], 10) || 1;
27497             date = new Date(Date.UTC(year, month, day));
27498             // optional time
27499             var type = match[7];
27500             if (type) {
27501                 var hours = parseInt(match[4], 10);
27502                 var minutes = parseInt(match[5], 10);
27503                 var secFrac = parseFloat(match[6]);
27504                 var seconds = secFrac | 0;
27505                 var milliseconds = Math.round(1000 * (secFrac - seconds));
27506                 date.setUTCHours(hours, minutes, seconds, milliseconds);
27507                 // check offset
27508                 if (type !== "Z") {
27509                     var hoursOffset = parseInt(type, 10);
27510                     var minutesOffset = parseInt(match[8], 10) || 0;
27511                     var offset = -1000 * (60 * (hoursOffset * 60) + minutesOffset * 60);
27512                     date = new Date(date.getTime() + offset);
27513                 }
27514             }
27515         } else {
27516             date = new Date("invalid");
27517         }
27518         return date;
27519     }
27520 };
27521 /* ======================================================================
27522     OpenLayers/Format/Filter/v1.js
27523    ====================================================================== */
27524
27525 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27526  * full list of contributors). Published under the 2-clause BSD license.
27527  * See license.txt in the OpenLayers distribution or repository for the
27528  * full text of the license. */
27529 /**
27530  * @requires OpenLayers/Format/Filter.js
27531  * @requires OpenLayers/Format/XML.js
27532  * @requires OpenLayers/Filter/Function.js
27533  * @requires OpenLayers/BaseTypes/Date.js
27534  */
27535
27536 /**
27537  * Class: OpenLayers.Format.Filter.v1
27538  * Superclass for Filter version 1 parsers.
27539  *
27540  * Inherits from:
27541  *  - <OpenLayers.Format.XML>
27542  */
27543 OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
27544     
27545     /**
27546      * Property: namespaces
27547      * {Object} Mapping of namespace aliases to namespace URIs.
27548      */
27549     namespaces: {
27550         ogc: "http://www.opengis.net/ogc",
27551         gml: "http://www.opengis.net/gml",
27552         xlink: "http://www.w3.org/1999/xlink",
27553         xsi: "http://www.w3.org/2001/XMLSchema-instance"
27554     },
27555
27556     /**
27557      * Property: defaultPrefix
27558      */
27559     defaultPrefix: "ogc",
27560
27561     /**
27562      * Property: schemaLocation
27563      * {String} Schema location for a particular minor version.
27564      */
27565     schemaLocation: null,
27566     
27567     /**
27568      * Constructor: OpenLayers.Format.Filter.v1
27569      * Instances of this class are not created directly.  Use the
27570      *     <OpenLayers.Format.Filter> constructor instead.
27571      *
27572      * Parameters:
27573      * options - {Object} An optional object whose properties will be set on
27574      *     this instance.
27575      */
27576     initialize: function(options) {
27577         OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
27578     },
27579     
27580     /**
27581      * Method: read
27582      *
27583      * Parameters:
27584      * data - {DOMElement} A Filter document element.
27585      *
27586      * Returns:
27587      * {<OpenLayers.Filter>} A filter object.
27588      */
27589     read: function(data) {
27590         var obj = {};
27591         this.readers.ogc["Filter"].apply(this, [data, obj]);
27592         return obj.filter;
27593     },
27594     
27595     /**
27596      * Property: readers
27597      * Contains public functions, grouped by namespace prefix, that will
27598      *     be applied when a namespaced node is found matching the function
27599      *     name.  The function will be applied in the scope of this parser
27600      *     with two arguments: the node being read and a context object passed
27601      *     from the parent.
27602      */
27603     readers: {
27604         "ogc": {
27605             "_expression": function(node) {
27606                 // only the simplest of ogc:expression handled
27607                 // "some text and an <PropertyName>attribute</PropertyName>"}
27608                 var obj, value = "";
27609                 for(var child=node.firstChild; child; child=child.nextSibling) {
27610                     switch(child.nodeType) {
27611                         case 1:
27612                             obj = this.readNode(child);
27613                             if (obj.property) {
27614                                 value += "${" + obj.property + "}";
27615                             } else if (obj.value !== undefined) {
27616                                 value += obj.value;
27617                             }
27618                             break;
27619                         case 3: // text node
27620                         case 4: // cdata section
27621                             value += child.nodeValue;
27622                     }
27623                 }
27624                 return value;
27625             },
27626             "Filter": function(node, parent) {
27627                 // Filters correspond to subclasses of OpenLayers.Filter.
27628                 // Since they contain information we don't persist, we
27629                 // create a temporary object and then pass on the filter
27630                 // (ogc:Filter) to the parent obj.
27631                 var obj = {
27632                     fids: [],
27633                     filters: []
27634                 };
27635                 this.readChildNodes(node, obj);
27636                 if(obj.fids.length > 0) {
27637                     parent.filter = new OpenLayers.Filter.FeatureId({
27638                         fids: obj.fids
27639                     });
27640                 } else if(obj.filters.length > 0) {
27641                     parent.filter = obj.filters[0];
27642                 }
27643             },
27644             "FeatureId": function(node, obj) {
27645                 var fid = node.getAttribute("fid");
27646                 if(fid) {
27647                     obj.fids.push(fid);
27648                 }
27649             },
27650             "And": function(node, obj) {
27651                 var filter = new OpenLayers.Filter.Logical({
27652                     type: OpenLayers.Filter.Logical.AND
27653                 });
27654                 this.readChildNodes(node, filter);
27655                 obj.filters.push(filter);
27656             },
27657             "Or": function(node, obj) {
27658                 var filter = new OpenLayers.Filter.Logical({
27659                     type: OpenLayers.Filter.Logical.OR
27660                 });
27661                 this.readChildNodes(node, filter);
27662                 obj.filters.push(filter);
27663             },
27664             "Not": function(node, obj) {
27665                 var filter = new OpenLayers.Filter.Logical({
27666                     type: OpenLayers.Filter.Logical.NOT
27667                 });
27668                 this.readChildNodes(node, filter);
27669                 obj.filters.push(filter);
27670             },
27671             "PropertyIsLessThan": function(node, obj) {
27672                 var filter = new OpenLayers.Filter.Comparison({
27673                     type: OpenLayers.Filter.Comparison.LESS_THAN
27674                 });
27675                 this.readChildNodes(node, filter);
27676                 obj.filters.push(filter);
27677             },
27678             "PropertyIsGreaterThan": function(node, obj) {
27679                 var filter = new OpenLayers.Filter.Comparison({
27680                     type: OpenLayers.Filter.Comparison.GREATER_THAN
27681                 });
27682                 this.readChildNodes(node, filter);
27683                 obj.filters.push(filter);
27684             },
27685             "PropertyIsLessThanOrEqualTo": function(node, obj) {
27686                 var filter = new OpenLayers.Filter.Comparison({
27687                     type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO
27688                 });
27689                 this.readChildNodes(node, filter);
27690                 obj.filters.push(filter);
27691             },
27692             "PropertyIsGreaterThanOrEqualTo": function(node, obj) {
27693                 var filter = new OpenLayers.Filter.Comparison({
27694                     type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO
27695                 });
27696                 this.readChildNodes(node, filter);
27697                 obj.filters.push(filter);
27698             },
27699             "PropertyIsBetween": function(node, obj) {
27700                 var filter = new OpenLayers.Filter.Comparison({
27701                     type: OpenLayers.Filter.Comparison.BETWEEN
27702                 });
27703                 this.readChildNodes(node, filter);
27704                 obj.filters.push(filter);
27705             },
27706             "Literal": function(node, obj) {
27707                 obj.value = OpenLayers.String.numericIf(
27708                     this.getChildValue(node), true);
27709             },
27710             "PropertyName": function(node, filter) {
27711                 filter.property = this.getChildValue(node);
27712             },
27713             "LowerBoundary": function(node, filter) {
27714                 filter.lowerBoundary = OpenLayers.String.numericIf(
27715                     this.readers.ogc._expression.call(this, node), true);
27716             },
27717             "UpperBoundary": function(node, filter) {
27718                 filter.upperBoundary = OpenLayers.String.numericIf(
27719                     this.readers.ogc._expression.call(this, node), true);
27720             },
27721             "Intersects": function(node, obj) {
27722                 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.INTERSECTS);
27723             },
27724             "Within": function(node, obj) {
27725                 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.WITHIN);
27726             },
27727             "Contains": function(node, obj) {
27728                 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.CONTAINS);
27729             },
27730             "DWithin": function(node, obj) {
27731                 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.DWITHIN);
27732             },
27733             "Distance": function(node, obj) {
27734                 obj.distance = parseInt(this.getChildValue(node));
27735                 obj.distanceUnits = node.getAttribute("units");
27736             },
27737             "Function": function(node, obj) {
27738                 //TODO write decoder for it
27739                 return;
27740             },
27741             "PropertyIsNull": function(node, obj) {
27742                 var filter = new OpenLayers.Filter.Comparison({
27743                     type: OpenLayers.Filter.Comparison.IS_NULL
27744                 });
27745                 this.readChildNodes(node, filter);
27746                 obj.filters.push(filter);
27747             }
27748         }
27749     },
27750     
27751     /**
27752      * Method: readSpatial
27753      *
27754      * Read a {<OpenLayers.Filter.Spatial>} filter.
27755      * 
27756      * Parameters:
27757      * node - {DOMElement} A DOM element that contains an ogc:expression.
27758      * obj - {Object} The target object.
27759      * type - {String} One of the OpenLayers.Filter.Spatial.* constants.
27760      *
27761      * Returns:
27762      * {<OpenLayers.Filter.Spatial>} The created filter.
27763      */
27764     readSpatial: function(node, obj, type) {
27765         var filter = new OpenLayers.Filter.Spatial({
27766             type: type
27767         });
27768         this.readChildNodes(node, filter);
27769         filter.value = filter.components[0];
27770         delete filter.components;
27771         obj.filters.push(filter);
27772     },
27773
27774     /**
27775      * APIMethod: encodeLiteral
27776      * Generates the string representation of a value for use in <Literal> 
27777      *     elements.  The default encoder writes Date values as ISO 8601 
27778      *     strings.
27779      *
27780      * Parameters:
27781      * value - {Object} Literal value to encode
27782      *
27783      * Returns:
27784      * {String} String representation of the provided value.
27785      */
27786     encodeLiteral: function(value) {
27787         if (value instanceof Date) {
27788             value = OpenLayers.Date.toISOString(value);
27789         }
27790         return value;
27791     },
27792
27793     /**
27794      * Method: writeOgcExpression
27795      * Limited support for writing OGC expressions. Currently it supports
27796      * (<OpenLayers.Filter.Function> || String || Number)
27797      *
27798      * Parameters:
27799      * value - (<OpenLayers.Filter.Function> || String || Number)
27800      * node - {DOMElement} A parent DOM element 
27801      *
27802      * Returns:
27803      * {DOMElement} Updated node element.
27804      */
27805     writeOgcExpression: function(value, node) {
27806         if (value instanceof OpenLayers.Filter.Function){
27807             this.writeNode("Function", value, node);
27808         } else {
27809             this.writeNode("Literal", value, node);
27810         }
27811         return node;
27812     },    
27813     
27814     /**
27815      * Method: write
27816      *
27817      * Parameters:
27818      * filter - {<OpenLayers.Filter>} A filter object.
27819      *
27820      * Returns:
27821      * {DOMElement} An ogc:Filter element.
27822      */
27823     write: function(filter) {
27824         return this.writers.ogc["Filter"].apply(this, [filter]);
27825     },
27826     
27827     /**
27828      * Property: writers
27829      * As a compliment to the readers property, this structure contains public
27830      *     writing functions grouped by namespace alias and named like the
27831      *     node names they produce.
27832      */
27833     writers: {
27834         "ogc": {
27835             "Filter": function(filter) {
27836                 var node = this.createElementNSPlus("ogc:Filter");
27837                 this.writeNode(this.getFilterType(filter), filter, node);
27838                 return node;
27839             },
27840             "_featureIds": function(filter) {
27841                 var node = this.createDocumentFragment();
27842                 for (var i=0, ii=filter.fids.length; i<ii; ++i) {
27843                     this.writeNode("ogc:FeatureId", filter.fids[i], node);
27844                 }
27845                 return node;
27846             },
27847             "FeatureId": function(fid) {
27848                 return this.createElementNSPlus("ogc:FeatureId", {
27849                     attributes: {fid: fid}
27850                 });
27851             },
27852             "And": function(filter) {
27853                 var node = this.createElementNSPlus("ogc:And");
27854                 var childFilter;
27855                 for (var i=0, ii=filter.filters.length; i<ii; ++i) {
27856                     childFilter = filter.filters[i];
27857                     this.writeNode(
27858                         this.getFilterType(childFilter), childFilter, node
27859                     );
27860                 }
27861                 return node;
27862             },
27863             "Or": function(filter) {
27864                 var node = this.createElementNSPlus("ogc:Or");
27865                 var childFilter;
27866                 for (var i=0, ii=filter.filters.length; i<ii; ++i) {
27867                     childFilter = filter.filters[i];
27868                     this.writeNode(
27869                         this.getFilterType(childFilter), childFilter, node
27870                     );
27871                 }
27872                 return node;
27873             },
27874             "Not": function(filter) {
27875                 var node = this.createElementNSPlus("ogc:Not");
27876                 var childFilter = filter.filters[0];
27877                 this.writeNode(
27878                     this.getFilterType(childFilter), childFilter, node
27879                 );
27880                 return node;
27881             },
27882             "PropertyIsLessThan": function(filter) {
27883                 var node = this.createElementNSPlus("ogc:PropertyIsLessThan");
27884                 // no ogc:expression handling for PropertyName for now
27885                 this.writeNode("PropertyName", filter, node);
27886                 // handle Literals or Functions for now
27887                 this.writeOgcExpression(filter.value, node);
27888                 return node;
27889             },
27890             "PropertyIsGreaterThan": function(filter) {
27891                 var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan");
27892                 // no ogc:expression handling for PropertyName for now
27893                 this.writeNode("PropertyName", filter, node);
27894                 // handle Literals or Functions for now
27895                 this.writeOgcExpression(filter.value, node);
27896                 return node;
27897             },
27898             "PropertyIsLessThanOrEqualTo": function(filter) {
27899                 var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo");
27900                 // no ogc:expression handling for PropertyName for now
27901                 this.writeNode("PropertyName", filter, node);
27902                 // handle Literals or Functions for now
27903                 this.writeOgcExpression(filter.value, node);
27904                 return node;
27905             },
27906             "PropertyIsGreaterThanOrEqualTo": function(filter) {
27907                 var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo");
27908                 // no ogc:expression handling for PropertyName for now
27909                 this.writeNode("PropertyName", filter, node);
27910                 // handle Literals or Functions for now
27911                 this.writeOgcExpression(filter.value, node);
27912                 return node;
27913             },
27914             "PropertyIsBetween": function(filter) {
27915                 var node = this.createElementNSPlus("ogc:PropertyIsBetween");
27916                 // no ogc:expression handling for PropertyName for now
27917                 this.writeNode("PropertyName", filter, node);
27918                 this.writeNode("LowerBoundary", filter, node);
27919                 this.writeNode("UpperBoundary", filter, node);
27920                 return node;
27921             },
27922             "PropertyName": function(filter) {
27923                 // no ogc:expression handling for now
27924                 return this.createElementNSPlus("ogc:PropertyName", {
27925                     value: filter.property
27926                 });
27927             },
27928             "Literal": function(value) {
27929                 var encode = this.encodeLiteral ||
27930                     OpenLayers.Format.Filter.v1.prototype.encodeLiteral;
27931                 return this.createElementNSPlus("ogc:Literal", {
27932                     value: encode(value)
27933                 });
27934             },
27935             "LowerBoundary": function(filter) {
27936                 // handle Literals or Functions for now
27937                 var node = this.createElementNSPlus("ogc:LowerBoundary");
27938                 this.writeOgcExpression(filter.lowerBoundary, node);
27939                 return node;
27940             },
27941             "UpperBoundary": function(filter) {
27942                 // handle Literals or Functions for now
27943                 var node = this.createElementNSPlus("ogc:UpperBoundary");
27944                 this.writeNode("Literal", filter.upperBoundary, node);
27945                 return node;
27946             },
27947             "INTERSECTS": function(filter) {
27948                 return this.writeSpatial(filter, "Intersects");
27949             },
27950             "WITHIN": function(filter) {
27951                 return this.writeSpatial(filter, "Within");
27952             },
27953             "CONTAINS": function(filter) {
27954                 return this.writeSpatial(filter, "Contains");
27955             },
27956             "DWITHIN": function(filter) {
27957                 var node = this.writeSpatial(filter, "DWithin");
27958                 this.writeNode("Distance", filter, node);
27959                 return node;
27960             },
27961             "Distance": function(filter) {
27962                 return this.createElementNSPlus("ogc:Distance", {
27963                     attributes: {
27964                         units: filter.distanceUnits
27965                     },
27966                     value: filter.distance
27967                 });
27968             },
27969             "Function": function(filter) {
27970                 var node = this.createElementNSPlus("ogc:Function", {
27971                     attributes: {
27972                         name: filter.name
27973                     }
27974                 });
27975                 var params = filter.params;
27976                 for(var i=0, len=params.length; i<len; i++){
27977                     this.writeOgcExpression(params[i], node);
27978                 }
27979                 return node;
27980             },
27981             "PropertyIsNull": function(filter) {
27982                 var node = this.createElementNSPlus("ogc:PropertyIsNull");
27983                 this.writeNode("PropertyName", filter, node);
27984                 return node;
27985             }
27986         }
27987     },
27988
27989     /**
27990      * Method: getFilterType
27991      */
27992     getFilterType: function(filter) {
27993         var filterType = this.filterMap[filter.type];
27994         if(!filterType) {
27995             throw "Filter writing not supported for rule type: " + filter.type;
27996         }
27997         return filterType;
27998     },
27999     
28000     /**
28001      * Property: filterMap
28002      * {Object} Contains a member for each filter type.  Values are node names
28003      *     for corresponding OGC Filter child elements.
28004      */
28005     filterMap: {
28006         "&&": "And",
28007         "||": "Or",
28008         "!": "Not",
28009         "==": "PropertyIsEqualTo",
28010         "!=": "PropertyIsNotEqualTo",
28011         "<": "PropertyIsLessThan",
28012         ">": "PropertyIsGreaterThan",
28013         "<=": "PropertyIsLessThanOrEqualTo",
28014         ">=": "PropertyIsGreaterThanOrEqualTo",
28015         "..": "PropertyIsBetween",
28016         "~": "PropertyIsLike",
28017         "NULL": "PropertyIsNull",
28018         "BBOX": "BBOX",
28019         "DWITHIN": "DWITHIN",
28020         "WITHIN": "WITHIN",
28021         "CONTAINS": "CONTAINS",
28022         "INTERSECTS": "INTERSECTS",
28023         "FID": "_featureIds"
28024     },
28025
28026     CLASS_NAME: "OpenLayers.Format.Filter.v1" 
28027
28028 });
28029 /* ======================================================================
28030     OpenLayers/Format/Filter/v1_0_0.js
28031    ====================================================================== */
28032
28033 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
28034  * full list of contributors). Published under the 2-clause BSD license.
28035  * See license.txt in the OpenLayers distribution or repository for the
28036  * full text of the license. */
28037
28038 /**
28039  * @requires OpenLayers/Format/GML/v2.js
28040  * @requires OpenLayers/Format/Filter/v1.js
28041  */
28042
28043 /**
28044  * Class: OpenLayers.Format.Filter.v1_0_0
28045  * Write ogc:Filter version 1.0.0.
28046  * 
28047  * Inherits from:
28048  *  - <OpenLayers.Format.GML.v2>
28049  *  - <OpenLayers.Format.Filter.v1>
28050  */
28051 OpenLayers.Format.Filter.v1_0_0 = OpenLayers.Class(
28052     OpenLayers.Format.GML.v2, OpenLayers.Format.Filter.v1, {
28053     
28054     /**
28055      * Constant: VERSION
28056      * {String} 1.0.0
28057      */
28058     VERSION: "1.0.0",
28059     
28060     /**
28061      * Property: schemaLocation
28062      * {String} http://www.opengis.net/ogc/filter/1.0.0/filter.xsd
28063      */
28064     schemaLocation: "http://www.opengis.net/ogc/filter/1.0.0/filter.xsd",
28065
28066     /**
28067      * Constructor: OpenLayers.Format.Filter.v1_0_0
28068      * Instances of this class are not created directly.  Use the
28069      *     <OpenLayers.Format.Filter> constructor instead.
28070      *
28071      * Parameters:
28072      * options - {Object} An optional object whose properties will be set on
28073      *     this instance.
28074      */
28075     initialize: function(options) {
28076         OpenLayers.Format.GML.v2.prototype.initialize.apply(
28077             this, [options]
28078         );
28079     },
28080
28081     /**
28082      * Property: readers
28083      * Contains public functions, grouped by namespace prefix, that will
28084      *     be applied when a namespaced node is found matching the function
28085      *     name.  The function will be applied in the scope of this parser
28086      *     with two arguments: the node being read and a context object passed
28087      *     from the parent.
28088      */
28089     readers: {
28090         "ogc": OpenLayers.Util.applyDefaults({
28091             "PropertyIsEqualTo": function(node, obj) {
28092                 var filter = new OpenLayers.Filter.Comparison({
28093                     type: OpenLayers.Filter.Comparison.EQUAL_TO
28094                 });
28095                 this.readChildNodes(node, filter);
28096                 obj.filters.push(filter);
28097             },
28098             "PropertyIsNotEqualTo": function(node, obj) {
28099                 var filter = new OpenLayers.Filter.Comparison({
28100                     type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO
28101                 });
28102                 this.readChildNodes(node, filter);
28103                 obj.filters.push(filter);
28104             },
28105             "PropertyIsLike": function(node, obj) {
28106                 var filter = new OpenLayers.Filter.Comparison({
28107                     type: OpenLayers.Filter.Comparison.LIKE
28108                 });
28109                 this.readChildNodes(node, filter);
28110                 var wildCard = node.getAttribute("wildCard");
28111                 var singleChar = node.getAttribute("singleChar");
28112                 var esc = node.getAttribute("escape");
28113                 filter.value2regex(wildCard, singleChar, esc);
28114                 obj.filters.push(filter);
28115             }
28116         }, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]),
28117         "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"],
28118         "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"]        
28119     },
28120
28121     /**
28122      * Property: writers
28123      * As a compliment to the readers property, this structure contains public
28124      *     writing functions grouped by namespace alias and named like the
28125      *     node names they produce.
28126      */
28127     writers: {
28128         "ogc": OpenLayers.Util.applyDefaults({
28129             "PropertyIsEqualTo": function(filter) {
28130                 var node = this.createElementNSPlus("ogc:PropertyIsEqualTo");
28131                 // no ogc:expression handling for PropertyName for now
28132                 this.writeNode("PropertyName", filter, node);
28133                 // handle Literals or Functions for now
28134                 this.writeOgcExpression(filter.value, node);
28135                 return node;
28136             },
28137             "PropertyIsNotEqualTo": function(filter) {
28138                 var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo");
28139                 // no ogc:expression handling for PropertyName for now
28140                 this.writeNode("PropertyName", filter, node);
28141                 // handle Literals or Functions for now
28142                 this.writeOgcExpression(filter.value, node);
28143                 return node;
28144             },
28145             "PropertyIsLike": function(filter) {
28146                 var node = this.createElementNSPlus("ogc:PropertyIsLike", {
28147                     attributes: {
28148                         wildCard: "*", singleChar: ".", escape: "!"
28149                     }
28150                 });
28151                 // no ogc:expression handling for now
28152                 this.writeNode("PropertyName", filter, node);
28153                 // convert regex string to ogc string
28154                 this.writeNode("Literal", filter.regex2value(), node);
28155                 return node;
28156             },
28157             "BBOX": function(filter) {
28158                 var node = this.createElementNSPlus("ogc:BBOX");
28159                 // PropertyName is mandatory in 1.0.0, but e.g. GeoServer also
28160                 // accepts filters without it. When this is used with
28161                 // OpenLayers.Protocol.WFS, OpenLayers.Format.WFST will set a
28162                 // missing filter.property to the geometryName that is
28163                 // configured with the protocol, which defaults to "the_geom".
28164                 // So the only way to omit this mandatory property is to not
28165                 // set the property on the filter and to set the geometryName
28166                 // on the WFS protocol to null. The latter also happens when
28167                 // the protocol is configured without a geometryName and a
28168                 // featureNS.
28169                 filter.property && this.writeNode("PropertyName", filter, node);
28170                 var box = this.writeNode("gml:Box", filter.value, node);
28171                 if(filter.projection) {
28172                     box.setAttribute("srsName", filter.projection);
28173                 }
28174                 return node;
28175             }
28176         }, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]),
28177         "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"],
28178         "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"]
28179     },
28180
28181     /**
28182      * Method: writeSpatial
28183      *
28184      * Read a {<OpenLayers.Filter.Spatial>} filter and converts it into XML.
28185      *
28186      * Parameters:
28187      * filter - {<OpenLayers.Filter.Spatial>} The filter.
28188      * name - {String} Name of the generated XML element.
28189      *
28190      * Returns:
28191      * {DOMElement} The created XML element.
28192      */
28193     writeSpatial: function(filter, name) {
28194         var node = this.createElementNSPlus("ogc:"+name);
28195         this.writeNode("PropertyName", filter, node);
28196         if(filter.value instanceof OpenLayers.Filter.Function) {
28197             this.writeNode("Function", filter.value, node);
28198         } else {
28199         var child;
28200         if(filter.value instanceof OpenLayers.Geometry) {
28201             child = this.writeNode("feature:_geometry", filter.value).firstChild;
28202         } else {
28203             child = this.writeNode("gml:Box", filter.value);
28204         }
28205         if(filter.projection) {
28206             child.setAttribute("srsName", filter.projection);
28207         }
28208         node.appendChild(child);
28209         }
28210         return node;
28211     },
28212
28213
28214     CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0" 
28215
28216 });
28217 /* ======================================================================
28218     OpenLayers/Format/WFST/v1_0_0.js
28219    ====================================================================== */
28220
28221 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
28222  * full list of contributors). Published under the 2-clause BSD license.
28223  * See license.txt in the OpenLayers distribution or repository for the
28224  * full text of the license. */
28225
28226 /**
28227  * @requires OpenLayers/Format/WFST/v1.js
28228  * @requires OpenLayers/Format/Filter/v1_0_0.js
28229  */
28230
28231 /**
28232  * Class: OpenLayers.Format.WFST.v1_0_0
28233  * A format for creating WFS v1.0.0 transactions.  Create a new instance with the
28234  *     <OpenLayers.Format.WFST.v1_0_0> constructor.
28235  *
28236  * Inherits from:
28237  *  - <OpenLayers.Format.Filter.v1_0_0>
28238  *  - <OpenLayers.Format.WFST.v1>
28239  */
28240 OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class(
28241     OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, {
28242     
28243     /**
28244      * Property: version
28245      * {String} WFS version number.
28246      */
28247     version: "1.0.0",
28248
28249     /**
28250      * APIProperty: srsNameInQuery
28251      * {Boolean} If true the reference system is passed in Query requests
28252      *     via the "srsName" attribute to the "wfs:Query" element, this
28253      *     property defaults to false as it isn't WFS 1.0.0 compliant.
28254      */
28255     srsNameInQuery: false,
28256     
28257     /**
28258      * Property: schemaLocations
28259      * {Object} Properties are namespace aliases, values are schema locations.
28260      */
28261     schemaLocations: {
28262         "wfs": "http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd"
28263     },
28264
28265     /**
28266      * Constructor: OpenLayers.Format.WFST.v1_0_0
28267      * A class for parsing and generating WFS v1.0.0 transactions.
28268      *
28269      * Parameters:
28270      * options - {Object} Optional object whose properties will be set on the
28271      *     instance.
28272      *
28273      * Valid options properties:
28274      * featureType - {String} Local (without prefix) feature typeName (required).
28275      * featureNS - {String} Feature namespace (optional).
28276      * featurePrefix - {String} Feature namespace alias (optional - only used
28277      *     if featureNS is provided).  Default is 'feature'.
28278      * geometryName - {String} Name of geometry attribute.  Default is 'the_geom'.
28279      */
28280     initialize: function(options) {
28281         OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]);
28282         OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);
28283     },
28284     
28285     /**
28286      * Method: readNode
28287      * Shorthand for applying one of the named readers given the node
28288      *     namespace and local name.  Readers take two args (node, obj) and
28289      *     generally extend or modify the second.
28290      *
28291      * Parameters:
28292      * node - {DOMElement} The node to be read (required).
28293      * obj - {Object} The object to be modified (optional).
28294      * first - {Boolean} Should be set to true for the first node read. This
28295      *     is usually the readNode call in the read method. Without this being
28296      *     set, auto-configured properties will stick on subsequent reads.
28297      *
28298      * Returns:
28299      * {Object} The input object, modified (or a new one if none was provided).
28300      */
28301     readNode: function(node, obj, first) {
28302         // Not the superclass, only the mixin classes inherit from
28303         // Format.GML.v2. We need this because we don't want to get readNode
28304         // from the superclass's superclass, which is OpenLayers.Format.XML.
28305         return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, arguments);
28306     },
28307     
28308     /**
28309      * Property: readers
28310      * Contains public functions, grouped by namespace prefix, that will
28311      *     be applied when a namespaced node is found matching the function
28312      *     name.  The function will be applied in the scope of this parser
28313      *     with two arguments: the node being read and a context object passed
28314      *     from the parent.
28315      */
28316     readers: {
28317         "wfs": OpenLayers.Util.applyDefaults({
28318             "WFS_TransactionResponse": function(node, obj) {
28319                 obj.insertIds = [];
28320                 obj.success = false;
28321                 this.readChildNodes(node, obj);
28322             },
28323             "InsertResult": function(node, container) {
28324                 var obj = {fids: []};
28325                 this.readChildNodes(node, obj);
28326                 container.insertIds = container.insertIds.concat(obj.fids);
28327             },
28328             "TransactionResult": function(node, obj) {
28329                 this.readChildNodes(node, obj);
28330             },
28331             "Status": function(node, obj) {
28332                 this.readChildNodes(node, obj);
28333             },
28334             "SUCCESS": function(node, obj) {
28335                 obj.success = true;
28336             }
28337         }, OpenLayers.Format.WFST.v1.prototype.readers["wfs"]),
28338         "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"],
28339         "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"],
28340         "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.readers["ogc"]
28341     },
28342
28343     /**
28344      * Property: writers
28345      * As a compliment to the readers property, this structure contains public
28346      *     writing functions grouped by namespace alias and named like the
28347      *     node names they produce.
28348      */
28349     writers: {
28350         "wfs": OpenLayers.Util.applyDefaults({
28351             "Query": function(options) {
28352                 options = OpenLayers.Util.extend({
28353                     featureNS: this.featureNS,
28354                     featurePrefix: this.featurePrefix,
28355                     featureType: this.featureType,
28356                     srsName: this.srsName,
28357                     srsNameInQuery: this.srsNameInQuery
28358                 }, options);
28359                 var prefix = options.featurePrefix;
28360                 var node = this.createElementNSPlus("wfs:Query", {
28361                     attributes: {
28362                         typeName: (prefix ? prefix + ":" : "") +
28363                             options.featureType
28364                     }
28365                 });
28366                 if(options.srsNameInQuery && options.srsName) {
28367                     node.setAttribute("srsName", options.srsName);
28368                 }
28369                 if(options.featureNS) {
28370                     node.setAttribute("xmlns:" + prefix, options.featureNS);
28371                 }
28372                 if(options.propertyNames) {
28373                     for(var i=0,len = options.propertyNames.length; i<len; i++) {
28374                         this.writeNode(
28375                             "ogc:PropertyName", 
28376                             {property: options.propertyNames[i]},
28377                             node
28378                         );
28379                     }
28380                 }
28381                 if(options.filter) {
28382                     this.setFilterProperty(options.filter);
28383                     this.writeNode("ogc:Filter", options.filter, node);
28384                 }
28385                 return node;
28386             }
28387         }, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]),
28388         "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"],
28389         "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"],
28390         "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.writers["ogc"]
28391     },
28392    
28393     CLASS_NAME: "OpenLayers.Format.WFST.v1_0_0" 
28394 });
28395 /* ======================================================================
28396     OpenLayers/Renderer/Elements.js
28397    ====================================================================== */
28398
28399 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
28400  * full list of contributors). Published under the 2-clause BSD license.
28401  * See license.txt in the OpenLayers distribution or repository for the
28402  * full text of the license. */
28403
28404 /**
28405  * @requires OpenLayers/Renderer.js
28406  */
28407
28408 /**
28409  * Class: OpenLayers.ElementsIndexer
28410  * This class takes care of figuring out which order elements should be
28411  *     placed in the DOM based on given indexing methods. 
28412  */
28413 OpenLayers.ElementsIndexer = OpenLayers.Class({
28414    
28415     /**
28416      * Property: maxZIndex
28417      * {Integer} This is the largest-most z-index value for a node
28418      *     contained within the indexer.
28419      */
28420     maxZIndex: null,
28421     
28422     /**
28423      * Property: order
28424      * {Array<String>} This is an array of node id's stored in the
28425      *     order that they should show up on screen. Id's higher up in the
28426      *     array (higher array index) represent nodes with higher z-indeces.
28427      */
28428     order: null, 
28429     
28430     /**
28431      * Property: indices
28432      * {Object} This is a hash that maps node ids to their z-index value
28433      *     stored in the indexer. This is done to make finding a nodes z-index 
28434      *     value O(1).
28435      */
28436     indices: null,
28437     
28438     /**
28439      * Property: compare
28440      * {Function} This is the function used to determine placement of
28441      *     of a new node within the indexer. If null, this defaults to to
28442      *     the Z_ORDER_DRAWING_ORDER comparison method.
28443      */
28444     compare: null,
28445     
28446     /**
28447      * APIMethod: initialize
28448      * Create a new indexer with 
28449      * 
28450      * Parameters:
28451      * yOrdering - {Boolean} Whether to use y-ordering.
28452      */
28453     initialize: function(yOrdering) {
28454
28455         this.compare = yOrdering ? 
28456             OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER :
28457             OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;
28458
28459         this.clear();
28460     },
28461     
28462     /**
28463      * APIMethod: insert
28464      * Insert a new node into the indexer. In order to find the correct 
28465      *     positioning for the node to be inserted, this method uses a binary 
28466      *     search. This makes inserting O(log(n)). 
28467      * 
28468      * Parameters:
28469      * newNode - {DOMElement} The new node to be inserted.
28470      * 
28471      * Returns
28472      * {DOMElement} the node before which we should insert our newNode, or
28473      *     null if newNode can just be appended.
28474      */
28475     insert: function(newNode) {
28476         // If the node is known to the indexer, remove it so we can
28477         // recalculate where it should go.
28478         if (this.exists(newNode)) {
28479             this.remove(newNode);
28480         }
28481         
28482         var nodeId = newNode.id;
28483         
28484         this.determineZIndex(newNode);       
28485
28486         var leftIndex = -1;
28487         var rightIndex = this.order.length;
28488         var middle;
28489
28490         while (rightIndex - leftIndex > 1) {
28491             middle = parseInt((leftIndex + rightIndex) / 2);
28492             
28493             var placement = this.compare(this, newNode,
28494                 OpenLayers.Util.getElement(this.order[middle]));
28495             
28496             if (placement > 0) {
28497                 leftIndex = middle;
28498             } else {
28499                 rightIndex = middle;
28500             } 
28501         }
28502         
28503         this.order.splice(rightIndex, 0, nodeId);
28504         this.indices[nodeId] = this.getZIndex(newNode);
28505         
28506         // If the new node should be before another in the index
28507         // order, return the node before which we have to insert the new one;
28508         // else, return null to indicate that the new node can be appended.
28509         return this.getNextElement(rightIndex);
28510     },
28511     
28512     /**
28513      * APIMethod: remove
28514      * 
28515      * Parameters:
28516      * node - {DOMElement} The node to be removed.
28517      */
28518     remove: function(node) {
28519         var nodeId = node.id;
28520         var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);
28521         if (arrayIndex >= 0) {
28522             // Remove it from the order array, as well as deleting the node
28523             // from the indeces hash.
28524             this.order.splice(arrayIndex, 1);
28525             delete this.indices[nodeId];
28526             
28527             // Reset the maxium z-index based on the last item in the 
28528             // order array.
28529             if (this.order.length > 0) {
28530                 var lastId = this.order[this.order.length - 1];
28531                 this.maxZIndex = this.indices[lastId];
28532             } else {
28533                 this.maxZIndex = 0;
28534             }
28535         }
28536     },
28537     
28538     /**
28539      * APIMethod: clear
28540      */
28541     clear: function() {
28542         this.order = [];
28543         this.indices = {};
28544         this.maxZIndex = 0;
28545     },
28546     
28547     /**
28548      * APIMethod: exists
28549      *
28550      * Parameters:
28551      * node - {DOMElement} The node to test for existence.
28552      *
28553      * Returns:
28554      * {Boolean} Whether or not the node exists in the indexer?
28555      */
28556     exists: function(node) {
28557         return (this.indices[node.id] != null);
28558     },
28559
28560     /**
28561      * APIMethod: getZIndex
28562      * Get the z-index value for the current node from the node data itself.
28563      * 
28564      * Parameters:
28565      * node - {DOMElement} The node whose z-index to get.
28566      * 
28567      * Returns:
28568      * {Integer} The z-index value for the specified node (from the node 
28569      *     data itself).
28570      */
28571     getZIndex: function(node) {
28572         return node._style.graphicZIndex;  
28573     },
28574     
28575     /**
28576      * Method: determineZIndex
28577      * Determine the z-index for the current node if there isn't one, 
28578      *     and set the maximum value if we've found a new maximum.
28579      * 
28580      * Parameters:
28581      * node - {DOMElement} 
28582      */
28583     determineZIndex: function(node) {
28584         var zIndex = node._style.graphicZIndex;
28585         
28586         // Everything must have a zIndex. If none is specified,
28587         // this means the user *must* (hint: assumption) want this
28588         // node to succomb to drawing order. To enforce drawing order
28589         // over all indexing methods, we'll create a new z-index that's
28590         // greater than any currently in the indexer.
28591         if (zIndex == null) {
28592             zIndex = this.maxZIndex;
28593             node._style.graphicZIndex = zIndex; 
28594         } else if (zIndex > this.maxZIndex) {
28595             this.maxZIndex = zIndex;
28596         }
28597     },
28598
28599     /**
28600      * APIMethod: getNextElement
28601      * Get the next element in the order stack.
28602      * 
28603      * Parameters:
28604      * index - {Integer} The index of the current node in this.order.
28605      * 
28606      * Returns:
28607      * {DOMElement} the node following the index passed in, or
28608      *     null.
28609      */
28610     getNextElement: function(index) {
28611         var nextIndex = index + 1;
28612         if (nextIndex < this.order.length) {
28613             var nextElement = OpenLayers.Util.getElement(this.order[nextIndex]);
28614             if (nextElement == undefined) {
28615                 nextElement = this.getNextElement(nextIndex);
28616             }
28617             return nextElement;
28618         } else {
28619             return null;
28620         } 
28621     },
28622     
28623     CLASS_NAME: "OpenLayers.ElementsIndexer"
28624 });
28625
28626 /**
28627  * Namespace: OpenLayers.ElementsIndexer.IndexingMethods
28628  * These are the compare methods for figuring out where a new node should be 
28629  *     placed within the indexer. These methods are very similar to general 
28630  *     sorting methods in that they return -1, 0, and 1 to specify the 
28631  *     direction in which new nodes fall in the ordering.
28632  */
28633 OpenLayers.ElementsIndexer.IndexingMethods = {
28634     
28635     /**
28636      * Method: Z_ORDER
28637      * This compare method is used by other comparison methods.
28638      *     It can be used individually for ordering, but is not recommended,
28639      *     because it doesn't subscribe to drawing order.
28640      * 
28641      * Parameters:
28642      * indexer - {<OpenLayers.ElementsIndexer>}
28643      * newNode - {DOMElement}
28644      * nextNode - {DOMElement}
28645      * 
28646      * Returns:
28647      * {Integer}
28648      */
28649     Z_ORDER: function(indexer, newNode, nextNode) {
28650         var newZIndex = indexer.getZIndex(newNode);
28651
28652         var returnVal = 0;
28653         if (nextNode) {
28654             var nextZIndex = indexer.getZIndex(nextNode);
28655             returnVal = newZIndex - nextZIndex; 
28656         }
28657         
28658         return returnVal;
28659     },
28660
28661     /**
28662      * APIMethod: Z_ORDER_DRAWING_ORDER
28663      * This method orders nodes by their z-index, but does so in a way
28664      *     that, if there are other nodes with the same z-index, the newest 
28665      *     drawn will be the front most within that z-index. This is the 
28666      *     default indexing method.
28667      * 
28668      * Parameters:
28669      * indexer - {<OpenLayers.ElementsIndexer>}
28670      * newNode - {DOMElement}
28671      * nextNode - {DOMElement}
28672      * 
28673      * Returns:
28674      * {Integer}
28675      */
28676     Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) {
28677         var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
28678             indexer, 
28679             newNode, 
28680             nextNode
28681         );
28682         
28683         // Make Z_ORDER subscribe to drawing order by pushing it above
28684         // all of the other nodes with the same z-index.
28685         if (nextNode && returnVal == 0) {
28686             returnVal = 1;
28687         }
28688         
28689         return returnVal;
28690     },
28691
28692     /**
28693      * APIMethod: Z_ORDER_Y_ORDER
28694      * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it
28695      *     best describes which ordering methods have precedence (though, the 
28696      *     name would be too long). This method orders nodes by their z-index, 
28697      *     but does so in a way that, if there are other nodes with the same 
28698      *     z-index, the nodes with the lower y position will be "closer" than 
28699      *     those with a higher y position. If two nodes have the exact same y 
28700      *     position, however, then this method will revert to using drawing  
28701      *     order to decide placement.
28702      * 
28703      * Parameters:
28704      * indexer - {<OpenLayers.ElementsIndexer>}
28705      * newNode - {DOMElement}
28706      * nextNode - {DOMElement}
28707      * 
28708      * Returns:
28709      * {Integer}
28710      */
28711     Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) {
28712         var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
28713             indexer, 
28714             newNode, 
28715             nextNode
28716         );
28717         
28718         if (nextNode && returnVal === 0) {            
28719             var result = nextNode._boundsBottom - newNode._boundsBottom;
28720             returnVal = (result === 0) ? 1 : result;
28721         }
28722         
28723         return returnVal;       
28724     }
28725 };
28726
28727 /**
28728  * Class: OpenLayers.Renderer.Elements
28729  * This is another virtual class in that it should never be instantiated by 
28730  *  itself as a Renderer. It exists because there is *tons* of shared 
28731  *  functionality between different vector libraries which use nodes/elements
28732  *  as a base for rendering vectors. 
28733  * 
28734  * The highlevel bits of code that are implemented here are the adding and 
28735  *  removing of geometries, which is essentially the same for any 
28736  *  element-based renderer. The details of creating each node and drawing the
28737  *  paths are of course different, but the machinery is the same. 
28738  * 
28739  * Inherits:
28740  *  - <OpenLayers.Renderer>
28741  */
28742 OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
28743
28744     /**
28745      * Property: rendererRoot
28746      * {DOMElement}
28747      */
28748     rendererRoot: null,
28749     
28750     /**
28751      * Property: root
28752      * {DOMElement}
28753      */
28754     root: null,
28755     
28756     /**
28757      * Property: vectorRoot
28758      * {DOMElement}
28759      */
28760     vectorRoot: null,
28761
28762     /**
28763      * Property: textRoot
28764      * {DOMElement}
28765      */
28766     textRoot: null,
28767
28768     /**
28769      * Property: xmlns
28770      * {String}
28771      */    
28772     xmlns: null,
28773     
28774     /**
28775      * Property: xOffset
28776      * {Number} Offset to apply to the renderer viewport translation in x
28777      * direction. If the renderer extent's center is on the right of the
28778      * dateline (i.e. exceeds the world bounds), we shift the viewport to the
28779      * left by one world width. This avoids that features disappear from the
28780      * map viewport. Because our dateline handling logic in other places
28781      * ensures that extents crossing the dateline always have a center
28782      * exceeding the world bounds on the left, we need this offset to make sure
28783      * that the same is true for the renderer extent in pixel space as well.
28784      */
28785     xOffset: 0,
28786     
28787     /**
28788      * Property: rightOfDateLine
28789      * {Boolean} Keeps track of the location of the map extent relative to the
28790      * date line. The <setExtent> method compares this value (which is the one
28791      * from the previous <setExtent> call) with the current position of the map
28792      * extent relative to the date line and updates the xOffset when the extent
28793      * has moved from one side of the date line to the other.
28794      */
28795     
28796     /**
28797      * Property: Indexer
28798      * {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer 
28799      *     created upon initialization if the zIndexing or yOrdering options
28800      *     passed to this renderer's constructor are set to true.
28801      */
28802     indexer: null, 
28803     
28804     /**
28805      * Constant: BACKGROUND_ID_SUFFIX
28806      * {String}
28807      */
28808     BACKGROUND_ID_SUFFIX: "_background",
28809     
28810     /**
28811      * Constant: LABEL_ID_SUFFIX
28812      * {String}
28813      */
28814     LABEL_ID_SUFFIX: "_label",
28815     
28816     /**
28817      * Constant: LABEL_OUTLINE_SUFFIX
28818      * {String}
28819      */
28820     LABEL_OUTLINE_SUFFIX: "_outline",
28821
28822     /**
28823      * Constructor: OpenLayers.Renderer.Elements
28824      * 
28825      * Parameters:
28826      * containerID - {String}
28827      * options - {Object} options for this renderer. 
28828      *
28829      * Supported options are:
28830      *     yOrdering - {Boolean} Whether to use y-ordering
28831      *     zIndexing - {Boolean} Whether to use z-indexing. Will be ignored
28832      *         if yOrdering is set to true.
28833      */
28834     initialize: function(containerID, options) {
28835         OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
28836
28837         this.rendererRoot = this.createRenderRoot();
28838         this.root = this.createRoot("_root");
28839         this.vectorRoot = this.createRoot("_vroot");
28840         this.textRoot = this.createRoot("_troot");
28841         
28842         this.root.appendChild(this.vectorRoot);
28843         this.root.appendChild(this.textRoot);
28844         
28845         this.rendererRoot.appendChild(this.root);
28846         this.container.appendChild(this.rendererRoot);
28847         
28848         if(options && (options.zIndexing || options.yOrdering)) {
28849             this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering);
28850         }
28851     },
28852     
28853     /**
28854      * Method: destroy
28855      */
28856     destroy: function() {
28857
28858         this.clear(); 
28859
28860         this.rendererRoot = null;
28861         this.root = null;
28862         this.xmlns = null;
28863
28864         OpenLayers.Renderer.prototype.destroy.apply(this, arguments);
28865     },
28866     
28867     /**
28868      * Method: clear
28869      * Remove all the elements from the root
28870      */    
28871     clear: function() {
28872         var child;
28873         var root = this.vectorRoot;
28874         if (root) {
28875             while (child = root.firstChild) {
28876                 root.removeChild(child);
28877             }
28878         }
28879         root = this.textRoot;
28880         if (root) {
28881             while (child = root.firstChild) {
28882                 root.removeChild(child);
28883             }
28884         }
28885         if (this.indexer) {
28886             this.indexer.clear();
28887         }
28888     },
28889     
28890     /**
28891      * Method: setExtent
28892      * Set the visible part of the layer.
28893      *
28894      * Parameters:
28895      * extent - {<OpenLayers.Bounds>}
28896      * resolutionChanged - {Boolean}
28897      *
28898      * Returns:
28899      * {Boolean} true to notify the layer that the new extent does not exceed
28900      *     the coordinate range, and the features will not need to be redrawn.
28901      *     False otherwise.
28902      */
28903     setExtent: function(extent, resolutionChanged) {
28904         var coordSysUnchanged = OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);
28905         var resolution = this.getResolution();
28906         if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
28907             var rightOfDateLine,
28908                 ratio = extent.getWidth() / this.map.getExtent().getWidth(),
28909                 extent = extent.scale(1 / ratio),
28910                 world = this.map.getMaxExtent();
28911             if (world.right > extent.left && world.right < extent.right) {
28912                 rightOfDateLine = true;
28913             } else if (world.left > extent.left && world.left < extent.right) {
28914                 rightOfDateLine = false;
28915             }
28916             if (rightOfDateLine !== this.rightOfDateLine || resolutionChanged) {
28917                 coordSysUnchanged = false;
28918                 this.xOffset = rightOfDateLine === true ?
28919                     world.getWidth() / resolution : 0;
28920             }
28921             this.rightOfDateLine = rightOfDateLine;
28922         }
28923         return coordSysUnchanged;
28924     },
28925
28926     /** 
28927      * Method: getNodeType
28928      * This function is in charge of asking the specific renderer which type
28929      *     of node to create for the given geometry and style. All geometries
28930      *     in an Elements-based renderer consist of one node and some
28931      *     attributes. We have the nodeFactory() function which creates a node
28932      *     for us, but it takes a 'type' as input, and that is precisely what
28933      *     this function tells us.  
28934      *  
28935      * Parameters:
28936      * geometry - {<OpenLayers.Geometry>}
28937      * style - {Object}
28938      * 
28939      * Returns:
28940      * {String} The corresponding node type for the specified geometry
28941      */
28942     getNodeType: function(geometry, style) { },
28943
28944     /** 
28945      * Method: drawGeometry 
28946      * Draw the geometry, creating new nodes, setting paths, setting style,
28947      *     setting featureId on the node.  This method should only be called
28948      *     by the renderer itself.
28949      *
28950      * Parameters:
28951      * geometry - {<OpenLayers.Geometry>}
28952      * style - {Object}
28953      * featureId - {String}
28954      * 
28955      * Returns:
28956      * {Boolean} true if the geometry has been drawn completely; null if
28957      *     incomplete; false otherwise
28958      */
28959     drawGeometry: function(geometry, style, featureId) {
28960         var className = geometry.CLASS_NAME;
28961         var rendered = true;
28962         if ((className == "OpenLayers.Geometry.Collection") ||
28963             (className == "OpenLayers.Geometry.MultiPoint") ||
28964             (className == "OpenLayers.Geometry.MultiLineString") ||
28965             (className == "OpenLayers.Geometry.MultiPolygon")) {
28966             for (var i = 0, len=geometry.components.length; i<len; i++) {
28967                 rendered = this.drawGeometry(
28968                     geometry.components[i], style, featureId) && rendered;
28969             }
28970             return rendered;
28971         }
28972
28973         rendered = false;
28974         var removeBackground = false;
28975         if (style.display != "none") {
28976             if (style.backgroundGraphic) {
28977                 this.redrawBackgroundNode(geometry.id, geometry, style,
28978                     featureId);
28979             } else {
28980                 removeBackground = true;
28981             }
28982             rendered = this.redrawNode(geometry.id, geometry, style,
28983                 featureId);
28984         }
28985         if (rendered == false) {
28986             var node = document.getElementById(geometry.id);
28987             if (node) {
28988                 if (node._style.backgroundGraphic) {
28989                     removeBackground = true;
28990                 }
28991                 node.parentNode.removeChild(node);
28992             }
28993         }
28994         if (removeBackground) {
28995             var node = document.getElementById(
28996                 geometry.id + this.BACKGROUND_ID_SUFFIX);
28997             if (node) {
28998                 node.parentNode.removeChild(node);
28999             }
29000         }
29001         return rendered;
29002     },
29003     
29004     /**
29005      * Method: redrawNode
29006      * 
29007      * Parameters:
29008      * id - {String}
29009      * geometry - {<OpenLayers.Geometry>}
29010      * style - {Object}
29011      * featureId - {String}
29012      * 
29013      * Returns:
29014      * {Boolean} true if the complete geometry could be drawn, null if parts of
29015      *     the geometry could not be drawn, false otherwise
29016      */
29017     redrawNode: function(id, geometry, style, featureId) {
29018         style = this.applyDefaultSymbolizer(style);
29019         // Get the node if it's already on the map.
29020         var node = this.nodeFactory(id, this.getNodeType(geometry, style));
29021         
29022         // Set the data for the node, then draw it.
29023         node._featureId = featureId;
29024         node._boundsBottom = geometry.getBounds().bottom;
29025         node._geometryClass = geometry.CLASS_NAME;
29026         node._style = style;
29027
29028         var drawResult = this.drawGeometryNode(node, geometry, style);
29029         if(drawResult === false) {
29030             return false;
29031         }
29032          
29033         node = drawResult.node;
29034         
29035         // Insert the node into the indexer so it can show us where to
29036         // place it. Note that this operation is O(log(n)). If there's a
29037         // performance problem (when dragging, for instance) this is
29038         // likely where it would be.
29039         if (this.indexer) {
29040             var insert = this.indexer.insert(node);
29041             if (insert) {
29042                 this.vectorRoot.insertBefore(node, insert);
29043             } else {
29044                 this.vectorRoot.appendChild(node);
29045             }
29046         } else {
29047             // if there's no indexer, simply append the node to root,
29048             // but only if the node is a new one
29049             if (node.parentNode !== this.vectorRoot){ 
29050                 this.vectorRoot.appendChild(node);
29051             }
29052         }
29053         
29054         this.postDraw(node);
29055         
29056         return drawResult.complete;
29057     },
29058     
29059     /**
29060      * Method: redrawBackgroundNode
29061      * Redraws the node using special 'background' style properties. Basically
29062      *     just calls redrawNode(), but instead of directly using the 
29063      *     'externalGraphic', 'graphicXOffset', 'graphicYOffset', and 
29064      *     'graphicZIndex' properties directly from the specified 'style' 
29065      *     parameter, we create a new style object and set those properties 
29066      *     from the corresponding 'background'-prefixed properties from 
29067      *     specified 'style' parameter.
29068      * 
29069      * Parameters:
29070      * id - {String}
29071      * geometry - {<OpenLayers.Geometry>}
29072      * style - {Object}
29073      * featureId - {String}
29074      * 
29075      * Returns:
29076      * {Boolean} true if the complete geometry could be drawn, null if parts of
29077      *     the geometry could not be drawn, false otherwise
29078      */
29079     redrawBackgroundNode: function(id, geometry, style, featureId) {
29080         var backgroundStyle = OpenLayers.Util.extend({}, style);
29081         
29082         // Set regular style attributes to apply to the background styles.
29083         backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;
29084         backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;
29085         backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;
29086         backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;
29087         backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth;
29088         backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight;
29089         
29090         // Erase background styles.
29091         backgroundStyle.backgroundGraphic = null;
29092         backgroundStyle.backgroundXOffset = null;
29093         backgroundStyle.backgroundYOffset = null;
29094         backgroundStyle.backgroundGraphicZIndex = null;
29095         
29096         return this.redrawNode(
29097             id + this.BACKGROUND_ID_SUFFIX, 
29098             geometry, 
29099             backgroundStyle, 
29100             null
29101         );
29102     },
29103
29104     /**
29105      * Method: drawGeometryNode
29106      * Given a node, draw a geometry on the specified layer.
29107      *     node and geometry are required arguments, style is optional.
29108      *     This method is only called by the render itself.
29109      *
29110      * Parameters:
29111      * node - {DOMElement}
29112      * geometry - {<OpenLayers.Geometry>}
29113      * style - {Object}
29114      * 
29115      * Returns:
29116      * {Object} a hash with properties "node" (the drawn node) and "complete"
29117      *     (null if parts of the geometry could not be drawn, false if nothing
29118      *     could be drawn)
29119      */
29120     drawGeometryNode: function(node, geometry, style) {
29121         style = style || node._style;
29122
29123         var options = {
29124             'isFilled': style.fill === undefined ?
29125                 true :
29126                 style.fill,
29127             'isStroked': style.stroke === undefined ?
29128                 !!style.strokeWidth :
29129                 style.stroke
29130         };
29131         var drawn;
29132         switch (geometry.CLASS_NAME) {
29133             case "OpenLayers.Geometry.Point":
29134                 if(style.graphic === false) {
29135                     options.isFilled = false;
29136                     options.isStroked = false;
29137                 }
29138                 drawn = this.drawPoint(node, geometry);
29139                 break;
29140             case "OpenLayers.Geometry.LineString":
29141                 options.isFilled = false;
29142                 drawn = this.drawLineString(node, geometry);
29143                 break;
29144             case "OpenLayers.Geometry.LinearRing":
29145                 drawn = this.drawLinearRing(node, geometry);
29146                 break;
29147             case "OpenLayers.Geometry.Polygon":
29148                 drawn = this.drawPolygon(node, geometry);
29149                 break;
29150             case "OpenLayers.Geometry.Rectangle":
29151                 drawn = this.drawRectangle(node, geometry);
29152                 break;
29153             default:
29154                 break;
29155         }
29156
29157         node._options = options; 
29158
29159         //set style
29160         //TBD simplify this
29161         if (drawn != false) {
29162             return {
29163                 node: this.setStyle(node, style, options, geometry),
29164                 complete: drawn
29165             };
29166         } else {
29167             return false;
29168         }
29169     },
29170     
29171     /**
29172      * Method: postDraw
29173      * Things that have do be done after the geometry node is appended
29174      *     to its parent node. To be overridden by subclasses.
29175      * 
29176      * Parameters:
29177      * node - {DOMElement}
29178      */
29179     postDraw: function(node) {},
29180     
29181     /**
29182      * Method: drawPoint
29183      * Virtual function for drawing Point Geometry. 
29184      *     Should be implemented by subclasses.
29185      *     This method is only called by the renderer itself.
29186      * 
29187      * Parameters: 
29188      * node - {DOMElement}
29189      * geometry - {<OpenLayers.Geometry>}
29190      * 
29191      * Returns:
29192      * {DOMElement} or false if the renderer could not draw the point
29193      */ 
29194     drawPoint: function(node, geometry) {},
29195
29196     /**
29197      * Method: drawLineString
29198      * Virtual function for drawing LineString Geometry. 
29199      *     Should be implemented by subclasses.
29200      *     This method is only called by the renderer itself.
29201      * 
29202      * Parameters: 
29203      * node - {DOMElement}
29204      * geometry - {<OpenLayers.Geometry>}
29205      * 
29206      * Returns:
29207      * {DOMElement} or null if the renderer could not draw all components of
29208      *     the linestring, or false if nothing could be drawn
29209      */ 
29210     drawLineString: function(node, geometry) {},
29211
29212     /**
29213      * Method: drawLinearRing
29214      * Virtual function for drawing LinearRing Geometry. 
29215      *     Should be implemented by subclasses.
29216      *     This method is only called by the renderer itself.
29217      * 
29218      * Parameters: 
29219      * node - {DOMElement}
29220      * geometry - {<OpenLayers.Geometry>}
29221      * 
29222      * Returns:
29223      * {DOMElement} or null if the renderer could not draw all components
29224      *     of the linear ring, or false if nothing could be drawn
29225      */ 
29226     drawLinearRing: function(node, geometry) {},
29227
29228     /**
29229      * Method: drawPolygon
29230      * Virtual function for drawing Polygon Geometry. 
29231      *    Should be implemented by subclasses.
29232      *    This method is only called by the renderer itself.
29233      * 
29234      * Parameters: 
29235      * node - {DOMElement}
29236      * geometry - {<OpenLayers.Geometry>}
29237      * 
29238      * Returns:
29239      * {DOMElement} or null if the renderer could not draw all components
29240      *     of the polygon, or false if nothing could be drawn
29241      */ 
29242     drawPolygon: function(node, geometry) {},
29243
29244     /**
29245      * Method: drawRectangle
29246      * Virtual function for drawing Rectangle Geometry. 
29247      *     Should be implemented by subclasses.
29248      *     This method is only called by the renderer itself.
29249      * 
29250      * Parameters: 
29251      * node - {DOMElement}
29252      * geometry - {<OpenLayers.Geometry>}
29253      * 
29254      * Returns:
29255      * {DOMElement} or false if the renderer could not draw the rectangle
29256      */ 
29257     drawRectangle: function(node, geometry) {},
29258
29259     /**
29260      * Method: drawCircle
29261      * Virtual function for drawing Circle Geometry. 
29262      *     Should be implemented by subclasses.
29263      *     This method is only called by the renderer itself.
29264      * 
29265      * Parameters: 
29266      * node - {DOMElement}
29267      * geometry - {<OpenLayers.Geometry>}
29268      * 
29269      * Returns:
29270      * {DOMElement} or false if the renderer could not draw the circle
29271      */ 
29272     drawCircle: function(node, geometry) {},
29273
29274     /**
29275      * Method: removeText
29276      * Removes a label
29277      * 
29278      * Parameters:
29279      * featureId - {String}
29280      */
29281     removeText: function(featureId) {
29282         var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);
29283         if (label) {
29284             this.textRoot.removeChild(label);
29285         }
29286         var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX);
29287         if (outline) {
29288             this.textRoot.removeChild(outline);
29289         }
29290     },
29291
29292     /**
29293      * Method: getFeatureIdFromEvent
29294      * 
29295      * Parameters:
29296      * evt - {Object} An <OpenLayers.Event> object
29297      *
29298      * Returns:
29299      * {String} A feature id or undefined.
29300      */
29301     getFeatureIdFromEvent: function(evt) {
29302         var target = evt.target;
29303         var useElement = target && target.correspondingUseElement;
29304         var node = useElement ? useElement : (target || evt.srcElement);
29305         return node._featureId;
29306     },
29307
29308     /** 
29309      * Method: eraseGeometry
29310      * Erase a geometry from the renderer. In the case of a multi-geometry, 
29311      *     we cycle through and recurse on ourselves. Otherwise, we look for a 
29312      *     node with the geometry.id, destroy its geometry, and remove it from
29313      *     the DOM.
29314      * 
29315      * Parameters:
29316      * geometry - {<OpenLayers.Geometry>}
29317      * featureId - {String}
29318      */
29319     eraseGeometry: function(geometry, featureId) {
29320         if ((geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") ||
29321             (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString") ||
29322             (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") ||
29323             (geometry.CLASS_NAME == "OpenLayers.Geometry.Collection")) {
29324             for (var i=0, len=geometry.components.length; i<len; i++) {
29325                 this.eraseGeometry(geometry.components[i], featureId);
29326             }
29327         } else {    
29328             var element = OpenLayers.Util.getElement(geometry.id);
29329             if (element && element.parentNode) {
29330                 if (element.geometry) {
29331                     element.geometry.destroy();
29332                     element.geometry = null;
29333                 }
29334                 element.parentNode.removeChild(element);
29335
29336                 if (this.indexer) {
29337                     this.indexer.remove(element);
29338                 }
29339                 
29340                 if (element._style.backgroundGraphic) {
29341                     var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;
29342                     var bElem = OpenLayers.Util.getElement(backgroundId);
29343                     if (bElem && bElem.parentNode) {
29344                         // No need to destroy the geometry since the element and the background
29345                         // node share the same geometry.
29346                         bElem.parentNode.removeChild(bElem);
29347                     }
29348                 }
29349             }
29350         }
29351     },
29352
29353     /** 
29354      * Method: nodeFactory
29355      * Create new node of the specified type, with the (optional) specified id.
29356      * 
29357      * If node already exists with same ID and a different type, we remove it
29358      *     and then call ourselves again to recreate it.
29359      * 
29360      * Parameters:
29361      * id - {String}
29362      * type - {String} type Kind of node to draw.
29363      * 
29364      * Returns:
29365      * {DOMElement} A new node of the given type and id.
29366      */
29367     nodeFactory: function(id, type) {
29368         var node = OpenLayers.Util.getElement(id);
29369         if (node) {
29370             if (!this.nodeTypeCompare(node, type)) {
29371                 node.parentNode.removeChild(node);
29372                 node = this.nodeFactory(id, type);
29373             }
29374         } else {
29375             node = this.createNode(type, id);
29376         }
29377         return node;
29378     },
29379     
29380     /** 
29381      * Method: nodeTypeCompare
29382      * 
29383      * Parameters:
29384      * node - {DOMElement}
29385      * type - {String} Kind of node
29386      * 
29387      * Returns:
29388      * {Boolean} Whether or not the specified node is of the specified type
29389      *     This function must be overridden by subclasses.
29390      */
29391     nodeTypeCompare: function(node, type) {},
29392     
29393     /** 
29394      * Method: createNode
29395      * 
29396      * Parameters:
29397      * type - {String} Kind of node to draw.
29398      * id - {String} Id for node.
29399      * 
29400      * Returns:
29401      * {DOMElement} A new node of the given type and id.
29402      *     This function must be overridden by subclasses.
29403      */
29404     createNode: function(type, id) {},
29405
29406     /**
29407      * Method: moveRoot
29408      * moves this renderer's root to a different renderer.
29409      * 
29410      * Parameters:
29411      * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
29412      */
29413     moveRoot: function(renderer) {
29414         var root = this.root;
29415         if(renderer.root.parentNode == this.rendererRoot) {
29416             root = renderer.root;
29417         }
29418         root.parentNode.removeChild(root);
29419         renderer.rendererRoot.appendChild(root);
29420     },
29421     
29422     /**
29423      * Method: getRenderLayerId
29424      * Gets the layer that this renderer's output appears on. If moveRoot was
29425      * used, this will be different from the id of the layer containing the
29426      * features rendered by this renderer.
29427      * 
29428      * Returns:
29429      * {String} the id of the output layer.
29430      */
29431     getRenderLayerId: function() {
29432         return this.root.parentNode.parentNode.id;
29433     },
29434     
29435     /**
29436      * Method: isComplexSymbol
29437      * Determines if a symbol cannot be rendered using drawCircle
29438      * 
29439      * Parameters:
29440      * graphicName - {String}
29441      * 
29442      * Returns
29443      * {Boolean} true if the symbol is complex, false if not
29444      */
29445     isComplexSymbol: function(graphicName) {
29446         return (graphicName != "circle") && !!graphicName;
29447     },
29448
29449     CLASS_NAME: "OpenLayers.Renderer.Elements"
29450 });
29451
29452 /* ======================================================================
29453     OpenLayers/Control/Zoom.js
29454    ====================================================================== */
29455
29456 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
29457  * full list of contributors). Published under the 2-clause BSD license.
29458  * See license.txt in the OpenLayers distribution or repository for the
29459  * full text of the license. */
29460
29461 /**
29462  * @requires OpenLayers/Control.js
29463  * @requires OpenLayers/Events/buttonclick.js
29464  */
29465
29466 /**
29467  * Class: OpenLayers.Control.Zoom
29468  * The Zoom control is a pair of +/- links for zooming in and out.
29469  *
29470  * Inherits from:
29471  *  - <OpenLayers.Control>
29472  */
29473 OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, {
29474     
29475     /**
29476      * APIProperty: zoomInText
29477      * {String}
29478      * Text for zoom-in link.  Default is "+".
29479      */
29480     zoomInText: "+",
29481
29482     /**
29483      * APIProperty: zoomInId
29484      * {String}
29485      * Instead of having the control create a zoom in link, you can provide 
29486      *     the identifier for an anchor element already added to the document.
29487      *     By default, an element with id "olZoomInLink" will be searched for
29488      *     and used if it exists.
29489      */
29490     zoomInId: "olZoomInLink",
29491
29492     /**
29493      * APIProperty: zoomOutText
29494      * {String}
29495      * Text for zoom-out link.  Default is "\u2212".
29496      */
29497     zoomOutText: "\u2212",
29498
29499     /**
29500      * APIProperty: zoomOutId
29501      * {String}
29502      * Instead of having the control create a zoom out link, you can provide 
29503      *     the identifier for an anchor element already added to the document.
29504      *     By default, an element with id "olZoomOutLink" will be searched for
29505      *     and used if it exists.
29506      */
29507     zoomOutId: "olZoomOutLink",
29508
29509     /**
29510      * Method: draw
29511      *
29512      * Returns:
29513      * {DOMElement} A reference to the DOMElement containing the zoom links.
29514      */
29515     draw: function() {
29516         var div = OpenLayers.Control.prototype.draw.apply(this),
29517             links = this.getOrCreateLinks(div),
29518             zoomIn = links.zoomIn,
29519             zoomOut = links.zoomOut,
29520             eventsInstance = this.map.events;
29521         
29522         if (zoomOut.parentNode !== div) {
29523             eventsInstance = this.events;
29524             eventsInstance.attachToElement(zoomOut.parentNode);
29525         }
29526         eventsInstance.register("buttonclick", this, this.onZoomClick);
29527         
29528         this.zoomInLink = zoomIn;
29529         this.zoomOutLink = zoomOut;
29530         return div;
29531     },
29532     
29533     /**
29534      * Method: getOrCreateLinks
29535      * 
29536      * Parameters:
29537      * el - {DOMElement}
29538      *
29539      * Return: 
29540      * {Object} Object with zoomIn and zoomOut properties referencing links.
29541      */
29542     getOrCreateLinks: function(el) {
29543         var zoomIn = document.getElementById(this.zoomInId),
29544             zoomOut = document.getElementById(this.zoomOutId);
29545         if (!zoomIn) {
29546             zoomIn = document.createElement("a");
29547             zoomIn.href = "#zoomIn";
29548             zoomIn.appendChild(document.createTextNode(this.zoomInText));
29549             zoomIn.className = "olControlZoomIn";
29550             el.appendChild(zoomIn);
29551         }
29552         OpenLayers.Element.addClass(zoomIn, "olButton");
29553         if (!zoomOut) {
29554             zoomOut = document.createElement("a");
29555             zoomOut.href = "#zoomOut";
29556             zoomOut.appendChild(document.createTextNode(this.zoomOutText));
29557             zoomOut.className = "olControlZoomOut";
29558             el.appendChild(zoomOut);
29559         }
29560         OpenLayers.Element.addClass(zoomOut, "olButton");
29561         return {
29562             zoomIn: zoomIn, zoomOut: zoomOut
29563         };
29564     },
29565     
29566     /**
29567      * Method: onZoomClick
29568      * Called when zoomin/out link is clicked.
29569      */
29570     onZoomClick: function(evt) {
29571         var button = evt.buttonElement;
29572         if (button === this.zoomInLink) {
29573             this.map.zoomIn();
29574         } else if (button === this.zoomOutLink) {
29575             this.map.zoomOut();
29576         }
29577     },
29578
29579     /** 
29580      * Method: destroy
29581      * Clean up.
29582      */
29583     destroy: function() {
29584         if (this.map) {
29585             this.map.events.unregister("buttonclick", this, this.onZoomClick);
29586         }
29587         delete this.zoomInLink;
29588         delete this.zoomOutLink;
29589         OpenLayers.Control.prototype.destroy.apply(this);
29590     },
29591
29592     CLASS_NAME: "OpenLayers.Control.Zoom"
29593 });
29594 /* ======================================================================
29595     OpenLayers/Protocol.js
29596    ====================================================================== */
29597
29598 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
29599  * full list of contributors). Published under the 2-clause BSD license.
29600  * See license.txt in the OpenLayers distribution or repository for the
29601  * full text of the license. */
29602
29603 /**
29604  * @requires OpenLayers/BaseTypes/Class.js
29605  */
29606
29607 /**
29608  * Class: OpenLayers.Protocol
29609  * Abstract vector layer protocol class.  Not to be instantiated directly.  Use
29610  *     one of the protocol subclasses instead.
29611  */
29612 OpenLayers.Protocol = OpenLayers.Class({
29613     
29614     /**
29615      * Property: format
29616      * {<OpenLayers.Format>} The format used by this protocol.
29617      */
29618     format: null,
29619     
29620     /**
29621      * Property: options
29622      * {Object} Any options sent to the constructor.
29623      */
29624     options: null,
29625
29626     /**
29627      * Property: autoDestroy
29628      * {Boolean} The creator of the protocol can set autoDestroy to false
29629      *      to fully control when the protocol is destroyed. Defaults to
29630      *      true.
29631      */
29632     autoDestroy: true,
29633    
29634     /**
29635      * Property: defaultFilter
29636      * {<OpenLayers.Filter>} Optional default filter to read requests
29637      */
29638     defaultFilter: null,
29639     
29640     /**
29641      * Constructor: OpenLayers.Protocol
29642      * Abstract class for vector protocols.  Create instances of a subclass.
29643      *
29644      * Parameters:
29645      * options - {Object} Optional object whose properties will be set on the
29646      *     instance.
29647      */
29648     initialize: function(options) {
29649         options = options || {};
29650         OpenLayers.Util.extend(this, options);
29651         this.options = options;
29652     },
29653
29654     /**
29655      * Method: mergeWithDefaultFilter
29656      * Merge filter passed to the read method with the default one
29657      *
29658      * Parameters:
29659      * filter - {<OpenLayers.Filter>}
29660      */
29661     mergeWithDefaultFilter: function(filter) {
29662         var merged;
29663         if (filter && this.defaultFilter) {
29664             merged = new OpenLayers.Filter.Logical({
29665                 type: OpenLayers.Filter.Logical.AND,
29666                 filters: [this.defaultFilter, filter]
29667             });
29668         } else {
29669             merged = filter || this.defaultFilter || undefined;
29670         }
29671         return merged;
29672     },
29673
29674     /**
29675      * APIMethod: destroy
29676      * Clean up the protocol.
29677      */
29678     destroy: function() {
29679         this.options = null;
29680         this.format = null;
29681     },
29682     
29683     /**
29684      * APIMethod: read
29685      * Construct a request for reading new features.
29686      *
29687      * Parameters:
29688      * options - {Object} Optional object for configuring the request.
29689      *
29690      * Returns:
29691      * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
29692      * object, the same object will be passed to the callback function passed
29693      * if one exists in the options object.
29694      */
29695     read: function(options) {
29696         options = options || {};
29697         options.filter = this.mergeWithDefaultFilter(options.filter);
29698     },
29699     
29700     
29701     /**
29702      * APIMethod: create
29703      * Construct a request for writing newly created features.
29704      *
29705      * Parameters:
29706      * features - {Array({<OpenLayers.Feature.Vector>})} or
29707      *            {<OpenLayers.Feature.Vector>}
29708      * options - {Object} Optional object for configuring the request.
29709      *
29710      * Returns:
29711      * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
29712      * object, the same object will be passed to the callback function passed
29713      * if one exists in the options object.
29714      */
29715     create: function() {
29716     },
29717     
29718     /**
29719      * APIMethod: update
29720      * Construct a request updating modified features.
29721      *
29722      * Parameters:
29723      * features - {Array({<OpenLayers.Feature.Vector>})} or
29724      *            {<OpenLayers.Feature.Vector>}
29725      * options - {Object} Optional object for configuring the request.
29726      *
29727      * Returns:
29728      * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
29729      * object, the same object will be passed to the callback function passed
29730      * if one exists in the options object.
29731      */
29732     update: function() {
29733     },
29734     
29735     /**
29736      * APIMethod: delete
29737      * Construct a request deleting a removed feature.
29738      *
29739      * Parameters:
29740      * feature - {<OpenLayers.Feature.Vector>}
29741      * options - {Object} Optional object for configuring the request.
29742      *
29743      * Returns:
29744      * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
29745      * object, the same object will be passed to the callback function passed
29746      * if one exists in the options object.
29747      */
29748     "delete": function() {
29749     },
29750
29751     /**
29752      * APIMethod: commit
29753      * Go over the features and for each take action
29754      * based on the feature state. Possible actions are create,
29755      * update and delete.
29756      *
29757      * Parameters:
29758      * features - {Array({<OpenLayers.Feature.Vector>})}
29759      * options - {Object} Object whose possible keys are "create", "update",
29760      *      "delete", "callback" and "scope", the values referenced by the
29761      *      first three are objects as passed to the "create", "update", and
29762      *      "delete" methods, the value referenced by the "callback" key is
29763      *      a function which is called when the commit operation is complete
29764      *      using the scope referenced by the "scope" key.
29765      *
29766      * Returns:
29767      * {Array({<OpenLayers.Protocol.Response>})} An array of
29768      * <OpenLayers.Protocol.Response> objects.
29769      */
29770     commit: function() {
29771     },
29772
29773     /**
29774      * Method: abort
29775      * Abort an ongoing request.
29776      *
29777      * Parameters:
29778      * response - {<OpenLayers.Protocol.Response>}
29779      */
29780     abort: function(response) {
29781     },
29782    
29783     /**
29784      * Method: createCallback
29785      * Returns a function that applies the given public method with resp and
29786      *     options arguments.
29787      *
29788      * Parameters:
29789      * method - {Function} The method to be applied by the callback.
29790      * response - {<OpenLayers.Protocol.Response>} The protocol response object.
29791      * options - {Object} Options sent to the protocol method
29792      */
29793     createCallback: function(method, response, options) {
29794         return OpenLayers.Function.bind(function() {
29795             method.apply(this, [response, options]);
29796         }, this);
29797     },
29798    
29799     CLASS_NAME: "OpenLayers.Protocol" 
29800 });
29801
29802 /**
29803  * Class: OpenLayers.Protocol.Response
29804  * Protocols return Response objects to their users.
29805  */
29806 OpenLayers.Protocol.Response = OpenLayers.Class({
29807     /**
29808      * Property: code
29809      * {Number} - OpenLayers.Protocol.Response.SUCCESS or
29810      *            OpenLayers.Protocol.Response.FAILURE
29811      */
29812     code: null,
29813
29814     /**
29815      * Property: requestType
29816      * {String} The type of request this response corresponds to. Either
29817      *      "create", "read", "update" or "delete".
29818      */
29819     requestType: null,
29820
29821     /**
29822      * Property: last
29823      * {Boolean} - true if this is the last response expected in a commit,
29824      * false otherwise, defaults to true.
29825      */
29826     last: true,
29827
29828     /**
29829      * Property: features
29830      * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
29831      * The features returned in the response by the server. Depending on the 
29832      * protocol's read payload, either features or data will be populated.
29833      */
29834     features: null,
29835
29836     /**
29837      * Property: data
29838      * {Object}
29839      * The data returned in the response by the server. Depending on the 
29840      * protocol's read payload, either features or data will be populated.
29841      */
29842     data: null,
29843
29844     /**
29845      * Property: reqFeatures
29846      * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
29847      * The features provided by the user and placed in the request by the
29848      *      protocol.
29849      */
29850     reqFeatures: null,
29851
29852     /**
29853      * Property: priv
29854      */
29855     priv: null,
29856
29857     /**
29858      * Property: error
29859      * {Object} The error object in case a service exception was encountered.
29860      */
29861     error: null,
29862
29863     /**
29864      * Constructor: OpenLayers.Protocol.Response
29865      *
29866      * Parameters:
29867      * options - {Object} Optional object whose properties will be set on the
29868      *     instance.
29869      */
29870     initialize: function(options) {
29871         OpenLayers.Util.extend(this, options);
29872     },
29873
29874     /**
29875      * Method: success
29876      *
29877      * Returns:
29878      * {Boolean} - true on success, false otherwise
29879      */
29880     success: function() {
29881         return this.code > 0;
29882     },
29883
29884     CLASS_NAME: "OpenLayers.Protocol.Response"
29885 });
29886
29887 OpenLayers.Protocol.Response.SUCCESS = 1;
29888 OpenLayers.Protocol.Response.FAILURE = 0;
29889 /* ======================================================================
29890     OpenLayers/Protocol/WFS.js
29891    ====================================================================== */
29892
29893 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
29894  * full list of contributors). Published under the 2-clause BSD license.
29895  * See license.txt in the OpenLayers distribution or repository for the
29896  * full text of the license. */
29897
29898 /**
29899  * @requires OpenLayers/Protocol.js
29900  */
29901
29902 /**
29903  * Class: OpenLayers.Protocol.WFS
29904  * Used to create a versioned WFS protocol.  Default version is 1.0.0.
29905  *
29906  * Returns:
29907  * {<OpenLayers.Protocol>} A WFS protocol of the given version.
29908  *
29909  * Example:
29910  * (code)
29911  *     var protocol = new OpenLayers.Protocol.WFS({
29912  *         version: "1.1.0",
29913  *         url:  "http://demo.opengeo.org/geoserver/wfs",
29914  *         featureType: "tasmania_roads",
29915  *         featureNS: "http://www.openplans.org/topp",
29916  *         geometryName: "the_geom"
29917  *     });
29918  * (end)
29919  *
29920  * See the protocols for specific WFS versions for more detail.
29921  */
29922 OpenLayers.Protocol.WFS = function(options) {
29923     options = OpenLayers.Util.applyDefaults(
29924         options, OpenLayers.Protocol.WFS.DEFAULTS
29925     );
29926     var cls = OpenLayers.Protocol.WFS["v"+options.version.replace(/\./g, "_")];
29927     if(!cls) {
29928         throw "Unsupported WFS version: " + options.version;
29929     }
29930     return new cls(options);
29931 };
29932
29933 /**
29934  * Function: fromWMSLayer
29935  * Convenience function to create a WFS protocol from a WMS layer.  This makes
29936  *     the assumption that a WFS requests can be issued at the same URL as
29937  *     WMS requests and that a WFS featureType exists with the same name as the
29938  *     WMS layer.
29939  *     
29940  * This function is designed to auto-configure <url>, <featureType>,
29941  *     <featurePrefix> and <srsName> for WFS <version> 1.1.0. Note that
29942  *     srsName matching with the WMS layer will not work with WFS 1.0.0.
29943  * 
29944  * Parameters:
29945  * layer - {<OpenLayers.Layer.WMS>} WMS layer that has a matching WFS
29946  *     FeatureType at the same server url with the same typename.
29947  * options - {Object} Default properties to be set on the protocol.
29948  *
29949  * Returns:
29950  * {<OpenLayers.Protocol.WFS>}
29951  */
29952 OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) {
29953     var typeName, featurePrefix;
29954     var param = layer.params["LAYERS"];
29955     var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(":");
29956     if(parts.length > 1) {
29957         featurePrefix = parts[0];
29958     }
29959     typeName = parts.pop();
29960     var protocolOptions = {
29961         url: layer.url,
29962         featureType: typeName,
29963         featurePrefix: featurePrefix,
29964         srsName: layer.projection && layer.projection.getCode() ||
29965                  layer.map && layer.map.getProjectionObject().getCode(),
29966         version: "1.1.0"
29967     };
29968     return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(
29969         options, protocolOptions
29970     ));
29971 };
29972
29973 /**
29974  * Constant: OpenLayers.Protocol.WFS.DEFAULTS
29975  */
29976 OpenLayers.Protocol.WFS.DEFAULTS = {
29977     "version": "1.0.0"
29978 };
29979 /* ======================================================================
29980     OpenLayers/Layer/Markers.js
29981    ====================================================================== */
29982
29983 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
29984  * full list of contributors). Published under the 2-clause BSD license.
29985  * See license.txt in the OpenLayers distribution or repository for the
29986  * full text of the license. */
29987
29988
29989 /**
29990  * @requires OpenLayers/Layer.js
29991  */
29992
29993 /**
29994  * Class: OpenLayers.Layer.Markers
29995  * 
29996  * Inherits from:
29997  *  - <OpenLayers.Layer> 
29998  */
29999 OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, {
30000     
30001     /** 
30002      * APIProperty: isBaseLayer 
30003      * {Boolean} Markers layer is never a base layer.  
30004      */
30005     isBaseLayer: false,
30006     
30007     /** 
30008      * APIProperty: markers 
30009      * {Array(<OpenLayers.Marker>)} internal marker list 
30010      */
30011     markers: null,
30012
30013
30014     /** 
30015      * Property: drawn 
30016      * {Boolean} internal state of drawing. This is a workaround for the fact
30017      * that the map does not call moveTo with a zoomChanged when the map is
30018      * first starting up. This lets us catch the case where we have *never*
30019      * drawn the layer, and draw it even if the zoom hasn't changed.
30020      */
30021     drawn: false,
30022     
30023     /**
30024      * Constructor: OpenLayers.Layer.Markers 
30025      * Create a Markers layer.
30026      *
30027      * Parameters:
30028      * name - {String} 
30029      * options - {Object} Hashtable of extra options to tag onto the layer
30030      */
30031     initialize: function(name, options) {
30032         OpenLayers.Layer.prototype.initialize.apply(this, arguments);
30033         this.markers = [];
30034     },
30035     
30036     /**
30037      * APIMethod: destroy 
30038      */
30039     destroy: function() {
30040         this.clearMarkers();
30041         this.markers = null;
30042         OpenLayers.Layer.prototype.destroy.apply(this, arguments);
30043     },
30044
30045     /**
30046      * APIMethod: setOpacity
30047      * Sets the opacity for all the markers.
30048      * 
30049      * Parameters:
30050      * opacity - {Float}
30051      */
30052     setOpacity: function(opacity) {
30053         if (opacity != this.opacity) {
30054             this.opacity = opacity;
30055             for (var i=0, len=this.markers.length; i<len; i++) {
30056                 this.markers[i].setOpacity(this.opacity);
30057             }
30058         }
30059     },
30060
30061     /** 
30062      * Method: moveTo
30063      *
30064      * Parameters:
30065      * bounds - {<OpenLayers.Bounds>} 
30066      * zoomChanged - {Boolean} 
30067      * dragging - {Boolean} 
30068      */
30069     moveTo:function(bounds, zoomChanged, dragging) {
30070         OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
30071
30072         if (zoomChanged || !this.drawn) {
30073             for(var i=0, len=this.markers.length; i<len; i++) {
30074                 this.drawMarker(this.markers[i]);
30075             }
30076             this.drawn = true;
30077         }
30078     },
30079
30080     /**
30081      * APIMethod: addMarker
30082      *
30083      * Parameters:
30084      * marker - {<OpenLayers.Marker>} 
30085      */
30086     addMarker: function(marker) {
30087         this.markers.push(marker);
30088
30089         if (this.opacity < 1) {
30090             marker.setOpacity(this.opacity);
30091         }
30092
30093         if (this.map && this.map.getExtent()) {
30094             marker.map = this.map;
30095             this.drawMarker(marker);
30096         }
30097     },
30098
30099     /**
30100      * APIMethod: removeMarker
30101      *
30102      * Parameters:
30103      * marker - {<OpenLayers.Marker>} 
30104      */
30105     removeMarker: function(marker) {
30106         if (this.markers && this.markers.length) {
30107             OpenLayers.Util.removeItem(this.markers, marker);
30108             marker.erase();
30109         }
30110     },
30111
30112     /**
30113      * Method: clearMarkers
30114      * This method removes all markers from a layer. The markers are not
30115      * destroyed by this function, but are removed from the list of markers.
30116      */
30117     clearMarkers: function() {
30118         if (this.markers != null) {
30119             while(this.markers.length > 0) {
30120                 this.removeMarker(this.markers[0]);
30121             }
30122         }
30123     },
30124
30125     /** 
30126      * Method: drawMarker
30127      * Calculate the pixel location for the marker, create it, and 
30128      *    add it to the layer's div
30129      *
30130      * Parameters:
30131      * marker - {<OpenLayers.Marker>} 
30132      */
30133     drawMarker: function(marker) {
30134         var px = this.map.getLayerPxFromLonLat(marker.lonlat);
30135         if (px == null) {
30136             marker.display(false);
30137         } else {
30138             if (!marker.isDrawn()) {
30139                 var markerImg = marker.draw(px);
30140                 this.div.appendChild(markerImg);
30141             } else if(marker.icon) {
30142                 marker.icon.moveTo(px);
30143             }
30144         }
30145     },
30146     
30147     /** 
30148      * APIMethod: getDataExtent
30149      * Calculates the max extent which includes all of the markers.
30150      * 
30151      * Returns:
30152      * {<OpenLayers.Bounds>}
30153      */
30154     getDataExtent: function () {
30155         var maxExtent = null;
30156         
30157         if ( this.markers && (this.markers.length > 0)) {
30158             var maxExtent = new OpenLayers.Bounds();
30159             for(var i=0, len=this.markers.length; i<len; i++) {
30160                 var marker = this.markers[i];
30161                 maxExtent.extend(marker.lonlat);
30162             }
30163         }
30164
30165         return maxExtent;
30166     },
30167
30168     CLASS_NAME: "OpenLayers.Layer.Markers"
30169 });
30170 /* ======================================================================
30171     OpenLayers/Strategy/BBOX.js
30172    ====================================================================== */
30173
30174 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
30175  * full list of contributors). Published under the 2-clause BSD license.
30176  * See license.txt in the OpenLayers distribution or repository for the
30177  * full text of the license. */
30178
30179 /**
30180  * @requires OpenLayers/Strategy.js
30181  * @requires OpenLayers/Filter/Spatial.js
30182  */
30183
30184 /**
30185  * Class: OpenLayers.Strategy.BBOX
30186  * A simple strategy that reads new features when the viewport invalidates
30187  *     some bounds.
30188  *
30189  * Inherits from:
30190  *  - <OpenLayers.Strategy>
30191  */
30192 OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {
30193     
30194     /**
30195      * Property: bounds
30196      * {<OpenLayers.Bounds>} The current data bounds (in the same projection
30197      *     as the layer - not always the same projection as the map).
30198      */
30199     bounds: null,
30200     
30201     /** 
30202      * Property: resolution 
30203      * {Float} The current data resolution. 
30204      */ 
30205     resolution: null, 
30206            
30207     /**
30208      * APIProperty: ratio
30209      * {Float} The ratio of the data bounds to the viewport bounds (in each
30210      *     dimension).  Default is 2.
30211      */
30212     ratio: 2,
30213
30214     /** 
30215      * Property: resFactor 
30216      * {Float} Optional factor used to determine when previously requested 
30217      *     features are invalid.  If set, the resFactor will be compared to the
30218      *     resolution of the previous request to the current map resolution.
30219      *     If resFactor > (old / new) and 1/resFactor < (old / new).  If you
30220      *     set a resFactor of 1, data will be requested every time the
30221      *     resolution changes.  If you set a resFactor of 3, data will be
30222      *     requested if the old resolution is 3 times the new, or if the new is
30223      *     3 times the old.  If the old bounds do not contain the new bounds
30224      *     new data will always be requested (with or without considering
30225      *     resFactor). 
30226      */ 
30227     resFactor: null, 
30228     
30229     /**
30230      * Property: response
30231      * {<OpenLayers.Protocol.Response>} The protocol response object returned
30232      *      by the layer protocol.
30233      */
30234     response: null,
30235
30236     /**
30237      * Constructor: OpenLayers.Strategy.BBOX
30238      * Create a new BBOX strategy.
30239      *
30240      * Parameters:
30241      * options - {Object} Optional object whose properties will be set on the
30242      *     instance.
30243      */
30244     
30245     /**
30246      * Method: activate
30247      * Set up strategy with regard to reading new batches of remote data.
30248      * 
30249      * Returns:
30250      * {Boolean} The strategy was successfully activated.
30251      */
30252     activate: function() {
30253         var activated = OpenLayers.Strategy.prototype.activate.call(this);
30254         if(activated) {
30255             this.layer.events.on({
30256                 "moveend": this.update,
30257                 "refresh": this.update,
30258                 "visibilitychanged": this.update,
30259                 scope: this
30260             });
30261             this.update();
30262         }
30263         return activated;
30264     },
30265     
30266     /**
30267      * Method: deactivate
30268      * Tear down strategy with regard to reading new batches of remote data.
30269      * 
30270      * Returns:
30271      * {Boolean} The strategy was successfully deactivated.
30272      */
30273     deactivate: function() {
30274         var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
30275         if(deactivated) {
30276             this.layer.events.un({
30277                 "moveend": this.update,
30278                 "refresh": this.update,
30279                 "visibilitychanged": this.update,
30280                 scope: this
30281             });
30282         }
30283         return deactivated;
30284     },
30285
30286     /**
30287      * Method: update
30288      * Callback function called on "moveend" or "refresh" layer events.
30289      *
30290      * Parameters:
30291      * options - {Object} Optional object whose properties will determine
30292      *     the behaviour of this Strategy
30293      *
30294      * Valid options include:
30295      * force - {Boolean} if true, new data must be unconditionally read.
30296      * noAbort - {Boolean} if true, do not abort previous requests.
30297      */
30298     update: function(options) {
30299         var mapBounds = this.getMapBounds();
30300         if (mapBounds !== null && ((options && options.force) ||
30301           (this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) {
30302             this.calculateBounds(mapBounds);
30303             this.resolution = this.layer.map.getResolution(); 
30304             this.triggerRead(options);
30305         }
30306     },
30307     
30308     /**
30309      * Method: getMapBounds
30310      * Get the map bounds expressed in the same projection as this layer.
30311      *
30312      * Returns:
30313      * {<OpenLayers.Bounds>} Map bounds in the projection of the layer.
30314      */
30315     getMapBounds: function() {
30316         if (this.layer.map === null) {
30317             return null;
30318         }
30319         var bounds = this.layer.map.getExtent();
30320         if(bounds && !this.layer.projection.equals(
30321                 this.layer.map.getProjectionObject())) {
30322             bounds = bounds.clone().transform(
30323                 this.layer.map.getProjectionObject(), this.layer.projection
30324             );
30325         }
30326         return bounds;
30327     },
30328
30329     /**
30330      * Method: invalidBounds
30331      * Determine whether the previously requested set of features is invalid. 
30332      *     This occurs when the new map bounds do not contain the previously 
30333      *     requested bounds.  In addition, if <resFactor> is set, it will be 
30334      *     considered.
30335      *
30336      * Parameters:
30337      * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
30338      *      retrieved from the map object if not provided
30339      *
30340      * Returns:
30341      * {Boolean} 
30342      */
30343     invalidBounds: function(mapBounds) {
30344         if(!mapBounds) {
30345             mapBounds = this.getMapBounds();
30346         }
30347         var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);
30348         if(!invalid && this.resFactor) {
30349             var ratio = this.resolution / this.layer.map.getResolution();
30350             invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor));
30351         }
30352         return invalid;
30353     },
30354  
30355     /**
30356      * Method: calculateBounds
30357      *
30358      * Parameters:
30359      * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
30360      *      retrieved from the map object if not provided
30361      */
30362     calculateBounds: function(mapBounds) {
30363         if(!mapBounds) {
30364             mapBounds = this.getMapBounds();
30365         }
30366         var center = mapBounds.getCenterLonLat();
30367         var dataWidth = mapBounds.getWidth() * this.ratio;
30368         var dataHeight = mapBounds.getHeight() * this.ratio;
30369         this.bounds = new OpenLayers.Bounds(
30370             center.lon - (dataWidth / 2),
30371             center.lat - (dataHeight / 2),
30372             center.lon + (dataWidth / 2),
30373             center.lat + (dataHeight / 2)
30374         );
30375     },
30376     
30377     /**
30378      * Method: triggerRead
30379      *
30380      * Parameters:
30381      * options - {Object} Additional options for the protocol's read method 
30382      *     (optional)
30383      *
30384      * Returns:
30385      * {<OpenLayers.Protocol.Response>} The protocol response object
30386      *      returned by the layer protocol.
30387      */
30388     triggerRead: function(options) {
30389         if (this.response && !(options && options.noAbort === true)) {
30390             this.layer.protocol.abort(this.response);
30391             this.layer.events.triggerEvent("loadend");
30392         }
30393         var evt = {filter: this.createFilter()};
30394         this.layer.events.triggerEvent("loadstart", evt);
30395         this.response = this.layer.protocol.read(
30396             OpenLayers.Util.applyDefaults({
30397                 filter: evt.filter,
30398                 callback: this.merge,
30399                 scope: this
30400         }, options));
30401     },
30402  
30403     /**
30404      * Method: createFilter
30405      * Creates a spatial BBOX filter. If the layer that this strategy belongs
30406      * to has a filter property, this filter will be combined with the BBOX 
30407      * filter.
30408      * 
30409      * Returns
30410      * {<OpenLayers.Filter>} The filter object.
30411      */
30412     createFilter: function() {
30413         var filter = new OpenLayers.Filter.Spatial({
30414             type: OpenLayers.Filter.Spatial.BBOX,
30415             value: this.bounds,
30416             projection: this.layer.projection
30417         });
30418         if (this.layer.filter) {
30419             filter = new OpenLayers.Filter.Logical({
30420                 type: OpenLayers.Filter.Logical.AND,
30421                 filters: [this.layer.filter, filter]
30422             });
30423         }
30424         return filter;
30425     },
30426    
30427     /**
30428      * Method: merge
30429      * Given a list of features, determine which ones to add to the layer.
30430      *     If the layer projection differs from the map projection, features
30431      *     will be transformed from the layer projection to the map projection.
30432      *
30433      * Parameters:
30434      * resp - {<OpenLayers.Protocol.Response>} The response object passed
30435      *      by the protocol.
30436      */
30437     merge: function(resp) {
30438         this.layer.destroyFeatures();
30439         if (resp.success()) {
30440             var features = resp.features;
30441             if(features && features.length > 0) {
30442                 var remote = this.layer.projection;
30443                 var local = this.layer.map.getProjectionObject();
30444                 if(!local.equals(remote)) {
30445                     var geom;
30446                     for(var i=0, len=features.length; i<len; ++i) {
30447                         geom = features[i].geometry;
30448                         if(geom) {
30449                             geom.transform(remote, local);
30450                         }
30451                     }
30452                 }
30453                 this.layer.addFeatures(features);
30454             }
30455         } else {
30456             this.bounds = null;
30457         }
30458         this.response = null;
30459         this.layer.events.triggerEvent("loadend", {response: resp});
30460     },
30461    
30462     CLASS_NAME: "OpenLayers.Strategy.BBOX" 
30463 });
30464 /* ======================================================================
30465     OpenLayers/Handler/Feature.js
30466    ====================================================================== */
30467
30468 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
30469  * full list of contributors). Published under the 2-clause BSD license.
30470  * See license.txt in the OpenLayers distribution or repository for the
30471  * full text of the license. */
30472
30473
30474 /**
30475  * @requires OpenLayers/Handler.js
30476  */
30477
30478 /**
30479  * Class: OpenLayers.Handler.Feature 
30480  * Handler to respond to mouse events related to a drawn feature.  Callbacks
30481  *     with the following keys will be notified of the following events
30482  *     associated with features: click, clickout, over, out, and dblclick.
30483  *
30484  * This handler stops event propagation for mousedown and mouseup if those
30485  *     browser events target features that can be selected.
30486  *
30487  * Inherits from:
30488  *  - <OpenLayers.Handler>
30489  */
30490 OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {
30491
30492     /**
30493      * Property: EVENTMAP
30494      * {Object} A object mapping the browser events to objects with callback
30495      *     keys for in and out.
30496      */
30497     EVENTMAP: {
30498         'click': {'in': 'click', 'out': 'clickout'},
30499         'mousemove': {'in': 'over', 'out': 'out'},
30500         'dblclick': {'in': 'dblclick', 'out': null},
30501         'mousedown': {'in': null, 'out': null},
30502         'mouseup': {'in': null, 'out': null},
30503         'touchstart': {'in': 'click', 'out': 'clickout'}
30504     },
30505
30506     /**
30507      * Property: feature
30508      * {<OpenLayers.Feature.Vector>} The last feature that was hovered.
30509      */
30510     feature: null,
30511
30512     /**
30513      * Property: lastFeature
30514      * {<OpenLayers.Feature.Vector>} The last feature that was handled.
30515      */
30516     lastFeature: null,
30517
30518     /**
30519      * Property: down
30520      * {<OpenLayers.Pixel>} The location of the last mousedown.
30521      */
30522     down: null,
30523
30524     /**
30525      * Property: up
30526      * {<OpenLayers.Pixel>} The location of the last mouseup.
30527      */
30528     up: null,
30529     
30530     /**
30531      * Property: clickTolerance
30532      * {Number} The number of pixels the mouse can move between mousedown
30533      *     and mouseup for the event to still be considered a click.
30534      *     Dragging the map should not trigger the click and clickout callbacks
30535      *     unless the map is moved by less than this tolerance. Defaults to 4.
30536      */
30537     clickTolerance: 4,
30538
30539     /**
30540      * Property: geometryTypes
30541      * To restrict dragging to a limited set of geometry types, send a list
30542      * of strings corresponding to the geometry class names.
30543      * 
30544      * @type Array(String)
30545      */
30546     geometryTypes: null,
30547
30548     /**
30549      * Property: stopClick
30550      * {Boolean} If stopClick is set to true, handled clicks do not
30551      *      propagate to other click listeners. Otherwise, handled clicks
30552      *      do propagate. Unhandled clicks always propagate, whatever the
30553      *      value of stopClick. Defaults to true.
30554      */
30555     stopClick: true,
30556
30557     /**
30558      * Property: stopDown
30559      * {Boolean} If stopDown is set to true, handled mousedowns do not
30560      *      propagate to other mousedown listeners. Otherwise, handled
30561      *      mousedowns do propagate. Unhandled mousedowns always propagate,
30562      *      whatever the value of stopDown. Defaults to true.
30563      */
30564     stopDown: true,
30565
30566     /**
30567      * Property: stopUp
30568      * {Boolean} If stopUp is set to true, handled mouseups do not
30569      *      propagate to other mouseup listeners. Otherwise, handled mouseups
30570      *      do propagate. Unhandled mouseups always propagate, whatever the
30571      *      value of stopUp. Defaults to false.
30572      */
30573     stopUp: false,
30574     
30575     /**
30576      * Constructor: OpenLayers.Handler.Feature
30577      *
30578      * Parameters:
30579      * control - {<OpenLayers.Control>} 
30580      * layer - {<OpenLayers.Layer.Vector>}
30581      * callbacks - {Object} An object with a 'over' property whos value is
30582      *     a function to be called when the mouse is over a feature. The 
30583      *     callback should expect to recieve a single argument, the feature.
30584      * options - {Object} 
30585      */
30586     initialize: function(control, layer, callbacks, options) {
30587         OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);
30588         this.layer = layer;
30589     },
30590
30591     /**
30592      * Method: touchstart
30593      * Handle touchstart events
30594      *
30595      * Parameters:
30596      * evt - {Event}
30597      *
30598      * Returns:
30599      * {Boolean} Let the event propagate.
30600      */
30601     touchstart: function(evt) {
30602         this.startTouch(); 
30603         return OpenLayers.Event.isMultiTouch(evt) ?
30604                 true : this.mousedown(evt);
30605     },
30606
30607     /**
30608      * Method: touchmove
30609      * Handle touchmove events. We just prevent the browser default behavior,
30610      *    for Android Webkit not to select text when moving the finger after
30611      *    selecting a feature.
30612      *
30613      * Parameters:
30614      * evt - {Event}
30615      */
30616     touchmove: function(evt) {
30617         OpenLayers.Event.preventDefault(evt);
30618     },
30619
30620     /**
30621      * Method: mousedown
30622      * Handle mouse down.  Stop propagation if a feature is targeted by this
30623      *     event (stops map dragging during feature selection).
30624      * 
30625      * Parameters:
30626      * evt - {Event} 
30627      */
30628     mousedown: function(evt) {
30629         // Feature selection is only done with a left click. Other handlers may stop the
30630         // propagation of left-click mousedown events but not right-click mousedown events.
30631         // This mismatch causes problems when comparing the location of the down and up
30632         // events in the click function so it is important ignore right-clicks.
30633         if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) {
30634             this.down = evt.xy;
30635         }
30636         return this.handle(evt) ? !this.stopDown : true;
30637     },
30638     
30639     /**
30640      * Method: mouseup
30641      * Handle mouse up.  Stop propagation if a feature is targeted by this
30642      *     event.
30643      * 
30644      * Parameters:
30645      * evt - {Event} 
30646      */
30647     mouseup: function(evt) {
30648         this.up = evt.xy;
30649         return this.handle(evt) ? !this.stopUp : true;
30650     },
30651
30652     /**
30653      * Method: click
30654      * Handle click.  Call the "click" callback if click on a feature,
30655      *     or the "clickout" callback if click outside any feature.
30656      * 
30657      * Parameters:
30658      * evt - {Event} 
30659      *
30660      * Returns:
30661      * {Boolean}
30662      */
30663     click: function(evt) {
30664         return this.handle(evt) ? !this.stopClick : true;
30665     },
30666         
30667     /**
30668      * Method: mousemove
30669      * Handle mouse moves.  Call the "over" callback if moving in to a feature,
30670      *     or the "out" callback if moving out of a feature.
30671      * 
30672      * Parameters:
30673      * evt - {Event} 
30674      *
30675      * Returns:
30676      * {Boolean}
30677      */
30678     mousemove: function(evt) {
30679         if (!this.callbacks['over'] && !this.callbacks['out']) {
30680             return true;
30681         }     
30682         this.handle(evt);
30683         return true;
30684     },
30685     
30686     /**
30687      * Method: dblclick
30688      * Handle dblclick.  Call the "dblclick" callback if dblclick on a feature.
30689      *
30690      * Parameters:
30691      * evt - {Event} 
30692      *
30693      * Returns:
30694      * {Boolean}
30695      */
30696     dblclick: function(evt) {
30697         return !this.handle(evt);
30698     },
30699
30700     /**
30701      * Method: geometryTypeMatches
30702      * Return true if the geometry type of the passed feature matches
30703      *     one of the geometry types in the geometryTypes array.
30704      *
30705      * Parameters:
30706      * feature - {<OpenLayers.Vector.Feature>}
30707      *
30708      * Returns:
30709      * {Boolean}
30710      */
30711     geometryTypeMatches: function(feature) {
30712         return this.geometryTypes == null ||
30713             OpenLayers.Util.indexOf(this.geometryTypes,
30714                                     feature.geometry.CLASS_NAME) > -1;
30715     },
30716
30717     /**
30718      * Method: handle
30719      *
30720      * Parameters:
30721      * evt - {Event}
30722      *
30723      * Returns:
30724      * {Boolean} The event occurred over a relevant feature.
30725      */
30726     handle: function(evt) {
30727         if(this.feature && !this.feature.layer) {
30728             // feature has been destroyed
30729             this.feature = null;
30730         }
30731         var type = evt.type;
30732         var handled = false;
30733         var previouslyIn = !!(this.feature); // previously in a feature
30734         var click = (type == "click" || type == "dblclick" || type == "touchstart");
30735         this.feature = this.layer.getFeatureFromEvent(evt);
30736         if(this.feature && !this.feature.layer) {
30737             // feature has been destroyed
30738             this.feature = null;
30739         }
30740         if(this.lastFeature && !this.lastFeature.layer) {
30741             // last feature has been destroyed
30742             this.lastFeature = null;
30743         }
30744         if(this.feature) {
30745             if(type === "touchstart") {
30746                 // stop the event to prevent Android Webkit from
30747                 // "flashing" the map div
30748                 OpenLayers.Event.preventDefault(evt);
30749             }
30750             var inNew = (this.feature != this.lastFeature);
30751             if(this.geometryTypeMatches(this.feature)) {
30752                 // in to a feature
30753                 if(previouslyIn && inNew) {
30754                     // out of last feature and in to another
30755                     if(this.lastFeature) {
30756                         this.triggerCallback(type, 'out', [this.lastFeature]);
30757                     }
30758                     this.triggerCallback(type, 'in', [this.feature]);
30759                 } else if(!previouslyIn || click) {
30760                     // in feature for the first time
30761                     this.triggerCallback(type, 'in', [this.feature]);
30762                 }
30763                 this.lastFeature = this.feature;
30764                 handled = true;
30765             } else {
30766                 // not in to a feature
30767                 if(this.lastFeature && (previouslyIn && inNew || click)) {
30768                     // out of last feature for the first time
30769                     this.triggerCallback(type, 'out', [this.lastFeature]);
30770                 }
30771                 // next time the mouse goes in a feature whose geometry type
30772                 // doesn't match we don't want to call the 'out' callback
30773                 // again, so let's set this.feature to null so that
30774                 // previouslyIn will evaluate to false the next time
30775                 // we enter handle. Yes, a bit hackish...
30776                 this.feature = null;
30777             }
30778         } else if(this.lastFeature && (previouslyIn || click)) {
30779             this.triggerCallback(type, 'out', [this.lastFeature]);
30780         }
30781         return handled;
30782     },
30783     
30784     /**
30785      * Method: triggerCallback
30786      * Call the callback keyed in the event map with the supplied arguments.
30787      *     For click and clickout, the <clickTolerance> is checked first.
30788      *
30789      * Parameters:
30790      * type - {String}
30791      */
30792     triggerCallback: function(type, mode, args) {
30793         var key = this.EVENTMAP[type][mode];
30794         if(key) {
30795             if(type == 'click' && this.up && this.down) {
30796                 // for click/clickout, only trigger callback if tolerance is met
30797                 var dpx = Math.sqrt(
30798                     Math.pow(this.up.x - this.down.x, 2) +
30799                     Math.pow(this.up.y - this.down.y, 2)
30800                 );
30801                 if(dpx <= this.clickTolerance) {
30802                     this.callback(key, args);
30803                 }
30804                 // we're done with this set of events now: clear the cached
30805                 // positions so we can't trip over them later (this can occur
30806                 // if one of the up/down events gets eaten before it gets to us
30807                 // but we still get the click)
30808                 this.up = this.down = null;
30809             } else {
30810                 this.callback(key, args);
30811             }
30812         }
30813     },
30814
30815     /**
30816      * Method: activate 
30817      * Turn on the handler.  Returns false if the handler was already active.
30818      *
30819      * Returns:
30820      * {Boolean}
30821      */
30822     activate: function() {
30823         var activated = false;
30824         if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
30825             this.moveLayerToTop();
30826             this.map.events.on({
30827                 "removelayer": this.handleMapEvents,
30828                 "changelayer": this.handleMapEvents,
30829                 scope: this
30830             });
30831             activated = true;
30832         }
30833         return activated;
30834     },
30835     
30836     /**
30837      * Method: deactivate 
30838      * Turn off the handler.  Returns false if the handler was already active.
30839      *
30840      * Returns: 
30841      * {Boolean}
30842      */
30843     deactivate: function() {
30844         var deactivated = false;
30845         if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
30846             this.moveLayerBack();
30847             this.feature = null;
30848             this.lastFeature = null;
30849             this.down = null;
30850             this.up = null;
30851             this.map.events.un({
30852                 "removelayer": this.handleMapEvents,
30853                 "changelayer": this.handleMapEvents,
30854                 scope: this
30855             });
30856             deactivated = true;
30857         }
30858         return deactivated;
30859     },
30860     
30861     /**
30862      * Method: handleMapEvents
30863      * 
30864      * Parameters:
30865      * evt - {Object}
30866      */
30867     handleMapEvents: function(evt) {
30868         if (evt.type == "removelayer" || evt.property == "order") {
30869             this.moveLayerToTop();
30870         }
30871     },
30872     
30873     /**
30874      * Method: moveLayerToTop
30875      * Moves the layer for this handler to the top, so mouse events can reach
30876      * it.
30877      */
30878     moveLayerToTop: function() {
30879         var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,
30880             this.layer.getZIndex()) + 1;
30881         this.layer.setZIndex(index);
30882         
30883     },
30884     
30885     /**
30886      * Method: moveLayerBack
30887      * Moves the layer back to the position determined by the map's layers
30888      * array.
30889      */
30890     moveLayerBack: function() {
30891         var index = this.layer.getZIndex() - 1;
30892         if (index >= this.map.Z_INDEX_BASE['Feature']) {
30893             this.layer.setZIndex(index);
30894         } else {
30895             this.map.setLayerZIndex(this.layer,
30896                 this.map.getLayerIndex(this.layer));
30897         }
30898     },
30899
30900     CLASS_NAME: "OpenLayers.Handler.Feature"
30901 });
30902 /* ======================================================================
30903     OpenLayers/StyleMap.js
30904    ====================================================================== */
30905
30906 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
30907  * full list of contributors). Published under the 2-clause BSD license.
30908  * See license.txt in the OpenLayers distribution or repository for the
30909  * full text of the license. */
30910
30911 /**
30912  * @requires OpenLayers/BaseTypes/Class.js
30913  * @requires OpenLayers/Style.js
30914  * @requires OpenLayers/Feature/Vector.js
30915  */
30916  
30917 /**
30918  * Class: OpenLayers.StyleMap
30919  */
30920 OpenLayers.StyleMap = OpenLayers.Class({
30921     
30922     /**
30923      * Property: styles
30924      * {Object} Hash of {<OpenLayers.Style>}, keyed by names of well known
30925      * rendering intents (e.g. "default", "temporary", "select", "delete").
30926      */
30927     styles: null,
30928     
30929     /**
30930      * Property: extendDefault
30931      * {Boolean} if true, every render intent will extend the symbolizers
30932      * specified for the "default" intent at rendering time. Otherwise, every
30933      * rendering intent will be treated as a completely independent style.
30934      */
30935     extendDefault: true,
30936     
30937     /**
30938      * Constructor: OpenLayers.StyleMap
30939      * 
30940      * Parameters:
30941      * style   - {Object} Optional. Either a style hash, or a style object, or
30942      *           a hash of style objects (style hashes) keyed by rendering
30943      *           intent. If just one style hash or style object is passed,
30944      *           this will be used for all known render intents (default,
30945      *           select, temporary)
30946      * options - {Object} optional hash of additional options for this
30947      *           instance
30948      */
30949     initialize: function (style, options) {
30950         this.styles = {
30951             "default": new OpenLayers.Style(
30952                 OpenLayers.Feature.Vector.style["default"]),
30953             "select": new OpenLayers.Style(
30954                 OpenLayers.Feature.Vector.style["select"]),
30955             "temporary": new OpenLayers.Style(
30956                 OpenLayers.Feature.Vector.style["temporary"]),
30957             "delete": new OpenLayers.Style(
30958                 OpenLayers.Feature.Vector.style["delete"])
30959         };
30960         
30961         // take whatever the user passed as style parameter and convert it
30962         // into parts of stylemap.
30963         if(style instanceof OpenLayers.Style) {
30964             // user passed a style object
30965             this.styles["default"] = style;
30966             this.styles["select"] = style;
30967             this.styles["temporary"] = style;
30968             this.styles["delete"] = style;
30969         } else if(typeof style == "object") {
30970             for(var key in style) {
30971                 if(style[key] instanceof OpenLayers.Style) {
30972                     // user passed a hash of style objects
30973                     this.styles[key] = style[key];
30974                 } else if(typeof style[key] == "object") {
30975                     // user passsed a hash of style hashes
30976                     this.styles[key] = new OpenLayers.Style(style[key]);
30977                 } else {
30978                     // user passed a style hash (i.e. symbolizer)
30979                     this.styles["default"] = new OpenLayers.Style(style);
30980                     this.styles["select"] = new OpenLayers.Style(style);
30981                     this.styles["temporary"] = new OpenLayers.Style(style);
30982                     this.styles["delete"] = new OpenLayers.Style(style);
30983                     break;
30984                 }
30985             }
30986         }
30987         OpenLayers.Util.extend(this, options);
30988     },
30989
30990     /**
30991      * Method: destroy
30992      */
30993     destroy: function() {
30994         for(var key in this.styles) {
30995             this.styles[key].destroy();
30996         }
30997         this.styles = null;
30998     },
30999     
31000     /**
31001      * Method: createSymbolizer
31002      * Creates the symbolizer for a feature for a render intent.
31003      * 
31004      * Parameters:
31005      * feature - {<OpenLayers.Feature>} The feature to evaluate the rules
31006      *           of the intended style against.
31007      * intent  - {String} The intent determines the symbolizer that will be
31008      *           used to draw the feature. Well known intents are "default"
31009      *           (for just drawing the features), "select" (for selected
31010      *           features) and "temporary" (for drawing features).
31011      * 
31012      * Returns:
31013      * {Object} symbolizer hash
31014      */
31015     createSymbolizer: function(feature, intent) {
31016         if(!feature) {
31017             feature = new OpenLayers.Feature.Vector();
31018         }
31019         if(!this.styles[intent]) {
31020             intent = "default";
31021         }
31022         feature.renderIntent = intent;
31023         var defaultSymbolizer = {};
31024         if(this.extendDefault && intent != "default") {
31025             defaultSymbolizer = this.styles["default"].createSymbolizer(feature);
31026         }
31027         return OpenLayers.Util.extend(defaultSymbolizer,
31028             this.styles[intent].createSymbolizer(feature));
31029     },
31030     
31031     /**
31032      * Method: addUniqueValueRules
31033      * Convenience method to create comparison rules for unique values of a
31034      * property. The rules will be added to the style object for a specified
31035      * rendering intent. This method is a shortcut for creating something like
31036      * the "unique value legends" familiar from well known desktop GIS systems
31037      * 
31038      * Parameters:
31039      * renderIntent - {String} rendering intent to add the rules to
31040      * property     - {String} values of feature attributes to create the
31041      *                rules for
31042      * symbolizers  - {Object} Hash of symbolizers, keyed by the desired
31043      *                property values 
31044      * context      - {Object} An optional object with properties that
31045      *                symbolizers' property values should be evaluated
31046      *                against. If no context is specified, feature.attributes
31047      *                will be used
31048      */
31049     addUniqueValueRules: function(renderIntent, property, symbolizers, context) {
31050         var rules = [];
31051         for (var value in symbolizers) {
31052             rules.push(new OpenLayers.Rule({
31053                 symbolizer: symbolizers[value],
31054                 context: context,
31055                 filter: new OpenLayers.Filter.Comparison({
31056                     type: OpenLayers.Filter.Comparison.EQUAL_TO,
31057                     property: property,
31058                     value: value
31059                 })
31060             }));
31061         }
31062         this.styles[renderIntent].addRules(rules);
31063     },
31064
31065     CLASS_NAME: "OpenLayers.StyleMap"
31066 });
31067 /* ======================================================================
31068     OpenLayers/Layer/Vector.js
31069    ====================================================================== */
31070
31071 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
31072  * full list of contributors). Published under the 2-clause BSD license.
31073  * See license.txt in the OpenLayers distribution or repository for the
31074  * full text of the license. */
31075
31076 /**
31077  * @requires OpenLayers/Layer.js
31078  * @requires OpenLayers/Renderer.js
31079  * @requires OpenLayers/StyleMap.js
31080  * @requires OpenLayers/Feature/Vector.js
31081  * @requires OpenLayers/Console.js
31082  * @requires OpenLayers/Lang.js
31083  */
31084
31085 /**
31086  * Class: OpenLayers.Layer.Vector
31087  * Instances of OpenLayers.Layer.Vector are used to render vector data from
31088  *     a variety of sources. Create a new vector layer with the
31089  *     <OpenLayers.Layer.Vector> constructor.
31090  *
31091  * Inherits from:
31092  *  - <OpenLayers.Layer>
31093  */
31094 OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
31095
31096     /**
31097      * APIProperty: events
31098      * {<OpenLayers.Events>}
31099      *
31100      * Register a listener for a particular event with the following syntax:
31101      * (code)
31102      * layer.events.register(type, obj, listener);
31103      * (end)
31104      *
31105      * Listeners will be called with a reference to an event object.  The
31106      *     properties of this event depends on exactly what happened.
31107      *
31108      * All event objects have at least the following properties:
31109      * object - {Object} A reference to layer.events.object.
31110      * element - {DOMElement} A reference to layer.events.element.
31111      *
31112      * Supported map event types (in addition to those from <OpenLayers.Layer.events>):
31113      * beforefeatureadded - Triggered before a feature is added.  Listeners
31114      *      will receive an object with a *feature* property referencing the
31115      *      feature to be added.  To stop the feature from being added, a
31116      *      listener should return false.
31117      * beforefeaturesadded - Triggered before an array of features is added.
31118      *      Listeners will receive an object with a *features* property
31119      *      referencing the feature to be added. To stop the features from
31120      *      being added, a listener should return false.
31121      * featureadded - Triggered after a feature is added.  The event
31122      *      object passed to listeners will have a *feature* property with a
31123      *      reference to the added feature.
31124      * featuresadded - Triggered after features are added.  The event
31125      *      object passed to listeners will have a *features* property with a
31126      *      reference to an array of added features.
31127      * beforefeatureremoved - Triggered before a feature is removed. Listeners
31128      *      will receive an object with a *feature* property referencing the
31129      *      feature to be removed.
31130      * beforefeaturesremoved - Triggered before multiple features are removed. 
31131      *      Listeners will receive an object with a *features* property
31132      *      referencing the features to be removed.
31133      * featureremoved - Triggerd after a feature is removed. The event
31134      *      object passed to listeners will have a *feature* property with a
31135      *      reference to the removed feature.
31136      * featuresremoved - Triggered after features are removed. The event
31137      *      object passed to listeners will have a *features* property with a
31138      *      reference to an array of removed features.
31139      * beforefeatureselected - Triggered before a feature is selected.  Listeners
31140      *      will receive an object with a *feature* property referencing the
31141      *      feature to be selected. To stop the feature from being selectd, a
31142      *      listener should return false.
31143      * featureselected - Triggered after a feature is selected.  Listeners
31144      *      will receive an object with a *feature* property referencing the
31145      *      selected feature.
31146      * featureunselected - Triggered after a feature is unselected.
31147      *      Listeners will receive an object with a *feature* property
31148      *      referencing the unselected feature.
31149      * beforefeaturemodified - Triggered when a feature is selected to 
31150      *      be modified.  Listeners will receive an object with a *feature* 
31151      *      property referencing the selected feature.
31152      * featuremodified - Triggered when a feature has been modified.
31153      *      Listeners will receive an object with a *feature* property referencing 
31154      *      the modified feature.
31155      * afterfeaturemodified - Triggered when a feature is finished being modified.
31156      *      Listeners will receive an object with a *feature* property referencing 
31157      *      the modified feature.
31158      * vertexmodified - Triggered when a vertex within any feature geometry
31159      *      has been modified.  Listeners will receive an object with a
31160      *      *feature* property referencing the modified feature, a *vertex*
31161      *      property referencing the vertex modified (always a point geometry),
31162      *      and a *pixel* property referencing the pixel location of the
31163      *      modification.
31164      * vertexremoved - Triggered when a vertex within any feature geometry
31165      *      has been deleted.  Listeners will receive an object with a
31166      *      *feature* property referencing the modified feature, a *vertex*
31167      *      property referencing the vertex modified (always a point geometry),
31168      *      and a *pixel* property referencing the pixel location of the
31169      *      removal.
31170      * sketchstarted - Triggered when a feature sketch bound for this layer
31171      *      is started.  Listeners will receive an object with a *feature*
31172      *      property referencing the new sketch feature and a *vertex* property
31173      *      referencing the creation point.
31174      * sketchmodified - Triggered when a feature sketch bound for this layer
31175      *      is modified.  Listeners will receive an object with a *vertex*
31176      *      property referencing the modified vertex and a *feature* property
31177      *      referencing the sketch feature.
31178      * sketchcomplete - Triggered when a feature sketch bound for this layer
31179      *      is complete.  Listeners will receive an object with a *feature*
31180      *      property referencing the sketch feature.  By returning false, a
31181      *      listener can stop the sketch feature from being added to the layer.
31182      * refresh - Triggered when something wants a strategy to ask the protocol
31183      *      for a new set of features.
31184      */
31185
31186     /**
31187      * APIProperty: isBaseLayer
31188      * {Boolean} The layer is a base layer.  Default is false.  Set this property
31189      * in the layer options.
31190      */
31191     isBaseLayer: false,
31192
31193     /** 
31194      * APIProperty: isFixed
31195      * {Boolean} Whether the layer remains in one place while dragging the
31196      * map. Note that setting this to true will move the layer to the bottom
31197      * of the layer stack.
31198      */
31199     isFixed: false,
31200
31201     /** 
31202      * APIProperty: features
31203      * {Array(<OpenLayers.Feature.Vector>)} 
31204      */
31205     features: null,
31206     
31207     /** 
31208      * Property: filter
31209      * {<OpenLayers.Filter>} The filter set in this layer,
31210      *     a strategy launching read requests can combined
31211      *     this filter with its own filter.
31212      */
31213     filter: null,
31214     
31215     /** 
31216      * Property: selectedFeatures
31217      * {Array(<OpenLayers.Feature.Vector>)} 
31218      */
31219     selectedFeatures: null,
31220     
31221     /**
31222      * Property: unrenderedFeatures
31223      * {Object} hash of features, keyed by feature.id, that the renderer
31224      *     failed to draw
31225      */
31226     unrenderedFeatures: null,
31227
31228     /**
31229      * APIProperty: reportError
31230      * {Boolean} report friendly error message when loading of renderer
31231      * fails.
31232      */
31233     reportError: true, 
31234
31235     /** 
31236      * APIProperty: style
31237      * {Object} Default style for the layer
31238      */
31239     style: null,
31240     
31241     /**
31242      * Property: styleMap
31243      * {<OpenLayers.StyleMap>}
31244      */
31245     styleMap: null,
31246     
31247     /**
31248      * Property: strategies
31249      * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.
31250      */
31251     strategies: null,
31252     
31253     /**
31254      * Property: protocol
31255      * {<OpenLayers.Protocol>} Optional protocol for the layer.
31256      */
31257     protocol: null,
31258     
31259     /**
31260      * Property: renderers
31261      * {Array(String)} List of supported Renderer classes. Add to this list to
31262      * add support for additional renderers. This list is ordered:
31263      * the first renderer which returns true for the  'supported()'
31264      * method will be used, if not defined in the 'renderer' option.
31265      */
31266     renderers: ['SVG', 'VML', 'Canvas'],
31267     
31268     /** 
31269      * Property: renderer
31270      * {<OpenLayers.Renderer>}
31271      */
31272     renderer: null,
31273     
31274     /**
31275      * APIProperty: rendererOptions
31276      * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for
31277      *     supported options.
31278      */
31279     rendererOptions: null,
31280     
31281     /** 
31282      * APIProperty: geometryType
31283      * {String} geometryType allows you to limit the types of geometries this
31284      * layer supports. This should be set to something like
31285      * "OpenLayers.Geometry.Point" to limit types.
31286      */
31287     geometryType: null,
31288
31289     /** 
31290      * Property: drawn
31291      * {Boolean} Whether the Vector Layer features have been drawn yet.
31292      */
31293     drawn: false,
31294     
31295     /** 
31296      * APIProperty: ratio
31297      * {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map.
31298      */   
31299     ratio: 1,
31300
31301     /**
31302      * Constructor: OpenLayers.Layer.Vector
31303      * Create a new vector layer
31304      *
31305      * Parameters:
31306      * name - {String} A name for the layer
31307      * options - {Object} Optional object with non-default properties to set on
31308      *           the layer.
31309      *
31310      * Returns:
31311      * {<OpenLayers.Layer.Vector>} A new vector layer
31312      */
31313     initialize: function(name, options) {
31314         OpenLayers.Layer.prototype.initialize.apply(this, arguments);
31315
31316         // allow user-set renderer, otherwise assign one
31317         if (!this.renderer || !this.renderer.supported()) {  
31318             this.assignRenderer();
31319         }
31320
31321         // if no valid renderer found, display error
31322         if (!this.renderer || !this.renderer.supported()) {
31323             this.renderer = null;
31324             this.displayError();
31325         } 
31326
31327         if (!this.styleMap) {
31328             this.styleMap = new OpenLayers.StyleMap();
31329         }
31330
31331         this.features = [];
31332         this.selectedFeatures = [];
31333         this.unrenderedFeatures = {};
31334         
31335         // Allow for custom layer behavior
31336         if(this.strategies){
31337             for(var i=0, len=this.strategies.length; i<len; i++) {
31338                 this.strategies[i].setLayer(this);
31339             }
31340         }
31341
31342     },
31343
31344     /**
31345      * APIMethod: destroy
31346      * Destroy this layer
31347      */
31348     destroy: function() {
31349         if (this.strategies) {
31350             var strategy, i, len;
31351             for(i=0, len=this.strategies.length; i<len; i++) {
31352                 strategy = this.strategies[i];
31353                 if(strategy.autoDestroy) {
31354                     strategy.destroy();
31355                 }
31356             }
31357             this.strategies = null;
31358         }
31359         if (this.protocol) {
31360             if(this.protocol.autoDestroy) {
31361                 this.protocol.destroy();
31362             }
31363             this.protocol = null;
31364         }
31365         this.destroyFeatures();
31366         this.features = null;
31367         this.selectedFeatures = null;
31368         this.unrenderedFeatures = null;
31369         if (this.renderer) {
31370             this.renderer.destroy();
31371         }
31372         this.renderer = null;
31373         this.geometryType = null;
31374         this.drawn = null;
31375         OpenLayers.Layer.prototype.destroy.apply(this, arguments);  
31376     },
31377
31378     /**
31379      * Method: clone
31380      * Create a clone of this layer.
31381      * 
31382      * Note: Features of the layer are also cloned.
31383      *
31384      * Returns:
31385      * {<OpenLayers.Layer.Vector>} An exact clone of this layer
31386      */
31387     clone: function (obj) {
31388         
31389         if (obj == null) {
31390             obj = new OpenLayers.Layer.Vector(this.name, this.getOptions());
31391         }
31392
31393         //get all additions from superclasses
31394         obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
31395
31396         // copy/set any non-init, non-simple values here
31397         var features = this.features;
31398         var len = features.length;
31399         var clonedFeatures = new Array(len);
31400         for(var i=0; i<len; ++i) {
31401             clonedFeatures[i] = features[i].clone();
31402         }
31403         obj.features = clonedFeatures;
31404
31405         return obj;
31406     },    
31407     
31408     /**
31409      * Method: refresh
31410      * Ask the layer to request features again and redraw them.  Triggers
31411      *     the refresh event if the layer is in range and visible.
31412      *
31413      * Parameters:
31414      * obj - {Object} Optional object with properties for any listener of
31415      *     the refresh event.
31416      */
31417     refresh: function(obj) {
31418         if(this.calculateInRange() && this.visibility) {
31419             this.events.triggerEvent("refresh", obj);
31420         }
31421     },
31422
31423     /** 
31424      * Method: assignRenderer
31425      * Iterates through the available renderer implementations and selects 
31426      * and assigns the first one whose "supported()" function returns true.
31427      */    
31428     assignRenderer: function()  {
31429         for (var i=0, len=this.renderers.length; i<len; i++) {
31430             var rendererClass = this.renderers[i];
31431             var renderer = (typeof rendererClass == "function") ?
31432                 rendererClass :
31433                 OpenLayers.Renderer[rendererClass];
31434             if (renderer && renderer.prototype.supported()) {
31435                 this.renderer = new renderer(this.div, this.rendererOptions);
31436                 break;
31437             }  
31438         }  
31439     },
31440
31441     /** 
31442      * Method: displayError 
31443      * Let the user know their browser isn't supported.
31444      */
31445     displayError: function() {
31446         if (this.reportError) {
31447             OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported", 
31448                                      {renderers: this. renderers.join('\n')}));
31449         }    
31450     },
31451
31452     /** 
31453      * Method: setMap
31454      * The layer has been added to the map. 
31455      * 
31456      * If there is no renderer set, the layer can't be used. Remove it.
31457      * Otherwise, give the renderer a reference to the map and set its size.
31458      * 
31459      * Parameters:
31460      * map - {<OpenLayers.Map>} 
31461      */
31462     setMap: function(map) {        
31463         OpenLayers.Layer.prototype.setMap.apply(this, arguments);
31464
31465         if (!this.renderer) {
31466             this.map.removeLayer(this);
31467         } else {
31468             this.renderer.map = this.map;
31469
31470             var newSize = this.map.getSize();
31471             newSize.w = newSize.w * this.ratio;
31472             newSize.h = newSize.h * this.ratio;
31473             this.renderer.setSize(newSize);
31474         }
31475     },
31476
31477     /**
31478      * Method: afterAdd
31479      * Called at the end of the map.addLayer sequence.  At this point, the map
31480      *     will have a base layer.  Any autoActivate strategies will be
31481      *     activated here.
31482      */
31483     afterAdd: function() {
31484         if(this.strategies) {
31485             var strategy, i, len;
31486             for(i=0, len=this.strategies.length; i<len; i++) {
31487                 strategy = this.strategies[i];
31488                 if(strategy.autoActivate) {
31489                     strategy.activate();
31490                 }
31491             }
31492         }
31493     },
31494
31495     /**
31496      * Method: removeMap
31497      * The layer has been removed from the map.
31498      *
31499      * Parameters:
31500      * map - {<OpenLayers.Map>}
31501      */
31502     removeMap: function(map) {
31503         this.drawn = false;
31504         if(this.strategies) {
31505             var strategy, i, len;
31506             for(i=0, len=this.strategies.length; i<len; i++) {
31507                 strategy = this.strategies[i];
31508                 if(strategy.autoActivate) {
31509                     strategy.deactivate();
31510                 }
31511             }
31512         }
31513     },
31514     
31515     /**
31516      * Method: onMapResize
31517      * Notify the renderer of the change in size. 
31518      * 
31519      */
31520     onMapResize: function() {
31521         OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);
31522         
31523         var newSize = this.map.getSize();
31524         newSize.w = newSize.w * this.ratio;
31525         newSize.h = newSize.h * this.ratio;
31526         this.renderer.setSize(newSize);
31527     },
31528
31529     /**
31530      * Method: moveTo
31531      *  Reset the vector layer's div so that it once again is lined up with 
31532      *   the map. Notify the renderer of the change of extent, and in the
31533      *   case of a change of zoom level (resolution), have the 
31534      *   renderer redraw features.
31535      * 
31536      *  If the layer has not yet been drawn, cycle through the layer's 
31537      *   features and draw each one.
31538      * 
31539      * Parameters:
31540      * bounds - {<OpenLayers.Bounds>} 
31541      * zoomChanged - {Boolean} 
31542      * dragging - {Boolean} 
31543      */
31544     moveTo: function(bounds, zoomChanged, dragging) {
31545         OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
31546         
31547         var coordSysUnchanged = true;
31548         if (!dragging) {
31549             this.renderer.root.style.visibility = 'hidden';
31550
31551             var viewSize = this.map.getSize(),
31552                 viewWidth = viewSize.w,
31553                 viewHeight = viewSize.h,
31554                 offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2,
31555                 offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2;
31556             offsetLeft += this.map.layerContainerOriginPx.x;
31557             offsetLeft = -Math.round(offsetLeft);
31558             offsetTop += this.map.layerContainerOriginPx.y;
31559             offsetTop = -Math.round(offsetTop);
31560
31561             this.div.style.left = offsetLeft + 'px';
31562             this.div.style.top = offsetTop + 'px';
31563
31564             var extent = this.map.getExtent().scale(this.ratio);
31565             coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);
31566
31567             this.renderer.root.style.visibility = 'visible';
31568
31569             // Force a reflow on gecko based browsers to prevent jump/flicker.
31570             // This seems to happen on only certain configurations; it was originally
31571             // noticed in FF 2.0 and Linux.
31572             if (OpenLayers.IS_GECKO === true) {
31573                 this.div.scrollLeft = this.div.scrollLeft;
31574             }
31575             
31576             if (!zoomChanged && coordSysUnchanged) {
31577                 for (var i in this.unrenderedFeatures) {
31578                     var feature = this.unrenderedFeatures[i];
31579                     this.drawFeature(feature);
31580                 }
31581             }
31582         }
31583         if (!this.drawn || zoomChanged || !coordSysUnchanged) {
31584             this.drawn = true;
31585             var feature;
31586             for(var i=0, len=this.features.length; i<len; i++) {
31587                 this.renderer.locked = (i !== (len - 1));
31588                 feature = this.features[i];
31589                 this.drawFeature(feature);
31590             }
31591         }    
31592     },
31593     
31594     /** 
31595      * APIMethod: display
31596      * Hide or show the Layer
31597      * 
31598      * Parameters:
31599      * display - {Boolean}
31600      */
31601     display: function(display) {
31602         OpenLayers.Layer.prototype.display.apply(this, arguments);
31603         // we need to set the display style of the root in case it is attached
31604         // to a foreign layer
31605         var currentDisplay = this.div.style.display;
31606         if(currentDisplay != this.renderer.root.style.display) {
31607             this.renderer.root.style.display = currentDisplay;
31608         }
31609     },
31610
31611     /**
31612      * APIMethod: addFeatures
31613      * Add Features to the layer.
31614      *
31615      * Parameters:
31616      * features - {Array(<OpenLayers.Feature.Vector>)} 
31617      * options - {Object}
31618      */
31619     addFeatures: function(features, options) {
31620         if (!(OpenLayers.Util.isArray(features))) {
31621             features = [features];
31622         }
31623         
31624         var notify = !options || !options.silent;
31625         if(notify) {
31626             var event = {features: features};
31627             var ret = this.events.triggerEvent("beforefeaturesadded", event);
31628             if(ret === false) {
31629                 return;
31630             }
31631             features = event.features;
31632         }
31633         
31634         // Track successfully added features for featuresadded event, since
31635         // beforefeatureadded can veto single features.
31636         var featuresAdded = [];
31637         for (var i=0, len=features.length; i<len; i++) {
31638             if (i != (features.length - 1)) {
31639                 this.renderer.locked = true;
31640             } else {
31641                 this.renderer.locked = false;
31642             }    
31643             var feature = features[i];
31644             
31645             if (this.geometryType &&
31646               !(feature.geometry instanceof this.geometryType)) {
31647                 throw new TypeError('addFeatures: component should be an ' +
31648                                     this.geometryType.prototype.CLASS_NAME);
31649               }
31650
31651             //give feature reference to its layer
31652             feature.layer = this;
31653
31654             if (!feature.style && this.style) {
31655                 feature.style = OpenLayers.Util.extend({}, this.style);
31656             }
31657
31658             if (notify) {
31659                 if(this.events.triggerEvent("beforefeatureadded",
31660                                             {feature: feature}) === false) {
31661                     continue;
31662                 }
31663                 this.preFeatureInsert(feature);
31664             }
31665
31666             featuresAdded.push(feature);
31667             this.features.push(feature);
31668             this.drawFeature(feature);
31669             
31670             if (notify) {
31671                 this.events.triggerEvent("featureadded", {
31672                     feature: feature
31673                 });
31674                 this.onFeatureInsert(feature);
31675             }
31676         }
31677         
31678         if(notify) {
31679             this.events.triggerEvent("featuresadded", {features: featuresAdded});
31680         }
31681     },
31682
31683
31684     /**
31685      * APIMethod: removeFeatures
31686      * Remove features from the layer.  This erases any drawn features and
31687      *     removes them from the layer's control.  The beforefeatureremoved
31688      *     and featureremoved events will be triggered for each feature.  The
31689      *     featuresremoved event will be triggered after all features have
31690      *     been removed.  To supress event triggering, use the silent option.
31691      * 
31692      * Parameters:
31693      * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be
31694      *     removed.
31695      * options - {Object} Optional properties for changing behavior of the
31696      *     removal.
31697      *
31698      * Valid options:
31699      * silent - {Boolean} Supress event triggering.  Default is false.
31700      */
31701     removeFeatures: function(features, options) {
31702         if(!features || features.length === 0) {
31703             return;
31704         }
31705         if (features === this.features) {
31706             return this.removeAllFeatures(options);
31707         }
31708         if (!(OpenLayers.Util.isArray(features))) {
31709             features = [features];
31710         }
31711         if (features === this.selectedFeatures) {
31712             features = features.slice();
31713         }
31714
31715         var notify = !options || !options.silent;
31716         
31717         if (notify) {
31718             this.events.triggerEvent(
31719                 "beforefeaturesremoved", {features: features}
31720             );
31721         }
31722
31723         for (var i = features.length - 1; i >= 0; i--) {
31724             // We remain locked so long as we're not at 0
31725             // and the 'next' feature has a geometry. We do the geometry check
31726             // because if all the features after the current one are 'null', we
31727             // won't call eraseGeometry, so we break the 'renderer functions
31728             // will always be called with locked=false *last*' rule. The end result
31729             // is a possible gratiutious unlocking to save a loop through the rest 
31730             // of the list checking the remaining features every time. So long as
31731             // null geoms are rare, this is probably okay.    
31732             if (i != 0 && features[i-1].geometry) {
31733                 this.renderer.locked = true;
31734             } else {
31735                 this.renderer.locked = false;
31736             }
31737     
31738             var feature = features[i];
31739             delete this.unrenderedFeatures[feature.id];
31740
31741             if (notify) {
31742                 this.events.triggerEvent("beforefeatureremoved", {
31743                     feature: feature
31744                 });
31745             }
31746
31747             this.features = OpenLayers.Util.removeItem(this.features, feature);
31748             // feature has no layer at this point
31749             feature.layer = null;
31750
31751             if (feature.geometry) {
31752                 this.renderer.eraseFeatures(feature);
31753             }
31754                     
31755             //in the case that this feature is one of the selected features, 
31756             // remove it from that array as well.
31757             if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1){
31758                 OpenLayers.Util.removeItem(this.selectedFeatures, feature);
31759             }
31760
31761             if (notify) {
31762                 this.events.triggerEvent("featureremoved", {
31763                     feature: feature
31764                 });
31765             }
31766         }
31767
31768         if (notify) {
31769             this.events.triggerEvent("featuresremoved", {features: features});
31770         }
31771     },
31772     
31773     /** 
31774      * APIMethod: removeAllFeatures
31775      * Remove all features from the layer.
31776      *
31777      * Parameters:
31778      * options - {Object} Optional properties for changing behavior of the
31779      *     removal.
31780      *
31781      * Valid options:
31782      * silent - {Boolean} Supress event triggering.  Default is false.
31783      */
31784     removeAllFeatures: function(options) {
31785         var notify = !options || !options.silent;
31786         var features = this.features;
31787         if (notify) {
31788             this.events.triggerEvent(
31789                 "beforefeaturesremoved", {features: features}
31790             );
31791         }
31792         var feature;
31793         for (var i = features.length-1; i >= 0; i--) {
31794             feature = features[i];
31795             if (notify) {
31796                 this.events.triggerEvent("beforefeatureremoved", {
31797                     feature: feature
31798                 });
31799             }
31800             feature.layer = null;
31801             if (notify) {
31802                 this.events.triggerEvent("featureremoved", {
31803                     feature: feature
31804                 });
31805             }
31806         }
31807         this.renderer.clear();
31808         this.features = [];
31809         this.unrenderedFeatures = {};
31810         this.selectedFeatures = [];
31811         if (notify) {
31812             this.events.triggerEvent("featuresremoved", {features: features});
31813         }
31814     },
31815
31816     /**
31817      * APIMethod: destroyFeatures
31818      * Erase and destroy features on the layer.
31819      *
31820      * Parameters:
31821      * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of
31822      *     features to destroy.  If not supplied, all features on the layer
31823      *     will be destroyed.
31824      * options - {Object}
31825      */
31826     destroyFeatures: function(features, options) {
31827         var all = (features == undefined); // evaluates to true if
31828                                            // features is null
31829         if(all) {
31830             features = this.features;
31831         }
31832         if(features) {
31833             this.removeFeatures(features, options);
31834             for(var i=features.length-1; i>=0; i--) {
31835                 features[i].destroy();
31836             }
31837         }
31838     },
31839
31840     /**
31841      * APIMethod: drawFeature
31842      * Draw (or redraw) a feature on the layer.  If the optional style argument
31843      * is included, this style will be used.  If no style is included, the
31844      * feature's style will be used.  If the feature doesn't have a style,
31845      * the layer's style will be used.
31846      * 
31847      * This function is not designed to be used when adding features to 
31848      * the layer (use addFeatures instead). It is meant to be used when
31849      * the style of a feature has changed, or in some other way needs to 
31850      * visually updated *after* it has already been added to a layer. You
31851      * must add the feature to the layer for most layer-related events to 
31852      * happen.
31853      *
31854      * Parameters: 
31855      * feature - {<OpenLayers.Feature.Vector>} 
31856      * style - {String | Object} Named render intent or full symbolizer object.
31857      */
31858     drawFeature: function(feature, style) {
31859         // don't try to draw the feature with the renderer if the layer is not 
31860         // drawn itself
31861         if (!this.drawn) {
31862             return;
31863         }
31864         if (typeof style != "object") {
31865             if(!style && feature.state === OpenLayers.State.DELETE) {
31866                 style = "delete";
31867             }
31868             var renderIntent = style || feature.renderIntent;
31869             style = feature.style || this.style;
31870             if (!style) {
31871                 style = this.styleMap.createSymbolizer(feature, renderIntent);
31872             }
31873         }
31874         
31875         var drawn = this.renderer.drawFeature(feature, style);
31876         //TODO remove the check for null when we get rid of Renderer.SVG
31877         if (drawn === false || drawn === null) {
31878             this.unrenderedFeatures[feature.id] = feature;
31879         } else {
31880             delete this.unrenderedFeatures[feature.id];
31881         }
31882     },
31883     
31884     /**
31885      * Method: eraseFeatures
31886      * Erase features from the layer.
31887      *
31888      * Parameters:
31889      * features - {Array(<OpenLayers.Feature.Vector>)} 
31890      */
31891     eraseFeatures: function(features) {
31892         this.renderer.eraseFeatures(features);
31893     },
31894
31895     /**
31896      * Method: getFeatureFromEvent
31897      * Given an event, return a feature if the event occurred over one.
31898      * Otherwise, return null.
31899      *
31900      * Parameters:
31901      * evt - {Event} 
31902      *
31903      * Returns:
31904      * {<OpenLayers.Feature.Vector>} A feature if one was under the event.
31905      */
31906     getFeatureFromEvent: function(evt) {
31907         if (!this.renderer) {
31908             throw new Error('getFeatureFromEvent called on layer with no ' +
31909                             'renderer. This usually means you destroyed a ' +
31910                             'layer, but not some handler which is associated ' +
31911                             'with it.');
31912         }
31913         var feature = null;
31914         var featureId = this.renderer.getFeatureIdFromEvent(evt);
31915         if (featureId) {
31916             if (typeof featureId === "string") {
31917                 feature = this.getFeatureById(featureId);
31918             } else {
31919                 feature = featureId;
31920             }
31921         }
31922         return feature;
31923     },
31924
31925     /**
31926      * APIMethod: getFeatureBy
31927      * Given a property value, return the feature if it exists in the features array
31928      *
31929      * Parameters:
31930      * property - {String}
31931      * value - {String}
31932      *
31933      * Returns:
31934      * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
31935      * property value or null if there is no such feature.
31936      */
31937     getFeatureBy: function(property, value) {
31938         //TBD - would it be more efficient to use a hash for this.features?
31939         var feature = null;
31940         for(var i=0, len=this.features.length; i<len; ++i) {
31941             if(this.features[i][property] == value) {
31942                 feature = this.features[i];
31943                 break;
31944             }
31945         }
31946         return feature;
31947     },
31948
31949     /**
31950      * APIMethod: getFeatureById
31951      * Given a feature id, return the feature if it exists in the features array
31952      *
31953      * Parameters:
31954      * featureId - {String}
31955      *
31956      * Returns:
31957      * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
31958      * featureId or null if there is no such feature.
31959      */
31960     getFeatureById: function(featureId) {
31961         return this.getFeatureBy('id', featureId);
31962     },
31963
31964     /**
31965      * APIMethod: getFeatureByFid
31966      * Given a feature fid, return the feature if it exists in the features array
31967      *
31968      * Parameters:
31969      * featureFid - {String}
31970      *
31971      * Returns:
31972      * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
31973      * featureFid or null if there is no such feature.
31974      */
31975     getFeatureByFid: function(featureFid) {
31976         return this.getFeatureBy('fid', featureFid);
31977     },
31978     
31979     /**
31980      * APIMethod: getFeaturesByAttribute
31981      * Returns an array of features that have the given attribute key set to the
31982      * given value. Comparison of attribute values takes care of datatypes, e.g.
31983      * the string '1234' is not equal to the number 1234.
31984      *
31985      * Parameters:
31986      * attrName - {String}
31987      * attrValue - {Mixed}
31988      *
31989      * Returns:
31990      * Array({<OpenLayers.Feature.Vector>}) An array of features that have the 
31991      * passed named attribute set to the given value.
31992      */
31993     getFeaturesByAttribute: function(attrName, attrValue) {
31994         var i,
31995             feature,    
31996             len = this.features.length,
31997             foundFeatures = [];
31998         for(i = 0; i < len; i++) {            
31999             feature = this.features[i];
32000             if(feature && feature.attributes) {
32001                 if (feature.attributes[attrName] === attrValue) {
32002                     foundFeatures.push(feature);
32003                 }
32004             }
32005         }
32006         return foundFeatures;
32007     },
32008
32009     /**
32010      * Unselect the selected features
32011      * i.e. clears the featureSelection array
32012      * change the style back
32013     clearSelection: function() {
32014
32015        var vectorLayer = this.map.vectorLayer;
32016         for (var i = 0; i < this.map.featureSelection.length; i++) {
32017             var featureSelection = this.map.featureSelection[i];
32018             vectorLayer.drawFeature(featureSelection, vectorLayer.style);
32019         }
32020         this.map.featureSelection = [];
32021     },
32022      */
32023
32024
32025     /**
32026      * APIMethod: onFeatureInsert
32027      * method called after a feature is inserted.
32028      * Does nothing by default. Override this if you
32029      * need to do something on feature updates.
32030      *
32031      * Parameters: 
32032      * feature - {<OpenLayers.Feature.Vector>} 
32033      */
32034     onFeatureInsert: function(feature) {
32035     },
32036     
32037     /**
32038      * APIMethod: preFeatureInsert
32039      * method called before a feature is inserted.
32040      * Does nothing by default. Override this if you
32041      * need to do something when features are first added to the
32042      * layer, but before they are drawn, such as adjust the style.
32043      *
32044      * Parameters:
32045      * feature - {<OpenLayers.Feature.Vector>} 
32046      */
32047     preFeatureInsert: function(feature) {
32048     },
32049
32050     /** 
32051      * APIMethod: getDataExtent
32052      * Calculates the max extent which includes all of the features.
32053      * 
32054      * Returns:
32055      * {<OpenLayers.Bounds>} or null if the layer has no features with
32056      * geometries.
32057      */
32058     getDataExtent: function () {
32059         var maxExtent = null;
32060         var features = this.features;
32061         if(features && (features.length > 0)) {
32062             var geometry = null;
32063             for(var i=0, len=features.length; i<len; i++) {
32064                 geometry = features[i].geometry;
32065                 if (geometry) {
32066                     if (maxExtent === null) {
32067                         maxExtent = new OpenLayers.Bounds();
32068                     }
32069                     maxExtent.extend(geometry.getBounds());
32070                 }
32071             }
32072         }
32073         return maxExtent;
32074     },
32075
32076     CLASS_NAME: "OpenLayers.Layer.Vector"
32077 });
32078 /* ======================================================================
32079     OpenLayers/Layer/Vector/RootContainer.js
32080    ====================================================================== */
32081
32082 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
32083  * full list of contributors). Published under the 2-clause BSD license.
32084  * See license.txt in the OpenLayers distribution or repository for the
32085  * full text of the license. */
32086
32087 /**
32088  * @requires OpenLayers/Layer/Vector.js
32089  */
32090
32091 /**
32092  * Class: OpenLayers.Layer.Vector.RootContainer
32093  * A special layer type to combine multiple vector layers inside a single
32094  *     renderer root container. This class is not supposed to be instantiated
32095  *     from user space, it is a helper class for controls that require event
32096  *     processing for multiple vector layers.
32097  *
32098  * Inherits from:
32099  *  - <OpenLayers.Layer.Vector>
32100  */
32101 OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {
32102     
32103     /**
32104      * Property: displayInLayerSwitcher
32105      * Set to false for this layer type
32106      */
32107     displayInLayerSwitcher: false,
32108     
32109     /**
32110      * APIProperty: layers
32111      * Layers that are attached to this container. Required config option.
32112      */
32113     layers: null,
32114     
32115     /**
32116      * Constructor: OpenLayers.Layer.Vector.RootContainer
32117      * Create a new root container for multiple vector layer. This constructor
32118      * is not supposed to be used from user space, it is only to be used by
32119      * controls that need feature selection across multiple vector layers.
32120      *
32121      * Parameters:
32122      * name - {String} A name for the layer
32123      * options - {Object} Optional object with non-default properties to set on
32124      *           the layer.
32125      * 
32126      * Required options properties:
32127      * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this
32128      *     container
32129      *
32130      * Returns:
32131      * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root
32132      *     container
32133      */
32134     
32135     /**
32136      * Method: display
32137      */
32138     display: function() {},
32139     
32140     /**
32141      * Method: getFeatureFromEvent
32142      * walk through the layers to find the feature returned by the event
32143      * 
32144      * Parameters:
32145      * evt - {Object} event object with a feature property
32146      * 
32147      * Returns:
32148      * {<OpenLayers.Feature.Vector>}
32149      */
32150     getFeatureFromEvent: function(evt) {
32151         var layers = this.layers;
32152         var feature;
32153         for(var i=0; i<layers.length; i++) {
32154             feature = layers[i].getFeatureFromEvent(evt);
32155             if(feature) {
32156                 return feature;
32157             }
32158         }
32159     },
32160     
32161     /**
32162      * Method: setMap
32163      * 
32164      * Parameters:
32165      * map - {<OpenLayers.Map>}
32166      */
32167     setMap: function(map) {
32168         OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
32169         this.collectRoots();
32170         map.events.register("changelayer", this, this.handleChangeLayer);
32171     },
32172     
32173     /**
32174      * Method: removeMap
32175      * 
32176      * Parameters:
32177      * map - {<OpenLayers.Map>}
32178      */
32179     removeMap: function(map) {
32180         map.events.unregister("changelayer", this, this.handleChangeLayer);
32181         this.resetRoots();
32182         OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);
32183     },
32184     
32185     /**
32186      * Method: collectRoots
32187      * Collects the root nodes of all layers this control is configured with
32188      * and moveswien the nodes to this control's layer
32189      */
32190     collectRoots: function() {
32191         var layer;
32192         // walk through all map layers, because we want to keep the order
32193         for(var i=0; i<this.map.layers.length; ++i) {
32194             layer = this.map.layers[i];
32195             if(OpenLayers.Util.indexOf(this.layers, layer) != -1) {
32196                 layer.renderer.moveRoot(this.renderer);
32197             }
32198         }
32199     },
32200     
32201     /**
32202      * Method: resetRoots
32203      * Resets the root nodes back into the layers they belong to.
32204      */
32205     resetRoots: function() {
32206         var layer;
32207         for(var i=0; i<this.layers.length; ++i) {
32208             layer = this.layers[i];
32209             if(this.renderer && layer.renderer.getRenderLayerId() == this.id) {
32210                 this.renderer.moveRoot(layer.renderer);
32211             }
32212         }
32213     },
32214     
32215     /**
32216      * Method: handleChangeLayer
32217      * Event handler for the map's changelayer event. We need to rebuild
32218      * this container's layer dom if order of one of its layers changes.
32219      * This handler is added with the setMap method, and removed with the
32220      * removeMap method.
32221      * 
32222      * Parameters:
32223      * evt - {Object}
32224      */
32225     handleChangeLayer: function(evt) {
32226         var layer = evt.layer;
32227         if(evt.property == "order" &&
32228                         OpenLayers.Util.indexOf(this.layers, layer) != -1) {
32229             this.resetRoots();
32230             this.collectRoots();
32231         }
32232     },
32233
32234     CLASS_NAME: "OpenLayers.Layer.Vector.RootContainer"
32235 });
32236 /* ======================================================================
32237     OpenLayers/Control/SelectFeature.js
32238    ====================================================================== */
32239
32240 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
32241  * full list of contributors). Published under the 2-clause BSD license.
32242  * See license.txt in the OpenLayers distribution or repository for the
32243  * full text of the license. */
32244
32245
32246 /**
32247  * @requires OpenLayers/Control.js
32248  * @requires OpenLayers/Feature/Vector.js
32249  * @requires OpenLayers/Handler/Feature.js
32250  * @requires OpenLayers/Layer/Vector/RootContainer.js
32251  */
32252
32253 /**
32254  * Class: OpenLayers.Control.SelectFeature
32255  * The SelectFeature control selects vector features from a given layer on 
32256  * click or hover. 
32257  *
32258  * Inherits from:
32259  *  - <OpenLayers.Control>
32260  */
32261 OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
32262
32263     /** 
32264      * APIProperty: events
32265      * {<OpenLayers.Events>} Events instance for listeners and triggering
32266      *     control specific events.
32267      *
32268      * Register a listener for a particular event with the following syntax:
32269      * (code)
32270      * control.events.register(type, obj, listener);
32271      * (end)
32272      *
32273      * Supported event types (in addition to those from <OpenLayers.Control.events>):
32274      * beforefeaturehighlighted - Triggered before a feature is highlighted
32275      * featurehighlighted - Triggered when a feature is highlighted
32276      * featureunhighlighted - Triggered when a feature is unhighlighted
32277      * boxselectionstart - Triggered before box selection starts
32278      * boxselectionend - Triggered after box selection ends
32279      */
32280     
32281     /**
32282      * Property: multipleKey
32283      * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
32284      *     the <multiple> property to true.  Default is null.
32285      */
32286     multipleKey: null,
32287     
32288     /**
32289      * Property: toggleKey
32290      * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
32291      *     the <toggle> property to true.  Default is null.
32292      */
32293     toggleKey: null,
32294     
32295     /**
32296      * APIProperty: multiple
32297      * {Boolean} Allow selection of multiple geometries.  Default is false.
32298      */
32299     multiple: false, 
32300
32301     /**
32302      * APIProperty: clickout
32303      * {Boolean} Unselect features when clicking outside any feature.
32304      *     Default is true.
32305      */
32306     clickout: true,
32307
32308     /**
32309      * APIProperty: toggle
32310      * {Boolean} Unselect a selected feature on click.  Default is false.  Only
32311      *     has meaning if hover is false.
32312      */
32313     toggle: false,
32314
32315     /**
32316      * APIProperty: hover
32317      * {Boolean} Select on mouse over and deselect on mouse out.  If true, this
32318      * ignores clicks and only listens to mouse moves.
32319      */
32320     hover: false,
32321
32322     /**
32323      * APIProperty: highlightOnly
32324      * {Boolean} If true do not actually select features (that is place them in 
32325      * the layer's selected features array), just highlight them. This property
32326      * has no effect if hover is false. Defaults to false.
32327      */
32328     highlightOnly: false,
32329     
32330     /**
32331      * APIProperty: box
32332      * {Boolean} Allow feature selection by drawing a box.
32333      */
32334     box: false,
32335     
32336     /**
32337      * Property: onBeforeSelect 
32338      * {Function} Optional function to be called before a feature is selected.
32339      *     The function should expect to be called with a feature.
32340      */
32341     onBeforeSelect: function() {},
32342     
32343     /**
32344      * APIProperty: onSelect 
32345      * {Function} Optional function to be called when a feature is selected.
32346      *     The function should expect to be called with a feature.
32347      */
32348     onSelect: function() {},
32349
32350     /**
32351      * APIProperty: onUnselect
32352      * {Function} Optional function to be called when a feature is unselected.
32353      *     The function should expect to be called with a feature.
32354      */
32355     onUnselect: function() {},
32356     
32357     /**
32358      * Property: scope
32359      * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect
32360      *     callbacks. If null the scope will be this control.
32361      */
32362     scope: null,
32363
32364     /**
32365      * APIProperty: geometryTypes
32366      * {Array(String)} To restrict selecting to a limited set of geometry types,
32367      *     send a list of strings corresponding to the geometry class names.
32368      */
32369     geometryTypes: null,
32370
32371     /**
32372      * Property: layer
32373      * {<OpenLayers.Layer.Vector>} The vector layer with a common renderer
32374      * root for all layers this control is configured with (if an array of
32375      * layers was passed to the constructor), or the vector layer the control
32376      * was configured with (if a single layer was passed to the constructor).
32377      */
32378     layer: null,
32379     
32380     /**
32381      * Property: layers
32382      * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on,
32383      * or null if the control was configured with a single layer
32384      */
32385     layers: null,
32386     
32387     /**
32388      * APIProperty: callbacks
32389      * {Object} The functions that are sent to the handlers.feature for callback
32390      */
32391     callbacks: null,
32392     
32393     /**
32394      * APIProperty: selectStyle 
32395      * {Object} Hash of styles
32396      */
32397     selectStyle: null,
32398     
32399     /**
32400      * Property: renderIntent
32401      * {String} key used to retrieve the select style from the layer's
32402      * style map.
32403      */
32404     renderIntent: "select",
32405
32406     /**
32407      * Property: handlers
32408      * {Object} Object with references to multiple <OpenLayers.Handler>
32409      *     instances.
32410      */
32411     handlers: null,
32412
32413     /**
32414      * Constructor: OpenLayers.Control.SelectFeature
32415      * Create a new control for selecting features.
32416      *
32417      * Parameters:
32418      * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The
32419      *     layer(s) this control will select features from.
32420      * options - {Object} 
32421      */
32422     initialize: function(layers, options) {
32423         OpenLayers.Control.prototype.initialize.apply(this, [options]);
32424         
32425         if(this.scope === null) {
32426             this.scope = this;
32427         }
32428         this.initLayer(layers);
32429         var callbacks = {
32430             click: this.clickFeature,
32431             clickout: this.clickoutFeature
32432         };
32433         if (this.hover) {
32434             callbacks.over = this.overFeature;
32435             callbacks.out = this.outFeature;
32436         }
32437              
32438         this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
32439         this.handlers = {
32440             feature: new OpenLayers.Handler.Feature(
32441                 this, this.layer, this.callbacks,
32442                 {geometryTypes: this.geometryTypes}
32443             )
32444         };
32445
32446         if (this.box) {
32447             this.handlers.box = new OpenLayers.Handler.Box(
32448                 this, {done: this.selectBox},
32449                 {boxDivClassName: "olHandlerBoxSelectFeature"}
32450             ); 
32451         }
32452     },
32453
32454     /**
32455      * Method: initLayer
32456      * Assign the layer property. If layers is an array, we need to use
32457      *     a RootContainer.
32458      *
32459      * Parameters:
32460      * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers.
32461      */
32462     initLayer: function(layers) {
32463         if(OpenLayers.Util.isArray(layers)) {
32464             this.layers = layers;
32465             this.layer = new OpenLayers.Layer.Vector.RootContainer(
32466                 this.id + "_container", {
32467                     layers: layers
32468                 }
32469             );
32470         } else {
32471             this.layer = layers;
32472         }
32473     },
32474     
32475     /**
32476      * Method: destroy
32477      */
32478     destroy: function() {
32479         if(this.active && this.layers) {
32480             this.map.removeLayer(this.layer);
32481         }
32482         OpenLayers.Control.prototype.destroy.apply(this, arguments);
32483         if(this.layers) {
32484             this.layer.destroy();
32485         }
32486     },
32487
32488     /**
32489      * Method: activate
32490      * Activates the control.
32491      * 
32492      * Returns:
32493      * {Boolean} The control was effectively activated.
32494      */
32495     activate: function () {
32496         if (!this.active) {
32497             if(this.layers) {
32498                 this.map.addLayer(this.layer);
32499             }
32500             this.handlers.feature.activate();
32501             if(this.box && this.handlers.box) {
32502                 this.handlers.box.activate();
32503             }
32504         }
32505         return OpenLayers.Control.prototype.activate.apply(
32506             this, arguments
32507         );
32508     },
32509
32510     /**
32511      * Method: deactivate
32512      * Deactivates the control.
32513      * 
32514      * Returns:
32515      * {Boolean} The control was effectively deactivated.
32516      */
32517     deactivate: function () {
32518         if (this.active) {
32519             this.handlers.feature.deactivate();
32520             if(this.handlers.box) {
32521                 this.handlers.box.deactivate();
32522             }
32523             if(this.layers) {
32524                 this.map.removeLayer(this.layer);
32525             }
32526         }
32527         return OpenLayers.Control.prototype.deactivate.apply(
32528             this, arguments
32529         );
32530     },
32531
32532     /**
32533      * Method: unselectAll
32534      * Unselect all selected features.  To unselect all except for a single
32535      *     feature, set the options.except property to the feature.
32536      *
32537      * Parameters:
32538      * options - {Object} Optional configuration object.
32539      */
32540     unselectAll: function(options) {
32541         // we'll want an option to supress notification here
32542         var layers = this.layers || [this.layer],
32543             layer, feature, l, numExcept;
32544         for(l=0; l<layers.length; ++l) {
32545             layer = layers[l];
32546             numExcept = 0;
32547             //layer.selectedFeatures is null when layer is destroyed and 
32548             //one of it's preremovelayer listener calls setLayer 
32549             //with another layer on this control
32550             if(layer.selectedFeatures != null) {
32551                 while(layer.selectedFeatures.length > numExcept) {
32552                     feature = layer.selectedFeatures[numExcept];
32553                     if(!options || options.except != feature) {
32554                         this.unselect(feature);
32555                     } else {
32556                         ++numExcept;
32557                     }
32558                 }
32559             }
32560         }
32561     },
32562
32563     /**
32564      * Method: clickFeature
32565      * Called on click in a feature
32566      * Only responds if this.hover is false.
32567      *
32568      * Parameters:
32569      * feature - {<OpenLayers.Feature.Vector>} 
32570      */
32571     clickFeature: function(feature) {
32572         if(!this.hover) {
32573             var selected = (OpenLayers.Util.indexOf(
32574                 feature.layer.selectedFeatures, feature) > -1);
32575             if(selected) {
32576                 if(this.toggleSelect()) {
32577                     this.unselect(feature);
32578                 } else if(!this.multipleSelect()) {
32579                     this.unselectAll({except: feature});
32580                 }
32581             } else {
32582                 if(!this.multipleSelect()) {
32583                     this.unselectAll({except: feature});
32584                 }
32585                 this.select(feature);
32586             }
32587         }
32588     },
32589
32590     /**
32591      * Method: multipleSelect
32592      * Allow for multiple selected features based on <multiple> property and
32593      *     <multipleKey> event modifier.
32594      *
32595      * Returns:
32596      * {Boolean} Allow for multiple selected features.
32597      */
32598     multipleSelect: function() {
32599         return this.multiple || (this.handlers.feature.evt &&
32600                                  this.handlers.feature.evt[this.multipleKey]);
32601     },
32602     
32603     /**
32604      * Method: toggleSelect
32605      * Event should toggle the selected state of a feature based on <toggle>
32606      *     property and <toggleKey> event modifier.
32607      *
32608      * Returns:
32609      * {Boolean} Toggle the selected state of a feature.
32610      */
32611     toggleSelect: function() {
32612         return this.toggle || (this.handlers.feature.evt &&
32613                                this.handlers.feature.evt[this.toggleKey]);
32614     },
32615
32616     /**
32617      * Method: clickoutFeature
32618      * Called on click outside a previously clicked (selected) feature.
32619      * Only responds if this.hover is false.
32620      *
32621      * Parameters:
32622      * feature - {<OpenLayers.Vector.Feature>} 
32623      */
32624     clickoutFeature: function(feature) {
32625         if(!this.hover && this.clickout) {
32626             this.unselectAll();
32627         }
32628     },
32629
32630     /**
32631      * Method: overFeature
32632      * Called on over a feature.
32633      * Only responds if this.hover is true.
32634      *
32635      * Parameters:
32636      * feature - {<OpenLayers.Feature.Vector>} 
32637      */
32638     overFeature: function(feature) {
32639         var layer = feature.layer;
32640         if(this.hover) {
32641             if(this.highlightOnly) {
32642                 this.highlight(feature);
32643             } else if(OpenLayers.Util.indexOf(
32644                 layer.selectedFeatures, feature) == -1) {
32645                 this.select(feature);
32646             }
32647         }
32648     },
32649
32650     /**
32651      * Method: outFeature
32652      * Called on out of a selected feature.
32653      * Only responds if this.hover is true.
32654      *
32655      * Parameters:
32656      * feature - {<OpenLayers.Feature.Vector>} 
32657      */
32658     outFeature: function(feature) {
32659         if(this.hover) {
32660             if(this.highlightOnly) {
32661                 // we do nothing if we're not the last highlighter of the
32662                 // feature
32663                 if(feature._lastHighlighter == this.id) {
32664                     // if another select control had highlighted the feature before
32665                     // we did it ourself then we use that control to highlight the
32666                     // feature as it was before we highlighted it, else we just
32667                     // unhighlight it
32668                     if(feature._prevHighlighter &&
32669                        feature._prevHighlighter != this.id) {
32670                         delete feature._lastHighlighter;
32671                         var control = this.map.getControl(
32672                             feature._prevHighlighter);
32673                         if(control) {
32674                             control.highlight(feature);
32675                         }
32676                     } else {
32677                         this.unhighlight(feature);
32678                     }
32679                 }
32680             } else {
32681                 this.unselect(feature);
32682             }
32683         }
32684     },
32685
32686     /**
32687      * Method: highlight
32688      * Redraw feature with the select style.
32689      *
32690      * Parameters:
32691      * feature - {<OpenLayers.Feature.Vector>} 
32692      */
32693     highlight: function(feature) {
32694         var layer = feature.layer;
32695         var cont = this.events.triggerEvent("beforefeaturehighlighted", {
32696             feature : feature
32697         });
32698         if(cont !== false) {
32699             feature._prevHighlighter = feature._lastHighlighter;
32700             feature._lastHighlighter = this.id;
32701             var style = this.selectStyle || this.renderIntent;
32702             layer.drawFeature(feature, style);
32703             this.events.triggerEvent("featurehighlighted", {feature : feature});
32704         }
32705     },
32706
32707     /**
32708      * Method: unhighlight
32709      * Redraw feature with the "default" style
32710      *
32711      * Parameters:
32712      * feature - {<OpenLayers.Feature.Vector>} 
32713      */
32714     unhighlight: function(feature) {
32715         var layer = feature.layer;
32716         // three cases:
32717         // 1. there's no other highlighter, in that case _prev is undefined,
32718         //    and we just need to undef _last
32719         // 2. another control highlighted the feature after we did it, in
32720         //    that case _last references this other control, and we just
32721         //    need to undef _prev
32722         // 3. another control highlighted the feature before we did it, in
32723         //    that case _prev references this other control, and we need to
32724         //    set _last to _prev and undef _prev
32725         if(feature._prevHighlighter == undefined) {
32726             delete feature._lastHighlighter;
32727         } else if(feature._prevHighlighter == this.id) {
32728             delete feature._prevHighlighter;
32729         } else {
32730             feature._lastHighlighter = feature._prevHighlighter;
32731             delete feature._prevHighlighter;
32732         }
32733         layer.drawFeature(feature, feature.style || feature.layer.style ||
32734             "default");
32735         this.events.triggerEvent("featureunhighlighted", {feature : feature});
32736     },
32737     
32738     /**
32739      * Method: select
32740      * Add feature to the layer's selectedFeature array, render the feature as
32741      * selected, and call the onSelect function.
32742      * 
32743      * Parameters:
32744      * feature - {<OpenLayers.Feature.Vector>} 
32745      */
32746     select: function(feature) {
32747         var cont = this.onBeforeSelect.call(this.scope, feature);
32748         var layer = feature.layer;
32749         if(cont !== false) {
32750             cont = layer.events.triggerEvent("beforefeatureselected", {
32751                 feature: feature
32752             });
32753             if(cont !== false) {
32754                 layer.selectedFeatures.push(feature);
32755                 this.highlight(feature);
32756                 // if the feature handler isn't involved in the feature
32757                 // selection (because the box handler is used or the
32758                 // feature is selected programatically) we fake the
32759                 // feature handler to allow unselecting on click
32760                 if(!this.handlers.feature.lastFeature) {
32761                     this.handlers.feature.lastFeature = layer.selectedFeatures[0];
32762                 }
32763                 layer.events.triggerEvent("featureselected", {feature: feature});
32764                 this.onSelect.call(this.scope, feature);
32765             }
32766         }
32767     },
32768
32769     /**
32770      * Method: unselect
32771      * Remove feature from the layer's selectedFeature array, render the feature as
32772      * normal, and call the onUnselect function.
32773      *
32774      * Parameters:
32775      * feature - {<OpenLayers.Feature.Vector>}
32776      */
32777     unselect: function(feature) {
32778         var layer = feature.layer;
32779         // Store feature style for restoration later
32780         this.unhighlight(feature);
32781         OpenLayers.Util.removeItem(layer.selectedFeatures, feature);
32782         layer.events.triggerEvent("featureunselected", {feature: feature});
32783         this.onUnselect.call(this.scope, feature);
32784     },
32785     
32786     /**
32787      * Method: selectBox
32788      * Callback from the handlers.box set up when <box> selection is true
32789      *     on.
32790      *
32791      * Parameters:
32792      * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> }  
32793      */
32794     selectBox: function(position) {
32795         if (position instanceof OpenLayers.Bounds) {
32796             var minXY = this.map.getLonLatFromPixel({
32797                 x: position.left,
32798                 y: position.bottom
32799             });
32800             var maxXY = this.map.getLonLatFromPixel({
32801                 x: position.right,
32802                 y: position.top
32803             });
32804             var bounds = new OpenLayers.Bounds(
32805                 minXY.lon, minXY.lat, maxXY.lon, maxXY.lat
32806             );
32807             
32808             // if multiple is false, first deselect currently selected features
32809             if (!this.multipleSelect()) {
32810                 this.unselectAll();
32811             }
32812             
32813             // because we're using a box, we consider we want multiple selection
32814             var prevMultiple = this.multiple;
32815             this.multiple = true;
32816             var layers = this.layers || [this.layer];
32817             this.events.triggerEvent("boxselectionstart", {layers: layers}); 
32818             var layer;
32819             for(var l=0; l<layers.length; ++l) {
32820                 layer = layers[l];
32821                 for(var i=0, len = layer.features.length; i<len; ++i) {
32822                     var feature = layer.features[i];
32823                     // check if the feature is displayed
32824                     if (!feature.getVisibility()) {
32825                         continue;
32826                     }
32827
32828                     if (this.geometryTypes == null || OpenLayers.Util.indexOf(
32829                             this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {
32830                         if (bounds.toGeometry().intersects(feature.geometry)) {
32831                             if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {
32832                                 this.select(feature);
32833                             }
32834                         }
32835                     }
32836                 }
32837             }
32838             this.multiple = prevMultiple;
32839             this.events.triggerEvent("boxselectionend", {layers: layers}); 
32840         }
32841     },
32842
32843     /** 
32844      * Method: setMap
32845      * Set the map property for the control. 
32846      * 
32847      * Parameters:
32848      * map - {<OpenLayers.Map>} 
32849      */
32850     setMap: function(map) {
32851         this.handlers.feature.setMap(map);
32852         if (this.box) {
32853             this.handlers.box.setMap(map);
32854         }
32855         OpenLayers.Control.prototype.setMap.apply(this, arguments);
32856     },
32857     
32858     /**
32859      * APIMethod: setLayer
32860      * Attach a new layer to the control, overriding any existing layers.
32861      *
32862      * Parameters:
32863      * layers - Array of {<OpenLayers.Layer.Vector>} or a single
32864      *     {<OpenLayers.Layer.Vector>}
32865      */
32866     setLayer: function(layers) {
32867         var isActive = this.active;
32868         this.unselectAll();
32869         this.deactivate();
32870         if(this.layers) {
32871             this.layer.destroy();
32872             this.layers = null;
32873         }
32874         this.initLayer(layers);
32875         this.handlers.feature.layer = this.layer;
32876         if (isActive) {
32877             this.activate();
32878         }
32879     },
32880     
32881     CLASS_NAME: "OpenLayers.Control.SelectFeature"
32882 });
32883 /* ======================================================================
32884     OpenLayers/Control/Attribution.js
32885    ====================================================================== */
32886
32887 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
32888  * full list of contributors). Published under the 2-clause BSD license.
32889  * See license.txt in the OpenLayers distribution or repository for the
32890  * full text of the license. */
32891
32892 /**
32893  * @requires OpenLayers/Control.js
32894  */
32895
32896 /**
32897  * Class: OpenLayers.Control.Attribution
32898  * The attribution control adds attribution from layers to the map display. 
32899  * It uses 'attribution' property of each layer.
32900  *
32901  * Inherits from:
32902  *  - <OpenLayers.Control>
32903  */
32904 OpenLayers.Control.Attribution = 
32905   OpenLayers.Class(OpenLayers.Control, {
32906     
32907     /**
32908      * APIProperty: separator
32909      * {String} String used to separate layers.
32910      */
32911     separator: ", ",
32912     
32913     /**
32914      * APIProperty: template
32915      * {String} Template for the attribution. This has to include the substring
32916      *     "${layers}", which will be replaced by the layer specific
32917      *     attributions, separated by <separator>. The default is "${layers}".
32918      */
32919     template: "${layers}",
32920     
32921     /**
32922      * Constructor: OpenLayers.Control.Attribution 
32923      * 
32924      * Parameters:
32925      * options - {Object} Options for control.
32926      */
32927
32928     /** 
32929      * Method: destroy
32930      * Destroy control.
32931      */
32932     destroy: function() {
32933         this.map.events.un({
32934             "removelayer": this.updateAttribution,
32935             "addlayer": this.updateAttribution,
32936             "changelayer": this.updateAttribution,
32937             "changebaselayer": this.updateAttribution,
32938             scope: this
32939         });
32940         
32941         OpenLayers.Control.prototype.destroy.apply(this, arguments);
32942     },    
32943     
32944     /**
32945      * Method: draw
32946      * Initialize control.
32947      * 
32948      * Returns: 
32949      * {DOMElement} A reference to the DIV DOMElement containing the control
32950      */    
32951     draw: function() {
32952         OpenLayers.Control.prototype.draw.apply(this, arguments);
32953         
32954         this.map.events.on({
32955             'changebaselayer': this.updateAttribution,
32956             'changelayer': this.updateAttribution,
32957             'addlayer': this.updateAttribution,
32958             'removelayer': this.updateAttribution,
32959             scope: this
32960         });
32961         this.updateAttribution();
32962         
32963         return this.div;    
32964     },
32965
32966     /**
32967      * Method: updateAttribution
32968      * Update attribution string.
32969      */
32970     updateAttribution: function() {
32971         var attributions = [];
32972         if (this.map && this.map.layers) {
32973             for(var i=0, len=this.map.layers.length; i<len; i++) {
32974                 var layer = this.map.layers[i];
32975                 if (layer.attribution && layer.getVisibility()) {
32976                     // add attribution only if attribution text is unique
32977                     if (OpenLayers.Util.indexOf(
32978                                     attributions, layer.attribution) === -1) {
32979                         attributions.push( layer.attribution );
32980                     }
32981                 }
32982             } 
32983             this.div.innerHTML = OpenLayers.String.format(this.template, {
32984                 layers: attributions.join(this.separator)
32985             });
32986         }
32987     },
32988
32989     CLASS_NAME: "OpenLayers.Control.Attribution"
32990 });
32991 /* ======================================================================
32992     OpenLayers/Handler/Drag.js
32993    ====================================================================== */
32994
32995 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
32996  * full list of contributors). Published under the 2-clause BSD license.
32997  * See license.txt in the OpenLayers distribution or repository for the
32998  * full text of the license. */
32999
33000 /**
33001  * @requires OpenLayers/Handler.js
33002  */
33003
33004 /**
33005  * Class: OpenLayers.Handler.Drag
33006  * The drag handler is used to deal with sequences of browser events related
33007  *     to dragging.  The handler is used by controls that want to know when
33008  *     a drag sequence begins, when a drag is happening, and when it has
33009  *     finished.
33010  *
33011  * Controls that use the drag handler typically construct it with callbacks
33012  *     for 'down', 'move', and 'done'.  Callbacks for these keys are called
33013  *     when the drag begins, with each move, and when the drag is done.  In
33014  *     addition, controls can have callbacks keyed to 'up' and 'out' if they
33015  *     care to differentiate between the types of events that correspond with
33016  *     the end of a drag sequence.  If no drag actually occurs (no mouse move)
33017  *     the 'down' and 'up' callbacks will be called, but not the 'done'
33018  *     callback.
33019  *
33020  * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.
33021  *
33022  * Inherits from:
33023  *  - <OpenLayers.Handler>
33024  */
33025 OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {
33026   
33027     /** 
33028      * Property: started
33029      * {Boolean} When a mousedown or touchstart event is received, we want to
33030      * record it, but not set 'dragging' until the mouse moves after starting.
33031      */
33032     started: false,
33033
33034     /**
33035      * Property: stopDown
33036      * {Boolean} Stop propagation of mousedown events from getting to listeners
33037      *     on the same element.  Default is true.
33038      */
33039     stopDown: true,
33040
33041     /** 
33042      * Property: dragging 
33043      * {Boolean} 
33044      */
33045     dragging: false,
33046
33047     /** 
33048      * Property: last
33049      * {<OpenLayers.Pixel>} The last pixel location of the drag.
33050      */
33051     last: null,
33052
33053     /** 
33054      * Property: start
33055      * {<OpenLayers.Pixel>} The first pixel location of the drag.
33056      */
33057     start: null,
33058
33059     /**
33060      * Property: lastMoveEvt
33061      * {Object} The last mousemove event that occurred. Used to
33062      *     position the map correctly when our "delay drag"
33063      *     timeout expired.
33064      */
33065     lastMoveEvt: null,
33066
33067     /**
33068      * Property: oldOnselectstart
33069      * {Function}
33070      */
33071     oldOnselectstart: null,
33072     
33073     /**
33074      * Property: interval
33075      * {Integer} In order to increase performance, an interval (in 
33076      *     milliseconds) can be set to reduce the number of drag events 
33077      *     called. If set, a new drag event will not be set until the 
33078      *     interval has passed. 
33079      *     Defaults to 0, meaning no interval. 
33080      */
33081     interval: 0,
33082     
33083     /**
33084      * Property: timeoutId
33085      * {String} The id of the timeout used for the mousedown interval.
33086      *     This is "private", and should be left alone.
33087      */
33088     timeoutId: null,
33089     
33090     /**
33091      * APIProperty: documentDrag
33092      * {Boolean} If set to true, the handler will also handle mouse moves when
33093      *     the cursor has moved out of the map viewport. Default is false.
33094      */
33095     documentDrag: false,
33096     
33097     /**
33098      * Property: documentEvents
33099      * {Boolean} Are we currently observing document events?
33100      */
33101     documentEvents: null,
33102
33103     /**
33104      * Constructor: OpenLayers.Handler.Drag
33105      * Returns OpenLayers.Handler.Drag
33106      * 
33107      * Parameters:
33108      * control - {<OpenLayers.Control>} The control that is making use of
33109      *     this handler.  If a handler is being used without a control, the
33110      *     handlers setMap method must be overridden to deal properly with
33111      *     the map.
33112      * callbacks - {Object} An object containing a single function to be
33113      *     called when the drag operation is finished. The callback should
33114      *     expect to recieve a single argument, the pixel location of the event.
33115      *     Callbacks for 'move' and 'done' are supported. You can also speficy
33116      *     callbacks for 'down', 'up', and 'out' to respond to those events.
33117      * options - {Object} 
33118      */
33119     initialize: function(control, callbacks, options) {
33120         OpenLayers.Handler.prototype.initialize.apply(this, arguments);
33121         
33122         if (this.documentDrag === true) {
33123             var me = this;
33124             this._docMove = function(evt) {
33125                 me.mousemove({
33126                     xy: {x: evt.clientX, y: evt.clientY},
33127                     element: document
33128                 });
33129             };
33130             this._docUp = function(evt) {
33131                 me.mouseup({xy: {x: evt.clientX, y: evt.clientY}});
33132             };
33133         }
33134     },
33135
33136     
33137     /**
33138      * Method: dragstart
33139      * This private method is factorized from mousedown and touchstart methods
33140      *
33141      * Parameters:
33142      * evt - {Event} The event
33143      *
33144      * Returns:
33145      * {Boolean} Let the event propagate.
33146      */
33147     dragstart: function (evt) {
33148         var propagate = true;
33149         this.dragging = false;
33150         if (this.checkModifiers(evt) &&
33151                (OpenLayers.Event.isLeftClick(evt) ||
33152                 OpenLayers.Event.isSingleTouch(evt))) {
33153             this.started = true;
33154             this.start = evt.xy;
33155             this.last = evt.xy;
33156             OpenLayers.Element.addClass(
33157                 this.map.viewPortDiv, "olDragDown"
33158             );
33159             this.down(evt);
33160             this.callback("down", [evt.xy]);
33161
33162             // prevent document dragging
33163             OpenLayers.Event.preventDefault(evt);
33164
33165             if(!this.oldOnselectstart) {
33166                 this.oldOnselectstart = document.onselectstart ?
33167                     document.onselectstart : OpenLayers.Function.True;
33168             }
33169             document.onselectstart = OpenLayers.Function.False;
33170
33171             propagate = !this.stopDown;
33172         } else {
33173             this.started = false;
33174             this.start = null;
33175             this.last = null;
33176         }
33177         return propagate;
33178     },
33179
33180     /**
33181      * Method: dragmove
33182      * This private method is factorized from mousemove and touchmove methods
33183      *
33184      * Parameters:
33185      * evt - {Event} The event
33186      *
33187      * Returns:
33188      * {Boolean} Let the event propagate.
33189      */
33190     dragmove: function (evt) {
33191         this.lastMoveEvt = evt;
33192         if (this.started && !this.timeoutId && (evt.xy.x != this.last.x ||
33193                                                 evt.xy.y != this.last.y)) {
33194             if(this.documentDrag === true && this.documentEvents) {
33195                 if(evt.element === document) {
33196                     this.adjustXY(evt);
33197                     // do setEvent manually because the documentEvents are not
33198                     // registered with the map
33199                     this.setEvent(evt);
33200                 } else {
33201                     this.removeDocumentEvents();
33202                 }
33203             }
33204             if (this.interval > 0) {
33205                 this.timeoutId = setTimeout(
33206                     OpenLayers.Function.bind(this.removeTimeout, this),
33207                     this.interval);
33208             }
33209             this.dragging = true;
33210
33211             this.move(evt);
33212             this.callback("move", [evt.xy]);
33213             if(!this.oldOnselectstart) {
33214                 this.oldOnselectstart = document.onselectstart;
33215                 document.onselectstart = OpenLayers.Function.False;
33216             }
33217             this.last = evt.xy;
33218         }
33219         return true;
33220     },
33221
33222     /**
33223      * Method: dragend
33224      * This private method is factorized from mouseup and touchend methods
33225      *
33226      * Parameters:
33227      * evt - {Event} The event
33228      *
33229      * Returns:
33230      * {Boolean} Let the event propagate.
33231      */
33232     dragend: function (evt) {
33233         if (this.started) {
33234             if(this.documentDrag === true && this.documentEvents) {
33235                 this.adjustXY(evt);
33236                 this.removeDocumentEvents();
33237             }
33238             var dragged = (this.start != this.last);
33239             this.started = false;
33240             this.dragging = false;
33241             OpenLayers.Element.removeClass(
33242                 this.map.viewPortDiv, "olDragDown"
33243             );
33244             this.up(evt);
33245             this.callback("up", [evt.xy]);
33246             if(dragged) {
33247                 this.callback("done", [evt.xy]);
33248             }
33249             document.onselectstart = this.oldOnselectstart;
33250         }
33251         return true;
33252     },
33253
33254     /**
33255      * The four methods below (down, move, up, and out) are used by subclasses
33256      *     to do their own processing related to these mouse events.
33257      */
33258
33259     /**
33260      * Method: down
33261      * This method is called during the handling of the mouse down event.
33262      *     Subclasses can do their own processing here.
33263      *
33264      * Parameters:
33265      * evt - {Event} The mouse down event
33266      */
33267     down: function(evt) {
33268     },
33269
33270     /**
33271      * Method: move
33272      * This method is called during the handling of the mouse move event.
33273      *     Subclasses can do their own processing here.
33274      *
33275      * Parameters:
33276      * evt - {Event} The mouse move event
33277      *
33278      */
33279     move: function(evt) {
33280     },
33281
33282     /**
33283      * Method: up
33284      * This method is called during the handling of the mouse up event.
33285      *     Subclasses can do their own processing here.
33286      *
33287      * Parameters:
33288      * evt - {Event} The mouse up event
33289      */
33290     up: function(evt) {
33291     },
33292
33293     /**
33294      * Method: out
33295      * This method is called during the handling of the mouse out event.
33296      *     Subclasses can do their own processing here.
33297      *
33298      * Parameters:
33299      * evt - {Event} The mouse out event
33300      */
33301     out: function(evt) {
33302     },
33303
33304     /**
33305      * The methods below are part of the magic of event handling.  Because
33306      *     they are named like browser events, they are registered as listeners
33307      *     for the events they represent.
33308      */
33309
33310     /**
33311      * Method: mousedown
33312      * Handle mousedown events
33313      *
33314      * Parameters:
33315      * evt - {Event}
33316      *
33317      * Returns:
33318      * {Boolean} Let the event propagate.
33319      */
33320     mousedown: function(evt) {
33321         return this.dragstart(evt);
33322     },
33323
33324     /**
33325      * Method: touchstart
33326      * Handle touchstart events
33327      *
33328      * Parameters:
33329      * evt - {Event}
33330      *
33331      * Returns:
33332      * {Boolean} Let the event propagate.
33333      */
33334     touchstart: function(evt) {
33335         this.startTouch();
33336         return this.dragstart(evt);
33337     },
33338
33339     /**
33340      * Method: mousemove
33341      * Handle mousemove events
33342      *
33343      * Parameters:
33344      * evt - {Event}
33345      *
33346      * Returns:
33347      * {Boolean} Let the event propagate.
33348      */
33349     mousemove: function(evt) {
33350         return this.dragmove(evt);
33351     },
33352
33353     /**
33354      * Method: touchmove
33355      * Handle touchmove events
33356      *
33357      * Parameters:
33358      * evt - {Event}
33359      *
33360      * Returns:
33361      * {Boolean} Let the event propagate.
33362      */
33363     touchmove: function(evt) {
33364         return this.dragmove(evt);
33365     },
33366
33367     /**
33368      * Method: removeTimeout
33369      * Private. Called by mousemove() to remove the drag timeout.
33370      */
33371     removeTimeout: function() {
33372         this.timeoutId = null;
33373         // if timeout expires while we're still dragging (mouseup
33374         // hasn't occurred) then call mousemove to move to the
33375         // correct position
33376         if(this.dragging) {
33377             this.mousemove(this.lastMoveEvt);
33378         }
33379     },
33380
33381     /**
33382      * Method: mouseup
33383      * Handle mouseup events
33384      *
33385      * Parameters:
33386      * evt - {Event}
33387      *
33388      * Returns:
33389      * {Boolean} Let the event propagate.
33390      */
33391     mouseup: function(evt) {
33392         return this.dragend(evt);
33393     },
33394
33395     /**
33396      * Method: touchend
33397      * Handle touchend events
33398      *
33399      * Parameters:
33400      * evt - {Event}
33401      *
33402      * Returns:
33403      * {Boolean} Let the event propagate.
33404      */
33405     touchend: function(evt) {
33406         // override evt.xy with last position since touchend does not have
33407         // any touch position
33408         evt.xy = this.last;
33409         return this.dragend(evt);
33410     },
33411
33412     /**
33413      * Method: mouseout
33414      * Handle mouseout events
33415      *
33416      * Parameters:
33417      * evt - {Event}
33418      *
33419      * Returns:
33420      * {Boolean} Let the event propagate.
33421      */
33422     mouseout: function (evt) {
33423         if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {
33424             if(this.documentDrag === true) {
33425                 this.addDocumentEvents();
33426             } else {
33427                 var dragged = (this.start != this.last);
33428                 this.started = false; 
33429                 this.dragging = false;
33430                 OpenLayers.Element.removeClass(
33431                     this.map.viewPortDiv, "olDragDown"
33432                 );
33433                 this.out(evt);
33434                 this.callback("out", []);
33435                 if(dragged) {
33436                     this.callback("done", [evt.xy]);
33437                 }
33438                 if(document.onselectstart) {
33439                     document.onselectstart = this.oldOnselectstart;
33440                 }
33441             }
33442         }
33443         return true;
33444     },
33445
33446     /**
33447      * Method: click
33448      * The drag handler captures the click event.  If something else registers
33449      *     for clicks on the same element, its listener will not be called 
33450      *     after a drag.
33451      * 
33452      * Parameters: 
33453      * evt - {Event} 
33454      * 
33455      * Returns:
33456      * {Boolean} Let the event propagate.
33457      */
33458     click: function (evt) {
33459         // let the click event propagate only if the mouse moved
33460         return (this.start == this.last);
33461     },
33462
33463     /**
33464      * Method: activate
33465      * Activate the handler.
33466      * 
33467      * Returns:
33468      * {Boolean} The handler was successfully activated.
33469      */
33470     activate: function() {
33471         var activated = false;
33472         if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
33473             this.dragging = false;
33474             activated = true;
33475         }
33476         return activated;
33477     },
33478
33479     /**
33480      * Method: deactivate 
33481      * Deactivate the handler.
33482      * 
33483      * Returns:
33484      * {Boolean} The handler was successfully deactivated.
33485      */
33486     deactivate: function() {
33487         var deactivated = false;
33488         if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
33489             this.started = false;
33490             this.dragging = false;
33491             this.start = null;
33492             this.last = null;
33493             deactivated = true;
33494             OpenLayers.Element.removeClass(
33495                 this.map.viewPortDiv, "olDragDown"
33496             );
33497         }
33498         return deactivated;
33499     },
33500     
33501     /**
33502      * Method: adjustXY
33503      * Converts event coordinates that are relative to the document body to
33504      * ones that are relative to the map viewport. The latter is the default in
33505      * OpenLayers.
33506      * 
33507      * Parameters:
33508      * evt - {Object}
33509      */
33510     adjustXY: function(evt) {
33511         var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv);
33512         evt.xy.x -= pos[0];
33513         evt.xy.y -= pos[1];
33514     },
33515     
33516     /**
33517      * Method: addDocumentEvents
33518      * Start observing document events when documentDrag is true and the mouse
33519      * cursor leaves the map viewport while dragging.
33520      */
33521     addDocumentEvents: function() {
33522         OpenLayers.Element.addClass(document.body, "olDragDown");
33523         this.documentEvents = true;
33524         OpenLayers.Event.observe(document, "mousemove", this._docMove);
33525         OpenLayers.Event.observe(document, "mouseup", this._docUp);
33526     },
33527     
33528     /**
33529      * Method: removeDocumentEvents
33530      * Stops observing document events when documentDrag is true and the mouse
33531      * cursor re-enters the map viewport while dragging.
33532      */
33533     removeDocumentEvents: function() {
33534         OpenLayers.Element.removeClass(document.body, "olDragDown");
33535         this.documentEvents = false;
33536         OpenLayers.Event.stopObserving(document, "mousemove", this._docMove);
33537         OpenLayers.Event.stopObserving(document, "mouseup", this._docUp);
33538     },
33539
33540     CLASS_NAME: "OpenLayers.Handler.Drag"
33541 });
33542 /* ======================================================================
33543     OpenLayers/Handler/Box.js
33544    ====================================================================== */
33545
33546 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
33547  * full list of contributors). Published under the 2-clause BSD license.
33548  * See license.txt in the OpenLayers distribution or repository for the
33549  * full text of the license. */
33550
33551 /**
33552  * @requires OpenLayers/Handler.js
33553  * @requires OpenLayers/Handler/Drag.js
33554  */
33555
33556 /**
33557  * Class: OpenLayers.Handler.Box
33558  * Handler for dragging a rectangle across the map.  Box is displayed 
33559  * on mouse down, moves on mouse move, and is finished on mouse up.
33560  *
33561  * Inherits from:
33562  *  - <OpenLayers.Handler> 
33563  */
33564 OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {
33565
33566     /** 
33567      * Property: dragHandler 
33568      * {<OpenLayers.Handler.Drag>} 
33569      */
33570     dragHandler: null,
33571
33572     /**
33573      * APIProperty: boxDivClassName
33574      * {String} The CSS class to use for drawing the box. Default is
33575      *     olHandlerBoxZoomBox
33576      */
33577     boxDivClassName: 'olHandlerBoxZoomBox',
33578     
33579     /**
33580      * Property: boxOffsets
33581      * {Object} Caches box offsets from css. This is used by the getBoxOffsets
33582      * method.
33583      */
33584     boxOffsets: null,
33585
33586     /**
33587      * Constructor: OpenLayers.Handler.Box
33588      *
33589      * Parameters:
33590      * control - {<OpenLayers.Control>} 
33591      * callbacks - {Object} An object with a properties whose values are
33592      *     functions.  Various callbacks described below.
33593      * options - {Object} 
33594      *
33595      * Named callbacks:
33596      * start - Called when the box drag operation starts.
33597      * done - Called when the box drag operation is finished.
33598      *     The callback should expect to receive a single argument, the box 
33599      *     bounds or a pixel. If the box dragging didn't span more than a 5 
33600      *     pixel distance, a pixel will be returned instead of a bounds object.
33601      */
33602     initialize: function(control, callbacks, options) {
33603         OpenLayers.Handler.prototype.initialize.apply(this, arguments);
33604         this.dragHandler = new OpenLayers.Handler.Drag(
33605             this, 
33606             {
33607                 down: this.startBox, 
33608                 move: this.moveBox, 
33609                 out: this.removeBox,
33610                 up: this.endBox
33611             }, 
33612             {keyMask: this.keyMask}
33613         );
33614     },
33615
33616     /**
33617      * Method: destroy
33618      */
33619     destroy: function() {
33620         OpenLayers.Handler.prototype.destroy.apply(this, arguments);
33621         if (this.dragHandler) {
33622             this.dragHandler.destroy();
33623             this.dragHandler = null;
33624         }            
33625     },
33626
33627     /**
33628      * Method: setMap
33629      */
33630     setMap: function (map) {
33631         OpenLayers.Handler.prototype.setMap.apply(this, arguments);
33632         if (this.dragHandler) {
33633             this.dragHandler.setMap(map);
33634         }
33635     },
33636
33637     /**
33638     * Method: startBox
33639     *
33640     * Parameters:
33641     * xy - {<OpenLayers.Pixel>}
33642     */
33643     startBox: function (xy) {
33644         this.callback("start", []);
33645         this.zoomBox = OpenLayers.Util.createDiv('zoomBox', {
33646             x: -9999, y: -9999
33647         });
33648         this.zoomBox.className = this.boxDivClassName;                                         
33649         this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
33650         
33651         this.map.viewPortDiv.appendChild(this.zoomBox);
33652         
33653         OpenLayers.Element.addClass(
33654             this.map.viewPortDiv, "olDrawBox"
33655         );
33656     },
33657
33658     /**
33659     * Method: moveBox
33660     */
33661     moveBox: function (xy) {
33662         var startX = this.dragHandler.start.x;
33663         var startY = this.dragHandler.start.y;
33664         var deltaX = Math.abs(startX - xy.x);
33665         var deltaY = Math.abs(startY - xy.y);
33666
33667         var offset = this.getBoxOffsets();
33668         this.zoomBox.style.width = (deltaX + offset.width + 1) + "px";
33669         this.zoomBox.style.height = (deltaY + offset.height + 1) + "px";
33670         this.zoomBox.style.left = (xy.x < startX ?
33671             startX - deltaX - offset.left : startX - offset.left) + "px";
33672         this.zoomBox.style.top = (xy.y < startY ?
33673             startY - deltaY - offset.top : startY - offset.top) + "px";
33674     },
33675
33676     /**
33677     * Method: endBox
33678     */
33679     endBox: function(end) {
33680         var result;
33681         if (Math.abs(this.dragHandler.start.x - end.x) > 5 ||    
33682             Math.abs(this.dragHandler.start.y - end.y) > 5) {   
33683             var start = this.dragHandler.start;
33684             var top = Math.min(start.y, end.y);
33685             var bottom = Math.max(start.y, end.y);
33686             var left = Math.min(start.x, end.x);
33687             var right = Math.max(start.x, end.x);
33688             result = new OpenLayers.Bounds(left, bottom, right, top);
33689         } else {
33690             result = this.dragHandler.start.clone(); // i.e. OL.Pixel
33691         } 
33692         this.removeBox();
33693
33694         this.callback("done", [result]);
33695     },
33696
33697     /**
33698      * Method: removeBox
33699      * Remove the zoombox from the screen and nullify our reference to it.
33700      */
33701     removeBox: function() {
33702         this.map.viewPortDiv.removeChild(this.zoomBox);
33703         this.zoomBox = null;
33704         this.boxOffsets = null;
33705         OpenLayers.Element.removeClass(
33706             this.map.viewPortDiv, "olDrawBox"
33707         );
33708
33709     },
33710
33711     /**
33712      * Method: activate
33713      */
33714     activate: function () {
33715         if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
33716             this.dragHandler.activate();
33717             return true;
33718         } else {
33719             return false;
33720         }
33721     },
33722
33723     /**
33724      * Method: deactivate
33725      */
33726     deactivate: function () {
33727         if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
33728             if (this.dragHandler.deactivate()) {
33729                 if (this.zoomBox) {
33730                     this.removeBox();
33731                 }
33732             }
33733             return true;
33734         } else {
33735             return false;
33736         }
33737     },
33738     
33739     /**
33740      * Method: getBoxOffsets
33741      * Determines border offsets for a box, according to the box model.
33742      * 
33743      * Returns:
33744      * {Object} an object with the following offsets:
33745      *     - left
33746      *     - right
33747      *     - top
33748      *     - bottom
33749      *     - width
33750      *     - height
33751      */
33752     getBoxOffsets: function() {
33753         if (!this.boxOffsets) {
33754             // Determine the box model. If the testDiv's clientWidth is 3, then
33755             // the borders are outside and we are dealing with the w3c box
33756             // model. Otherwise, the browser uses the traditional box model and
33757             // the borders are inside the box bounds, leaving us with a
33758             // clientWidth of 1.
33759             var testDiv = document.createElement("div");
33760             //testDiv.style.visibility = "hidden";
33761             testDiv.style.position = "absolute";
33762             testDiv.style.border = "1px solid black";
33763             testDiv.style.width = "3px";
33764             document.body.appendChild(testDiv);
33765             var w3cBoxModel = testDiv.clientWidth == 3;
33766             document.body.removeChild(testDiv);
33767             
33768             var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
33769                 "border-left-width"));
33770             var right = parseInt(OpenLayers.Element.getStyle(
33771                 this.zoomBox, "border-right-width"));
33772             var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
33773                 "border-top-width"));
33774             var bottom = parseInt(OpenLayers.Element.getStyle(
33775                 this.zoomBox, "border-bottom-width"));
33776             this.boxOffsets = {
33777                 left: left,
33778                 right: right,
33779                 top: top,
33780                 bottom: bottom,
33781                 width: w3cBoxModel === false ? left + right : 0,
33782                 height: w3cBoxModel === false ? top + bottom : 0
33783             };
33784         }
33785         return this.boxOffsets;
33786     },
33787   
33788     CLASS_NAME: "OpenLayers.Handler.Box"
33789 });
33790 /* ======================================================================
33791     OpenLayers/Control/ZoomBox.js
33792    ====================================================================== */
33793
33794 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
33795  * full list of contributors). Published under the 2-clause BSD license.
33796  * See license.txt in the OpenLayers distribution or repository for the
33797  * full text of the license. */
33798
33799 /**
33800  * @requires OpenLayers/Control.js
33801  * @requires OpenLayers/Handler/Box.js
33802  */
33803
33804 /**
33805  * Class: OpenLayers.Control.ZoomBox
33806  * The ZoomBox control enables zooming directly to a given extent, by drawing 
33807  * a box on the map. The box is drawn by holding down shift, whilst dragging 
33808  * the mouse.
33809  *
33810  * Inherits from:
33811  *  - <OpenLayers.Control>
33812  */
33813 OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {
33814     /**
33815      * Property: type
33816      * {OpenLayers.Control.TYPE}
33817      */
33818     type: OpenLayers.Control.TYPE_TOOL,
33819
33820     /**
33821      * Property: out
33822      * {Boolean} Should the control be used for zooming out?
33823      */
33824     out: false,
33825
33826     /**
33827      * APIProperty: keyMask
33828      * {Integer} Zoom only occurs if the keyMask matches the combination of 
33829      *     keys down. Use bitwise operators and one or more of the
33830      *     <OpenLayers.Handler> constants to construct a keyMask. Leave null if 
33831      *     not used mask. Default is null.
33832      */
33833     keyMask: null,
33834
33835     /**
33836      * APIProperty: alwaysZoom
33837      * {Boolean} Always zoom in/out when box drawn, even if the zoom level does
33838      * not change.
33839      */
33840     alwaysZoom: false,
33841     
33842     /**
33843      * APIProperty: zoomOnClick
33844      * {Boolean} Should we zoom when no box was dragged, i.e. the user only
33845      * clicked? Default is true.
33846      */
33847     zoomOnClick: true,
33848
33849     /**
33850      * Method: draw
33851      */    
33852     draw: function() {
33853         this.handler = new OpenLayers.Handler.Box( this,
33854                             {done: this.zoomBox}, {keyMask: this.keyMask} );
33855     },
33856
33857     /**
33858      * Method: zoomBox
33859      *
33860      * Parameters:
33861      * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>}
33862      */
33863     zoomBox: function (position) {
33864         if (position instanceof OpenLayers.Bounds) {
33865             var bounds,
33866                 targetCenterPx = position.getCenterPixel();
33867             if (!this.out) {
33868                 var minXY = this.map.getLonLatFromPixel({
33869                     x: position.left,
33870                     y: position.bottom
33871                 });
33872                 var maxXY = this.map.getLonLatFromPixel({
33873                     x: position.right,
33874                     y: position.top
33875                 });
33876                 bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat,
33877                                                maxXY.lon, maxXY.lat);
33878             } else {
33879                 var pixWidth = position.right - position.left;
33880                 var pixHeight = position.bottom - position.top;
33881                 var zoomFactor = Math.min((this.map.size.h / pixHeight),
33882                     (this.map.size.w / pixWidth));
33883                 var extent = this.map.getExtent();
33884                 var center = this.map.getLonLatFromPixel(targetCenterPx);
33885                 var xmin = center.lon - (extent.getWidth()/2)*zoomFactor;
33886                 var xmax = center.lon + (extent.getWidth()/2)*zoomFactor;
33887                 var ymin = center.lat - (extent.getHeight()/2)*zoomFactor;
33888                 var ymax = center.lat + (extent.getHeight()/2)*zoomFactor;
33889                 bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax);
33890             }
33891             // always zoom in/out 
33892             var lastZoom = this.map.getZoom(),
33893                 size = this.map.getSize(),
33894                 centerPx = {x: size.w / 2, y: size.h / 2},
33895                 zoom = this.map.getZoomForExtent(bounds),
33896                 oldRes = this.map.getResolution(),
33897                 newRes = this.map.getResolutionForZoom(zoom);
33898             if (oldRes == newRes) {
33899                 this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx));
33900             } else {
33901               var zoomOriginPx = {
33902                     x: (oldRes * targetCenterPx.x - newRes * centerPx.x) /
33903                         (oldRes - newRes),
33904                     y: (oldRes * targetCenterPx.y - newRes * centerPx.y) /
33905                         (oldRes - newRes)
33906                 };
33907                 this.map.zoomTo(zoom, zoomOriginPx);
33908             }
33909             if (lastZoom == this.map.getZoom() && this.alwaysZoom == true){ 
33910                 this.map.zoomTo(lastZoom + (this.out ? -1 : 1)); 
33911             }
33912         } else if (this.zoomOnClick) { // it's a pixel
33913             if (!this.out) {
33914                 this.map.zoomTo(this.map.getZoom() + 1, position);
33915             } else {
33916                 this.map.zoomTo(this.map.getZoom() - 1, position);
33917             }
33918         }
33919     },
33920
33921     CLASS_NAME: "OpenLayers.Control.ZoomBox"
33922 });
33923 /* ======================================================================
33924     OpenLayers/Control/DragPan.js
33925    ====================================================================== */
33926
33927 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
33928  * full list of contributors). Published under the 2-clause BSD license.
33929  * See license.txt in the OpenLayers distribution or repository for the
33930  * full text of the license. */
33931
33932 /**
33933  * @requires OpenLayers/Control.js
33934  * @requires OpenLayers/Handler/Drag.js
33935  */
33936
33937 /**
33938  * Class: OpenLayers.Control.DragPan
33939  * The DragPan control pans the map with a drag of the mouse.
33940  *
33941  * Inherits from:
33942  *  - <OpenLayers.Control>
33943  */
33944 OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {
33945
33946     /** 
33947      * Property: type
33948      * {OpenLayers.Control.TYPES}
33949      */
33950     type: OpenLayers.Control.TYPE_TOOL,
33951     
33952     /**
33953      * Property: panned
33954      * {Boolean} The map moved.
33955      */
33956     panned: false,
33957     
33958     /**
33959      * Property: interval
33960      * {Integer} The number of milliseconds that should ellapse before
33961      *     panning the map again. Defaults to 0 milliseconds, which means that
33962      *     no separate cycle is used for panning. In most cases you won't want
33963      *     to change this value. For slow machines/devices larger values can be
33964      *     tried out.
33965      */
33966     interval: 0,
33967     
33968     /**
33969      * APIProperty: documentDrag
33970      * {Boolean} If set to true, mouse dragging will continue even if the
33971      *     mouse cursor leaves the map viewport. Default is false.
33972      */
33973     documentDrag: false,
33974
33975     /**
33976      * Property: kinetic
33977      * {<OpenLayers.Kinetic>} The OpenLayers.Kinetic object.
33978      */
33979     kinetic: null,
33980
33981     /**
33982      * APIProperty: enableKinetic
33983      * {Boolean} Set this option to enable "kinetic dragging". Can be
33984      *     set to true or to an object. If set to an object this
33985      *     object will be passed to the {<OpenLayers.Kinetic>}
33986      *     constructor. Defaults to true.
33987      *     To get kinetic dragging, ensure that OpenLayers/Kinetic.js is
33988      *     included in your build config.
33989      */
33990     enableKinetic: true,
33991
33992     /**
33993      * APIProperty: kineticInterval
33994      * {Integer} Interval in milliseconds between 2 steps in the "kinetic
33995      *     scrolling". Applies only if enableKinetic is set. Defaults
33996      *     to 10 milliseconds.
33997      */
33998     kineticInterval: 10,
33999
34000
34001     /**
34002      * Method: draw
34003      * Creates a Drag handler, using <panMap> and
34004      * <panMapDone> as callbacks.
34005      */    
34006     draw: function() {
34007         if (this.enableKinetic && OpenLayers.Kinetic) {
34008             var config = {interval: this.kineticInterval};
34009             if(typeof this.enableKinetic === "object") {
34010                 config = OpenLayers.Util.extend(config, this.enableKinetic);
34011             }
34012             this.kinetic = new OpenLayers.Kinetic(config);
34013         }
34014         this.handler = new OpenLayers.Handler.Drag(this, {
34015                 "move": this.panMap,
34016                 "done": this.panMapDone,
34017                 "down": this.panMapStart
34018             }, {
34019                 interval: this.interval,
34020                 documentDrag: this.documentDrag
34021             }
34022         );
34023     },
34024
34025     /**
34026      * Method: panMapStart
34027      */
34028     panMapStart: function() {
34029         if(this.kinetic) {
34030             this.kinetic.begin();
34031         }
34032     },
34033
34034     /**
34035     * Method: panMap
34036     *
34037     * Parameters:
34038     * xy - {<OpenLayers.Pixel>} Pixel of the mouse position
34039     */
34040     panMap: function(xy) {
34041         if(this.kinetic) {
34042             this.kinetic.update(xy);
34043         }
34044         this.panned = true;
34045         this.map.pan(
34046             this.handler.last.x - xy.x,
34047             this.handler.last.y - xy.y,
34048             {dragging: true, animate: false}
34049         );
34050     },
34051     
34052     /**
34053      * Method: panMapDone
34054      * Finish the panning operation.  Only call setCenter (through <panMap>)
34055      *     if the map has actually been moved.
34056      *
34057      * Parameters:
34058      * xy - {<OpenLayers.Pixel>} Pixel of the mouse position
34059      */
34060     panMapDone: function(xy) {
34061         if(this.panned) {
34062             var res = null;
34063             if (this.kinetic) {
34064                 res = this.kinetic.end(xy);
34065             }
34066             this.map.pan(
34067                 this.handler.last.x - xy.x,
34068                 this.handler.last.y - xy.y,
34069                 {dragging: !!res, animate: false}
34070             );
34071             if (res) {
34072                 var self = this;
34073                 this.kinetic.move(res, function(x, y, end) {
34074                     self.map.pan(x, y, {dragging: !end, animate: false});
34075                 });
34076             }
34077             this.panned = false;
34078         }
34079     },
34080
34081     CLASS_NAME: "OpenLayers.Control.DragPan"
34082 });
34083 /* ======================================================================
34084     OpenLayers/Handler/Click.js
34085    ====================================================================== */
34086
34087 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
34088  * full list of contributors). Published under the 2-clause BSD license.
34089  * See license.txt in the OpenLayers distribution or repository for the
34090  * full text of the license. */
34091
34092 /**
34093  * @requires OpenLayers/Handler.js
34094  */
34095
34096 /**
34097  * Class: OpenLayers.Handler.Click
34098  * A handler for mouse clicks.  The intention of this handler is to give
34099  *     controls more flexibility with handling clicks.  Browsers trigger
34100  *     click events twice for a double-click.  In addition, the mousedown,
34101  *     mousemove, mouseup sequence fires a click event.  With this handler,
34102  *     controls can decide whether to ignore clicks associated with a double
34103  *     click.  By setting a <pixelTolerance>, controls can also ignore clicks
34104  *     that include a drag.  Create a new instance with the
34105  *     <OpenLayers.Handler.Click> constructor.
34106  * 
34107  * Inherits from:
34108  *  - <OpenLayers.Handler> 
34109  */
34110 OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {
34111     /**
34112      * APIProperty: delay
34113      * {Number} Number of milliseconds between clicks before the event is
34114      *     considered a double-click.
34115      */
34116     delay: 300,
34117     
34118     /**
34119      * APIProperty: single
34120      * {Boolean} Handle single clicks.  Default is true.  If false, clicks
34121      * will not be reported.  If true, single-clicks will be reported.
34122      */
34123     single: true,
34124     
34125     /**
34126      * APIProperty: double
34127      * {Boolean} Handle double-clicks.  Default is false.
34128      */
34129     'double': false,
34130     
34131     /**
34132      * APIProperty: pixelTolerance
34133      * {Number} Maximum number of pixels between mouseup and mousedown for an
34134      *     event to be considered a click.  Default is 0.  If set to an
34135      *     integer value, clicks with a drag greater than the value will be
34136      *     ignored.  This property can only be set when the handler is
34137      *     constructed.
34138      */
34139     pixelTolerance: 0,
34140         
34141     /**
34142      * APIProperty: dblclickTolerance
34143      * {Number} Maximum distance in pixels between clicks for a sequence of 
34144      *     events to be considered a double click.  Default is 13.  If the
34145      *     distance between two clicks is greater than this value, a double-
34146      *     click will not be fired.
34147      */
34148     dblclickTolerance: 13,
34149         
34150     /**
34151      * APIProperty: stopSingle
34152      * {Boolean} Stop other listeners from being notified of clicks.  Default
34153      *     is false.  If true, any listeners registered before this one for 
34154      *     click or rightclick events will not be notified.
34155      */
34156     stopSingle: false,
34157     
34158     /**
34159      * APIProperty: stopDouble
34160      * {Boolean} Stop other listeners from being notified of double-clicks.
34161      *     Default is false.  If true, any click listeners registered before
34162      *     this one will not be notified of *any* double-click events.
34163      * 
34164      * The one caveat with stopDouble is that given a map with two click
34165      *     handlers, one with stopDouble true and the other with stopSingle
34166      *     true, the stopSingle handler should be activated last to get
34167      *     uniform cross-browser performance.  Since IE triggers one click
34168      *     with a dblclick and FF triggers two, if a stopSingle handler is
34169      *     activated first, all it gets in IE is a single click when the
34170      *     second handler stops propagation on the dblclick.
34171      */
34172     stopDouble: false,
34173
34174     /**
34175      * Property: timerId
34176      * {Number} The id of the timeout waiting to clear the <delayedCall>.
34177      */
34178     timerId: null,
34179     
34180     /**
34181      * Property: down
34182      * {Object} Object that store relevant information about the last
34183      *     mousedown or touchstart. Its 'xy' OpenLayers.Pixel property gives
34184      *     the average location of the mouse/touch event. Its 'touches'
34185      *     property records clientX/clientY of each touches.
34186      */
34187     down: null,
34188
34189     /**
34190      * Property: last
34191      * {Object} Object that store relevant information about the last
34192      *     mousemove or touchmove. Its 'xy' OpenLayers.Pixel property gives
34193      *     the average location of the mouse/touch event. Its 'touches'
34194      *     property records clientX/clientY of each touches.
34195      */
34196     last: null,
34197
34198     /** 
34199      * Property: first
34200      * {Object} When waiting for double clicks, this object will store 
34201      *     information about the first click in a two click sequence.
34202      */
34203     first: null,
34204
34205     /**
34206      * Property: rightclickTimerId
34207      * {Number} The id of the right mouse timeout waiting to clear the 
34208      *     <delayedEvent>.
34209      */
34210     rightclickTimerId: null,
34211     
34212     /**
34213      * Constructor: OpenLayers.Handler.Click
34214      * Create a new click handler.
34215      * 
34216      * Parameters:
34217      * control - {<OpenLayers.Control>} The control that is making use of
34218      *     this handler.  If a handler is being used without a control, the
34219      *     handler's setMap method must be overridden to deal properly with
34220      *     the map.
34221      * callbacks - {Object} An object with keys corresponding to callbacks
34222      *     that will be called by the handler. The callbacks should
34223      *     expect to recieve a single argument, the click event.
34224      *     Callbacks for 'click' and 'dblclick' are supported.
34225      * options - {Object} Optional object whose properties will be set on the
34226      *     handler.
34227      */
34228     
34229     /**
34230      * Method: touchstart
34231      * Handle touchstart.
34232      *
34233      * Returns:
34234      * {Boolean} Continue propagating this event.
34235      */
34236     touchstart: function(evt) {
34237         this.startTouch();
34238         this.down = this.getEventInfo(evt);
34239         this.last = this.getEventInfo(evt);
34240         return true;
34241     },
34242     
34243     /**
34244      * Method: touchmove
34245      *    Store position of last move, because touchend event can have
34246      *    an empty "touches" property.
34247      *
34248      * Returns:
34249      * {Boolean} Continue propagating this event.
34250      */
34251     touchmove: function(evt) {
34252         this.last = this.getEventInfo(evt);
34253         return true;
34254     },
34255
34256     /**
34257      * Method: touchend
34258      *   Correctly set event xy property, and add lastTouches to have
34259      *   touches property from last touchstart or touchmove
34260      *
34261      * Returns:
34262      * {Boolean} Continue propagating this event.
34263      */
34264     touchend: function(evt) {
34265         // touchstart may not have been allowed to propagate
34266         if (this.down) {
34267             evt.xy = this.last.xy;
34268             evt.lastTouches = this.last.touches;
34269             this.handleSingle(evt);
34270             this.down = null;
34271         }
34272         return true;
34273     },
34274
34275     /**
34276      * Method: mousedown
34277      * Handle mousedown.
34278      *
34279      * Returns:
34280      * {Boolean} Continue propagating this event.
34281      */
34282     mousedown: function(evt) {
34283         this.down = this.getEventInfo(evt);
34284         this.last = this.getEventInfo(evt);
34285         return true;
34286     },
34287
34288     /**
34289      * Method: mouseup
34290      * Handle mouseup.  Installed to support collection of right mouse events.
34291      * 
34292      * Returns:
34293      * {Boolean} Continue propagating this event.
34294      */
34295     mouseup: function (evt) {
34296         var propagate = true;
34297
34298         // Collect right mouse clicks from the mouseup
34299         //  IE - ignores the second right click in mousedown so using
34300         //  mouseup instead
34301         if (this.checkModifiers(evt) && this.control.handleRightClicks &&
34302            OpenLayers.Event.isRightClick(evt)) {
34303             propagate = this.rightclick(evt);
34304         }
34305
34306         return propagate;
34307     },
34308     
34309     /**
34310      * Method: rightclick
34311      * Handle rightclick.  For a dblrightclick, we get two clicks so we need 
34312      *     to always register for dblrightclick to properly handle single 
34313      *     clicks.
34314      *     
34315      * Returns:
34316      * {Boolean} Continue propagating this event.
34317      */
34318     rightclick: function(evt) {
34319         if(this.passesTolerance(evt)) {
34320            if(this.rightclickTimerId != null) {
34321                 //Second click received before timeout this must be 
34322                 // a double click
34323                 this.clearTimer();
34324                 this.callback('dblrightclick', [evt]);
34325                 return !this.stopDouble;
34326             } else { 
34327                 //Set the rightclickTimerId, send evt only if double is 
34328                 // true else trigger single
34329                 var clickEvent = this['double'] ?
34330                     OpenLayers.Util.extend({}, evt) : 
34331                     this.callback('rightclick', [evt]);
34332
34333                 var delayedRightCall = OpenLayers.Function.bind(
34334                     this.delayedRightCall, 
34335                     this, 
34336                     clickEvent
34337                 );
34338                 this.rightclickTimerId = window.setTimeout(
34339                     delayedRightCall, this.delay
34340                 );
34341             } 
34342         }
34343         return !this.stopSingle;
34344     },
34345     
34346     /**
34347      * Method: delayedRightCall
34348      * Sets <rightclickTimerId> to null.  And optionally triggers the 
34349      *     rightclick callback if evt is set.
34350      */
34351     delayedRightCall: function(evt) {
34352         this.rightclickTimerId = null;
34353         if (evt) {
34354            this.callback('rightclick', [evt]);
34355         }
34356     },
34357     
34358     /**
34359      * Method: click
34360      * Handle click events from the browser.  This is registered as a listener
34361      *     for click events and should not be called from other events in this
34362      *     handler.
34363      *
34364      * Returns:
34365      * {Boolean} Continue propagating this event.
34366      */
34367     click: function(evt) {
34368         if (!this.last) {
34369             this.last = this.getEventInfo(evt);
34370         }
34371         this.handleSingle(evt);
34372         return !this.stopSingle;
34373     },
34374
34375     /**
34376      * Method: dblclick
34377      * Handle dblclick.  For a dblclick, we get two clicks in some browsers
34378      *     (FF) and one in others (IE).  So we need to always register for
34379      *     dblclick to properly handle single clicks.  This method is registered
34380      *     as a listener for the dblclick browser event.  It should *not* be
34381      *     called by other methods in this handler.
34382      *     
34383      * Returns:
34384      * {Boolean} Continue propagating this event.
34385      */
34386     dblclick: function(evt) {
34387         this.handleDouble(evt);
34388         return !this.stopDouble;
34389     },
34390     
34391     /** 
34392      * Method: handleDouble
34393      * Handle double-click sequence.
34394      */
34395     handleDouble: function(evt) {
34396         if (this.passesDblclickTolerance(evt)) {
34397             if (this["double"]) {
34398                 this.callback("dblclick", [evt]);
34399             }
34400             // to prevent a dblclick from firing the click callback in IE
34401             this.clearTimer();
34402         }
34403     },
34404     
34405     /** 
34406      * Method: handleSingle
34407      * Handle single click sequence.
34408      */
34409     handleSingle: function(evt) {
34410         if (this.passesTolerance(evt)) {
34411             if (this.timerId != null) {
34412                 // already received a click
34413                 if (this.last.touches && this.last.touches.length === 1) {
34414                     // touch device, no dblclick event - this may be a double
34415                     if (this["double"]) {
34416                         // on Android don't let the browser zoom on the page
34417                         OpenLayers.Event.preventDefault(evt);
34418                     }
34419                     this.handleDouble(evt);
34420                 }
34421                 // if we're not in a touch environment we clear the click timer
34422                 // if we've got a second touch, we'll get two touchend events
34423                 if (!this.last.touches || this.last.touches.length !== 2) {
34424                     this.clearTimer();
34425                 }
34426             } else {
34427                 // remember the first click info so we can compare to the second
34428                 this.first = this.getEventInfo(evt);
34429                 // set the timer, send evt only if single is true
34430                 //use a clone of the event object because it will no longer 
34431                 //be a valid event object in IE in the timer callback
34432                 var clickEvent = this.single ?
34433                     OpenLayers.Util.extend({}, evt) : null;
34434                 this.queuePotentialClick(clickEvent);
34435             }
34436         }
34437     },
34438     
34439     /** 
34440      * Method: queuePotentialClick
34441      * This method is separated out largely to make testing easier (so we
34442      *     don't have to override window.setTimeout)
34443      */
34444     queuePotentialClick: function(evt) {
34445         this.timerId = window.setTimeout(
34446             OpenLayers.Function.bind(this.delayedCall, this, evt),
34447             this.delay
34448         );
34449     },
34450
34451     /**
34452      * Method: passesTolerance
34453      * Determine whether the event is within the optional pixel tolerance.  Note
34454      *     that the pixel tolerance check only works if mousedown events get to
34455      *     the listeners registered here.  If they are stopped by other elements,
34456      *     the <pixelTolerance> will have no effect here (this method will always
34457      *     return true).
34458      *
34459      * Returns:
34460      * {Boolean} The click is within the pixel tolerance (if specified).
34461      */
34462     passesTolerance: function(evt) {
34463         var passes = true;
34464         if (this.pixelTolerance != null && this.down && this.down.xy) {
34465             passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy);
34466             // for touch environments, we also enforce that all touches
34467             // start and end within the given tolerance to be considered a click
34468             if (passes && this.touch && 
34469                 this.down.touches.length === this.last.touches.length) {
34470                 // the touchend event doesn't come with touches, so we check
34471                 // down and last
34472                 for (var i=0, ii=this.down.touches.length; i<ii; ++i) {
34473                     if (this.getTouchDistance(
34474                             this.down.touches[i], 
34475                             this.last.touches[i]
34476                         ) > this.pixelTolerance) {
34477                         passes = false;
34478                         break;
34479                     }
34480                 }
34481             }
34482         }
34483         return passes;
34484     },
34485     
34486     /** 
34487      * Method: getTouchDistance
34488      *
34489      * Returns:
34490      * {Boolean} The pixel displacement between two touches.
34491      */
34492     getTouchDistance: function(from, to) {
34493         return Math.sqrt(
34494             Math.pow(from.clientX - to.clientX, 2) +
34495             Math.pow(from.clientY - to.clientY, 2)
34496         );
34497     },
34498     
34499     /**
34500      * Method: passesDblclickTolerance
34501      * Determine whether the event is within the optional double-cick pixel 
34502      *     tolerance.
34503      *
34504      * Returns:
34505      * {Boolean} The click is within the double-click pixel tolerance.
34506      */
34507     passesDblclickTolerance: function(evt) {
34508         var passes = true;
34509         if (this.down && this.first) {
34510             passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance;
34511         }
34512         return passes;
34513     },
34514
34515     /**
34516      * Method: clearTimer
34517      * Clear the timer and set <timerId> to null.
34518      */
34519     clearTimer: function() {
34520         if (this.timerId != null) {
34521             window.clearTimeout(this.timerId);
34522             this.timerId = null;
34523         }
34524         if (this.rightclickTimerId != null) {
34525             window.clearTimeout(this.rightclickTimerId);
34526             this.rightclickTimerId = null;
34527         }
34528     },
34529     
34530     /**
34531      * Method: delayedCall
34532      * Sets <timerId> to null.  And optionally triggers the click callback if
34533      *     evt is set.
34534      */
34535     delayedCall: function(evt) {
34536         this.timerId = null;
34537         if (evt) {
34538             this.callback("click", [evt]);
34539         }
34540     },
34541
34542     /**
34543      * Method: getEventInfo
34544      * This method allows us to store event information without storing the
34545      *     actual event.  In touch devices (at least), the same event is 
34546      *     modified between touchstart, touchmove, and touchend.
34547      *
34548      * Returns:
34549      * {Object} An object with event related info.
34550      */
34551     getEventInfo: function(evt) {
34552         var touches;
34553         if (evt.touches) {
34554             var len = evt.touches.length;
34555             touches = new Array(len);
34556             var touch;
34557             for (var i=0; i<len; i++) {
34558                 touch = evt.touches[i];
34559                 touches[i] = {
34560                     clientX: touch.olClientX,
34561                     clientY: touch.olClientY
34562                 };
34563             }
34564         }
34565         return {
34566             xy: evt.xy,
34567             touches: touches
34568         };
34569     },
34570
34571     /**
34572      * APIMethod: deactivate
34573      * Deactivate the handler.
34574      *
34575      * Returns:
34576      * {Boolean} The handler was successfully deactivated.
34577      */
34578     deactivate: function() {
34579         var deactivated = false;
34580         if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
34581             this.clearTimer();
34582             this.down = null;
34583             this.first = null;
34584             this.last = null;
34585             deactivated = true;
34586         }
34587         return deactivated;
34588     },
34589
34590     CLASS_NAME: "OpenLayers.Handler.Click"
34591 });
34592 /* ======================================================================
34593     OpenLayers/Control/Navigation.js
34594    ====================================================================== */
34595
34596 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
34597  * full list of contributors). Published under the 2-clause BSD license.
34598  * See license.txt in the OpenLayers distribution or repository for the
34599  * full text of the license. */
34600
34601 /**
34602  * @requires OpenLayers/Control/ZoomBox.js
34603  * @requires OpenLayers/Control/DragPan.js
34604  * @requires OpenLayers/Handler/MouseWheel.js
34605  * @requires OpenLayers/Handler/Click.js
34606  */
34607
34608 /**
34609  * Class: OpenLayers.Control.Navigation
34610  * The navigation control handles map browsing with mouse events (dragging,
34611  *     double-clicking, and scrolling the wheel).  Create a new navigation 
34612  *     control with the <OpenLayers.Control.Navigation> control.  
34613  * 
34614  *     Note that this control is added to the map by default (if no controls 
34615  *     array is sent in the options object to the <OpenLayers.Map> 
34616  *     constructor).
34617  * 
34618  * Inherits:
34619  *  - <OpenLayers.Control>
34620  */
34621 OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {
34622
34623     /** 
34624      * Property: dragPan
34625      * {<OpenLayers.Control.DragPan>} 
34626      */
34627     dragPan: null,
34628
34629     /**
34630      * APIProperty: dragPanOptions
34631      * {Object} Options passed to the DragPan control.
34632      */
34633     dragPanOptions: null,
34634
34635     /**
34636      * Property: pinchZoom
34637      * {<OpenLayers.Control.PinchZoom>}
34638      */
34639     pinchZoom: null,
34640
34641     /**
34642      * APIProperty: pinchZoomOptions
34643      * {Object} Options passed to the PinchZoom control.
34644      */
34645     pinchZoomOptions: null,
34646
34647     /**
34648      * APIProperty: documentDrag
34649      * {Boolean} Allow panning of the map by dragging outside map viewport.
34650      *     Default is false.
34651      */
34652     documentDrag: false,
34653
34654     /** 
34655      * Property: zoomBox
34656      * {<OpenLayers.Control.ZoomBox>}
34657      */
34658     zoomBox: null,
34659
34660     /**
34661      * APIProperty: zoomBoxEnabled
34662      * {Boolean} Whether the user can draw a box to zoom
34663      */
34664     zoomBoxEnabled: true, 
34665
34666     /**
34667      * APIProperty: zoomWheelEnabled
34668      * {Boolean} Whether the mousewheel should zoom the map
34669      */
34670     zoomWheelEnabled: true,
34671     
34672     /**
34673      * Property: mouseWheelOptions
34674      * {Object} Options passed to the MouseWheel control (only useful if
34675      *     <zoomWheelEnabled> is set to true). Default is no options for maps
34676      *     with fractionalZoom set to true, otherwise
34677      *     {cumulative: false, interval: 50, maxDelta: 6} 
34678      */
34679     mouseWheelOptions: null,
34680
34681     /**
34682      * APIProperty: handleRightClicks
34683      * {Boolean} Whether or not to handle right clicks. Default is false.
34684      */
34685     handleRightClicks: false,
34686
34687     /**
34688      * APIProperty: zoomBoxKeyMask
34689      * {Integer} <OpenLayers.Handler> key code of the key, which has to be
34690      *    pressed, while drawing the zoom box with the mouse on the screen. 
34691      *    You should probably set handleRightClicks to true if you use this
34692      *    with MOD_CTRL, to disable the context menu for machines which use
34693      *    CTRL-Click as a right click.
34694      * Default: <OpenLayers.Handler.MOD_SHIFT>
34695      */
34696     zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,
34697     
34698     /**
34699      * APIProperty: autoActivate
34700      * {Boolean} Activate the control when it is added to a map.  Default is
34701      *     true.
34702      */
34703     autoActivate: true,
34704
34705     /**
34706      * Constructor: OpenLayers.Control.Navigation
34707      * Create a new navigation control
34708      * 
34709      * Parameters:
34710      * options - {Object} An optional object whose properties will be set on
34711      *                    the control
34712      */
34713     initialize: function(options) {
34714         this.handlers = {};
34715         OpenLayers.Control.prototype.initialize.apply(this, arguments);
34716     },
34717
34718     /**
34719      * Method: destroy
34720      * The destroy method is used to perform any clean up before the control
34721      * is dereferenced.  Typically this is where event listeners are removed
34722      * to prevent memory leaks.
34723      */
34724     destroy: function() {
34725         this.deactivate();
34726
34727         if (this.dragPan) {
34728             this.dragPan.destroy();
34729         }
34730         this.dragPan = null;
34731
34732         if (this.zoomBox) {
34733             this.zoomBox.destroy();
34734         }
34735         this.zoomBox = null;
34736
34737         if (this.pinchZoom) {
34738             this.pinchZoom.destroy();
34739         }
34740         this.pinchZoom = null;
34741
34742         OpenLayers.Control.prototype.destroy.apply(this,arguments);
34743     },
34744     
34745     /**
34746      * Method: activate
34747      */
34748     activate: function() {
34749         this.dragPan.activate();
34750         if (this.zoomWheelEnabled) {
34751             this.handlers.wheel.activate();
34752         }    
34753         this.handlers.click.activate();
34754         if (this.zoomBoxEnabled) {
34755             this.zoomBox.activate();
34756         }
34757         if (this.pinchZoom) {
34758             this.pinchZoom.activate();
34759         }
34760         return OpenLayers.Control.prototype.activate.apply(this,arguments);
34761     },
34762
34763     /**
34764      * Method: deactivate
34765      */
34766     deactivate: function() {
34767         if (this.pinchZoom) {
34768             this.pinchZoom.deactivate();
34769         }
34770         this.zoomBox.deactivate();
34771         this.dragPan.deactivate();
34772         this.handlers.click.deactivate();
34773         this.handlers.wheel.deactivate();
34774         return OpenLayers.Control.prototype.deactivate.apply(this,arguments);
34775     },
34776     
34777     /**
34778      * Method: draw
34779      */
34780     draw: function() {
34781         // disable right mouse context menu for support of right click events
34782         if (this.handleRightClicks) {
34783             this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False;
34784         }
34785
34786         var clickCallbacks = { 
34787             'click': this.defaultClick,
34788             'dblclick': this.defaultDblClick, 
34789             'dblrightclick': this.defaultDblRightClick 
34790         };
34791         var clickOptions = {
34792             'double': true, 
34793             'stopDouble': true
34794         };
34795         this.handlers.click = new OpenLayers.Handler.Click(
34796             this, clickCallbacks, clickOptions
34797         );
34798         this.dragPan = new OpenLayers.Control.DragPan(
34799             OpenLayers.Util.extend({
34800                 map: this.map,
34801                 documentDrag: this.documentDrag
34802             }, this.dragPanOptions)
34803         );
34804         this.zoomBox = new OpenLayers.Control.ZoomBox(
34805                     {map: this.map, keyMask: this.zoomBoxKeyMask});
34806         this.dragPan.draw();
34807         this.zoomBox.draw();
34808         var wheelOptions = this.map.fractionalZoom ? {} : {
34809             cumulative: false,
34810             interval: 50,
34811             maxDelta: 6
34812         };
34813         this.handlers.wheel = new OpenLayers.Handler.MouseWheel(
34814             this, {up : this.wheelUp, down: this.wheelDown},
34815             OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions)
34816         );
34817         if (OpenLayers.Control.PinchZoom) {
34818             this.pinchZoom = new OpenLayers.Control.PinchZoom(
34819                 OpenLayers.Util.extend(
34820                     {map: this.map}, this.pinchZoomOptions));
34821         }
34822     },
34823
34824     /**
34825      * Method: defaultClick
34826      *
34827      * Parameters:
34828      * evt - {Event}
34829      */
34830     defaultClick: function (evt) {
34831         if (evt.lastTouches && evt.lastTouches.length == 2) {
34832             this.map.zoomOut();
34833         }
34834     },
34835
34836     /**
34837      * Method: defaultDblClick 
34838      * 
34839      * Parameters:
34840      * evt - {Event} 
34841      */
34842     defaultDblClick: function (evt) {
34843         this.map.zoomTo(this.map.zoom + 1, evt.xy);
34844     },
34845
34846     /**
34847      * Method: defaultDblRightClick 
34848      * 
34849      * Parameters:
34850      * evt - {Event} 
34851      */
34852     defaultDblRightClick: function (evt) {
34853         this.map.zoomTo(this.map.zoom - 1, evt.xy);
34854     },
34855     
34856     /**
34857      * Method: wheelChange  
34858      *
34859      * Parameters:
34860      * evt - {Event}
34861      * deltaZ - {Integer}
34862      */
34863     wheelChange: function(evt, deltaZ) {
34864         if (!this.map.fractionalZoom) {
34865             deltaZ =  Math.round(deltaZ);
34866         }
34867         var currentZoom = this.map.getZoom(),
34868             newZoom = currentZoom + deltaZ;
34869         newZoom = Math.max(newZoom, 0);
34870         newZoom = Math.min(newZoom, this.map.getNumZoomLevels());
34871         if (newZoom === currentZoom) {
34872             return;
34873         }
34874         this.map.zoomTo(newZoom, evt.xy);
34875     },
34876
34877     /** 
34878      * Method: wheelUp
34879      * User spun scroll wheel up
34880      * 
34881      * Parameters:
34882      * evt - {Event}
34883      * delta - {Integer}
34884      */
34885     wheelUp: function(evt, delta) {
34886         this.wheelChange(evt, delta || 1);
34887     },
34888
34889     /** 
34890      * Method: wheelDown
34891      * User spun scroll wheel down
34892      * 
34893      * Parameters:
34894      * evt - {Event}
34895      * delta - {Integer}
34896      */
34897     wheelDown: function(evt, delta) {
34898         this.wheelChange(evt, delta || -1);
34899     },
34900     
34901     /**
34902      * Method: disableZoomBox
34903      */
34904     disableZoomBox : function() {
34905         this.zoomBoxEnabled = false;
34906         this.zoomBox.deactivate();       
34907     },
34908     
34909     /**
34910      * Method: enableZoomBox
34911      */
34912     enableZoomBox : function() {
34913         this.zoomBoxEnabled = true;
34914         if (this.active) {
34915             this.zoomBox.activate();
34916         }    
34917     },
34918     
34919     /**
34920      * Method: disableZoomWheel
34921      */
34922     
34923     disableZoomWheel : function() {
34924         this.zoomWheelEnabled = false;
34925         this.handlers.wheel.deactivate();       
34926     },
34927     
34928     /**
34929      * Method: enableZoomWheel
34930      */
34931     
34932     enableZoomWheel : function() {
34933         this.zoomWheelEnabled = true;
34934         if (this.active) {
34935             this.handlers.wheel.activate();
34936         }    
34937     },
34938
34939     CLASS_NAME: "OpenLayers.Control.Navigation"
34940 });
34941 /* ======================================================================
34942     OpenLayers/Renderer/SVG.js
34943    ====================================================================== */
34944
34945 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
34946  * full list of contributors). Published under the 2-clause BSD license.
34947  * See license.txt in the OpenLayers distribution or repository for the
34948  * full text of the license. */
34949
34950 /**
34951  * @requires OpenLayers/Renderer/Elements.js
34952  */
34953
34954 /**
34955  * Class: OpenLayers.Renderer.SVG
34956  * 
34957  * Inherits:
34958  *  - <OpenLayers.Renderer.Elements>
34959  */
34960 OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
34961
34962     /** 
34963      * Property: xmlns
34964      * {String}
34965      */
34966     xmlns: "http://www.w3.org/2000/svg",
34967     
34968     /**
34969      * Property: xlinkns
34970      * {String}
34971      */
34972     xlinkns: "http://www.w3.org/1999/xlink",
34973
34974     /**
34975      * Constant: MAX_PIXEL
34976      * {Integer} Firefox has a limitation where values larger or smaller than  
34977      *           about 15000 in an SVG document lock the browser up. This 
34978      *           works around it.
34979      */
34980     MAX_PIXEL: 15000,
34981
34982     /**
34983      * Property: translationParameters
34984      * {Object} Hash with "x" and "y" properties
34985      */
34986     translationParameters: null,
34987     
34988     /**
34989      * Property: symbolMetrics
34990      * {Object} Cache for symbol metrics according to their svg coordinate
34991      *     space. This is an object keyed by the symbol's id, and values are
34992      *     an array of [width, centerX, centerY].
34993      */
34994     symbolMetrics: null,
34995     
34996     /**
34997      * Constructor: OpenLayers.Renderer.SVG
34998      * 
34999      * Parameters:
35000      * containerID - {String}
35001      */
35002     initialize: function(containerID) {
35003         if (!this.supported()) { 
35004             return; 
35005         }
35006         OpenLayers.Renderer.Elements.prototype.initialize.apply(this, 
35007                                                                 arguments);
35008         this.translationParameters = {x: 0, y: 0};
35009         
35010         this.symbolMetrics = {};
35011     },
35012
35013     /**
35014      * APIMethod: supported
35015      * 
35016      * Returns:
35017      * {Boolean} Whether or not the browser supports the SVG renderer
35018      */
35019     supported: function() {
35020         var svgFeature = "http://www.w3.org/TR/SVG11/feature#";
35021         return (document.implementation && 
35022            (document.implementation.hasFeature("org.w3c.svg", "1.0") || 
35023             document.implementation.hasFeature(svgFeature + "SVG", "1.1") || 
35024             document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1") ));
35025     },    
35026
35027     /**
35028      * Method: inValidRange
35029      * See #669 for more information
35030      *
35031      * Parameters:
35032      * x      - {Integer}
35033      * y      - {Integer}
35034      * xyOnly - {Boolean} whether or not to just check for x and y, which means
35035      *     to not take the current translation parameters into account if true.
35036      * 
35037      * Returns:
35038      * {Boolean} Whether or not the 'x' and 'y' coordinates are in the  
35039      *           valid range.
35040      */ 
35041     inValidRange: function(x, y, xyOnly) {
35042         var left = x + (xyOnly ? 0 : this.translationParameters.x);
35043         var top = y + (xyOnly ? 0 : this.translationParameters.y);
35044         return (left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL &&
35045                 top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL);
35046     },
35047
35048     /**
35049      * Method: setExtent
35050      * 
35051      * Parameters:
35052      * extent - {<OpenLayers.Bounds>}
35053      * resolutionChanged - {Boolean}
35054      * 
35055      * Returns:
35056      * {Boolean} true to notify the layer that the new extent does not exceed
35057      *     the coordinate range, and the features will not need to be redrawn.
35058      *     False otherwise.
35059      */
35060     setExtent: function(extent, resolutionChanged) {
35061         var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);
35062         
35063         var resolution = this.getResolution(),
35064             left = -extent.left / resolution,
35065             top = extent.top / resolution;
35066
35067         // If the resolution has changed, start over changing the corner, because
35068         // the features will redraw.
35069         if (resolutionChanged) {
35070             this.left = left;
35071             this.top = top;
35072             // Set the viewbox
35073             var extentString = "0 0 " + this.size.w + " " + this.size.h;
35074
35075             this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
35076             this.translate(this.xOffset, 0);
35077             return true;
35078         } else {
35079             var inRange = this.translate(left - this.left + this.xOffset, top - this.top);
35080             if (!inRange) {
35081                 // recenter the coordinate system
35082                 this.setExtent(extent, true);
35083             }
35084             return coordSysUnchanged && inRange;
35085         }
35086     },
35087     
35088     /**
35089      * Method: translate
35090      * Transforms the SVG coordinate system
35091      * 
35092      * Parameters:
35093      * x - {Float}
35094      * y - {Float}
35095      * 
35096      * Returns:
35097      * {Boolean} true if the translation parameters are in the valid coordinates
35098      *     range, false otherwise.
35099      */
35100     translate: function(x, y) {
35101         if (!this.inValidRange(x, y, true)) {
35102             return false;
35103         } else {
35104             var transformString = "";
35105             if (x || y) {
35106                 transformString = "translate(" + x + "," + y + ")";
35107             }
35108             this.root.setAttributeNS(null, "transform", transformString);
35109             this.translationParameters = {x: x, y: y};
35110             return true;
35111         }
35112     },
35113
35114     /**
35115      * Method: setSize
35116      * Sets the size of the drawing surface.
35117      * 
35118      * Parameters:
35119      * size - {<OpenLayers.Size>} The size of the drawing surface
35120      */
35121     setSize: function(size) {
35122         OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
35123         
35124         this.rendererRoot.setAttributeNS(null, "width", this.size.w);
35125         this.rendererRoot.setAttributeNS(null, "height", this.size.h);
35126     },
35127
35128     /** 
35129      * Method: getNodeType 
35130      * 
35131      * Parameters:
35132      * geometry - {<OpenLayers.Geometry>}
35133      * style - {Object}
35134      * 
35135      * Returns:
35136      * {String} The corresponding node type for the specified geometry
35137      */
35138     getNodeType: function(geometry, style) {
35139         var nodeType = null;
35140         switch (geometry.CLASS_NAME) {
35141             case "OpenLayers.Geometry.Point":
35142                 if (style.externalGraphic) {
35143                     nodeType = "image";
35144                 } else if (this.isComplexSymbol(style.graphicName)) {
35145                     nodeType = "svg";
35146                 } else {
35147                     nodeType = "circle";
35148                 }
35149                 break;
35150             case "OpenLayers.Geometry.Rectangle":
35151                 nodeType = "rect";
35152                 break;
35153             case "OpenLayers.Geometry.LineString":
35154                 nodeType = "polyline";
35155                 break;
35156             case "OpenLayers.Geometry.LinearRing":
35157                 nodeType = "polygon";
35158                 break;
35159             case "OpenLayers.Geometry.Polygon":
35160             case "OpenLayers.Geometry.Curve":
35161                 nodeType = "path";
35162                 break;
35163             default:
35164                 break;
35165         }
35166         return nodeType;
35167     },
35168
35169     /** 
35170      * Method: setStyle
35171      * Use to set all the style attributes to a SVG node.
35172      * 
35173      * Takes care to adjust stroke width and point radius to be
35174      * resolution-relative
35175      *
35176      * Parameters:
35177      * node - {SVGDomElement} An SVG element to decorate
35178      * style - {Object}
35179      * options - {Object} Currently supported options include 
35180      *                              'isFilled' {Boolean} and
35181      *                              'isStroked' {Boolean}
35182      */
35183     setStyle: function(node, style, options) {
35184         style = style  || node._style;
35185         options = options || node._options;
35186
35187         var title = style.title || style.graphicTitle;
35188         if (title) {
35189             node.setAttributeNS(null, "title", title);
35190             //Standards-conformant SVG
35191             // Prevent duplicate nodes. See issue https://github.com/openlayers/openlayers/issues/92 
35192             var titleNode = node.getElementsByTagName("title");
35193             if (titleNode.length > 0) {
35194                 titleNode[0].firstChild.textContent = title;
35195             } else {
35196                 var label = this.nodeFactory(null, "title");
35197                 label.textContent = title;
35198                 node.appendChild(label);
35199             }
35200         }
35201
35202         var r = parseFloat(node.getAttributeNS(null, "r"));
35203         var widthFactor = 1;
35204         var pos;
35205         if (node._geometryClass == "OpenLayers.Geometry.Point" && r) {
35206             node.style.visibility = "";
35207             if (style.graphic === false) {
35208                 node.style.visibility = "hidden";
35209             } else if (style.externalGraphic) {
35210                 pos = this.getPosition(node);
35211                 if (style.graphicWidth && style.graphicHeight) {
35212                   node.setAttributeNS(null, "preserveAspectRatio", "none");
35213                 }
35214                 var width = style.graphicWidth || style.graphicHeight;
35215                 var height = style.graphicHeight || style.graphicWidth;
35216                 width = width ? width : style.pointRadius*2;
35217                 height = height ? height : style.pointRadius*2;
35218                 var xOffset = (style.graphicXOffset != undefined) ?
35219                     style.graphicXOffset : -(0.5 * width);
35220                 var yOffset = (style.graphicYOffset != undefined) ?
35221                     style.graphicYOffset : -(0.5 * height);
35222
35223                 var opacity = style.graphicOpacity || style.fillOpacity;
35224                 
35225                 node.setAttributeNS(null, "x", (pos.x + xOffset).toFixed());
35226                 node.setAttributeNS(null, "y", (pos.y + yOffset).toFixed());
35227                 node.setAttributeNS(null, "width", width);
35228                 node.setAttributeNS(null, "height", height);
35229                 node.setAttributeNS(this.xlinkns, "xlink:href", style.externalGraphic);
35230                 node.setAttributeNS(null, "style", "opacity: "+opacity);
35231                 node.onclick = OpenLayers.Event.preventDefault;
35232             } else if (this.isComplexSymbol(style.graphicName)) {
35233                 // the symbol viewBox is three times as large as the symbol
35234                 var offset = style.pointRadius * 3;
35235                 var size = offset * 2;
35236                 var src = this.importSymbol(style.graphicName);
35237                 pos = this.getPosition(node);
35238                 widthFactor = this.symbolMetrics[src.id][0] * 3 / size;
35239                 
35240                 // remove the node from the dom before we modify it. This
35241                 // prevents various rendering issues in Safari and FF
35242                 var parent = node.parentNode;
35243                 var nextSibling = node.nextSibling;
35244                 if(parent) {
35245                     parent.removeChild(node);
35246                 }
35247                 
35248                 // The more appropriate way to implement this would be use/defs,
35249                 // but due to various issues in several browsers, it is safer to
35250                 // copy the symbols instead of referencing them. 
35251                 // See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985 
35252                 // and this email thread
35253                 // http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html
35254                 node.firstChild && node.removeChild(node.firstChild);
35255                 node.appendChild(src.firstChild.cloneNode(true));
35256                 node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox"));
35257                 
35258                 node.setAttributeNS(null, "width", size);
35259                 node.setAttributeNS(null, "height", size);
35260                 node.setAttributeNS(null, "x", pos.x - offset);
35261                 node.setAttributeNS(null, "y", pos.y - offset);
35262                 
35263                 // now that the node has all its new properties, insert it
35264                 // back into the dom where it was
35265                 if(nextSibling) {
35266                     parent.insertBefore(node, nextSibling);
35267                 } else if(parent) {
35268                     parent.appendChild(node);
35269                 }
35270             } else {
35271                 node.setAttributeNS(null, "r", style.pointRadius);
35272             }
35273
35274             var rotation = style.rotation;
35275             
35276             if ((rotation !== undefined || node._rotation !== undefined) && pos) {
35277                 node._rotation = rotation;
35278                 rotation |= 0;
35279                 if (node.nodeName !== "svg") { 
35280                     node.setAttributeNS(null, "transform", 
35281                         "rotate(" + rotation + " " + pos.x + " " + 
35282                         pos.y + ")"); 
35283                 } else {
35284                     var metrics = this.symbolMetrics[src.id];
35285                     node.firstChild.setAttributeNS(null, "transform", "rotate(" 
35286                         + rotation + " " 
35287                         + metrics[1] + " "
35288                         + metrics[2] + ")");
35289                 }
35290             }
35291         }
35292         
35293         if (options.isFilled) {
35294             node.setAttributeNS(null, "fill", style.fillColor);
35295             node.setAttributeNS(null, "fill-opacity", style.fillOpacity);
35296         } else {
35297             node.setAttributeNS(null, "fill", "none");
35298         }
35299
35300         if (options.isStroked) {
35301             node.setAttributeNS(null, "stroke", style.strokeColor);
35302             node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity);
35303             node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor);
35304             node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap || "round");
35305             // Hard-coded linejoin for now, to make it look the same as in VML.
35306             // There is no strokeLinejoin property yet for symbolizers.
35307             node.setAttributeNS(null, "stroke-linejoin", "round");
35308             style.strokeDashstyle && node.setAttributeNS(null,
35309                 "stroke-dasharray", this.dashStyle(style, widthFactor));
35310         } else {
35311             node.setAttributeNS(null, "stroke", "none");
35312         }
35313         
35314         if (style.pointerEvents) {
35315             node.setAttributeNS(null, "pointer-events", style.pointerEvents);
35316         }
35317                 
35318         if (style.cursor != null) {
35319             node.setAttributeNS(null, "cursor", style.cursor);
35320         }
35321         
35322         return node;
35323     },
35324
35325     /** 
35326      * Method: dashStyle
35327      * 
35328      * Parameters:
35329      * style - {Object}
35330      * widthFactor - {Number}
35331      * 
35332      * Returns:
35333      * {String} A SVG compliant 'stroke-dasharray' value
35334      */
35335     dashStyle: function(style, widthFactor) {
35336         var w = style.strokeWidth * widthFactor;
35337         var str = style.strokeDashstyle;
35338         switch (str) {
35339             case 'solid':
35340                 return 'none';
35341             case 'dot':
35342                 return [1, 4 * w].join();
35343             case 'dash':
35344                 return [4 * w, 4 * w].join();
35345             case 'dashdot':
35346                 return [4 * w, 4 * w, 1, 4 * w].join();
35347             case 'longdash':
35348                 return [8 * w, 4 * w].join();
35349             case 'longdashdot':
35350                 return [8 * w, 4 * w, 1, 4 * w].join();
35351             default:
35352                 return OpenLayers.String.trim(str).replace(/\s+/g, ",");
35353         }
35354     },
35355     
35356     /** 
35357      * Method: createNode
35358      * 
35359      * Parameters:
35360      * type - {String} Kind of node to draw
35361      * id - {String} Id for node
35362      * 
35363      * Returns:
35364      * {DOMElement} A new node of the given type and id
35365      */
35366     createNode: function(type, id) {
35367         var node = document.createElementNS(this.xmlns, type);
35368         if (id) {
35369             node.setAttributeNS(null, "id", id);
35370         }
35371         return node;    
35372     },
35373     
35374     /** 
35375      * Method: nodeTypeCompare
35376      * 
35377      * Parameters:
35378      * node - {SVGDomElement} An SVG element
35379      * type - {String} Kind of node
35380      * 
35381      * Returns:
35382      * {Boolean} Whether or not the specified node is of the specified type
35383      */
35384     nodeTypeCompare: function(node, type) {
35385         return (type == node.nodeName);
35386     },
35387    
35388     /**
35389      * Method: createRenderRoot
35390      * 
35391      * Returns:
35392      * {DOMElement} The specific render engine's root element
35393      */
35394     createRenderRoot: function() {
35395         var svg = this.nodeFactory(this.container.id + "_svgRoot", "svg");
35396         svg.style.display = "block";
35397         return svg;
35398     },
35399
35400     /**
35401      * Method: createRoot
35402      * 
35403      * Parameters:
35404      * suffix - {String} suffix to append to the id
35405      * 
35406      * Returns:
35407      * {DOMElement}
35408      */
35409     createRoot: function(suffix) {
35410         return this.nodeFactory(this.container.id + suffix, "g");
35411     },
35412
35413     /**
35414      * Method: createDefs
35415      *
35416      * Returns:
35417      * {DOMElement} The element to which we'll add the symbol definitions
35418      */
35419     createDefs: function() {
35420         var defs = this.nodeFactory(this.container.id + "_defs", "defs");
35421         this.rendererRoot.appendChild(defs);
35422         return defs;
35423     },
35424
35425     /**************************************
35426      *                                    *
35427      *     GEOMETRY DRAWING FUNCTIONS     *
35428      *                                    *
35429      **************************************/
35430
35431     /**
35432      * Method: drawPoint
35433      * This method is only called by the renderer itself.
35434      * 
35435      * Parameters: 
35436      * node - {DOMElement}
35437      * geometry - {<OpenLayers.Geometry>}
35438      * 
35439      * Returns:
35440      * {DOMElement} or false if the renderer could not draw the point
35441      */ 
35442     drawPoint: function(node, geometry) {
35443         return this.drawCircle(node, geometry, 1);
35444     },
35445
35446     /**
35447      * Method: drawCircle
35448      * This method is only called by the renderer itself.
35449      * 
35450      * Parameters: 
35451      * node - {DOMElement}
35452      * geometry - {<OpenLayers.Geometry>}
35453      * radius - {Float}
35454      * 
35455      * Returns:
35456      * {DOMElement} or false if the renderer could not draw the circle
35457      */
35458     drawCircle: function(node, geometry, radius) {
35459         var resolution = this.getResolution();
35460         var x = ((geometry.x - this.featureDx) / resolution + this.left);
35461         var y = (this.top - geometry.y / resolution);
35462
35463         if (this.inValidRange(x, y)) { 
35464             node.setAttributeNS(null, "cx", x);
35465             node.setAttributeNS(null, "cy", y);
35466             node.setAttributeNS(null, "r", radius);
35467             return node;
35468         } else {
35469             return false;
35470         }    
35471             
35472     },
35473     
35474     /**
35475      * Method: drawLineString
35476      * This method is only called by the renderer itself.
35477      * 
35478      * Parameters: 
35479      * node - {DOMElement}
35480      * geometry - {<OpenLayers.Geometry>}
35481      * 
35482      * Returns:
35483      * {DOMElement} or null if the renderer could not draw all components of
35484      *     the linestring, or false if nothing could be drawn
35485      */ 
35486     drawLineString: function(node, geometry) {
35487         var componentsResult = this.getComponentsString(geometry.components);
35488         if (componentsResult.path) {
35489             node.setAttributeNS(null, "points", componentsResult.path);
35490             return (componentsResult.complete ? node : null);  
35491         } else {
35492             return false;
35493         }
35494     },
35495     
35496     /**
35497      * Method: drawLinearRing
35498      * This method is only called by the renderer itself.
35499      * 
35500      * Parameters: 
35501      * node - {DOMElement}
35502      * geometry - {<OpenLayers.Geometry>}
35503      * 
35504      * Returns:
35505      * {DOMElement} or null if the renderer could not draw all components
35506      *     of the linear ring, or false if nothing could be drawn
35507      */ 
35508     drawLinearRing: function(node, geometry) {
35509         var componentsResult = this.getComponentsString(geometry.components);
35510         if (componentsResult.path) {
35511             node.setAttributeNS(null, "points", componentsResult.path);
35512             return (componentsResult.complete ? node : null);  
35513         } else {
35514             return false;
35515         }
35516     },
35517     
35518     /**
35519      * Method: drawPolygon
35520      * This method is only called by the renderer itself.
35521      * 
35522      * Parameters: 
35523      * node - {DOMElement}
35524      * geometry - {<OpenLayers.Geometry>}
35525      * 
35526      * Returns:
35527      * {DOMElement} or null if the renderer could not draw all components
35528      *     of the polygon, or false if nothing could be drawn
35529      */ 
35530     drawPolygon: function(node, geometry) {
35531         var d = "";
35532         var draw = true;
35533         var complete = true;
35534         var linearRingResult, path;
35535         for (var j=0, len=geometry.components.length; j<len; j++) {
35536             d += " M";
35537             linearRingResult = this.getComponentsString(
35538                 geometry.components[j].components, " ");
35539             path = linearRingResult.path;
35540             if (path) {
35541                 d += " " + path;
35542                 complete = linearRingResult.complete && complete;
35543             } else {
35544                 draw = false;
35545             }
35546         }
35547         d += " z";
35548         if (draw) {
35549             node.setAttributeNS(null, "d", d);
35550             node.setAttributeNS(null, "fill-rule", "evenodd");
35551             return complete ? node : null;
35552         } else {
35553             return false;
35554         }    
35555     },
35556     
35557     /**
35558      * Method: drawRectangle
35559      * This method is only called by the renderer itself.
35560      * 
35561      * Parameters: 
35562      * node - {DOMElement}
35563      * geometry - {<OpenLayers.Geometry>}
35564      * 
35565      * Returns:
35566      * {DOMElement} or false if the renderer could not draw the rectangle
35567      */ 
35568     drawRectangle: function(node, geometry) {
35569         var resolution = this.getResolution();
35570         var x = ((geometry.x - this.featureDx) / resolution + this.left);
35571         var y = (this.top - geometry.y / resolution);
35572
35573         if (this.inValidRange(x, y)) { 
35574             node.setAttributeNS(null, "x", x);
35575             node.setAttributeNS(null, "y", y);
35576             node.setAttributeNS(null, "width", geometry.width / resolution);
35577             node.setAttributeNS(null, "height", geometry.height / resolution);
35578             return node;
35579         } else {
35580             return false;
35581         }
35582     },
35583     
35584     /**
35585      * Method: drawText
35586      * This method is only called by the renderer itself.
35587      *
35588      * Parameters:
35589      * featureId - {String}
35590      * style -
35591      * location - {<OpenLayers.Geometry.Point>}
35592      */
35593     drawText: function(featureId, style, location) {
35594         var drawOutline = (!!style.labelOutlineWidth);
35595         // First draw text in halo color and size and overlay the
35596         // normal text afterwards
35597         if (drawOutline) {
35598             var outlineStyle = OpenLayers.Util.extend({}, style);
35599             outlineStyle.fontColor = outlineStyle.labelOutlineColor;
35600             outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor;
35601             outlineStyle.fontStrokeWidth = style.labelOutlineWidth;
35602             if (style.labelOutlineOpacity) {
35603                 outlineStyle.fontOpacity = style.labelOutlineOpacity;
35604             }
35605             delete outlineStyle.labelOutlineWidth;
35606             this.drawText(featureId, outlineStyle, location);
35607         }
35608
35609         var resolution = this.getResolution();
35610
35611         var x = ((location.x - this.featureDx) / resolution + this.left);
35612         var y = (location.y / resolution - this.top);
35613
35614         var suffix = (drawOutline)?this.LABEL_OUTLINE_SUFFIX:this.LABEL_ID_SUFFIX;
35615         var label = this.nodeFactory(featureId + suffix, "text");
35616
35617         label.setAttributeNS(null, "x", x);
35618         label.setAttributeNS(null, "y", -y);
35619
35620         if (style.fontColor) {
35621             label.setAttributeNS(null, "fill", style.fontColor);
35622         }
35623         if (style.fontStrokeColor) {
35624             label.setAttributeNS(null, "stroke", style.fontStrokeColor);
35625         }
35626         if (style.fontStrokeWidth) {
35627             label.setAttributeNS(null, "stroke-width", style.fontStrokeWidth);
35628         }
35629         if (style.fontOpacity) {
35630             label.setAttributeNS(null, "opacity", style.fontOpacity);
35631         }
35632         if (style.fontFamily) {
35633             label.setAttributeNS(null, "font-family", style.fontFamily);
35634         }
35635         if (style.fontSize) {
35636             label.setAttributeNS(null, "font-size", style.fontSize);
35637         }
35638         if (style.fontWeight) {
35639             label.setAttributeNS(null, "font-weight", style.fontWeight);
35640         }
35641         if (style.fontStyle) {
35642             label.setAttributeNS(null, "font-style", style.fontStyle);
35643         }
35644         if (style.labelSelect === true) {
35645             label.setAttributeNS(null, "pointer-events", "visible");
35646             label._featureId = featureId;
35647         } else {
35648             label.setAttributeNS(null, "pointer-events", "none");
35649         }
35650         var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign;
35651         label.setAttributeNS(null, "text-anchor",
35652             OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle");
35653
35654         if (OpenLayers.IS_GECKO === true) {
35655             label.setAttributeNS(null, "dominant-baseline",
35656                 OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central");
35657         }
35658
35659         var labelRows = style.label.split('\n');
35660         var numRows = labelRows.length;
35661         while (label.childNodes.length > numRows) {
35662             label.removeChild(label.lastChild);
35663         }
35664         for (var i = 0; i < numRows; i++) {
35665             var tspan = this.nodeFactory(featureId + suffix + "_tspan_" + i, "tspan");
35666             if (style.labelSelect === true) {
35667                 tspan._featureId = featureId;
35668                 tspan._geometry = location;
35669                 tspan._geometryClass = location.CLASS_NAME;
35670             }
35671             if (OpenLayers.IS_GECKO === false) {
35672                 tspan.setAttributeNS(null, "baseline-shift",
35673                     OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%");
35674             }
35675             tspan.setAttribute("x", x);
35676             if (i == 0) {
35677                 var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]];
35678                 if (vfactor == null) {
35679                      vfactor = -.5;
35680                 }
35681                 tspan.setAttribute("dy", (vfactor*(numRows-1)) + "em");
35682             } else {
35683                 tspan.setAttribute("dy", "1em");
35684             }
35685             tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i];
35686             if (!tspan.parentNode) {
35687                 label.appendChild(tspan);
35688             }
35689         }
35690
35691         if (!label.parentNode) {
35692             this.textRoot.appendChild(label);
35693         }
35694     },
35695     
35696     /** 
35697      * Method: getComponentString
35698      * 
35699      * Parameters:
35700      * components - {Array(<OpenLayers.Geometry.Point>)} Array of points
35701      * separator - {String} character between coordinate pairs. Defaults to ","
35702      * 
35703      * Returns:
35704      * {Object} hash with properties "path" (the string created from the
35705      *     components and "complete" (false if the renderer was unable to
35706      *     draw all components)
35707      */
35708     getComponentsString: function(components, separator) {
35709         var renderCmp = [];
35710         var complete = true;
35711         var len = components.length;
35712         var strings = [];
35713         var str, component;
35714         for(var i=0; i<len; i++) {
35715             component = components[i];
35716             renderCmp.push(component);
35717             str = this.getShortString(component);
35718             if (str) {
35719                 strings.push(str);
35720             } else {
35721                 // The current component is outside the valid range. Let's
35722                 // see if the previous or next component is inside the range.
35723                 // If so, add the coordinate of the intersection with the
35724                 // valid range bounds.
35725                 if (i > 0) {
35726                     if (this.getShortString(components[i - 1])) {
35727                         strings.push(this.clipLine(components[i],
35728                             components[i-1]));
35729                     }
35730                 }
35731                 if (i < len - 1) {
35732                     if (this.getShortString(components[i + 1])) {
35733                         strings.push(this.clipLine(components[i],
35734                             components[i+1]));
35735                     }
35736                 }
35737                 complete = false;
35738             }
35739         }
35740
35741         return {
35742             path: strings.join(separator || ","),
35743             complete: complete
35744         };
35745     },
35746     
35747     /**
35748      * Method: clipLine
35749      * Given two points (one inside the valid range, and one outside),
35750      * clips the line betweeen the two points so that the new points are both
35751      * inside the valid range.
35752      * 
35753      * Parameters:
35754      * badComponent - {<OpenLayers.Geometry.Point>} original geometry of the
35755      *     invalid point
35756      * goodComponent - {<OpenLayers.Geometry.Point>} original geometry of the
35757      *     valid point
35758      * Returns
35759      * {String} the SVG coordinate pair of the clipped point (like
35760      *     getShortString), or an empty string if both passed componets are at
35761      *     the same point.
35762      */
35763     clipLine: function(badComponent, goodComponent) {
35764         if (goodComponent.equals(badComponent)) {
35765             return "";
35766         }
35767         var resolution = this.getResolution();
35768         var maxX = this.MAX_PIXEL - this.translationParameters.x;
35769         var maxY = this.MAX_PIXEL - this.translationParameters.y;
35770         var x1 = (goodComponent.x - this.featureDx) / resolution + this.left;
35771         var y1 = this.top - goodComponent.y / resolution;
35772         var x2 = (badComponent.x - this.featureDx) / resolution + this.left;
35773         var y2 = this.top - badComponent.y / resolution;
35774         var k;
35775         if (x2 < -maxX || x2 > maxX) {
35776             k = (y2 - y1) / (x2 - x1);
35777             x2 = x2 < 0 ? -maxX : maxX;
35778             y2 = y1 + (x2 - x1) * k;
35779         }
35780         if (y2 < -maxY || y2 > maxY) {
35781             k = (x2 - x1) / (y2 - y1);
35782             y2 = y2 < 0 ? -maxY : maxY;
35783             x2 = x1 + (y2 - y1) * k;
35784         }
35785         return x2 + "," + y2;
35786     },
35787
35788     /** 
35789      * Method: getShortString
35790      * 
35791      * Parameters:
35792      * point - {<OpenLayers.Geometry.Point>}
35793      * 
35794      * Returns:
35795      * {String} or false if point is outside the valid range
35796      */
35797     getShortString: function(point) {
35798         var resolution = this.getResolution();
35799         var x = ((point.x - this.featureDx) / resolution + this.left);
35800         var y = (this.top - point.y / resolution);
35801
35802         if (this.inValidRange(x, y)) { 
35803             return x + "," + y;
35804         } else {
35805             return false;
35806         }
35807     },
35808     
35809     /**
35810      * Method: getPosition
35811      * Finds the position of an svg node.
35812      * 
35813      * Parameters:
35814      * node - {DOMElement}
35815      * 
35816      * Returns:
35817      * {Object} hash with x and y properties, representing the coordinates
35818      *     within the svg coordinate system
35819      */
35820     getPosition: function(node) {
35821         return({
35822             x: parseFloat(node.getAttributeNS(null, "cx")),
35823             y: parseFloat(node.getAttributeNS(null, "cy"))
35824         });
35825     },
35826
35827     /**
35828      * Method: importSymbol
35829      * add a new symbol definition from the rendererer's symbol hash
35830      * 
35831      * Parameters:
35832      * graphicName - {String} name of the symbol to import
35833      * 
35834      * Returns:
35835      * {DOMElement} - the imported symbol
35836      */      
35837     importSymbol: function (graphicName)  {
35838         if (!this.defs) {
35839             // create svg defs tag
35840             this.defs = this.createDefs();
35841         }
35842         var id = this.container.id + "-" + graphicName;
35843         
35844         // check if symbol already exists in the defs
35845         var existing = document.getElementById(id);
35846         if (existing != null) {
35847             return existing;
35848         }
35849         
35850         var symbol = OpenLayers.Renderer.symbol[graphicName];
35851         if (!symbol) {
35852             throw new Error(graphicName + ' is not a valid symbol name');
35853         }
35854
35855         var symbolNode = this.nodeFactory(id, "symbol");
35856         var node = this.nodeFactory(null, "polygon");
35857         symbolNode.appendChild(node);
35858         var symbolExtent = new OpenLayers.Bounds(
35859                                     Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
35860
35861         var points = [];
35862         var x,y;
35863         for (var i=0; i<symbol.length; i=i+2) {
35864             x = symbol[i];
35865             y = symbol[i+1];
35866             symbolExtent.left = Math.min(symbolExtent.left, x);
35867             symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
35868             symbolExtent.right = Math.max(symbolExtent.right, x);
35869             symbolExtent.top = Math.max(symbolExtent.top, y);
35870             points.push(x, ",", y);
35871         }
35872         
35873         node.setAttributeNS(null, "points", points.join(" "));
35874         
35875         var width = symbolExtent.getWidth();
35876         var height = symbolExtent.getHeight();
35877         // create a viewBox three times as large as the symbol itself,
35878         // to allow for strokeWidth being displayed correctly at the corners.
35879         var viewBox = [symbolExtent.left - width,
35880                         symbolExtent.bottom - height, width * 3, height * 3];
35881         symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
35882         this.symbolMetrics[id] = [
35883             Math.max(width, height),
35884             symbolExtent.getCenterLonLat().lon,
35885             symbolExtent.getCenterLonLat().lat
35886         ];
35887         
35888         this.defs.appendChild(symbolNode);
35889         return symbolNode;
35890     },
35891     
35892     /**
35893      * Method: getFeatureIdFromEvent
35894      * 
35895      * Parameters:
35896      * evt - {Object} An <OpenLayers.Event> object
35897      *
35898      * Returns:
35899      * {String} A feature id or undefined.
35900      */
35901     getFeatureIdFromEvent: function(evt) {
35902         var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);
35903         if(!featureId) {
35904             var target = evt.target;
35905             featureId = target.parentNode && target != this.rendererRoot ?
35906                 target.parentNode._featureId : undefined;
35907         }
35908         return featureId;
35909     },
35910
35911     CLASS_NAME: "OpenLayers.Renderer.SVG"
35912 });
35913
35914 /**
35915  * Constant: OpenLayers.Renderer.SVG.LABEL_ALIGN
35916  * {Object}
35917  */
35918 OpenLayers.Renderer.SVG.LABEL_ALIGN = {
35919     "l": "start",
35920     "r": "end",
35921     "b": "bottom",
35922     "t": "hanging"
35923 };
35924
35925 /**
35926  * Constant: OpenLayers.Renderer.SVG.LABEL_VSHIFT
35927  * {Object}
35928  */
35929 OpenLayers.Renderer.SVG.LABEL_VSHIFT = {
35930     // according to
35931     // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html
35932     // a baseline-shift of -70% shifts the text exactly from the
35933     // bottom to the top of the baseline, so -35% moves the text to
35934     // the center of the baseline.
35935     "t": "-70%",
35936     "b": "0"    
35937 };
35938
35939 /**
35940  * Constant: OpenLayers.Renderer.SVG.LABEL_VFACTOR
35941  * {Object}
35942  */
35943 OpenLayers.Renderer.SVG.LABEL_VFACTOR = {
35944     "t": 0,
35945     "b": -1
35946 };
35947
35948 /**
35949  * Function: OpenLayers.Renderer.SVG.preventDefault
35950  * *Deprecated*.  Use <OpenLayers.Event.preventDefault> method instead.
35951  * Used to prevent default events (especially opening images in a new tab on
35952  * ctrl-click) from being executed for externalGraphic symbols
35953  */
35954 OpenLayers.Renderer.SVG.preventDefault = function(e) {
35955     OpenLayers.Event.preventDefault(e);
35956 };
35957 /* ======================================================================
35958     OpenLayers/Control/PanZoom.js
35959    ====================================================================== */
35960
35961 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
35962  * full list of contributors). Published under the 2-clause BSD license.
35963  * See license.txt in the OpenLayers distribution or repository for the
35964  * full text of the license. */
35965
35966
35967 /**
35968  * @requires OpenLayers/Control.js
35969  * @requires OpenLayers/Events/buttonclick.js
35970  */
35971
35972 /**
35973  * Class: OpenLayers.Control.PanZoom
35974  * The PanZoom is a visible control, composed of a
35975  * <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomPanel>. By
35976  * default it is drawn in the upper left corner of the map.
35977  *
35978  * Inherits from:
35979  *  - <OpenLayers.Control>
35980  */
35981 OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, {
35982
35983     /** 
35984      * APIProperty: slideFactor
35985      * {Integer} Number of pixels by which we'll pan the map in any direction 
35986      *     on clicking the arrow buttons.  If you want to pan by some ratio
35987      *     of the map dimensions, use <slideRatio> instead.
35988      */
35989     slideFactor: 50,
35990
35991     /** 
35992      * APIProperty: slideRatio
35993      * {Number} The fraction of map width/height by which we'll pan the map            
35994      *     on clicking the arrow buttons.  Default is null.  If set, will
35995      *     override <slideFactor>. E.g. if slideRatio is .5, then the Pan Up
35996      *     button will pan up half the map height. 
35997      */
35998     slideRatio: null,
35999
36000     /** 
36001      * Property: buttons
36002      * {Array(DOMElement)} Array of Button Divs 
36003      */
36004     buttons: null,
36005
36006     /** 
36007      * Property: position
36008      * {<OpenLayers.Pixel>} 
36009      */
36010     position: null,
36011
36012     /**
36013      * Constructor: OpenLayers.Control.PanZoom
36014      * 
36015      * Parameters:
36016      * options - {Object}
36017      */
36018     initialize: function(options) {
36019         this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,
36020                                              OpenLayers.Control.PanZoom.Y);
36021         OpenLayers.Control.prototype.initialize.apply(this, arguments);
36022     },
36023
36024     /**
36025      * APIMethod: destroy
36026      */
36027     destroy: function() {
36028         if (this.map) {
36029             this.map.events.unregister("buttonclick", this, this.onButtonClick);
36030         }
36031         this.removeButtons();
36032         this.buttons = null;
36033         this.position = null;
36034         OpenLayers.Control.prototype.destroy.apply(this, arguments);
36035     },
36036
36037     /** 
36038      * Method: setMap
36039      *
36040      * Properties:
36041      * map - {<OpenLayers.Map>} 
36042      */
36043     setMap: function(map) {
36044         OpenLayers.Control.prototype.setMap.apply(this, arguments);
36045         this.map.events.register("buttonclick", this, this.onButtonClick);
36046     },
36047
36048     /**
36049      * Method: draw
36050      *
36051      * Parameters:
36052      * px - {<OpenLayers.Pixel>} 
36053      * 
36054      * Returns:
36055      * {DOMElement} A reference to the container div for the PanZoom control.
36056      */
36057     draw: function(px) {
36058         // initialize our internal div
36059         OpenLayers.Control.prototype.draw.apply(this, arguments);
36060         px = this.position;
36061
36062         // place the controls
36063         this.buttons = [];
36064
36065         var sz = {w: 18, h: 18};
36066         var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
36067
36068         this._addButton("panup", "north-mini.png", centered, sz);
36069         px.y = centered.y+sz.h;
36070         this._addButton("panleft", "west-mini.png", px, sz);
36071         this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz);
36072         this._addButton("pandown", "south-mini.png", 
36073                         centered.add(0, sz.h*2), sz);
36074         this._addButton("zoomin", "zoom-plus-mini.png", 
36075                         centered.add(0, sz.h*3+5), sz);
36076         this._addButton("zoomworld", "zoom-world-mini.png", 
36077                         centered.add(0, sz.h*4+5), sz);
36078         this._addButton("zoomout", "zoom-minus-mini.png", 
36079                         centered.add(0, sz.h*5+5), sz);
36080         return this.div;
36081     },
36082     
36083     /**
36084      * Method: _addButton
36085      * 
36086      * Parameters:
36087      * id - {String} 
36088      * img - {String} 
36089      * xy - {<OpenLayers.Pixel>} 
36090      * sz - {<OpenLayers.Size>} 
36091      * 
36092      * Returns:
36093      * {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the
36094      *     image of the button, and has all the proper event handlers set.
36095      */
36096     _addButton:function(id, img, xy, sz) {
36097         var imgLocation = OpenLayers.Util.getImageLocation(img);
36098         var btn = OpenLayers.Util.createAlphaImageDiv(
36099                                     this.id + "_" + id, 
36100                                     xy, sz, imgLocation, "absolute");
36101         btn.style.cursor = "pointer";
36102         //we want to add the outer div
36103         this.div.appendChild(btn);
36104         btn.action = id;
36105         btn.className = "olButton";
36106     
36107         //we want to remember/reference the outer div
36108         this.buttons.push(btn);
36109         return btn;
36110     },
36111     
36112     /**
36113      * Method: _removeButton
36114      * 
36115      * Parameters:
36116      * btn - {Object}
36117      */
36118     _removeButton: function(btn) {
36119         this.div.removeChild(btn);
36120         OpenLayers.Util.removeItem(this.buttons, btn);
36121     },
36122     
36123     /**
36124      * Method: removeButtons
36125      */
36126     removeButtons: function() {
36127         for(var i=this.buttons.length-1; i>=0; --i) {
36128             this._removeButton(this.buttons[i]);
36129         }
36130     },
36131     
36132     /**
36133      * Method: onButtonClick
36134      *
36135      * Parameters:
36136      * evt - {Event}
36137      */
36138     onButtonClick: function(evt) {
36139         var btn = evt.buttonElement;
36140         switch (btn.action) {
36141             case "panup": 
36142                 this.map.pan(0, -this.getSlideFactor("h"));
36143                 break;
36144             case "pandown": 
36145                 this.map.pan(0, this.getSlideFactor("h"));
36146                 break;
36147             case "panleft": 
36148                 this.map.pan(-this.getSlideFactor("w"), 0);
36149                 break;
36150             case "panright": 
36151                 this.map.pan(this.getSlideFactor("w"), 0);
36152                 break;
36153             case "zoomin": 
36154                 this.map.zoomIn(); 
36155                 break;
36156             case "zoomout": 
36157                 this.map.zoomOut(); 
36158                 break;
36159             case "zoomworld": 
36160                 this.map.zoomToMaxExtent(); 
36161                 break;
36162         }
36163     },
36164     
36165     /**
36166      * Method: getSlideFactor
36167      *
36168      * Parameters:
36169      * dim - {String} "w" or "h" (for width or height).
36170      *
36171      * Returns:
36172      * {Number} The slide factor for panning in the requested direction.
36173      */
36174     getSlideFactor: function(dim) {
36175         return this.slideRatio ?
36176             this.map.getSize()[dim] * this.slideRatio :
36177             this.slideFactor;
36178     },
36179
36180     CLASS_NAME: "OpenLayers.Control.PanZoom"
36181 });
36182
36183 /**
36184  * Constant: X
36185  * {Integer}
36186  */
36187 OpenLayers.Control.PanZoom.X = 4;
36188
36189 /**
36190  * Constant: Y
36191  * {Integer}
36192  */
36193 OpenLayers.Control.PanZoom.Y = 4;
36194 /* ======================================================================
36195     OpenLayers/Icon.js
36196    ====================================================================== */
36197
36198 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
36199  * full list of contributors). Published under the 2-clause BSD license.
36200  * See license.txt in the OpenLayers distribution or repository for the
36201  * full text of the license. */
36202
36203 /**
36204  * @requires OpenLayers/BaseTypes/Class.js
36205  */
36206
36207 /**
36208  * Class: OpenLayers.Icon
36209  * 
36210  * The icon represents a graphical icon on the screen.  Typically used in
36211  * conjunction with a <OpenLayers.Marker> to represent markers on a screen.
36212  *
36213  * An icon has a url, size and position.  It also contains an offset which 
36214  * allows the center point to be represented correctly.  This can be
36215  * provided either as a fixed offset or a function provided to calculate
36216  * the desired offset. 
36217  * 
36218  */
36219 OpenLayers.Icon = OpenLayers.Class({
36220     
36221     /** 
36222      * Property: url 
36223      * {String}  image url
36224      */
36225     url: null,
36226     
36227     /** 
36228      * Property: size 
36229      * {<OpenLayers.Size>|Object} An OpenLayers.Size or
36230      * an object with a 'w' and 'h' properties.
36231      */
36232     size: null,
36233
36234     /** 
36235      * Property: offset 
36236      * {<OpenLayers.Pixel>|Object} distance in pixels to offset the
36237      * image when being rendered. An OpenLayers.Pixel or an object
36238      * with a 'x' and 'y' properties.
36239      */
36240     offset: null,    
36241     
36242     /** 
36243      * Property: calculateOffset 
36244      * {Function} Function to calculate the offset (based on the size)
36245      */
36246     calculateOffset: null,    
36247     
36248     /** 
36249      * Property: imageDiv 
36250      * {DOMElement} 
36251      */
36252     imageDiv: null,
36253
36254     /** 
36255      * Property: px 
36256      * {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object
36257      * with a 'x' and 'y' properties.
36258      */
36259     px: null,
36260     
36261     /** 
36262      * Constructor: OpenLayers.Icon
36263      * Creates an icon, which is an image tag in a div.  
36264      *
36265      * url - {String} 
36266      * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or an
36267      *                                   object with a 'w' and 'h'
36268      *                                   properties.
36269      * offset - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an
36270      *                                      object with a 'x' and 'y'
36271      *                                      properties.
36272      * calculateOffset - {Function} 
36273      */
36274     initialize: function(url, size, offset, calculateOffset) {
36275         this.url = url;
36276         this.size = size || {w: 20, h: 20};
36277         this.offset = offset || {x: -(this.size.w/2), y: -(this.size.h/2)};
36278         this.calculateOffset = calculateOffset;
36279
36280         var id = OpenLayers.Util.createUniqueID("OL_Icon_");
36281         this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id);
36282     },
36283     
36284     /** 
36285      * Method: destroy
36286      * Nullify references and remove event listeners to prevent circular 
36287      * references and memory leaks
36288      */
36289     destroy: function() {
36290         // erase any drawn elements
36291         this.erase();
36292
36293         OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild); 
36294         this.imageDiv.innerHTML = "";
36295         this.imageDiv = null;
36296     },
36297
36298     /** 
36299      * Method: clone
36300      * 
36301      * Returns:
36302      * {<OpenLayers.Icon>} A fresh copy of the icon.
36303      */
36304     clone: function() {
36305         return new OpenLayers.Icon(this.url, 
36306                                    this.size, 
36307                                    this.offset, 
36308                                    this.calculateOffset);
36309     },
36310     
36311     /**
36312      * Method: setSize
36313      * 
36314      * Parameters:
36315      * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or
36316      * an object with a 'w' and 'h' properties.
36317      */
36318     setSize: function(size) {
36319         if (size != null) {
36320             this.size = size;
36321         }
36322         this.draw();
36323     },
36324     
36325     /**
36326      * Method: setUrl
36327      * 
36328      * Parameters:
36329      * url - {String} 
36330      */
36331     setUrl: function(url) {
36332         if (url != null) {
36333             this.url = url;
36334         }
36335         this.draw();
36336     },
36337
36338     /** 
36339      * Method: draw
36340      * Move the div to the given pixel.
36341      * 
36342      * Parameters:
36343      * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an
36344      *                                  object with a 'x' and 'y' properties.
36345      * 
36346      * Returns:
36347      * {DOMElement} A new DOM Image of this icon set at the location passed-in
36348      */
36349     draw: function(px) {
36350         OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, 
36351                                             null, 
36352                                             null, 
36353                                             this.size, 
36354                                             this.url, 
36355                                             "absolute");
36356         this.moveTo(px);
36357         return this.imageDiv;
36358     }, 
36359
36360     /** 
36361      * Method: erase
36362      * Erase the underlying image element.
36363      */
36364     erase: function() {
36365         if (this.imageDiv != null && this.imageDiv.parentNode != null) {
36366             OpenLayers.Element.remove(this.imageDiv);
36367         }
36368     }, 
36369     
36370     /** 
36371      * Method: setOpacity
36372      * Change the icon's opacity
36373      *
36374      * Parameters:
36375      * opacity - {float} 
36376      */
36377     setOpacity: function(opacity) {
36378         OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null, 
36379                                             null, null, null, null, opacity);
36380
36381     },
36382     
36383     /**
36384      * Method: moveTo
36385      * move icon to passed in px.
36386      *
36387      * Parameters:
36388      * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.
36389      * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.
36390      */
36391     moveTo: function (px) {
36392         //if no px passed in, use stored location
36393         if (px != null) {
36394             this.px = px;
36395         }
36396
36397         if (this.imageDiv != null) {
36398             if (this.px == null) {
36399                 this.display(false);
36400             } else {
36401                 if (this.calculateOffset) {
36402                     this.offset = this.calculateOffset(this.size);  
36403                 }
36404                 OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, {
36405                     x: this.px.x + this.offset.x,
36406                     y: this.px.y + this.offset.y
36407                 });
36408             }
36409         }
36410     },
36411     
36412     /** 
36413      * Method: display
36414      * Hide or show the icon
36415      *
36416      * Parameters:
36417      * display - {Boolean} 
36418      */
36419     display: function(display) {
36420         this.imageDiv.style.display = (display) ? "" : "none"; 
36421     },
36422     
36423
36424     /**
36425      * APIMethod: isDrawn
36426      * 
36427      * Returns:
36428      * {Boolean} Whether or not the icon is drawn.
36429      */
36430     isDrawn: function() {
36431         // nodeType 11 for ie, whose nodes *always* have a parentNode
36432         // (of type document fragment)
36433         var isDrawn = (this.imageDiv && this.imageDiv.parentNode && 
36434                        (this.imageDiv.parentNode.nodeType != 11));    
36435
36436         return isDrawn;   
36437     },
36438
36439     CLASS_NAME: "OpenLayers.Icon"
36440 });
36441 /* ======================================================================
36442     OpenLayers/Marker.js
36443    ====================================================================== */
36444
36445 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
36446  * full list of contributors). Published under the 2-clause BSD license.
36447  * See license.txt in the OpenLayers distribution or repository for the
36448  * full text of the license. */
36449
36450
36451 /**
36452  * @requires OpenLayers/BaseTypes/Class.js
36453  * @requires OpenLayers/Events.js
36454  * @requires OpenLayers/Icon.js
36455  */
36456
36457 /**
36458  * Class: OpenLayers.Marker
36459  * Instances of OpenLayers.Marker are a combination of a 
36460  * <OpenLayers.LonLat> and an <OpenLayers.Icon>.  
36461  *
36462  * Markers are generally added to a special layer called
36463  * <OpenLayers.Layer.Markers>.
36464  *
36465  * Example:
36466  * (code)
36467  * var markers = new OpenLayers.Layer.Markers( "Markers" );
36468  * map.addLayer(markers);
36469  *
36470  * var size = new OpenLayers.Size(21,25);
36471  * var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
36472  * var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset);
36473  * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon));
36474  * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone()));
36475  *
36476  * (end)
36477  *
36478  * Note that if you pass an icon into the Marker constructor, it will take
36479  * that icon and use it. This means that you should not share icons between
36480  * markers -- you use them once, but you should clone() for any additional
36481  * markers using that same icon.
36482  */
36483 OpenLayers.Marker = OpenLayers.Class({
36484     
36485     /** 
36486      * Property: icon 
36487      * {<OpenLayers.Icon>} The icon used by this marker.
36488      */
36489     icon: null,
36490
36491     /** 
36492      * Property: lonlat 
36493      * {<OpenLayers.LonLat>} location of object
36494      */
36495     lonlat: null,
36496     
36497     /** 
36498      * Property: events 
36499      * {<OpenLayers.Events>} the event handler.
36500      */
36501     events: null,
36502     
36503     /** 
36504      * Property: map 
36505      * {<OpenLayers.Map>} the map this marker is attached to
36506      */
36507     map: null,
36508     
36509     /** 
36510      * Constructor: OpenLayers.Marker
36511      *
36512      * Parameters:
36513      * lonlat - {<OpenLayers.LonLat>} the position of this marker
36514      * icon - {<OpenLayers.Icon>}  the icon for this marker
36515      */
36516     initialize: function(lonlat, icon) {
36517         this.lonlat = lonlat;
36518         
36519         var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon();
36520         if (this.icon == null) {
36521             this.icon = newIcon;
36522         } else {
36523             this.icon.url = newIcon.url;
36524             this.icon.size = newIcon.size;
36525             this.icon.offset = newIcon.offset;
36526             this.icon.calculateOffset = newIcon.calculateOffset;
36527         }
36528         this.events = new OpenLayers.Events(this, this.icon.imageDiv);
36529     },
36530     
36531     /**
36532      * APIMethod: destroy
36533      * Destroy the marker. You must first remove the marker from any 
36534      * layer which it has been added to, or you will get buggy behavior.
36535      * (This can not be done within the marker since the marker does not
36536      * know which layer it is attached to.)
36537      */
36538     destroy: function() {
36539         // erase any drawn features
36540         this.erase();
36541
36542         this.map = null;
36543
36544         this.events.destroy();
36545         this.events = null;
36546
36547         if (this.icon != null) {
36548             this.icon.destroy();
36549             this.icon = null;
36550         }
36551     },
36552     
36553     /** 
36554     * Method: draw
36555     * Calls draw on the icon, and returns that output.
36556     * 
36557     * Parameters:
36558     * px - {<OpenLayers.Pixel>}
36559     * 
36560     * Returns:
36561     * {DOMElement} A new DOM Image with this marker's icon set at the 
36562     * location passed-in
36563     */
36564     draw: function(px) {
36565         return this.icon.draw(px);
36566     }, 
36567
36568     /** 
36569     * Method: erase
36570     * Erases any drawn elements for this marker.
36571     */
36572     erase: function() {
36573         if (this.icon != null) {
36574             this.icon.erase();
36575         }
36576     }, 
36577
36578     /**
36579     * Method: moveTo
36580     * Move the marker to the new location.
36581     *
36582     * Parameters:
36583     * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.
36584     * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.
36585     */
36586     moveTo: function (px) {
36587         if ((px != null) && (this.icon != null)) {
36588             this.icon.moveTo(px);
36589         }           
36590         this.lonlat = this.map.getLonLatFromLayerPx(px);
36591     },
36592
36593     /**
36594      * APIMethod: isDrawn
36595      * 
36596      * Returns:
36597      * {Boolean} Whether or not the marker is drawn.
36598      */
36599     isDrawn: function() {
36600         var isDrawn = (this.icon && this.icon.isDrawn());
36601         return isDrawn;   
36602     },
36603
36604     /**
36605      * Method: onScreen
36606      *
36607      * Returns:
36608      * {Boolean} Whether or not the marker is currently visible on screen.
36609      */
36610     onScreen:function() {
36611         
36612         var onScreen = false;
36613         if (this.map) {
36614             var screenBounds = this.map.getExtent();
36615             onScreen = screenBounds.containsLonLat(this.lonlat);
36616         }    
36617         return onScreen;
36618     },
36619     
36620     /**
36621      * Method: inflate
36622      * Englarges the markers icon by the specified ratio.
36623      *
36624      * Parameters:
36625      * inflate - {float} the ratio to enlarge the marker by (passing 2
36626      *                   will double the size).
36627      */
36628     inflate: function(inflate) {
36629         if (this.icon) {
36630             this.icon.setSize({
36631                 w: this.icon.size.w * inflate,
36632                 h: this.icon.size.h * inflate
36633             });
36634         }        
36635     },
36636     
36637     /** 
36638      * Method: setOpacity
36639      * Change the opacity of the marker by changin the opacity of 
36640      *   its icon
36641      * 
36642      * Parameters:
36643      * opacity - {float}  Specified as fraction (0.4, etc)
36644      */
36645     setOpacity: function(opacity) {
36646         this.icon.setOpacity(opacity);
36647     },
36648
36649     /**
36650      * Method: setUrl
36651      * Change URL of the Icon Image.
36652      * 
36653      * url - {String} 
36654      */
36655     setUrl: function(url) {
36656         this.icon.setUrl(url);
36657     },    
36658
36659     /** 
36660      * Method: display
36661      * Hide or show the icon
36662      * 
36663      * display - {Boolean} 
36664      */
36665     display: function(display) {
36666         this.icon.display(display);
36667     },
36668
36669     CLASS_NAME: "OpenLayers.Marker"
36670 });
36671
36672
36673 /**
36674  * Function: defaultIcon
36675  * Creates a default <OpenLayers.Icon>.
36676  * 
36677  * Returns:
36678  * {<OpenLayers.Icon>} A default OpenLayers.Icon to use for a marker
36679  */
36680 OpenLayers.Marker.defaultIcon = function() {
36681     return new OpenLayers.Icon(OpenLayers.Util.getImageLocation("marker.png"),
36682                                {w: 21, h: 25}, {x: -10.5, y: -25});
36683 };
36684     
36685
36686 /* ======================================================================
36687     OpenLayers/Popup.js
36688    ====================================================================== */
36689
36690 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
36691  * full list of contributors). Published under the 2-clause BSD license.
36692  * See license.txt in the OpenLayers distribution or repository for the
36693  * full text of the license. */
36694
36695 /**
36696  * @requires OpenLayers/BaseTypes/Class.js
36697  */
36698
36699
36700 /**
36701  * Class: OpenLayers.Popup
36702  * A popup is a small div that can opened and closed on the map.
36703  * Typically opened in response to clicking on a marker.  
36704  * See <OpenLayers.Marker>.  Popup's don't require their own
36705  * layer and are added the the map using the <OpenLayers.Map.addPopup>
36706  * method.
36707  *
36708  * Example:
36709  * (code)
36710  * popup = new OpenLayers.Popup("chicken", 
36711  *                    new OpenLayers.LonLat(5,40),
36712  *                    new OpenLayers.Size(200,200),
36713  *                    "example popup",
36714  *                    true);
36715  *       
36716  * map.addPopup(popup);
36717  * (end)
36718  */
36719 OpenLayers.Popup = OpenLayers.Class({
36720
36721     /** 
36722      * Property: events  
36723      * {<OpenLayers.Events>} custom event manager 
36724      */
36725     events: null,
36726     
36727     /** Property: id
36728      * {String} the unique identifier assigned to this popup.
36729      */
36730     id: "",
36731
36732     /** 
36733      * Property: lonlat 
36734      * {<OpenLayers.LonLat>} the position of this popup on the map
36735      */
36736     lonlat: null,
36737
36738     /** 
36739      * Property: div 
36740      * {DOMElement} the div that contains this popup.
36741      */
36742     div: null,
36743
36744     /** 
36745      * Property: contentSize 
36746      * {<OpenLayers.Size>} the width and height of the content.
36747      */
36748     contentSize: null,    
36749
36750     /** 
36751      * Property: size 
36752      * {<OpenLayers.Size>} the width and height of the popup.
36753      */
36754     size: null,    
36755
36756     /** 
36757      * Property: contentHTML 
36758      * {String} An HTML string for this popup to display.
36759      */
36760     contentHTML: null,
36761     
36762     /** 
36763      * Property: backgroundColor 
36764      * {String} the background color used by the popup.
36765      */
36766     backgroundColor: "",
36767     
36768     /** 
36769      * Property: opacity 
36770      * {float} the opacity of this popup (between 0.0 and 1.0)
36771      */
36772     opacity: "",
36773
36774     /** 
36775      * Property: border 
36776      * {String} the border size of the popup.  (eg 2px)
36777      */
36778     border: "",
36779     
36780     /** 
36781      * Property: contentDiv 
36782      * {DOMElement} a reference to the element that holds the content of
36783      *              the div.
36784      */
36785     contentDiv: null,
36786     
36787     /** 
36788      * Property: groupDiv 
36789      * {DOMElement} First and only child of 'div'. The group Div contains the
36790      *     'contentDiv' and the 'closeDiv'.
36791      */
36792     groupDiv: null,
36793
36794     /** 
36795      * Property: closeDiv
36796      * {DOMElement} the optional closer image
36797      */
36798     closeDiv: null,
36799
36800     /** 
36801      * APIProperty: autoSize
36802      * {Boolean} Resize the popup to auto-fit the contents.
36803      *     Default is false.
36804      */
36805     autoSize: false,
36806
36807     /**
36808      * APIProperty: minSize
36809      * {<OpenLayers.Size>} Minimum size allowed for the popup's contents.
36810      */
36811     minSize: null,
36812
36813     /**
36814      * APIProperty: maxSize
36815      * {<OpenLayers.Size>} Maximum size allowed for the popup's contents.
36816      */
36817     maxSize: null,
36818
36819     /** 
36820      * Property: displayClass
36821      * {String} The CSS class of the popup.
36822      */
36823     displayClass: "olPopup",
36824
36825     /** 
36826      * Property: contentDisplayClass
36827      * {String} The CSS class of the popup content div.
36828      */
36829     contentDisplayClass: "olPopupContent",
36830
36831     /** 
36832      * Property: padding 
36833      * {int or <OpenLayers.Bounds>} An extra opportunity to specify internal 
36834      *     padding of the content div inside the popup. This was originally
36835      *     confused with the css padding as specified in style.css's 
36836      *     'olPopupContent' class. We would like to get rid of this altogether,
36837      *     except that it does come in handy for the framed and anchoredbubble
36838      *     popups, who need to maintain yet another barrier between their 
36839      *     content and the outer border of the popup itself. 
36840      * 
36841      *     Note that in order to not break API, we must continue to support 
36842      *     this property being set as an integer. Really, though, we'd like to 
36843      *     have this specified as a Bounds object so that user can specify
36844      *     distinct left, top, right, bottom paddings. With the 3.0 release
36845      *     we can make this only a bounds.
36846      */
36847     padding: 0,
36848
36849     /** 
36850      * Property: disableFirefoxOverflowHack
36851      * {Boolean} The hack for overflow in Firefox causes all elements 
36852      *     to be re-drawn, which causes Flash elements to be 
36853      *     re-initialized, which is troublesome.
36854      *     With this property the hack can be disabled.
36855      */
36856     disableFirefoxOverflowHack: false,
36857
36858     /**
36859      * Method: fixPadding
36860      * To be removed in 3.0, this function merely helps us to deal with the 
36861      *     case where the user may have set an integer value for padding, 
36862      *     instead of an <OpenLayers.Bounds> object.
36863      */
36864     fixPadding: function() {
36865         if (typeof this.padding == "number") {
36866             this.padding = new OpenLayers.Bounds(
36867                 this.padding, this.padding, this.padding, this.padding
36868             );
36869         }
36870     },
36871
36872     /**
36873      * APIProperty: panMapIfOutOfView
36874      * {Boolean} When drawn, pan map such that the entire popup is visible in
36875      *     the current viewport (if necessary).
36876      *     Default is false.
36877      */
36878     panMapIfOutOfView: false,
36879     
36880     /**
36881      * APIProperty: keepInMap 
36882      * {Boolean} If panMapIfOutOfView is false, and this property is true, 
36883      *     contrain the popup such that it always fits in the available map
36884      *     space. By default, this is not set on the base class. If you are
36885      *     creating popups that are near map edges and not allowing pannning,
36886      *     and especially if you have a popup which has a
36887      *     fixedRelativePosition, setting this to false may be a smart thing to
36888      *     do. Subclasses may want to override this setting.
36889      *   
36890      *     Default is false.
36891      */
36892     keepInMap: false,
36893
36894     /**
36895      * APIProperty: closeOnMove
36896      * {Boolean} When map pans, close the popup.
36897      *     Default is false.
36898      */
36899     closeOnMove: false,
36900     
36901     /** 
36902      * Property: map 
36903      * {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map
36904      */
36905     map: null,
36906
36907     /** 
36908     * Constructor: OpenLayers.Popup
36909     * Create a popup.
36910     * 
36911     * Parameters: 
36912     * id - {String} a unqiue identifier for this popup.  If null is passed
36913     *               an identifier will be automatically generated. 
36914     * lonlat - {<OpenLayers.LonLat>}  The position on the map the popup will
36915     *                                 be shown.
36916     * contentSize - {<OpenLayers.Size>} The size of the content.
36917     * contentHTML - {String}          An HTML string to display inside the   
36918     *                                 popup.
36919     * closeBox - {Boolean}            Whether to display a close box inside
36920     *                                 the popup.
36921     * closeBoxCallback - {Function}   Function to be called on closeBox click.
36922     */
36923     initialize:function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {
36924         if (id == null) {
36925             id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
36926         }
36927
36928         this.id = id;
36929         this.lonlat = lonlat;
36930
36931         this.contentSize = (contentSize != null) ? contentSize 
36932                                   : new OpenLayers.Size(
36933                                                    OpenLayers.Popup.WIDTH,
36934                                                    OpenLayers.Popup.HEIGHT);
36935         if (contentHTML != null) { 
36936              this.contentHTML = contentHTML;
36937         }
36938         this.backgroundColor = OpenLayers.Popup.COLOR;
36939         this.opacity = OpenLayers.Popup.OPACITY;
36940         this.border = OpenLayers.Popup.BORDER;
36941
36942         this.div = OpenLayers.Util.createDiv(this.id, null, null, 
36943                                              null, null, null, "hidden");
36944         this.div.className = this.displayClass;
36945         
36946         var groupDivId = this.id + "_GroupDiv";
36947         this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null, 
36948                                                     null, "relative", null,
36949                                                     "hidden");
36950
36951         var id = this.div.id + "_contentDiv";
36952         this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(), 
36953                                                     null, "relative");
36954         this.contentDiv.className = this.contentDisplayClass;
36955         this.groupDiv.appendChild(this.contentDiv);
36956         this.div.appendChild(this.groupDiv);
36957
36958         if (closeBox) {
36959             this.addCloseBox(closeBoxCallback);
36960         } 
36961
36962         this.registerEvents();
36963     },
36964
36965     /** 
36966      * Method: destroy
36967      * nullify references to prevent circular references and memory leaks
36968      */
36969     destroy: function() {
36970
36971         this.id = null;
36972         this.lonlat = null;
36973         this.size = null;
36974         this.contentHTML = null;
36975         
36976         this.backgroundColor = null;
36977         this.opacity = null;
36978         this.border = null;
36979         
36980         if (this.closeOnMove && this.map) {
36981             this.map.events.unregister("movestart", this, this.hide);
36982         }
36983
36984         this.events.destroy();
36985         this.events = null;
36986         
36987         if (this.closeDiv) {
36988             OpenLayers.Event.stopObservingElement(this.closeDiv); 
36989             this.groupDiv.removeChild(this.closeDiv);
36990         }
36991         this.closeDiv = null;
36992         
36993         this.div.removeChild(this.groupDiv);
36994         this.groupDiv = null;
36995
36996         if (this.map != null) {
36997             this.map.removePopup(this);
36998         }
36999         this.map = null;
37000         this.div = null;
37001         
37002         this.autoSize = null;
37003         this.minSize = null;
37004         this.maxSize = null;
37005         this.padding = null;
37006         this.panMapIfOutOfView = null;
37007     },
37008
37009     /** 
37010     * Method: draw
37011     * Constructs the elements that make up the popup.
37012     *
37013     * Parameters:
37014     * px - {<OpenLayers.Pixel>} the position the popup in pixels.
37015     * 
37016     * Returns:
37017     * {DOMElement} Reference to a div that contains the drawn popup
37018     */
37019     draw: function(px) {
37020         if (px == null) {
37021             if ((this.lonlat != null) && (this.map != null)) {
37022                 px = this.map.getLayerPxFromLonLat(this.lonlat);
37023             }
37024         }
37025
37026         // this assumes that this.map already exists, which is okay because 
37027         // this.draw is only called once the popup has been added to the map.
37028         if (this.closeOnMove) {
37029             this.map.events.register("movestart", this, this.hide);
37030         }
37031         
37032         //listen to movestart, moveend to disable overflow (FF bug)
37033         if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') {
37034             this.map.events.register("movestart", this, function() {
37035                 var style = document.defaultView.getComputedStyle(
37036                     this.contentDiv, null
37037                 );
37038                 var currentOverflow = style.getPropertyValue("overflow");
37039                 if (currentOverflow != "hidden") {
37040                     this.contentDiv._oldOverflow = currentOverflow;
37041                     this.contentDiv.style.overflow = "hidden";
37042                 }
37043             });
37044             this.map.events.register("moveend", this, function() {
37045                 var oldOverflow = this.contentDiv._oldOverflow;
37046                 if (oldOverflow) {
37047                     this.contentDiv.style.overflow = oldOverflow;
37048                     this.contentDiv._oldOverflow = null;
37049                 }
37050             });
37051         }
37052
37053         this.moveTo(px);
37054         if (!this.autoSize && !this.size) {
37055             this.setSize(this.contentSize);
37056         }
37057         this.setBackgroundColor();
37058         this.setOpacity();
37059         this.setBorder();
37060         this.setContentHTML();
37061         
37062         if (this.panMapIfOutOfView) {
37063             this.panIntoView();
37064         }    
37065
37066         return this.div;
37067     },
37068
37069     /** 
37070      * Method: updatePosition
37071      * if the popup has a lonlat and its map members set, 
37072      * then have it move itself to its proper position
37073      */
37074     updatePosition: function() {
37075         if ((this.lonlat) && (this.map)) {
37076             var px = this.map.getLayerPxFromLonLat(this.lonlat);
37077             if (px) {
37078                 this.moveTo(px);           
37079             }    
37080         }
37081     },
37082
37083     /**
37084      * Method: moveTo
37085      * 
37086      * Parameters:
37087      * px - {<OpenLayers.Pixel>} the top and left position of the popup div. 
37088      */
37089     moveTo: function(px) {
37090         if ((px != null) && (this.div != null)) {
37091             this.div.style.left = px.x + "px";
37092             this.div.style.top = px.y + "px";
37093         }
37094     },
37095
37096     /**
37097      * Method: visible
37098      *
37099      * Returns:      
37100      * {Boolean} Boolean indicating whether or not the popup is visible
37101      */
37102     visible: function() {
37103         return OpenLayers.Element.visible(this.div);
37104     },
37105
37106     /**
37107      * Method: toggle
37108      * Toggles visibility of the popup.
37109      */
37110     toggle: function() {
37111         if (this.visible()) {
37112             this.hide();
37113         } else {
37114             this.show();
37115         }
37116     },
37117
37118     /**
37119      * Method: show
37120      * Makes the popup visible.
37121      */
37122     show: function() {
37123         this.div.style.display = '';
37124
37125         if (this.panMapIfOutOfView) {
37126             this.panIntoView();
37127         }    
37128     },
37129
37130     /**
37131      * Method: hide
37132      * Makes the popup invisible.
37133      */
37134     hide: function() {
37135         this.div.style.display = 'none';
37136     },
37137
37138     /**
37139      * Method: setSize
37140      * Used to adjust the size of the popup. 
37141      *
37142      * Parameters:
37143      * contentSize - {<OpenLayers.Size>} the new size for the popup's 
37144      *     contents div (in pixels).
37145      */
37146     setSize:function(contentSize) { 
37147         this.size = contentSize.clone(); 
37148         
37149         // if our contentDiv has a css 'padding' set on it by a stylesheet, we 
37150         //  must add that to the desired "size". 
37151         var contentDivPadding = this.getContentDivPadding();
37152         var wPadding = contentDivPadding.left + contentDivPadding.right;
37153         var hPadding = contentDivPadding.top + contentDivPadding.bottom;
37154
37155         // take into account the popup's 'padding' property
37156         this.fixPadding();
37157         wPadding += this.padding.left + this.padding.right;
37158         hPadding += this.padding.top + this.padding.bottom;
37159
37160         // make extra space for the close div
37161         if (this.closeDiv) {
37162             var closeDivWidth = parseInt(this.closeDiv.style.width);
37163             wPadding += closeDivWidth + contentDivPadding.right;
37164         }
37165
37166         //increase size of the main popup div to take into account the 
37167         // users's desired padding and close div.        
37168         this.size.w += wPadding;
37169         this.size.h += hPadding;
37170
37171         //now if our browser is IE, we need to actually make the contents 
37172         // div itself bigger to take its own padding into effect. this makes 
37173         // me want to shoot someone, but so it goes.
37174         if (OpenLayers.BROWSER_NAME == "msie") {
37175             this.contentSize.w += 
37176                 contentDivPadding.left + contentDivPadding.right;
37177             this.contentSize.h += 
37178                 contentDivPadding.bottom + contentDivPadding.top;
37179         }
37180
37181         if (this.div != null) {
37182             this.div.style.width = this.size.w + "px";
37183             this.div.style.height = this.size.h + "px";
37184         }
37185         if (this.contentDiv != null){
37186             this.contentDiv.style.width = contentSize.w + "px";
37187             this.contentDiv.style.height = contentSize.h + "px";
37188         }
37189     },  
37190
37191     /**
37192      * APIMethod: updateSize
37193      * Auto size the popup so that it precisely fits its contents (as 
37194      *     determined by this.contentDiv.innerHTML). Popup size will, of
37195      *     course, be limited by the available space on the current map
37196      */
37197     updateSize: function() {
37198         
37199         // determine actual render dimensions of the contents by putting its
37200         // contents into a fake contentDiv (for the CSS) and then measuring it
37201         var preparedHTML = "<div class='" + this.contentDisplayClass+ "'>" + 
37202             this.contentDiv.innerHTML + 
37203             "</div>";
37204  
37205         var containerElement = (this.map) ? this.map.div : document.body;
37206         var realSize = OpenLayers.Util.getRenderedDimensions(
37207             preparedHTML, null, {
37208                 displayClass: this.displayClass,
37209                 containerElement: containerElement
37210             }
37211         );
37212
37213         // is the "real" size of the div is safe to display in our map?
37214         var safeSize = this.getSafeContentSize(realSize);
37215
37216         var newSize = null;
37217         if (safeSize.equals(realSize)) {
37218             //real size of content is small enough to fit on the map, 
37219             // so we use real size.
37220             newSize = realSize;
37221
37222         } else {
37223
37224             // make a new 'size' object with the clipped dimensions 
37225             // set or null if not clipped.
37226             var fixedSize = {
37227                 w: (safeSize.w < realSize.w) ? safeSize.w : null,
37228                 h: (safeSize.h < realSize.h) ? safeSize.h : null
37229             };
37230         
37231             if (fixedSize.w && fixedSize.h) {
37232                 //content is too big in both directions, so we will use 
37233                 // max popup size (safeSize), knowing well that it will 
37234                 // overflow both ways.                
37235                 newSize = safeSize;
37236             } else {
37237                 //content is clipped in only one direction, so we need to 
37238                 // run getRenderedDimensions() again with a fixed dimension
37239                 var clippedSize = OpenLayers.Util.getRenderedDimensions(
37240                     preparedHTML, fixedSize, {
37241                         displayClass: this.contentDisplayClass,
37242                         containerElement: containerElement
37243                     }
37244                 );
37245                 
37246                 //if the clipped size is still the same as the safeSize, 
37247                 // that means that our content must be fixed in the 
37248                 // offending direction. If overflow is 'auto', this means 
37249                 // we are going to have a scrollbar for sure, so we must 
37250                 // adjust for that.
37251                 //
37252                 var currentOverflow = OpenLayers.Element.getStyle(
37253                     this.contentDiv, "overflow"
37254                 );
37255                 if ( (currentOverflow != "hidden") && 
37256                      (clippedSize.equals(safeSize)) ) {
37257                     var scrollBar = OpenLayers.Util.getScrollbarWidth();
37258                     if (fixedSize.w) {
37259                         clippedSize.h += scrollBar;
37260                     } else {
37261                         clippedSize.w += scrollBar;
37262                     }
37263                 }
37264                 
37265                 newSize = this.getSafeContentSize(clippedSize);
37266             }
37267         }                        
37268         this.setSize(newSize);     
37269     },    
37270
37271     /**
37272      * Method: setBackgroundColor
37273      * Sets the background color of the popup.
37274      *
37275      * Parameters:
37276      * color - {String} the background color.  eg "#FFBBBB"
37277      */
37278     setBackgroundColor:function(color) { 
37279         if (color != undefined) {
37280             this.backgroundColor = color; 
37281         }
37282         
37283         if (this.div != null) {
37284             this.div.style.backgroundColor = this.backgroundColor;
37285         }
37286     },  
37287     
37288     /**
37289      * Method: setOpacity
37290      * Sets the opacity of the popup.
37291      * 
37292      * Parameters:
37293      * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).   
37294      */
37295     setOpacity:function(opacity) { 
37296         if (opacity != undefined) {
37297             this.opacity = opacity; 
37298         }
37299         
37300         if (this.div != null) {
37301             // for Mozilla and Safari
37302             this.div.style.opacity = this.opacity;
37303
37304             // for IE
37305             this.div.style.filter = 'alpha(opacity=' + this.opacity*100 + ')';
37306         }
37307     },  
37308     
37309     /**
37310      * Method: setBorder
37311      * Sets the border style of the popup.
37312      *
37313      * Parameters:
37314      * border - {String} The border style value. eg 2px 
37315      */
37316     setBorder:function(border) { 
37317         if (border != undefined) {
37318             this.border = border;
37319         }
37320         
37321         if (this.div != null) {
37322             this.div.style.border = this.border;
37323         }
37324     },      
37325     
37326     /**
37327      * Method: setContentHTML
37328      * Allows the user to set the HTML content of the popup.
37329      *
37330      * Parameters:
37331      * contentHTML - {String} HTML for the div.
37332      */
37333     setContentHTML:function(contentHTML) {
37334
37335         if (contentHTML != null) {
37336             this.contentHTML = contentHTML;
37337         }
37338        
37339         if ((this.contentDiv != null) && 
37340             (this.contentHTML != null) &&
37341             (this.contentHTML != this.contentDiv.innerHTML)) {
37342        
37343             this.contentDiv.innerHTML = this.contentHTML;
37344        
37345             if (this.autoSize) {
37346                 
37347                 //if popup has images, listen for when they finish
37348                 // loading and resize accordingly
37349                 this.registerImageListeners();
37350
37351                 //auto size the popup to its current contents
37352                 this.updateSize();
37353             }
37354         }    
37355
37356     },
37357     
37358     /**
37359      * Method: registerImageListeners
37360      * Called when an image contained by the popup loaded. this function
37361      *     updates the popup size, then unregisters the image load listener.
37362      */   
37363     registerImageListeners: function() { 
37364
37365         // As the images load, this function will call updateSize() to 
37366         // resize the popup to fit the content div (which presumably is now
37367         // bigger than when the image was not loaded).
37368         // 
37369         // If the 'panMapIfOutOfView' property is set, we will pan the newly
37370         // resized popup back into view.
37371         // 
37372         // Note that this function, when called, will have 'popup' and 
37373         // 'img' properties in the context.
37374         //
37375         var onImgLoad = function() {
37376             if (this.popup.id === null) { // this.popup has been destroyed!
37377                 return;
37378             }
37379             this.popup.updateSize();
37380      
37381             if ( this.popup.visible() && this.popup.panMapIfOutOfView ) {
37382                 this.popup.panIntoView();
37383             }
37384
37385             OpenLayers.Event.stopObserving(
37386                 this.img, "load", this.img._onImgLoad
37387             );
37388     
37389         };
37390
37391         //cycle through the images and if their size is 0x0, that means that 
37392         // they haven't been loaded yet, so we attach the listener, which 
37393         // will fire when the images finish loading and will resize the 
37394         // popup accordingly to its new size.
37395         var images = this.contentDiv.getElementsByTagName("img");
37396         for (var i = 0, len = images.length; i < len; i++) {
37397             var img = images[i];
37398             if (img.width == 0 || img.height == 0) {
37399
37400                 var context = {
37401                     'popup': this,
37402                     'img': img
37403                 };
37404
37405                 //expando this function to the image itself before registering
37406                 // it. This way we can easily and properly unregister it.
37407                 img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);
37408
37409                 OpenLayers.Event.observe(img, 'load', img._onImgLoad);
37410             }    
37411         } 
37412     },
37413
37414     /**
37415      * APIMethod: getSafeContentSize
37416      * 
37417      * Parameters:
37418      * size - {<OpenLayers.Size>} Desired size to make the popup.
37419      * 
37420      * Returns:
37421      * {<OpenLayers.Size>} A size to make the popup which is neither smaller
37422      *     than the specified minimum size, nor bigger than the maximum 
37423      *     size (which is calculated relative to the size of the viewport).
37424      */
37425     getSafeContentSize: function(size) {
37426
37427         var safeContentSize = size.clone();
37428
37429         // if our contentDiv has a css 'padding' set on it by a stylesheet, we 
37430         //  must add that to the desired "size". 
37431         var contentDivPadding = this.getContentDivPadding();
37432         var wPadding = contentDivPadding.left + contentDivPadding.right;
37433         var hPadding = contentDivPadding.top + contentDivPadding.bottom;
37434
37435         // take into account the popup's 'padding' property
37436         this.fixPadding();
37437         wPadding += this.padding.left + this.padding.right;
37438         hPadding += this.padding.top + this.padding.bottom;
37439
37440         if (this.closeDiv) {
37441             var closeDivWidth = parseInt(this.closeDiv.style.width);
37442             wPadding += closeDivWidth + contentDivPadding.right;
37443         }
37444
37445         // prevent the popup from being smaller than a specified minimal size
37446         if (this.minSize) {
37447             safeContentSize.w = Math.max(safeContentSize.w, 
37448                 (this.minSize.w - wPadding));
37449             safeContentSize.h = Math.max(safeContentSize.h, 
37450                 (this.minSize.h - hPadding));
37451         }
37452
37453         // prevent the popup from being bigger than a specified maximum size
37454         if (this.maxSize) {
37455             safeContentSize.w = Math.min(safeContentSize.w, 
37456                 (this.maxSize.w - wPadding));
37457             safeContentSize.h = Math.min(safeContentSize.h, 
37458                 (this.maxSize.h - hPadding));
37459         }
37460         
37461         //make sure the desired size to set doesn't result in a popup that 
37462         // is bigger than the map's viewport.
37463         //
37464         if (this.map && this.map.size) {
37465             
37466             var extraX = 0, extraY = 0;
37467             if (this.keepInMap && !this.panMapIfOutOfView) {
37468                 var px = this.map.getPixelFromLonLat(this.lonlat);
37469                 switch (this.relativePosition) {
37470                     case "tr":
37471                         extraX = px.x;
37472                         extraY = this.map.size.h - px.y;
37473                         break;
37474                     case "tl":
37475                         extraX = this.map.size.w - px.x;
37476                         extraY = this.map.size.h - px.y;
37477                         break;
37478                     case "bl":
37479                         extraX = this.map.size.w - px.x;
37480                         extraY = px.y;
37481                         break;
37482                     case "br":
37483                         extraX = px.x;
37484                         extraY = px.y;
37485                         break;
37486                     default:    
37487                         extraX = px.x;
37488                         extraY = this.map.size.h - px.y;
37489                         break;
37490                 }
37491             }    
37492           
37493             var maxY = this.map.size.h - 
37494                 this.map.paddingForPopups.top - 
37495                 this.map.paddingForPopups.bottom - 
37496                 hPadding - extraY;
37497             
37498             var maxX = this.map.size.w - 
37499                 this.map.paddingForPopups.left - 
37500                 this.map.paddingForPopups.right - 
37501                 wPadding - extraX;
37502             
37503             safeContentSize.w = Math.min(safeContentSize.w, maxX);
37504             safeContentSize.h = Math.min(safeContentSize.h, maxY);
37505         }
37506         
37507         return safeContentSize;
37508     },
37509     
37510     /**
37511      * Method: getContentDivPadding
37512      * Glorious, oh glorious hack in order to determine the css 'padding' of 
37513      *     the contentDiv. IE/Opera return null here unless we actually add the 
37514      *     popup's main 'div' element (which contains contentDiv) to the DOM. 
37515      *     So we make it invisible and then add it to the document temporarily. 
37516      *
37517      *     Once we've taken the padding readings we need, we then remove it 
37518      *     from the DOM (it will actually get added to the DOM in 
37519      *     Map.js's addPopup)
37520      *
37521      * Returns:
37522      * {<OpenLayers.Bounds>}
37523      */
37524     getContentDivPadding: function() {
37525
37526         //use cached value if we have it
37527         var contentDivPadding = this._contentDivPadding;
37528         if (!contentDivPadding) {
37529
37530             if (this.div.parentNode == null) {
37531                 //make the div invisible and add it to the page        
37532                 this.div.style.display = "none";
37533                 document.body.appendChild(this.div);
37534             }
37535                     
37536             //read the padding settings from css, put them in an OL.Bounds        
37537             contentDivPadding = new OpenLayers.Bounds(
37538                 OpenLayers.Element.getStyle(this.contentDiv, "padding-left"),
37539                 OpenLayers.Element.getStyle(this.contentDiv, "padding-bottom"),
37540                 OpenLayers.Element.getStyle(this.contentDiv, "padding-right"),
37541                 OpenLayers.Element.getStyle(this.contentDiv, "padding-top")
37542             );
37543     
37544             //cache the value
37545             this._contentDivPadding = contentDivPadding;
37546
37547             if (this.div.parentNode == document.body) {
37548                 //remove the div from the page and make it visible again
37549                 document.body.removeChild(this.div);
37550                 this.div.style.display = "";
37551             }
37552         }
37553         return contentDivPadding;
37554     },
37555
37556     /**
37557      * Method: addCloseBox
37558      * 
37559      * Parameters:
37560      * callback - {Function} The callback to be called when the close button
37561      *     is clicked.
37562      */
37563     addCloseBox: function(callback) {
37564
37565         this.closeDiv = OpenLayers.Util.createDiv(
37566             this.id + "_close", null, {w: 17, h: 17}
37567         );
37568         this.closeDiv.className = "olPopupCloseBox"; 
37569         
37570         // use the content div's css padding to determine if we should
37571         //  padd the close div
37572         var contentDivPadding = this.getContentDivPadding();
37573          
37574         this.closeDiv.style.right = contentDivPadding.right + "px";
37575         this.closeDiv.style.top = contentDivPadding.top + "px";
37576         this.groupDiv.appendChild(this.closeDiv);
37577
37578         var closePopup = callback || function(e) {
37579             this.hide();
37580             OpenLayers.Event.stop(e);
37581         };
37582         OpenLayers.Event.observe(this.closeDiv, "touchend", 
37583                 OpenLayers.Function.bindAsEventListener(closePopup, this));
37584         OpenLayers.Event.observe(this.closeDiv, "click", 
37585                 OpenLayers.Function.bindAsEventListener(closePopup, this));
37586     },
37587
37588     /**
37589      * Method: panIntoView
37590      * Pans the map such that the popup is totaly viewable (if necessary)
37591      */
37592     panIntoView: function() {
37593         
37594         var mapSize = this.map.getSize();
37595     
37596         //start with the top left corner of the popup, in px, 
37597         // relative to the viewport
37598         var origTL = this.map.getViewPortPxFromLayerPx( new OpenLayers.Pixel(
37599             parseInt(this.div.style.left),
37600             parseInt(this.div.style.top)
37601         ));
37602         var newTL = origTL.clone();
37603     
37604         //new left (compare to margins, using this.size to calculate right)
37605         if (origTL.x < this.map.paddingForPopups.left) {
37606             newTL.x = this.map.paddingForPopups.left;
37607         } else 
37608         if ( (origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) {
37609             newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w;
37610         }
37611         
37612         //new top (compare to margins, using this.size to calculate bottom)
37613         if (origTL.y < this.map.paddingForPopups.top) {
37614             newTL.y = this.map.paddingForPopups.top;
37615         } else 
37616         if ( (origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) {
37617             newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h;
37618         }
37619         
37620         var dx = origTL.x - newTL.x;
37621         var dy = origTL.y - newTL.y;
37622         
37623         this.map.pan(dx, dy);
37624     },
37625
37626     /** 
37627      * Method: registerEvents
37628      * Registers events on the popup.
37629      *
37630      * Do this in a separate function so that subclasses can 
37631      *   choose to override it if they wish to deal differently
37632      *   with mouse events
37633      * 
37634      *   Note in the following handler functions that some special
37635      *    care is needed to deal correctly with mousing and popups. 
37636      *   
37637      *   Because the user might select the zoom-rectangle option and
37638      *    then drag it over a popup, we need a safe way to allow the
37639      *    mousemove and mouseup events to pass through the popup when
37640      *    they are initiated from outside. The same procedure is needed for
37641      *    touchmove and touchend events.
37642      * 
37643      *   Otherwise, we want to essentially kill the event propagation
37644      *    for all other events, though we have to do so carefully, 
37645      *    without disabling basic html functionality, like clicking on 
37646      *    hyperlinks or drag-selecting text.
37647      */
37648      registerEvents:function() {
37649         this.events = new OpenLayers.Events(this, this.div, null, true);
37650
37651         function onTouchstart(evt) {
37652             OpenLayers.Event.stop(evt, true);
37653         }
37654         this.events.on({
37655             "mousedown": this.onmousedown,
37656             "mousemove": this.onmousemove,
37657             "mouseup": this.onmouseup,
37658             "click": this.onclick,
37659             "mouseout": this.onmouseout,
37660             "dblclick": this.ondblclick,
37661             "touchstart": onTouchstart,
37662             scope: this
37663         });
37664         
37665      },
37666
37667     /** 
37668      * Method: onmousedown 
37669      * When mouse goes down within the popup, make a note of
37670      *   it locally, and then do not propagate the mousedown 
37671      *   (but do so safely so that user can select text inside)
37672      * 
37673      * Parameters:
37674      * evt - {Event} 
37675      */
37676     onmousedown: function (evt) {
37677         this.mousedown = true;
37678         OpenLayers.Event.stop(evt, true);
37679     },
37680
37681     /** 
37682      * Method: onmousemove
37683      * If the drag was started within the popup, then 
37684      *   do not propagate the mousemove (but do so safely
37685      *   so that user can select text inside)
37686      * 
37687      * Parameters:
37688      * evt - {Event} 
37689      */
37690     onmousemove: function (evt) {
37691         if (this.mousedown) {
37692             OpenLayers.Event.stop(evt, true);
37693         }
37694     },
37695
37696     /** 
37697      * Method: onmouseup
37698      * When mouse comes up within the popup, after going down 
37699      *   in it, reset the flag, and then (once again) do not 
37700      *   propagate the event, but do so safely so that user can 
37701      *   select text inside
37702      * 
37703      * Parameters:
37704      * evt - {Event} 
37705      */
37706     onmouseup: function (evt) {
37707         if (this.mousedown) {
37708             this.mousedown = false;
37709             OpenLayers.Event.stop(evt, true);
37710         }
37711     },
37712
37713     /**
37714      * Method: onclick
37715      * Ignore clicks, but allowing default browser handling
37716      * 
37717      * Parameters:
37718      * evt - {Event} 
37719      */
37720     onclick: function (evt) {
37721         OpenLayers.Event.stop(evt, true);
37722     },
37723
37724     /** 
37725      * Method: onmouseout
37726      * When mouse goes out of the popup set the flag to false so that
37727      *   if they let go and then drag back in, we won't be confused.
37728      * 
37729      * Parameters:
37730      * evt - {Event} 
37731      */
37732     onmouseout: function (evt) {
37733         this.mousedown = false;
37734     },
37735     
37736     /** 
37737      * Method: ondblclick
37738      * Ignore double-clicks, but allowing default browser handling
37739      * 
37740      * Parameters:
37741      * evt - {Event} 
37742      */
37743     ondblclick: function (evt) {
37744         OpenLayers.Event.stop(evt, true);
37745     },
37746
37747     CLASS_NAME: "OpenLayers.Popup"
37748 });
37749
37750 OpenLayers.Popup.WIDTH = 200;
37751 OpenLayers.Popup.HEIGHT = 200;
37752 OpenLayers.Popup.COLOR = "white";
37753 OpenLayers.Popup.OPACITY = 1;
37754 OpenLayers.Popup.BORDER = "0px";
37755 /* ======================================================================
37756     OpenLayers/Popup/Anchored.js
37757    ====================================================================== */
37758
37759 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
37760  * full list of contributors). Published under the 2-clause BSD license.
37761  * See license.txt in the OpenLayers distribution or repository for the
37762  * full text of the license. */
37763
37764
37765 /**
37766  * @requires OpenLayers/Popup.js
37767  */
37768
37769 /**
37770  * Class: OpenLayers.Popup.Anchored
37771  * 
37772  * Inherits from:
37773  *  - <OpenLayers.Popup>
37774  */
37775 OpenLayers.Popup.Anchored = 
37776   OpenLayers.Class(OpenLayers.Popup, {
37777
37778     /** 
37779      * Property: relativePosition
37780      * {String} Relative position of the popup ("br", "tr", "tl" or "bl").
37781      */
37782     relativePosition: null,
37783     
37784     /**
37785      * APIProperty: keepInMap 
37786      * {Boolean} If panMapIfOutOfView is false, and this property is true, 
37787      *     contrain the popup such that it always fits in the available map
37788      *     space. By default, this is set. If you are creating popups that are
37789      *     near map edges and not allowing pannning, and especially if you have
37790      *     a popup which has a fixedRelativePosition, setting this to false may
37791      *     be a smart thing to do.
37792      *   
37793      *     For anchored popups, default is true, since subclasses will
37794      *     usually want this functionality.
37795      */
37796     keepInMap: true,
37797
37798     /**
37799      * Property: anchor
37800      * {Object} Object to which we'll anchor the popup. Must expose a 
37801      *     'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>).
37802      */
37803     anchor: null,
37804
37805     /** 
37806     * Constructor: OpenLayers.Popup.Anchored
37807     * 
37808     * Parameters:
37809     * id - {String}
37810     * lonlat - {<OpenLayers.LonLat>}
37811     * contentSize - {<OpenLayers.Size>}
37812     * contentHTML - {String}
37813     * anchor - {Object} Object which must expose a 'size' <OpenLayers.Size> 
37814     *     and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>).
37815     * closeBox - {Boolean}
37816     * closeBoxCallback - {Function} Function to be called on closeBox click.
37817     */
37818     initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
37819                         closeBoxCallback) {
37820         var newArguments = [
37821             id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback
37822         ];
37823         OpenLayers.Popup.prototype.initialize.apply(this, newArguments);
37824
37825         this.anchor = (anchor != null) ? anchor 
37826                                        : { size: new OpenLayers.Size(0,0),
37827                                            offset: new OpenLayers.Pixel(0,0)};
37828     },
37829
37830     /**
37831      * APIMethod: destroy
37832      */
37833     destroy: function() {
37834         this.anchor = null;
37835         this.relativePosition = null;
37836         
37837         OpenLayers.Popup.prototype.destroy.apply(this, arguments);        
37838     },
37839
37840     /**
37841      * APIMethod: show
37842      * Overridden from Popup since user might hide popup and then show() it 
37843      *     in a new location (meaning we might want to update the relative
37844      *     position on the show)
37845      */
37846     show: function() {
37847         this.updatePosition();
37848         OpenLayers.Popup.prototype.show.apply(this, arguments);
37849     },
37850
37851     /**
37852      * Method: moveTo
37853      * Since the popup is moving to a new px, it might need also to be moved
37854      *     relative to where the marker is. We first calculate the new 
37855      *     relativePosition, and then we calculate the new px where we will 
37856      *     put the popup, based on the new relative position. 
37857      * 
37858      *     If the relativePosition has changed, we must also call 
37859      *     updateRelativePosition() to make any visual changes to the popup 
37860      *     which are associated with putting it in a new relativePosition.
37861      * 
37862      * Parameters:
37863      * px - {<OpenLayers.Pixel>}
37864      */
37865     moveTo: function(px) {
37866         var oldRelativePosition = this.relativePosition;
37867         this.relativePosition = this.calculateRelativePosition(px);
37868
37869         OpenLayers.Popup.prototype.moveTo.call(this, this.calculateNewPx(px));
37870         
37871         //if this move has caused the popup to change its relative position, 
37872         // we need to make the appropriate cosmetic changes.
37873         if (this.relativePosition != oldRelativePosition) {
37874             this.updateRelativePosition();
37875         }
37876     },
37877
37878     /**
37879      * APIMethod: setSize
37880      * 
37881      * Parameters:
37882      * contentSize - {<OpenLayers.Size>} the new size for the popup's 
37883      *     contents div (in pixels).
37884      */
37885     setSize:function(contentSize) { 
37886         OpenLayers.Popup.prototype.setSize.apply(this, arguments);
37887
37888         if ((this.lonlat) && (this.map)) {
37889             var px = this.map.getLayerPxFromLonLat(this.lonlat);
37890             this.moveTo(px);
37891         }
37892     },  
37893     
37894     /** 
37895      * Method: calculateRelativePosition
37896      * 
37897      * Parameters:
37898      * px - {<OpenLayers.Pixel>}
37899      * 
37900      * Returns:
37901      * {String} The relative position ("br" "tr" "tl" "bl") at which the popup
37902      *     should be placed.
37903      */
37904     calculateRelativePosition:function(px) {
37905         var lonlat = this.map.getLonLatFromLayerPx(px);        
37906         
37907         var extent = this.map.getExtent();
37908         var quadrant = extent.determineQuadrant(lonlat);
37909         
37910         return OpenLayers.Bounds.oppositeQuadrant(quadrant);
37911     }, 
37912
37913     /**
37914      * Method: updateRelativePosition
37915      * The popup has been moved to a new relative location, so we may want to 
37916      *     make some cosmetic adjustments to it. 
37917      * 
37918      *     Note that in the classic Anchored popup, there is nothing to do 
37919      *     here, since the popup looks exactly the same in all four positions.
37920      *     Subclasses such as Framed, however, will want to do something
37921      *     special here.
37922      */
37923     updateRelativePosition: function() {
37924         //to be overridden by subclasses
37925     },
37926
37927     /** 
37928      * Method: calculateNewPx
37929      * 
37930      * Parameters:
37931      * px - {<OpenLayers.Pixel>}
37932      * 
37933      * Returns:
37934      * {<OpenLayers.Pixel>} The the new px position of the popup on the screen
37935      *     relative to the passed-in px.
37936      */
37937     calculateNewPx:function(px) {
37938         var newPx = px.offset(this.anchor.offset);
37939         
37940         //use contentSize if size is not already set
37941         var size = this.size || this.contentSize;
37942
37943         var top = (this.relativePosition.charAt(0) == 't');
37944         newPx.y += (top) ? -size.h : this.anchor.size.h;
37945         
37946         var left = (this.relativePosition.charAt(1) == 'l');
37947         newPx.x += (left) ? -size.w : this.anchor.size.w;
37948
37949         return newPx;   
37950     },
37951
37952     CLASS_NAME: "OpenLayers.Popup.Anchored"
37953 });
37954 /* ======================================================================
37955     OpenLayers/Popup/Framed.js
37956    ====================================================================== */
37957
37958 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
37959  * full list of contributors). Published under the 2-clause BSD license.
37960  * See license.txt in the OpenLayers distribution or repository for the
37961  * full text of the license. */
37962
37963 /**
37964  * @requires OpenLayers/Popup/Anchored.js
37965  */
37966
37967 /**
37968  * Class: OpenLayers.Popup.Framed
37969  * 
37970  * Inherits from:
37971  *  - <OpenLayers.Popup.Anchored>
37972  */
37973 OpenLayers.Popup.Framed =
37974   OpenLayers.Class(OpenLayers.Popup.Anchored, {
37975
37976     /**
37977      * Property: imageSrc
37978      * {String} location of the image to be used as the popup frame
37979      */
37980     imageSrc: null,
37981
37982     /**
37983      * Property: imageSize
37984      * {<OpenLayers.Size>} Size (measured in pixels) of the image located
37985      *     by the 'imageSrc' property.
37986      */
37987     imageSize: null,
37988
37989     /**
37990      * APIProperty: isAlphaImage
37991      * {Boolean} The image has some alpha and thus needs to use the alpha 
37992      *     image hack. Note that setting this to true will have no noticeable
37993      *     effect in FF or IE7 browsers, but will all but crush the ie6 
37994      *     browser. 
37995      *     Default is false.
37996      */
37997     isAlphaImage: false,
37998
37999     /**
38000      * Property: positionBlocks
38001      * {Object} Hash of different position blocks (Object/Hashs). Each block 
38002      *     will be keyed by a two-character 'relativePosition' 
38003      *     code string (ie "tl", "tr", "bl", "br"). Block properties are 
38004      *     'offset', 'padding' (self-explanatory), and finally the 'blocks'
38005      *     parameter, which is an array of the block objects. 
38006      * 
38007      *     Each block object must have 'size', 'anchor', and 'position' 
38008      *     properties.
38009      * 
38010      *     Note that positionBlocks should never be modified at runtime.
38011      */
38012     positionBlocks: null,
38013
38014     /**
38015      * Property: blocks
38016      * {Array[Object]} Array of objects, each of which is one "block" of the 
38017      *     popup. Each block has a 'div' and an 'image' property, both of 
38018      *     which are DOMElements, and the latter of which is appended to the 
38019      *     former. These are reused as the popup goes changing positions for
38020      *     great economy and elegance.
38021      */
38022     blocks: null,
38023
38024     /** 
38025      * APIProperty: fixedRelativePosition
38026      * {Boolean} We want the framed popup to work dynamically placed relative
38027      *     to its anchor but also in just one fixed position. A well designed
38028      *     framed popup will have the pixels and logic to display itself in 
38029      *     any of the four relative positions, but (understandably), this will
38030      *     not be the case for all of them. By setting this property to 'true', 
38031      *     framed popup will not recalculate for the best placement each time
38032      *     it's open, but will always open the same way. 
38033      *     Note that if this is set to true, it is generally advisable to also
38034      *     set the 'panIntoView' property to true so that the popup can be 
38035      *     scrolled into view (since it will often be offscreen on open)
38036      *     Default is false.
38037      */
38038     fixedRelativePosition: false,
38039
38040     /** 
38041      * Constructor: OpenLayers.Popup.Framed
38042      * 
38043      * Parameters:
38044      * id - {String}
38045      * lonlat - {<OpenLayers.LonLat>}
38046      * contentSize - {<OpenLayers.Size>}
38047      * contentHTML - {String}
38048      * anchor - {Object} Object to which we'll anchor the popup. Must expose 
38049      *     a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) 
38050      *     (Note that this is generally an <OpenLayers.Icon>).
38051      * closeBox - {Boolean}
38052      * closeBoxCallback - {Function} Function to be called on closeBox click.
38053      */
38054     initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox, 
38055                         closeBoxCallback) {
38056
38057         OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
38058
38059         if (this.fixedRelativePosition) {
38060             //based on our decided relativePostion, set the current padding
38061             // this keeps us from getting into trouble 
38062             this.updateRelativePosition();
38063             
38064             //make calculateRelativePosition always return the specified
38065             // fixed position.
38066             this.calculateRelativePosition = function(px) {
38067                 return this.relativePosition;
38068             };
38069         }
38070
38071         this.contentDiv.style.position = "absolute";
38072         this.contentDiv.style.zIndex = 1;
38073
38074         if (closeBox) {
38075             this.closeDiv.style.zIndex = 1;
38076         }
38077
38078         this.groupDiv.style.position = "absolute";
38079         this.groupDiv.style.top = "0px";
38080         this.groupDiv.style.left = "0px";
38081         this.groupDiv.style.height = "100%";
38082         this.groupDiv.style.width = "100%";
38083     },
38084
38085     /** 
38086      * APIMethod: destroy
38087      */
38088     destroy: function() {
38089         this.imageSrc = null;
38090         this.imageSize = null;
38091         this.isAlphaImage = null;
38092
38093         this.fixedRelativePosition = false;
38094         this.positionBlocks = null;
38095
38096         //remove our blocks
38097         for(var i = 0; i < this.blocks.length; i++) {
38098             var block = this.blocks[i];
38099
38100             if (block.image) {
38101                 block.div.removeChild(block.image);
38102             }
38103             block.image = null;
38104
38105             if (block.div) {
38106                 this.groupDiv.removeChild(block.div);
38107             }
38108             block.div = null;
38109         }
38110         this.blocks = null;
38111
38112         OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments);
38113     },
38114
38115     /**
38116      * APIMethod: setBackgroundColor
38117      */
38118     setBackgroundColor:function(color) {
38119         //does nothing since the framed popup's entire scheme is based on a 
38120         // an image -- changing the background color makes no sense. 
38121     },
38122
38123     /**
38124      * APIMethod: setBorder
38125      */
38126     setBorder:function() {
38127         //does nothing since the framed popup's entire scheme is based on a 
38128         // an image -- changing the popup's border makes no sense. 
38129     },
38130
38131     /**
38132      * Method: setOpacity
38133      * Sets the opacity of the popup.
38134      * 
38135      * Parameters:
38136      * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).   
38137      */
38138     setOpacity:function(opacity) {
38139         //does nothing since we suppose that we'll never apply an opacity
38140         // to a framed popup
38141     },
38142
38143     /**
38144      * APIMethod: setSize
38145      * Overridden here, because we need to update the blocks whenever the size
38146      *     of the popup has changed.
38147      * 
38148      * Parameters:
38149      * contentSize - {<OpenLayers.Size>} the new size for the popup's 
38150      *     contents div (in pixels).
38151      */
38152     setSize:function(contentSize) { 
38153         OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
38154
38155         this.updateBlocks();
38156     },
38157
38158     /**
38159      * Method: updateRelativePosition
38160      * When the relative position changes, we need to set the new padding 
38161      *     BBOX on the popup, reposition the close div, and update the blocks.
38162      */
38163     updateRelativePosition: function() {
38164
38165         //update the padding
38166         this.padding = this.positionBlocks[this.relativePosition].padding;
38167
38168         //update the position of our close box to new padding
38169         if (this.closeDiv) {
38170             // use the content div's css padding to determine if we should
38171             //  padd the close div
38172             var contentDivPadding = this.getContentDivPadding();
38173
38174             this.closeDiv.style.right = contentDivPadding.right + 
38175                                         this.padding.right + "px";
38176             this.closeDiv.style.top = contentDivPadding.top + 
38177                                       this.padding.top + "px";
38178         }
38179
38180         this.updateBlocks();
38181     },
38182
38183     /** 
38184      * Method: calculateNewPx
38185      * Besides the standard offset as determined by the Anchored class, our 
38186      *     Framed popups have a special 'offset' property for each of their 
38187      *     positions, which is used to offset the popup relative to its anchor.
38188      * 
38189      * Parameters:
38190      * px - {<OpenLayers.Pixel>}
38191      * 
38192      * Returns:
38193      * {<OpenLayers.Pixel>} The the new px position of the popup on the screen
38194      *     relative to the passed-in px.
38195      */
38196     calculateNewPx:function(px) {
38197         var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply(
38198             this, arguments
38199         );
38200
38201         newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset);
38202
38203         return newPx;
38204     },
38205
38206     /**
38207      * Method: createBlocks
38208      */
38209     createBlocks: function() {
38210         this.blocks = [];
38211
38212         //since all positions contain the same number of blocks, we can 
38213         // just pick the first position and use its blocks array to create
38214         // our blocks array
38215         var firstPosition = null;
38216         for(var key in this.positionBlocks) {
38217             firstPosition = key;
38218             break;
38219         }
38220         
38221         var position = this.positionBlocks[firstPosition];
38222         for (var i = 0; i < position.blocks.length; i++) {
38223
38224             var block = {};
38225             this.blocks.push(block);
38226
38227             var divId = this.id + '_FrameDecorationDiv_' + i;
38228             block.div = OpenLayers.Util.createDiv(divId, 
38229                 null, null, null, "absolute", null, "hidden", null
38230             );
38231
38232             var imgId = this.id + '_FrameDecorationImg_' + i;
38233             var imageCreator = 
38234                 (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv
38235                                     : OpenLayers.Util.createImage;
38236
38237             block.image = imageCreator(imgId, 
38238                 null, this.imageSize, this.imageSrc, 
38239                 "absolute", null, null, null
38240             );
38241
38242             block.div.appendChild(block.image);
38243             this.groupDiv.appendChild(block.div);
38244         }
38245     },
38246
38247     /**
38248      * Method: updateBlocks
38249      * Internal method, called on initialize and when the popup's relative
38250      *     position has changed. This function takes care of re-positioning
38251      *     the popup's blocks in their appropropriate places.
38252      */
38253     updateBlocks: function() {
38254         if (!this.blocks) {
38255             this.createBlocks();
38256         }
38257         
38258         if (this.size && this.relativePosition) {
38259             var position = this.positionBlocks[this.relativePosition];
38260             for (var i = 0; i < position.blocks.length; i++) {
38261     
38262                 var positionBlock = position.blocks[i];
38263                 var block = this.blocks[i];
38264     
38265                 // adjust sizes
38266                 var l = positionBlock.anchor.left;
38267                 var b = positionBlock.anchor.bottom;
38268                 var r = positionBlock.anchor.right;
38269                 var t = positionBlock.anchor.top;
38270     
38271                 //note that we use the isNaN() test here because if the 
38272                 // size object is initialized with a "auto" parameter, the 
38273                 // size constructor calls parseFloat() on the string, 
38274                 // which will turn it into NaN
38275                 //
38276                 var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l) 
38277                                                       : positionBlock.size.w;
38278     
38279                 var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t) 
38280                                                       : positionBlock.size.h;
38281     
38282                 block.div.style.width = (w < 0 ? 0 : w) + 'px';
38283                 block.div.style.height = (h < 0 ? 0 : h) + 'px';
38284     
38285                 block.div.style.left = (l != null) ? l + 'px' : '';
38286                 block.div.style.bottom = (b != null) ? b + 'px' : '';
38287                 block.div.style.right = (r != null) ? r + 'px' : '';            
38288                 block.div.style.top = (t != null) ? t + 'px' : '';
38289     
38290                 block.image.style.left = positionBlock.position.x + 'px';
38291                 block.image.style.top = positionBlock.position.y + 'px';
38292             }
38293     
38294             this.contentDiv.style.left = this.padding.left + "px";
38295             this.contentDiv.style.top = this.padding.top + "px";
38296         }
38297     },
38298
38299     CLASS_NAME: "OpenLayers.Popup.Framed"
38300 });
38301 /* ======================================================================
38302     OpenLayers/Format/JSON.js
38303    ====================================================================== */
38304
38305 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
38306  * full list of contributors). Published under the 2-clause BSD license.
38307  * See license.txt in the OpenLayers distribution or repository for the
38308  * full text of the license. */
38309
38310 /**
38311  * Note:
38312  * This work draws heavily from the public domain JSON serializer/deserializer
38313  *     at http://www.json.org/json.js. Rewritten so that it doesn't modify
38314  *     basic data prototypes.
38315  */
38316
38317 /**
38318  * @requires OpenLayers/Format.js
38319  */
38320
38321 /**
38322  * Class: OpenLayers.Format.JSON
38323  * A parser to read/write JSON safely.  Create a new instance with the
38324  *     <OpenLayers.Format.JSON> constructor.
38325  *
38326  * Inherits from:
38327  *  - <OpenLayers.Format>
38328  */
38329 OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {
38330     
38331     /**
38332      * APIProperty: indent
38333      * {String} For "pretty" printing, the indent string will be used once for
38334      *     each indentation level.
38335      */
38336     indent: "    ",
38337     
38338     /**
38339      * APIProperty: space
38340      * {String} For "pretty" printing, the space string will be used after
38341      *     the ":" separating a name/value pair.
38342      */
38343     space: " ",
38344     
38345     /**
38346      * APIProperty: newline
38347      * {String} For "pretty" printing, the newline string will be used at the
38348      *     end of each name/value pair or array item.
38349      */
38350     newline: "\n",
38351     
38352     /**
38353      * Property: level
38354      * {Integer} For "pretty" printing, this is incremented/decremented during
38355      *     serialization.
38356      */
38357     level: 0,
38358
38359     /**
38360      * Property: pretty
38361      * {Boolean} Serialize with extra whitespace for structure.  This is set
38362      *     by the <write> method.
38363      */
38364     pretty: false,
38365
38366     /**
38367      * Property: nativeJSON
38368      * {Boolean} Does the browser support native json?
38369      */
38370     nativeJSON: (function() {
38371         return !!(window.JSON && typeof JSON.parse == "function" && typeof JSON.stringify == "function");
38372     })(),
38373
38374     /**
38375      * Constructor: OpenLayers.Format.JSON
38376      * Create a new parser for JSON.
38377      *
38378      * Parameters:
38379      * options - {Object} An optional object whose properties will be set on
38380      *     this instance.
38381      */
38382
38383     /**
38384      * APIMethod: read
38385      * Deserialize a json string.
38386      *
38387      * Parameters:
38388      * json - {String} A JSON string
38389      * filter - {Function} A function which will be called for every key and
38390      *     value at every level of the final result. Each value will be
38391      *     replaced by the result of the filter function. This can be used to
38392      *     reform generic objects into instances of classes, or to transform
38393      *     date strings into Date objects.
38394      *     
38395      * Returns:
38396      * {Object} An object, array, string, or number .
38397      */
38398     read: function(json, filter) {
38399         var object;
38400         if (this.nativeJSON) {
38401             object = JSON.parse(json, filter);
38402         } else try {
38403             /**
38404              * Parsing happens in three stages. In the first stage, we run the
38405              *     text against a regular expression which looks for non-JSON
38406              *     characters. We are especially concerned with '()' and 'new'
38407              *     because they can cause invocation, and '=' because it can
38408              *     cause mutation. But just to be safe, we will reject all
38409              *     unexpected characters.
38410              */
38411             if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@').
38412                                 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
38413                                 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
38414
38415                 /**
38416                  * In the second stage we use the eval function to compile the
38417                  *     text into a JavaScript structure. The '{' operator is
38418                  *     subject to a syntactic ambiguity in JavaScript - it can
38419                  *     begin a block or an object literal. We wrap the text in
38420                  *     parens to eliminate the ambiguity.
38421                  */
38422                 object = eval('(' + json + ')');
38423
38424                 /**
38425                  * In the optional third stage, we recursively walk the new
38426                  *     structure, passing each name/value pair to a filter
38427                  *     function for possible transformation.
38428                  */
38429                 if(typeof filter === 'function') {
38430                     function walk(k, v) {
38431                         if(v && typeof v === 'object') {
38432                             for(var i in v) {
38433                                 if(v.hasOwnProperty(i)) {
38434                                     v[i] = walk(i, v[i]);
38435                                 }
38436                             }
38437                         }
38438                         return filter(k, v);
38439                     }
38440                     object = walk('', object);
38441                 }
38442             }
38443         } catch(e) {
38444             // Fall through if the regexp test fails.
38445         }
38446
38447         if(this.keepData) {
38448             this.data = object;
38449         }
38450
38451         return object;
38452     },
38453
38454     /**
38455      * APIMethod: write
38456      * Serialize an object into a JSON string.
38457      *
38458      * Parameters:
38459      * value - {String} The object, array, string, number, boolean or date
38460      *     to be serialized.
38461      * pretty - {Boolean} Structure the output with newlines and indentation.
38462      *     Default is false.
38463      *
38464      * Returns:
38465      * {String} The JSON string representation of the input value.
38466      */
38467     write: function(value, pretty) {
38468         this.pretty = !!pretty;
38469         var json = null;
38470         var type = typeof value;
38471         if(this.serialize[type]) {
38472             try {
38473                 json = (!this.pretty && this.nativeJSON) ?
38474                     JSON.stringify(value) :
38475                     this.serialize[type].apply(this, [value]);
38476             } catch(err) {
38477                 OpenLayers.Console.error("Trouble serializing: " + err);
38478             }
38479         }
38480         return json;
38481     },
38482     
38483     /**
38484      * Method: writeIndent
38485      * Output an indentation string depending on the indentation level.
38486      *
38487      * Returns:
38488      * {String} An appropriate indentation string.
38489      */
38490     writeIndent: function() {
38491         var pieces = [];
38492         if(this.pretty) {
38493             for(var i=0; i<this.level; ++i) {
38494                 pieces.push(this.indent);
38495             }
38496         }
38497         return pieces.join('');
38498     },
38499     
38500     /**
38501      * Method: writeNewline
38502      * Output a string representing a newline if in pretty printing mode.
38503      *
38504      * Returns:
38505      * {String} A string representing a new line.
38506      */
38507     writeNewline: function() {
38508         return (this.pretty) ? this.newline : '';
38509     },
38510     
38511     /**
38512      * Method: writeSpace
38513      * Output a string representing a space if in pretty printing mode.
38514      *
38515      * Returns:
38516      * {String} A space.
38517      */
38518     writeSpace: function() {
38519         return (this.pretty) ? this.space : '';
38520     },
38521
38522     /**
38523      * Property: serialize
38524      * Object with properties corresponding to the serializable data types.
38525      *     Property values are functions that do the actual serializing.
38526      */
38527     serialize: {
38528         /**
38529          * Method: serialize.object
38530          * Transform an object into a JSON string.
38531          *
38532          * Parameters:
38533          * object - {Object} The object to be serialized.
38534          * 
38535          * Returns:
38536          * {String} A JSON string representing the object.
38537          */
38538         'object': function(object) {
38539             // three special objects that we want to treat differently
38540             if(object == null) {
38541                 return "null";
38542             }
38543             if(object.constructor == Date) {
38544                 return this.serialize.date.apply(this, [object]);
38545             }
38546             if(object.constructor == Array) {
38547                 return this.serialize.array.apply(this, [object]);
38548             }
38549             var pieces = ['{'];
38550             this.level += 1;
38551             var key, keyJSON, valueJSON;
38552             
38553             var addComma = false;
38554             for(key in object) {
38555                 if(object.hasOwnProperty(key)) {
38556                     // recursive calls need to allow for sub-classing
38557                     keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
38558                                                     [key, this.pretty]);
38559                     valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
38560                                                     [object[key], this.pretty]);
38561                     if(keyJSON != null && valueJSON != null) {
38562                         if(addComma) {
38563                             pieces.push(',');
38564                         }
38565                         pieces.push(this.writeNewline(), this.writeIndent(),
38566                                     keyJSON, ':', this.writeSpace(), valueJSON);
38567                         addComma = true;
38568                     }
38569                 }
38570             }
38571             
38572             this.level -= 1;
38573             pieces.push(this.writeNewline(), this.writeIndent(), '}');
38574             return pieces.join('');
38575         },
38576         
38577         /**
38578          * Method: serialize.array
38579          * Transform an array into a JSON string.
38580          *
38581          * Parameters:
38582          * array - {Array} The array to be serialized
38583          * 
38584          * Returns:
38585          * {String} A JSON string representing the array.
38586          */
38587         'array': function(array) {
38588             var json;
38589             var pieces = ['['];
38590             this.level += 1;
38591     
38592             for(var i=0, len=array.length; i<len; ++i) {
38593                 // recursive calls need to allow for sub-classing
38594                 json = OpenLayers.Format.JSON.prototype.write.apply(this,
38595                                                     [array[i], this.pretty]);
38596                 if(json != null) {
38597                     if(i > 0) {
38598                         pieces.push(',');
38599                     }
38600                     pieces.push(this.writeNewline(), this.writeIndent(), json);
38601                 }
38602             }
38603
38604             this.level -= 1;    
38605             pieces.push(this.writeNewline(), this.writeIndent(), ']');
38606             return pieces.join('');
38607         },
38608         
38609         /**
38610          * Method: serialize.string
38611          * Transform a string into a JSON string.
38612          *
38613          * Parameters:
38614          * string - {String} The string to be serialized
38615          * 
38616          * Returns:
38617          * {String} A JSON string representing the string.
38618          */
38619         'string': function(string) {
38620             // If the string contains no control characters, no quote characters, and no
38621             // backslash characters, then we can simply slap some quotes around it.
38622             // Otherwise we must also replace the offending characters with safe
38623             // sequences.    
38624             var m = {
38625                 '\b': '\\b',
38626                 '\t': '\\t',
38627                 '\n': '\\n',
38628                 '\f': '\\f',
38629                 '\r': '\\r',
38630                 '"' : '\\"',
38631                 '\\': '\\\\'
38632             };
38633             if(/["\\\x00-\x1f]/.test(string)) {
38634                 return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) {
38635                     var c = m[b];
38636                     if(c) {
38637                         return c;
38638                     }
38639                     c = b.charCodeAt();
38640                     return '\\u00' +
38641                         Math.floor(c / 16).toString(16) +
38642                         (c % 16).toString(16);
38643                 }) + '"';
38644             }
38645             return '"' + string + '"';
38646         },
38647
38648         /**
38649          * Method: serialize.number
38650          * Transform a number into a JSON string.
38651          *
38652          * Parameters:
38653          * number - {Number} The number to be serialized.
38654          *
38655          * Returns:
38656          * {String} A JSON string representing the number.
38657          */
38658         'number': function(number) {
38659             return isFinite(number) ? String(number) : "null";
38660         },
38661         
38662         /**
38663          * Method: serialize.boolean
38664          * Transform a boolean into a JSON string.
38665          *
38666          * Parameters:
38667          * bool - {Boolean} The boolean to be serialized.
38668          * 
38669          * Returns:
38670          * {String} A JSON string representing the boolean.
38671          */
38672         'boolean': function(bool) {
38673             return String(bool);
38674         },
38675         
38676         /**
38677          * Method: serialize.object
38678          * Transform a date into a JSON string.
38679          *
38680          * Parameters:
38681          * date - {Date} The date to be serialized.
38682          * 
38683          * Returns:
38684          * {String} A JSON string representing the date.
38685          */
38686         'date': function(date) {    
38687             function format(number) {
38688                 // Format integers to have at least two digits.
38689                 return (number < 10) ? '0' + number : number;
38690             }
38691             return '"' + date.getFullYear() + '-' +
38692                     format(date.getMonth() + 1) + '-' +
38693                     format(date.getDate()) + 'T' +
38694                     format(date.getHours()) + ':' +
38695                     format(date.getMinutes()) + ':' +
38696                     format(date.getSeconds()) + '"';
38697         }
38698     },
38699
38700     CLASS_NAME: "OpenLayers.Format.JSON" 
38701
38702 });     
38703 /* ======================================================================
38704     OpenLayers/Format/GeoJSON.js
38705    ====================================================================== */
38706
38707 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
38708  * full list of contributors). Published under the 2-clause BSD license.
38709  * See license.txt in the OpenLayers distribution or repository for the
38710  * full text of the license. */
38711
38712 /**
38713  * @requires OpenLayers/Format/JSON.js
38714  * @requires OpenLayers/Feature/Vector.js
38715  * @requires OpenLayers/Geometry/Point.js
38716  * @requires OpenLayers/Geometry/MultiPoint.js
38717  * @requires OpenLayers/Geometry/LineString.js
38718  * @requires OpenLayers/Geometry/MultiLineString.js
38719  * @requires OpenLayers/Geometry/Polygon.js
38720  * @requires OpenLayers/Geometry/MultiPolygon.js
38721  * @requires OpenLayers/Console.js
38722  */
38723
38724 /**
38725  * Class: OpenLayers.Format.GeoJSON
38726  * Read and write GeoJSON. Create a new parser with the
38727  *     <OpenLayers.Format.GeoJSON> constructor.
38728  *
38729  * Inherits from:
38730  *  - <OpenLayers.Format.JSON>
38731  */
38732 OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {
38733
38734     /**
38735      * APIProperty: ignoreExtraDims
38736      * {Boolean} Ignore dimensions higher than 2 when reading geometry
38737      * coordinates.
38738      */ 
38739     ignoreExtraDims: false,
38740     
38741     /**
38742      * Constructor: OpenLayers.Format.GeoJSON
38743      * Create a new parser for GeoJSON.
38744      *
38745      * Parameters:
38746      * options - {Object} An optional object whose properties will be set on
38747      *     this instance.
38748      */
38749
38750     /**
38751      * APIMethod: read
38752      * Deserialize a GeoJSON string.
38753      *
38754      * Parameters:
38755      * json - {String} A GeoJSON string
38756      * type - {String} Optional string that determines the structure of
38757      *     the output.  Supported values are "Geometry", "Feature", and
38758      *     "FeatureCollection".  If absent or null, a default of
38759      *     "FeatureCollection" is assumed.
38760      * filter - {Function} A function which will be called for every key and
38761      *     value at every level of the final result. Each value will be
38762      *     replaced by the result of the filter function. This can be used to
38763      *     reform generic objects into instances of classes, or to transform
38764      *     date strings into Date objects.
38765      *
38766      * Returns: 
38767      * {Object} The return depends on the value of the type argument. If type
38768      *     is "FeatureCollection" (the default), the return will be an array
38769      *     of <OpenLayers.Feature.Vector>. If type is "Geometry", the input json
38770      *     must represent a single geometry, and the return will be an
38771      *     <OpenLayers.Geometry>.  If type is "Feature", the input json must
38772      *     represent a single feature, and the return will be an
38773      *     <OpenLayers.Feature.Vector>.
38774      */
38775     read: function(json, type, filter) {
38776         type = (type) ? type : "FeatureCollection";
38777         var results = null;
38778         var obj = null;
38779         if (typeof json == "string") {
38780             obj = OpenLayers.Format.JSON.prototype.read.apply(this,
38781                                                               [json, filter]);
38782         } else { 
38783             obj = json;
38784         }    
38785         if(!obj) {
38786             OpenLayers.Console.error("Bad JSON: " + json);
38787         } else if(typeof(obj.type) != "string") {
38788             OpenLayers.Console.error("Bad GeoJSON - no type: " + json);
38789         } else if(this.isValidType(obj, type)) {
38790             switch(type) {
38791                 case "Geometry":
38792                     try {
38793                         results = this.parseGeometry(obj);
38794                     } catch(err) {
38795                         OpenLayers.Console.error(err);
38796                     }
38797                     break;
38798                 case "Feature":
38799                     try {
38800                         results = this.parseFeature(obj);
38801                         results.type = "Feature";
38802                     } catch(err) {
38803                         OpenLayers.Console.error(err);
38804                     }
38805                     break;
38806                 case "FeatureCollection":
38807                     // for type FeatureCollection, we allow input to be any type
38808                     results = [];
38809                     switch(obj.type) {
38810                         case "Feature":
38811                             try {
38812                                 results.push(this.parseFeature(obj));
38813                             } catch(err) {
38814                                 results = null;
38815                                 OpenLayers.Console.error(err);
38816                             }
38817                             break;
38818                         case "FeatureCollection":
38819                             for(var i=0, len=obj.features.length; i<len; ++i) {
38820                                 try {
38821                                     results.push(this.parseFeature(obj.features[i]));
38822                                 } catch(err) {
38823                                     results = null;
38824                                     OpenLayers.Console.error(err);
38825                                 }
38826                             }
38827                             break;
38828                         default:
38829                             try {
38830                                 var geom = this.parseGeometry(obj);
38831                                 results.push(new OpenLayers.Feature.Vector(geom));
38832                             } catch(err) {
38833                                 results = null;
38834                                 OpenLayers.Console.error(err);
38835                             }
38836                     }
38837                 break;
38838             }
38839         }
38840         return results;
38841     },
38842     
38843     /**
38844      * Method: isValidType
38845      * Check if a GeoJSON object is a valid representative of the given type.
38846      *
38847      * Returns:
38848      * {Boolean} The object is valid GeoJSON object of the given type.
38849      */
38850     isValidType: function(obj, type) {
38851         var valid = false;
38852         switch(type) {
38853             case "Geometry":
38854                 if(OpenLayers.Util.indexOf(
38855                     ["Point", "MultiPoint", "LineString", "MultiLineString",
38856                      "Polygon", "MultiPolygon", "Box", "GeometryCollection"],
38857                     obj.type) == -1) {
38858                     // unsupported geometry type
38859                     OpenLayers.Console.error("Unsupported geometry type: " +
38860                                               obj.type);
38861                 } else {
38862                     valid = true;
38863                 }
38864                 break;
38865             case "FeatureCollection":
38866                 // allow for any type to be converted to a feature collection
38867                 valid = true;
38868                 break;
38869             default:
38870                 // for Feature types must match
38871                 if(obj.type == type) {
38872                     valid = true;
38873                 } else {
38874                     OpenLayers.Console.error("Cannot convert types from " +
38875                                               obj.type + " to " + type);
38876                 }
38877         }
38878         return valid;
38879     },
38880     
38881     /**
38882      * Method: parseFeature
38883      * Convert a feature object from GeoJSON into an
38884      *     <OpenLayers.Feature.Vector>.
38885      *
38886      * Parameters:
38887      * obj - {Object} An object created from a GeoJSON object
38888      *
38889      * Returns:
38890      * {<OpenLayers.Feature.Vector>} A feature.
38891      */
38892     parseFeature: function(obj) {
38893         var feature, geometry, attributes, bbox;
38894         attributes = (obj.properties) ? obj.properties : {};
38895         bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox;
38896         try {
38897             geometry = this.parseGeometry(obj.geometry);
38898         } catch(err) {
38899             // deal with bad geometries
38900             throw err;
38901         }
38902         feature = new OpenLayers.Feature.Vector(geometry, attributes);
38903         if(bbox) {
38904             feature.bounds = OpenLayers.Bounds.fromArray(bbox);
38905         }
38906         if(obj.id) {
38907             feature.fid = obj.id;
38908         }
38909         return feature;
38910     },
38911     
38912     /**
38913      * Method: parseGeometry
38914      * Convert a geometry object from GeoJSON into an <OpenLayers.Geometry>.
38915      *
38916      * Parameters:
38917      * obj - {Object} An object created from a GeoJSON object
38918      *
38919      * Returns: 
38920      * {<OpenLayers.Geometry>} A geometry.
38921      */
38922     parseGeometry: function(obj) {
38923         if (obj == null) {
38924             return null;
38925         }
38926         var geometry, collection = false;
38927         if(obj.type == "GeometryCollection") {
38928             if(!(OpenLayers.Util.isArray(obj.geometries))) {
38929                 throw "GeometryCollection must have geometries array: " + obj;
38930             }
38931             var numGeom = obj.geometries.length;
38932             var components = new Array(numGeom);
38933             for(var i=0; i<numGeom; ++i) {
38934                 components[i] = this.parseGeometry.apply(
38935                     this, [obj.geometries[i]]
38936                 );
38937             }
38938             geometry = new OpenLayers.Geometry.Collection(components);
38939             collection = true;
38940         } else {
38941             if(!(OpenLayers.Util.isArray(obj.coordinates))) {
38942                 throw "Geometry must have coordinates array: " + obj;
38943             }
38944             if(!this.parseCoords[obj.type.toLowerCase()]) {
38945                 throw "Unsupported geometry type: " + obj.type;
38946             }
38947             try {
38948                 geometry = this.parseCoords[obj.type.toLowerCase()].apply(
38949                     this, [obj.coordinates]
38950                 );
38951             } catch(err) {
38952                 // deal with bad coordinates
38953                 throw err;
38954             }
38955         }
38956         // We don't reproject collections because the children are reprojected
38957         // for us when they are created.
38958         if (this.internalProjection && this.externalProjection && !collection) {
38959             geometry.transform(this.externalProjection, 
38960                                this.internalProjection); 
38961         }                       
38962         return geometry;
38963     },
38964     
38965     /**
38966      * Property: parseCoords
38967      * Object with properties corresponding to the GeoJSON geometry types.
38968      *     Property values are functions that do the actual parsing.
38969      */
38970     parseCoords: {
38971         /**
38972          * Method: parseCoords.point
38973          * Convert a coordinate array from GeoJSON into an
38974          *     <OpenLayers.Geometry>.
38975          *
38976          * Parameters:
38977          * array - {Object} The coordinates array from the GeoJSON fragment.
38978          *
38979          * Returns:
38980          * {<OpenLayers.Geometry>} A geometry.
38981          */
38982         "point": function(array) {
38983             if (this.ignoreExtraDims == false && 
38984                   array.length != 2) {
38985                     throw "Only 2D points are supported: " + array;
38986             }
38987             return new OpenLayers.Geometry.Point(array[0], array[1]);
38988         },
38989         
38990         /**
38991          * Method: parseCoords.multipoint
38992          * Convert a coordinate array from GeoJSON into an
38993          *     <OpenLayers.Geometry>.
38994          *
38995          * Parameters:
38996          * array - {Object} The coordinates array from the GeoJSON fragment.
38997          *
38998          * Returns:
38999          * {<OpenLayers.Geometry>} A geometry.
39000          */
39001         "multipoint": function(array) {
39002             var points = [];
39003             var p = null;
39004             for(var i=0, len=array.length; i<len; ++i) {
39005                 try {
39006                     p = this.parseCoords["point"].apply(this, [array[i]]);
39007                 } catch(err) {
39008                     throw err;
39009                 }
39010                 points.push(p);
39011             }
39012             return new OpenLayers.Geometry.MultiPoint(points);
39013         },
39014
39015         /**
39016          * Method: parseCoords.linestring
39017          * Convert a coordinate array from GeoJSON into an
39018          *     <OpenLayers.Geometry>.
39019          *
39020          * Parameters:
39021          * array - {Object} The coordinates array from the GeoJSON fragment.
39022          *
39023          * Returns:
39024          * {<OpenLayers.Geometry>} A geometry.
39025          */
39026         "linestring": function(array) {
39027             var points = [];
39028             var p = null;
39029             for(var i=0, len=array.length; i<len; ++i) {
39030                 try {
39031                     p = this.parseCoords["point"].apply(this, [array[i]]);
39032                 } catch(err) {
39033                     throw err;
39034                 }
39035                 points.push(p);
39036             }
39037             return new OpenLayers.Geometry.LineString(points);
39038         },
39039         
39040         /**
39041          * Method: parseCoords.multilinestring
39042          * Convert a coordinate array from GeoJSON into an
39043          *     <OpenLayers.Geometry>.
39044          *
39045          * Parameters:
39046          * array - {Object} The coordinates array from the GeoJSON fragment.
39047          *
39048          * Returns:
39049          * {<OpenLayers.Geometry>} A geometry.
39050          */
39051         "multilinestring": function(array) {
39052             var lines = [];
39053             var l = null;
39054             for(var i=0, len=array.length; i<len; ++i) {
39055                 try {
39056                     l = this.parseCoords["linestring"].apply(this, [array[i]]);
39057                 } catch(err) {
39058                     throw err;
39059                 }
39060                 lines.push(l);
39061             }
39062             return new OpenLayers.Geometry.MultiLineString(lines);
39063         },
39064         
39065         /**
39066          * Method: parseCoords.polygon
39067          * Convert a coordinate array from GeoJSON into an
39068          *     <OpenLayers.Geometry>.
39069          *
39070          * Returns:
39071          * {<OpenLayers.Geometry>} A geometry.
39072          */
39073         "polygon": function(array) {
39074             var rings = [];
39075             var r, l;
39076             for(var i=0, len=array.length; i<len; ++i) {
39077                 try {
39078                     l = this.parseCoords["linestring"].apply(this, [array[i]]);
39079                 } catch(err) {
39080                     throw err;
39081                 }
39082                 r = new OpenLayers.Geometry.LinearRing(l.components);
39083                 rings.push(r);
39084             }
39085             return new OpenLayers.Geometry.Polygon(rings);
39086         },
39087
39088         /**
39089          * Method: parseCoords.multipolygon
39090          * Convert a coordinate array from GeoJSON into an
39091          *     <OpenLayers.Geometry>.
39092          *
39093          * Parameters:
39094          * array - {Object} The coordinates array from the GeoJSON fragment.
39095          *
39096          * Returns:
39097          * {<OpenLayers.Geometry>} A geometry.
39098          */
39099         "multipolygon": function(array) {
39100             var polys = [];
39101             var p = null;
39102             for(var i=0, len=array.length; i<len; ++i) {
39103                 try {
39104                     p = this.parseCoords["polygon"].apply(this, [array[i]]);
39105                 } catch(err) {
39106                     throw err;
39107                 }
39108                 polys.push(p);
39109             }
39110             return new OpenLayers.Geometry.MultiPolygon(polys);
39111         },
39112
39113         /**
39114          * Method: parseCoords.box
39115          * Convert a coordinate array from GeoJSON into an
39116          *     <OpenLayers.Geometry>.
39117          *
39118          * Parameters:
39119          * array - {Object} The coordinates array from the GeoJSON fragment.
39120          *
39121          * Returns:
39122          * {<OpenLayers.Geometry>} A geometry.
39123          */
39124         "box": function(array) {
39125             if(array.length != 2) {
39126                 throw "GeoJSON box coordinates must have 2 elements";
39127             }
39128             return new OpenLayers.Geometry.Polygon([
39129                 new OpenLayers.Geometry.LinearRing([
39130                     new OpenLayers.Geometry.Point(array[0][0], array[0][1]),
39131                     new OpenLayers.Geometry.Point(array[1][0], array[0][1]),
39132                     new OpenLayers.Geometry.Point(array[1][0], array[1][1]),
39133                     new OpenLayers.Geometry.Point(array[0][0], array[1][1]),
39134                     new OpenLayers.Geometry.Point(array[0][0], array[0][1])
39135                 ])
39136             ]);
39137         }
39138
39139     },
39140
39141     /**
39142      * APIMethod: write
39143      * Serialize a feature, geometry, array of features into a GeoJSON string.
39144      *
39145      * Parameters:
39146      * obj - {Object} An <OpenLayers.Feature.Vector>, <OpenLayers.Geometry>,
39147      *     or an array of features.
39148      * pretty - {Boolean} Structure the output with newlines and indentation.
39149      *     Default is false.
39150      *
39151      * Returns:
39152      * {String} The GeoJSON string representation of the input geometry,
39153      *     features, or array of features.
39154      */
39155     write: function(obj, pretty) {
39156         var geojson = {
39157             "type": null
39158         };
39159         if(OpenLayers.Util.isArray(obj)) {
39160             geojson.type = "FeatureCollection";
39161             var numFeatures = obj.length;
39162             geojson.features = new Array(numFeatures);
39163             for(var i=0; i<numFeatures; ++i) {
39164                 var element = obj[i];
39165                 if(!element instanceof OpenLayers.Feature.Vector) {
39166                     var msg = "FeatureCollection only supports collections " +
39167                               "of features: " + element;
39168                     throw msg;
39169                 }
39170                 geojson.features[i] = this.extract.feature.apply(
39171                     this, [element]
39172                 );
39173             }
39174         } else if (obj.CLASS_NAME.indexOf("OpenLayers.Geometry") == 0) {
39175             geojson = this.extract.geometry.apply(this, [obj]);
39176         } else if (obj instanceof OpenLayers.Feature.Vector) {
39177             geojson = this.extract.feature.apply(this, [obj]);
39178             if(obj.layer && obj.layer.projection) {
39179                 geojson.crs = this.createCRSObject(obj);
39180             }
39181         }
39182         return OpenLayers.Format.JSON.prototype.write.apply(this,
39183                                                             [geojson, pretty]);
39184     },
39185
39186     /**
39187      * Method: createCRSObject
39188      * Create the CRS object for an object.
39189      *
39190      * Parameters:
39191      * object - {<OpenLayers.Feature.Vector>} 
39192      *
39193      * Returns:
39194      * {Object} An object which can be assigned to the crs property
39195      * of a GeoJSON object.
39196      */
39197     createCRSObject: function(object) {
39198        var proj = object.layer.projection.toString();
39199        var crs = {};
39200        if (proj.match(/epsg:/i)) {
39201            var code = parseInt(proj.substring(proj.indexOf(":") + 1));
39202            if (code == 4326) {
39203                crs = {
39204                    "type": "name",
39205                    "properties": {
39206                        "name": "urn:ogc:def:crs:OGC:1.3:CRS84"
39207                    }
39208                };
39209            } else {    
39210                crs = {
39211                    "type": "name",
39212                    "properties": {
39213                        "name": "EPSG:" + code
39214                    }
39215                };
39216            }    
39217        }
39218        return crs;
39219     },
39220     
39221     /**
39222      * Property: extract
39223      * Object with properties corresponding to the GeoJSON types.
39224      *     Property values are functions that do the actual value extraction.
39225      */
39226     extract: {
39227         /**
39228          * Method: extract.feature
39229          * Return a partial GeoJSON object representing a single feature.
39230          *
39231          * Parameters:
39232          * feature - {<OpenLayers.Feature.Vector>}
39233          *
39234          * Returns:
39235          * {Object} An object representing the point.
39236          */
39237         'feature': function(feature) {
39238             var geom = this.extract.geometry.apply(this, [feature.geometry]);
39239             var json = {
39240                 "type": "Feature",
39241                 "properties": feature.attributes,
39242                 "geometry": geom
39243             };
39244             if (feature.fid != null) {
39245                 json.id = feature.fid;
39246             }
39247             return json;
39248         },
39249         
39250         /**
39251          * Method: extract.geometry
39252          * Return a GeoJSON object representing a single geometry.
39253          *
39254          * Parameters:
39255          * geometry - {<OpenLayers.Geometry>}
39256          *
39257          * Returns:
39258          * {Object} An object representing the geometry.
39259          */
39260         'geometry': function(geometry) {
39261             if (geometry == null) {
39262                 return null;
39263             }
39264             if (this.internalProjection && this.externalProjection) {
39265                 geometry = geometry.clone();
39266                 geometry.transform(this.internalProjection, 
39267                                    this.externalProjection);
39268             }                       
39269             var geometryType = geometry.CLASS_NAME.split('.')[2];
39270             var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);
39271             var json;
39272             if(geometryType == "Collection") {
39273                 json = {
39274                     "type": "GeometryCollection",
39275                     "geometries": data
39276                 };
39277             } else {
39278                 json = {
39279                     "type": geometryType,
39280                     "coordinates": data
39281                 };
39282             }
39283             
39284             return json;
39285         },
39286
39287         /**
39288          * Method: extract.point
39289          * Return an array of coordinates from a point.
39290          *
39291          * Parameters:
39292          * point - {<OpenLayers.Geometry.Point>}
39293          *
39294          * Returns: 
39295          * {Array} An array of coordinates representing the point.
39296          */
39297         'point': function(point) {
39298             return [point.x, point.y];
39299         },
39300
39301         /**
39302          * Method: extract.multipoint
39303          * Return an array of point coordinates from a multipoint.
39304          *
39305          * Parameters:
39306          * multipoint - {<OpenLayers.Geometry.MultiPoint>}
39307          *
39308          * Returns:
39309          * {Array} An array of point coordinate arrays representing
39310          *     the multipoint.
39311          */
39312         'multipoint': function(multipoint) {
39313             var array = [];
39314             for(var i=0, len=multipoint.components.length; i<len; ++i) {
39315                 array.push(this.extract.point.apply(this, [multipoint.components[i]]));
39316             }
39317             return array;
39318         },
39319         
39320         /**
39321          * Method: extract.linestring
39322          * Return an array of coordinate arrays from a linestring.
39323          *
39324          * Parameters:
39325          * linestring - {<OpenLayers.Geometry.LineString>}
39326          *
39327          * Returns:
39328          * {Array} An array of coordinate arrays representing
39329          *     the linestring.
39330          */
39331         'linestring': function(linestring) {
39332             var array = [];
39333             for(var i=0, len=linestring.components.length; i<len; ++i) {
39334                 array.push(this.extract.point.apply(this, [linestring.components[i]]));
39335             }
39336             return array;
39337         },
39338
39339         /**
39340          * Method: extract.multilinestring
39341          * Return an array of linestring arrays from a linestring.
39342          * 
39343          * Parameters:
39344          * multilinestring - {<OpenLayers.Geometry.MultiLineString>}
39345          * 
39346          * Returns:
39347          * {Array} An array of linestring arrays representing
39348          *     the multilinestring.
39349          */
39350         'multilinestring': function(multilinestring) {
39351             var array = [];
39352             for(var i=0, len=multilinestring.components.length; i<len; ++i) {
39353                 array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]));
39354             }
39355             return array;
39356         },
39357         
39358         /**
39359          * Method: extract.polygon
39360          * Return an array of linear ring arrays from a polygon.
39361          *
39362          * Parameters:
39363          * polygon - {<OpenLayers.Geometry.Polygon>}
39364          * 
39365          * Returns:
39366          * {Array} An array of linear ring arrays representing the polygon.
39367          */
39368         'polygon': function(polygon) {
39369             var array = [];
39370             for(var i=0, len=polygon.components.length; i<len; ++i) {
39371                 array.push(this.extract.linestring.apply(this, [polygon.components[i]]));
39372             }
39373             return array;
39374         },
39375
39376         /**
39377          * Method: extract.multipolygon
39378          * Return an array of polygon arrays from a multipolygon.
39379          * 
39380          * Parameters:
39381          * multipolygon - {<OpenLayers.Geometry.MultiPolygon>}
39382          * 
39383          * Returns:
39384          * {Array} An array of polygon arrays representing
39385          *     the multipolygon
39386          */
39387         'multipolygon': function(multipolygon) {
39388             var array = [];
39389             for(var i=0, len=multipolygon.components.length; i<len; ++i) {
39390                 array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]));
39391             }
39392             return array;
39393         },
39394         
39395         /**
39396          * Method: extract.collection
39397          * Return an array of geometries from a geometry collection.
39398          * 
39399          * Parameters:
39400          * collection - {<OpenLayers.Geometry.Collection>}
39401          * 
39402          * Returns:
39403          * {Array} An array of geometry objects representing the geometry
39404          *     collection.
39405          */
39406         'collection': function(collection) {
39407             var len = collection.components.length;
39408             var array = new Array(len);
39409             for(var i=0; i<len; ++i) {
39410                 array[i] = this.extract.geometry.apply(
39411                     this, [collection.components[i]]
39412                 );
39413             }
39414             return array;
39415         }
39416         
39417
39418     },
39419
39420     CLASS_NAME: "OpenLayers.Format.GeoJSON" 
39421
39422 });     
39423 /* ======================================================================
39424     OpenLayers/Protocol/WFS/v1.js
39425    ====================================================================== */
39426
39427 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
39428  * full list of contributors). Published under the 2-clause BSD license.
39429  * See license.txt in the OpenLayers distribution or repository for the
39430  * full text of the license. */
39431
39432 /**
39433  * @requires OpenLayers/Protocol/WFS.js
39434  */
39435
39436 /**
39437  * Class: OpenLayers.Protocol.WFS.v1
39438  * Abstract class for for v1.0.0 and v1.1.0 protocol.
39439  *
39440  * Inherits from:
39441  *  - <OpenLayers.Protocol>
39442  */
39443 OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, {
39444     
39445     /**
39446      * Property: version
39447      * {String} WFS version number.
39448      */
39449     version: null,
39450     
39451     /**
39452      * Property: srsName
39453      * {String} Name of spatial reference system.  Default is "EPSG:4326".
39454      */
39455     srsName: "EPSG:4326",
39456     
39457     /**
39458      * Property: featureType
39459      * {String} Local feature typeName.
39460      */
39461     featureType: null,
39462     
39463     /**
39464      * Property: featureNS
39465      * {String} Feature namespace.
39466      */
39467     featureNS: null,
39468     
39469     /**
39470      * Property: geometryName
39471      * {String} Name of the geometry attribute for features.  Default is
39472      *     "the_geom" for WFS <version> 1.0, and null for higher versions.
39473      */
39474     geometryName: "the_geom",
39475
39476     /**
39477      * Property: maxFeatures
39478      * {Integer} Optional maximum number of features to retrieve.
39479      */
39480     
39481     /**
39482      * Property: schema
39483      * {String} Optional schema location that will be included in the
39484      *     schemaLocation attribute value.  Note that the feature type schema
39485      *     is required for a strict XML validator (on transactions with an
39486      *     insert for example), but is *not* required by the WFS specification
39487      *     (since the server is supposed to know about feature type schemas).
39488      */
39489     schema: null,
39490
39491     /**
39492      * Property: featurePrefix
39493      * {String} Namespace alias for feature type.  Default is "feature".
39494      */
39495     featurePrefix: "feature",
39496     
39497     /**
39498      * Property: formatOptions
39499      * {Object} Optional options for the format.  If a format is not provided,
39500      *     this property can be used to extend the default format options.
39501      */
39502     formatOptions: null,
39503
39504     /** 
39505      * Property: readFormat 
39506      * {<OpenLayers.Format>} For WFS requests it is possible to get a  
39507      *     different output format than GML. In that case, we cannot parse  
39508      *     the response with the default format (WFST) and we need a different 
39509      *     format for reading. 
39510      */ 
39511     readFormat: null,
39512     
39513     /**
39514      * Property: readOptions
39515      * {Object} Optional object to pass to format's read.
39516      */
39517     readOptions: null,
39518     
39519     /**
39520      * Constructor: OpenLayers.Protocol.WFS
39521      * A class for giving layers WFS protocol.
39522      *
39523      * Parameters:
39524      * options - {Object} Optional object whose properties will be set on the
39525      *     instance.
39526      *
39527      * Valid options properties:
39528      * url - {String} URL to send requests to (required).
39529      * featureType - {String} Local (without prefix) feature typeName (required).
39530      * featureNS - {String} Feature namespace (required, but can be autodetected
39531      *     during the first query if GML is used as readFormat and
39532      *     featurePrefix is provided and matches the prefix used by the server
39533      *     for this featureType).
39534      * featurePrefix - {String} Feature namespace alias (optional - only used
39535      *     for writing if featureNS is provided).  Default is 'feature'.
39536      * geometryName - {String} Name of geometry attribute.  The default is
39537      *     'the_geom' for WFS <version> 1.0, and null for higher versions. If
39538      *     null, it will be set to the name of the first geometry found in the
39539      *     first read operation.
39540      * multi - {Boolean} If set to true, geometries will be casted to Multi
39541      *     geometries before they are written in a transaction. No casting will
39542      *     be done when reading features.
39543      */
39544     initialize: function(options) {
39545         OpenLayers.Protocol.prototype.initialize.apply(this, [options]);
39546         if(!options.format) {
39547             this.format = OpenLayers.Format.WFST(OpenLayers.Util.extend({
39548                 version: this.version,
39549                 featureType: this.featureType,
39550                 featureNS: this.featureNS,
39551                 featurePrefix: this.featurePrefix,
39552                 geometryName: this.geometryName,
39553                 srsName: this.srsName,
39554                 schema: this.schema
39555             }, this.formatOptions));
39556         }
39557         if (!options.geometryName && parseFloat(this.format.version) > 1.0) {
39558             this.setGeometryName(null);
39559         }
39560     },
39561     
39562     /**
39563      * APIMethod: destroy
39564      * Clean up the protocol.
39565      */
39566     destroy: function() {
39567         if(this.options && !this.options.format) {
39568             this.format.destroy();
39569         }
39570         this.format = null;
39571         OpenLayers.Protocol.prototype.destroy.apply(this);
39572     },
39573
39574     /**
39575      * APIMethod: read
39576      * Construct a request for reading new features.  Since WFS splits the
39577      *     basic CRUD operations into GetFeature requests (for read) and
39578      *     Transactions (for all others), this method does not make use of the
39579      *     format's read method (that is only about reading transaction
39580      *     responses).
39581      *
39582      * Parameters:
39583      * options - {Object} Options for the read operation, in addition to the
39584      *     options set on the instance (options set here will take precedence).
39585      *
39586      * To use a configured protocol to get e.g. a WFS hit count, applications
39587      * could do the following:
39588      *
39589      * (code)
39590      * protocol.read({
39591      *     readOptions: {output: "object"},
39592      *     resultType: "hits",
39593      *     maxFeatures: null,
39594      *     callback: function(resp) {
39595      *         // process resp.numberOfFeatures here
39596      *     }
39597      * });
39598      * (end)
39599      *
39600      * To use a configured protocol to use WFS paging (if supported by the
39601      * server), applications could do the following:
39602      *
39603      * (code)
39604      * protocol.read({
39605      *     startIndex: 0,
39606      *     count: 50
39607      * });
39608      * (end)
39609      *
39610      * To limit the attributes returned by the GetFeature request, applications
39611      * can use the propertyNames option to specify the properties to include in
39612      * the response:
39613      *
39614      * (code)
39615      * protocol.read({
39616      *     propertyNames: ["DURATION", "INTENSITY"]
39617      * });
39618      * (end)
39619      */
39620     read: function(options) {
39621         OpenLayers.Protocol.prototype.read.apply(this, arguments);
39622         options = OpenLayers.Util.extend({}, options);
39623         OpenLayers.Util.applyDefaults(options, this.options || {});
39624         var response = new OpenLayers.Protocol.Response({requestType: "read"});
39625         
39626         var data = OpenLayers.Format.XML.prototype.write.apply(
39627             this.format, [this.format.writeNode("wfs:GetFeature", options)]
39628         );
39629
39630         response.priv = OpenLayers.Request.POST({
39631             url: options.url,
39632             callback: this.createCallback(this.handleRead, response, options),
39633             params: options.params,
39634             headers: options.headers,
39635             data: data
39636         });
39637
39638         return response;
39639     },
39640
39641     /**
39642      * APIMethod: setFeatureType
39643      * Change the feature type on the fly.
39644      *
39645      * Parameters:
39646      * featureType - {String} Local (without prefix) feature typeName.
39647      */
39648     setFeatureType: function(featureType) {
39649         this.featureType = featureType;
39650         this.format.featureType = featureType;
39651     },
39652  
39653     /**
39654      * APIMethod: setGeometryName
39655      * Sets the geometryName option after instantiation.
39656      *
39657      * Parameters:
39658      * geometryName - {String} Name of geometry attribute.
39659      */
39660     setGeometryName: function(geometryName) {
39661         this.geometryName = geometryName;
39662         this.format.geometryName = geometryName;
39663     },
39664     
39665     /**
39666      * Method: handleRead
39667      * Deal with response from the read request.
39668      *
39669      * Parameters:
39670      * response - {<OpenLayers.Protocol.Response>} The response object to pass
39671      *     to the user callback.
39672      * options - {Object} The user options passed to the read call.
39673      */
39674     handleRead: function(response, options) {
39675         options = OpenLayers.Util.extend({}, options);
39676         OpenLayers.Util.applyDefaults(options, this.options);
39677
39678         if(options.callback) {
39679             var request = response.priv;
39680             if(request.status >= 200 && request.status < 300) {
39681                 // success
39682                 var result = this.parseResponse(request, options.readOptions);
39683                 if (result && result.success !== false) { 
39684                     if (options.readOptions && options.readOptions.output == "object") {
39685                         OpenLayers.Util.extend(response, result);
39686                     } else {
39687                         response.features = result;
39688                     }
39689                     response.code = OpenLayers.Protocol.Response.SUCCESS;
39690                 } else {
39691                     // failure (service exception)
39692                     response.code = OpenLayers.Protocol.Response.FAILURE;
39693                     response.error = result;
39694                 }
39695             } else {
39696                 // failure
39697                 response.code = OpenLayers.Protocol.Response.FAILURE;
39698             }
39699             options.callback.call(options.scope, response);
39700         }
39701     },
39702
39703     /**
39704      * Method: parseResponse
39705      * Read HTTP response body and return features
39706      *
39707      * Parameters:
39708      * request - {XMLHttpRequest} The request object
39709      * options - {Object} Optional object to pass to format's read
39710      *
39711      * Returns:
39712      * {Object} or {Array({<OpenLayers.Feature.Vector>})} or
39713      *     {<OpenLayers.Feature.Vector>} 
39714      * An object with a features property, an array of features or a single 
39715      * feature.
39716      */
39717     parseResponse: function(request, options) {
39718         var doc = request.responseXML;
39719         if(!doc || !doc.documentElement) {
39720             doc = request.responseText;
39721         }
39722         if(!doc || doc.length <= 0) {
39723             return null;
39724         }
39725         var result = (this.readFormat !== null) ? this.readFormat.read(doc) : 
39726             this.format.read(doc, options);
39727         if (!this.featureNS) {
39728             var format = this.readFormat || this.format;
39729             this.featureNS = format.featureNS;
39730             // no need to auto-configure again on subsequent reads
39731             format.autoConfig = false;
39732             if (!this.geometryName) {
39733                 this.setGeometryName(format.geometryName);
39734             }
39735         }
39736         return result;
39737     },
39738
39739     /**
39740      * Method: commit
39741      * Given a list of feature, assemble a batch request for update, create,
39742      *     and delete transactions.  A commit call on the prototype amounts
39743      *     to writing a WFS transaction - so the write method on the format
39744      *     is used.
39745      *
39746      * Parameters:
39747      * features - {Array(<OpenLayers.Feature.Vector>)}
39748      * options - {Object}
39749      *
39750      * Valid options properties:
39751      * nativeElements - {Array({Object})} Array of objects with information for writing
39752      * out <Native> elements, these objects have vendorId, safeToIgnore and
39753      * value properties. The <Native> element is intended to allow access to 
39754      * vendor specific capabilities of any particular web feature server or 
39755      * datastore.
39756      *
39757      * Returns:
39758      * {<OpenLayers.Protocol.Response>} A response object with a features
39759      *     property containing any insertIds and a priv property referencing
39760      *     the XMLHttpRequest object.
39761      */
39762     commit: function(features, options) {
39763
39764         options = OpenLayers.Util.extend({}, options);
39765         OpenLayers.Util.applyDefaults(options, this.options);
39766         
39767         var response = new OpenLayers.Protocol.Response({
39768             requestType: "commit",
39769             reqFeatures: features
39770         });
39771         response.priv = OpenLayers.Request.POST({
39772             url: options.url,
39773             headers: options.headers,
39774             data: this.format.write(features, options),
39775             callback: this.createCallback(this.handleCommit, response, options)
39776         });
39777         
39778         return response;
39779     },
39780     
39781     /**
39782      * Method: handleCommit
39783      * Called when the commit request returns.
39784      * 
39785      * Parameters:
39786      * response - {<OpenLayers.Protocol.Response>} The response object to pass
39787      *     to the user callback.
39788      * options - {Object} The user options passed to the commit call.
39789      */
39790     handleCommit: function(response, options) {
39791         if(options.callback) {
39792             var request = response.priv;
39793
39794             // ensure that we have an xml doc
39795             var data = request.responseXML;
39796             if(!data || !data.documentElement) {
39797                 data = request.responseText;
39798             }
39799             
39800             var obj = this.format.read(data) || {};
39801             
39802             response.insertIds = obj.insertIds || [];
39803             if (obj.success) {
39804                 response.code = OpenLayers.Protocol.Response.SUCCESS;
39805             } else {
39806                 response.code = OpenLayers.Protocol.Response.FAILURE;
39807                 response.error = obj;
39808             }
39809             options.callback.call(options.scope, response);
39810         }
39811     },
39812     
39813     /**
39814      * Method: filterDelete
39815      * Send a request that deletes all features by their filter.
39816      * 
39817      * Parameters:
39818      * filter - {<OpenLayers.Filter>} filter
39819      */
39820     filterDelete: function(filter, options) {
39821         options = OpenLayers.Util.extend({}, options);
39822         OpenLayers.Util.applyDefaults(options, this.options);    
39823         
39824         var response = new OpenLayers.Protocol.Response({
39825             requestType: "commit"
39826         });    
39827         
39828         var root = this.format.createElementNSPlus("wfs:Transaction", {
39829             attributes: {
39830                 service: "WFS",
39831                 version: this.version
39832             }
39833         });
39834         
39835         var deleteNode = this.format.createElementNSPlus("wfs:Delete", {
39836             attributes: {
39837                 typeName: (options.featureNS ? this.featurePrefix + ":" : "") +
39838                     options.featureType
39839             }
39840         });       
39841         
39842         if(options.featureNS) {
39843             deleteNode.setAttribute("xmlns:" + this.featurePrefix, options.featureNS);
39844         }
39845         var filterNode = this.format.writeNode("ogc:Filter", filter);
39846         
39847         deleteNode.appendChild(filterNode);
39848         
39849         root.appendChild(deleteNode);
39850         
39851         var data = OpenLayers.Format.XML.prototype.write.apply(
39852             this.format, [root]
39853         );
39854         
39855         return OpenLayers.Request.POST({
39856             url: this.url,
39857             callback : options.callback || function(){},
39858             data: data
39859         });   
39860         
39861     },
39862
39863     /**
39864      * Method: abort
39865      * Abort an ongoing request, the response object passed to
39866      * this method must come from this protocol (as a result
39867      * of a read, or commit operation).
39868      *
39869      * Parameters:
39870      * response - {<OpenLayers.Protocol.Response>}
39871      */
39872     abort: function(response) {
39873         if (response) {
39874             response.priv.abort();
39875         }
39876     },
39877   
39878     CLASS_NAME: "OpenLayers.Protocol.WFS.v1" 
39879 });
39880 /* ======================================================================
39881     OpenLayers/Layer/Google/v3.js
39882    ====================================================================== */
39883
39884 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
39885  * full list of contributors). Published under the 2-clause BSD license.
39886  * See license.txt in the OpenLayers distribution or repository for the
39887  * full text of the license. */
39888
39889
39890 /**
39891  * @requires OpenLayers/Layer/Google.js
39892  */
39893
39894 /**
39895  * Constant: OpenLayers.Layer.Google.v3
39896  * 
39897  * Mixin providing functionality specific to the Google Maps API v3.
39898  * 
39899  * To use this layer, you must include the GMaps v3 API in your html.
39900  * 
39901  * Note that this layer configures the google.maps.map object with the
39902  * "disableDefaultUI" option set to true. Using UI controls that the Google
39903  * Maps API provides is not supported by the OpenLayers API.
39904  */
39905 OpenLayers.Layer.Google.v3 = {
39906     
39907     /**
39908      * Constant: DEFAULTS
39909      * {Object} It is not recommended to change the properties set here. Note
39910      * that Google.v3 layers only work when sphericalMercator is set to true.
39911      * 
39912      * (code)
39913      * {
39914      *     sphericalMercator: true,
39915      *     projection: "EPSG:900913"
39916      * }
39917      * (end)
39918      */
39919     DEFAULTS: {
39920         sphericalMercator: true,
39921         projection: "EPSG:900913"
39922     },
39923
39924     /**
39925      * APIProperty: animationEnabled
39926      * {Boolean} If set to true, the transition between zoom levels will be
39927      *     animated (if supported by the GMaps API for the device used). Set to
39928      *     false to match the zooming experience of other layer types. Default
39929      *     is true. Note that the GMaps API does not give us control over zoom
39930      *     animation, so if set to false, when zooming, this will make the
39931      *     layer temporarily invisible, wait until GMaps reports the map being
39932      *     idle, and make it visible again. The result will be a blank layer
39933      *     for a few moments while zooming.
39934      */
39935     animationEnabled: true, 
39936
39937     /** 
39938      * Method: loadMapObject
39939      * Load the GMap and register appropriate event listeners.
39940      */
39941     loadMapObject: function() {
39942         if (!this.type) {
39943             this.type = google.maps.MapTypeId.ROADMAP;
39944         }
39945         var mapObject;
39946         var cache = OpenLayers.Layer.Google.cache[this.map.id];
39947         if (cache) {
39948             // there are already Google layers added to this map
39949             mapObject = cache.mapObject;
39950             // increment the layer count
39951             ++cache.count;
39952         } else {
39953             // this is the first Google layer for this map
39954             // create GMap
39955             var center = this.map.getCenter();
39956             var container = document.createElement('div');
39957             container.className = "olForeignContainer";
39958             container.style.width = '100%';
39959             container.style.height = '100%';
39960             mapObject = new google.maps.Map(container, {
39961                 center: center ?
39962                     new google.maps.LatLng(center.lat, center.lon) :
39963                     new google.maps.LatLng(0, 0),
39964                 zoom: this.map.getZoom() || 0,
39965                 mapTypeId: this.type,
39966                 disableDefaultUI: true,
39967                 keyboardShortcuts: false,
39968                 draggable: false,
39969                 disableDoubleClickZoom: true,
39970                 scrollwheel: false,
39971                 streetViewControl: false
39972             });
39973             var googleControl = document.createElement('div');
39974             googleControl.style.width = '100%';
39975             googleControl.style.height = '100%';
39976             mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl);
39977             
39978             // cache elements for use by any other google layers added to
39979             // this same map
39980             cache = {
39981                 googleControl: googleControl,
39982                 mapObject: mapObject,
39983                 count: 1
39984             };
39985             OpenLayers.Layer.Google.cache[this.map.id] = cache;
39986         }
39987         this.mapObject = mapObject;
39988         this.setGMapVisibility(this.visibility);
39989     },
39990     
39991     /**
39992      * APIMethod: onMapResize
39993      */
39994     onMapResize: function() {
39995         if (this.visibility) {
39996             google.maps.event.trigger(this.mapObject, "resize");
39997         }
39998     },
39999
40000     /**
40001      * Method: setGMapVisibility
40002      * Display the GMap container and associated elements.
40003      * 
40004      * Parameters:
40005      * visible - {Boolean} Display the GMap elements.
40006      */
40007     setGMapVisibility: function(visible) {
40008         var cache = OpenLayers.Layer.Google.cache[this.map.id];
40009         var map = this.map;
40010         if (cache) {
40011             var type = this.type;
40012             var layers = map.layers;
40013             var layer;
40014             for (var i=layers.length-1; i>=0; --i) {
40015                 layer = layers[i];
40016                 if (layer instanceof OpenLayers.Layer.Google &&
40017                             layer.visibility === true && layer.inRange === true) {
40018                     type = layer.type;
40019                     visible = true;
40020                     break;
40021                 }
40022             }
40023             var container = this.mapObject.getDiv();
40024             if (visible === true) {
40025                 if (container.parentNode !== map.div) {
40026                     if (!cache.rendered) {
40027                         var me = this;
40028                         google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() {
40029                             cache.rendered = true;
40030                             me.setGMapVisibility(me.getVisibility());
40031                             me.moveTo(me.map.getCenter());
40032                         });
40033                     } else {
40034                         map.div.appendChild(container);
40035                         cache.googleControl.appendChild(map.viewPortDiv);
40036                         google.maps.event.trigger(this.mapObject, 'resize');
40037                     }
40038                 }
40039                 this.mapObject.setMapTypeId(type);                
40040             } else if (cache.googleControl.hasChildNodes()) {
40041                 map.div.appendChild(map.viewPortDiv);
40042                 map.div.removeChild(container);
40043             }
40044         }
40045     },
40046     
40047     /**
40048      * Method: getMapContainer
40049      * 
40050      * Returns:
40051      * {DOMElement} the GMap container's div
40052      */
40053     getMapContainer: function() {
40054         return this.mapObject.getDiv();
40055     },
40056     
40057   //
40058   // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
40059   //
40060
40061     /**
40062      * APIMethod: getMapObjectBoundsFromOLBounds
40063      * 
40064      * Parameters:
40065      * olBounds - {<OpenLayers.Bounds>}
40066      * 
40067      * Returns:
40068      * {Object} A MapObject Bounds, translated from olBounds
40069      *          Returns null if null value is passed in
40070      */
40071     getMapObjectBoundsFromOLBounds: function(olBounds) {
40072         var moBounds = null;
40073         if (olBounds != null) {
40074             var sw = this.sphericalMercator ? 
40075               this.inverseMercator(olBounds.bottom, olBounds.left) : 
40076               new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
40077             var ne = this.sphericalMercator ? 
40078               this.inverseMercator(olBounds.top, olBounds.right) : 
40079               new OpenLayers.LonLat(olBounds.top, olBounds.right);
40080             moBounds = new google.maps.LatLngBounds(
40081                 new google.maps.LatLng(sw.lat, sw.lon),
40082                 new google.maps.LatLng(ne.lat, ne.lon)
40083             );
40084         }
40085         return moBounds;
40086     },
40087
40088
40089     /************************************
40090      *                                  *
40091      *   MapObject Interface Controls   *
40092      *                                  *
40093      ************************************/
40094
40095
40096   // LonLat - Pixel Translation
40097   
40098     /**
40099      * APIMethod: getMapObjectLonLatFromMapObjectPixel
40100      * 
40101      * Parameters:
40102      * moPixel - {Object} MapObject Pixel format
40103      * 
40104      * Returns:
40105      * {Object} MapObject LonLat translated from MapObject Pixel
40106      */
40107     getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
40108         var size = this.map.getSize();
40109         var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);
40110         var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);
40111         var res = this.map.getResolution();
40112
40113         var delta_x = moPixel.x - (size.w / 2);
40114         var delta_y = moPixel.y - (size.h / 2);
40115     
40116         var lonlat = new OpenLayers.LonLat(
40117             lon + delta_x * res,
40118             lat - delta_y * res
40119         ); 
40120
40121         if (this.wrapDateLine) {
40122             lonlat = lonlat.wrapDateLine(this.maxExtent);
40123         }
40124         return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);
40125     },
40126
40127     /**
40128      * APIMethod: getMapObjectPixelFromMapObjectLonLat
40129      * 
40130      * Parameters:
40131      * moLonLat - {Object} MapObject LonLat format
40132      * 
40133      * Returns:
40134      * {Object} MapObject Pixel transtlated from MapObject LonLat
40135      */
40136     getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
40137         var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
40138         var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
40139         var res = this.map.getResolution();
40140         var extent = this.map.getExtent();
40141         return this.getMapObjectPixelFromXY((1/res * (lon - extent.left)),
40142                                             (1/res * (extent.top - lat)));
40143     },
40144
40145   
40146     /** 
40147      * APIMethod: setMapObjectCenter
40148      * Set the mapObject to the specified center and zoom
40149      * 
40150      * Parameters:
40151      * center - {Object} MapObject LonLat format
40152      * zoom - {int} MapObject zoom format
40153      */
40154     setMapObjectCenter: function(center, zoom) {
40155         if (this.animationEnabled === false && zoom != this.mapObject.zoom) {
40156             var mapContainer = this.getMapContainer();
40157             google.maps.event.addListenerOnce(
40158                 this.mapObject, 
40159                 "idle", 
40160                 function() {
40161                     mapContainer.style.visibility = "";
40162                 }
40163             );
40164             mapContainer.style.visibility = "hidden";
40165         }
40166         this.mapObject.setOptions({
40167             center: center,
40168             zoom: zoom
40169         });
40170     },
40171    
40172     
40173   // Bounds
40174   
40175     /** 
40176      * APIMethod: getMapObjectZoomFromMapObjectBounds
40177      * 
40178      * Parameters:
40179      * moBounds - {Object} MapObject Bounds format
40180      * 
40181      * Returns:
40182      * {Object} MapObject Zoom for specified MapObject Bounds
40183      */
40184     getMapObjectZoomFromMapObjectBounds: function(moBounds) {
40185         return this.mapObject.getBoundsZoomLevel(moBounds);
40186     },
40187
40188     /************************************
40189      *                                  *
40190      *       MapObject Primitives       *
40191      *                                  *
40192      ************************************/
40193
40194
40195   // LonLat
40196     
40197     /**
40198      * APIMethod: getMapObjectLonLatFromLonLat
40199      * 
40200      * Parameters:
40201      * lon - {Float}
40202      * lat - {Float}
40203      * 
40204      * Returns:
40205      * {Object} MapObject LonLat built from lon and lat params
40206      */
40207     getMapObjectLonLatFromLonLat: function(lon, lat) {
40208         var gLatLng;
40209         if(this.sphericalMercator) {
40210             var lonlat = this.inverseMercator(lon, lat);
40211             gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);
40212         } else {
40213             gLatLng = new google.maps.LatLng(lat, lon);
40214         }
40215         return gLatLng;
40216     },
40217     
40218   // Pixel
40219     
40220     /**
40221      * APIMethod: getMapObjectPixelFromXY
40222      * 
40223      * Parameters:
40224      * x - {Integer}
40225      * y - {Integer}
40226      * 
40227      * Returns:
40228      * {Object} MapObject Pixel from x and y parameters
40229      */
40230     getMapObjectPixelFromXY: function(x, y) {
40231         return new google.maps.Point(x, y);
40232     }
40233     
40234 };
40235 /* ======================================================================
40236     OpenLayers/Request.js
40237    ====================================================================== */
40238
40239 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
40240  * full list of contributors). Published under the 2-clause BSD license.
40241  * See license.txt in the OpenLayers distribution or repository for the
40242  * full text of the license. */
40243
40244 /**
40245  * @requires OpenLayers/Events.js
40246  * @requires OpenLayers/Request/XMLHttpRequest.js
40247  */
40248
40249 /**
40250  * TODO: deprecate me
40251  * Use OpenLayers.Request.proxy instead.
40252  */
40253 OpenLayers.ProxyHost = "";
40254
40255 /**
40256  * Namespace: OpenLayers.Request
40257  * The OpenLayers.Request namespace contains convenience methods for working
40258  *     with XMLHttpRequests.  These methods work with a cross-browser
40259  *     W3C compliant <OpenLayers.Request.XMLHttpRequest> class.
40260  */
40261 if (!OpenLayers.Request) {
40262     /**
40263      * This allows for OpenLayers/Request/XMLHttpRequest.js to be included
40264      * before or after this script.
40265      */
40266     OpenLayers.Request = {};
40267 }
40268 OpenLayers.Util.extend(OpenLayers.Request, {
40269     
40270     /**
40271      * Constant: DEFAULT_CONFIG
40272      * {Object} Default configuration for all requests.
40273      */
40274     DEFAULT_CONFIG: {
40275         method: "GET",
40276         url: window.location.href,
40277         async: true,
40278         user: undefined,
40279         password: undefined,
40280         params: null,
40281         proxy: OpenLayers.ProxyHost,
40282         headers: {},
40283         data: null,
40284         callback: function() {},
40285         success: null,
40286         failure: null,
40287         scope: null
40288     },
40289     
40290     /**
40291      * Constant: URL_SPLIT_REGEX
40292      */
40293     URL_SPLIT_REGEX: /([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/,
40294     
40295     /**
40296      * APIProperty: events
40297      * {<OpenLayers.Events>} An events object that handles all 
40298      *     events on the {<OpenLayers.Request>} object.
40299      *
40300      * All event listeners will receive an event object with three properties:
40301      * request - {<OpenLayers.Request.XMLHttpRequest>} The request object.
40302      * config - {Object} The config object sent to the specific request method.
40303      * requestUrl - {String} The request url.
40304      * 
40305      * Supported event types:
40306      * complete - Triggered when we have a response from the request, if a
40307      *     listener returns false, no further response processing will take
40308      *     place.
40309      * success - Triggered when the HTTP response has a success code (200-299).
40310      * failure - Triggered when the HTTP response does not have a success code.
40311      */
40312     events: new OpenLayers.Events(this),
40313     
40314     /**
40315      * Method: makeSameOrigin
40316      * Using the specified proxy, returns a same origin url of the provided url.
40317      *
40318      * Parameters:
40319      * url - {String} An arbitrary url
40320      * proxy {String|Function} The proxy to use to make the provided url a
40321      *     same origin url.
40322      *
40323      * Returns
40324      * {String} the same origin url. If no proxy is provided, the returned url
40325      *     will be the same as the provided url.
40326      */
40327     makeSameOrigin: function(url, proxy) {
40328         var sameOrigin = url.indexOf("http") !== 0;
40329         var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);
40330         if (urlParts) {
40331             var location = window.location;
40332             sameOrigin =
40333                 urlParts[1] == location.protocol &&
40334                 urlParts[3] == location.hostname;
40335             var uPort = urlParts[4], lPort = location.port;
40336             if (uPort != 80 && uPort != "" || lPort != "80" && lPort != "") {
40337                 sameOrigin = sameOrigin && uPort == lPort;
40338             }
40339         }
40340         if (!sameOrigin) {
40341             if (proxy) {
40342                 if (typeof proxy == "function") {
40343                     url = proxy(url);
40344                 } else {
40345                     url = proxy + encodeURIComponent(url);
40346                 }
40347             }
40348         }
40349         return url;
40350     },
40351
40352     /**
40353      * APIMethod: issue
40354      * Create a new XMLHttpRequest object, open it, set any headers, bind
40355      *     a callback to done state, and send any data.  It is recommended that
40356      *     you use one <GET>, <POST>, <PUT>, <DELETE>, <OPTIONS>, or <HEAD>.
40357      *     This method is only documented to provide detail on the configuration
40358      *     options available to all request methods.
40359      *
40360      * Parameters:
40361      * config - {Object} Object containing properties for configuring the
40362      *     request.  Allowed configuration properties are described below.
40363      *     This object is modified and should not be reused.
40364      *
40365      * Allowed config properties:
40366      * method - {String} One of GET, POST, PUT, DELETE, HEAD, or
40367      *     OPTIONS.  Default is GET.
40368      * url - {String} URL for the request.
40369      * async - {Boolean} Open an asynchronous request.  Default is true.
40370      * user - {String} User for relevant authentication scheme.  Set
40371      *     to null to clear current user.
40372      * password - {String} Password for relevant authentication scheme.
40373      *     Set to null to clear current password.
40374      * proxy - {String} Optional proxy.  Defaults to
40375      *     <OpenLayers.ProxyHost>.
40376      * params - {Object} Any key:value pairs to be appended to the
40377      *     url as a query string.  Assumes url doesn't already include a query
40378      *     string or hash.  Typically, this is only appropriate for <GET>
40379      *     requests where the query string will be appended to the url.
40380      *     Parameter values that are arrays will be
40381      *     concatenated with a comma (note that this goes against form-encoding)
40382      *     as is done with <OpenLayers.Util.getParameterString>.
40383      * headers - {Object} Object with header:value pairs to be set on
40384      *     the request.
40385      * data - {String | Document} Optional data to send with the request.
40386      *     Typically, this is only used with <POST> and <PUT> requests.
40387      *     Make sure to provide the appropriate "Content-Type" header for your
40388      *     data.  For <POST> and <PUT> requests, the content type defaults to
40389      *     "application-xml".  If your data is a different content type, or
40390      *     if you are using a different HTTP method, set the "Content-Type"
40391      *     header to match your data type.
40392      * callback - {Function} Function to call when request is done.
40393      *     To determine if the request failed, check request.status (200
40394      *     indicates success).
40395      * success - {Function} Optional function to call if request status is in
40396      *     the 200s.  This will be called in addition to callback above and
40397      *     would typically only be used as an alternative.
40398      * failure - {Function} Optional function to call if request status is not
40399      *     in the 200s.  This will be called in addition to callback above and
40400      *     would typically only be used as an alternative.
40401      * scope - {Object} If callback is a public method on some object,
40402      *     set the scope to that object.
40403      *
40404      * Returns:
40405      * {XMLHttpRequest} Request object.  To abort the request before a response
40406      *     is received, call abort() on the request object.
40407      */
40408     issue: function(config) {        
40409         // apply default config - proxy host may have changed
40410         var defaultConfig = OpenLayers.Util.extend(
40411             this.DEFAULT_CONFIG,
40412             {proxy: OpenLayers.ProxyHost}
40413         );
40414         config = config || {};
40415         config.headers = config.headers || {};
40416         config = OpenLayers.Util.applyDefaults(config, defaultConfig);
40417         config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers);
40418         // Always set the "X-Requested-With" header to signal that this request
40419         // was issued through the XHR-object. Since header keys are case 
40420         // insensitive and we want to allow overriding of the "X-Requested-With"
40421         // header through the user we cannot use applyDefaults, but have to 
40422         // check manually whether we were called with a "X-Requested-With"
40423         // header.
40424         var customRequestedWithHeader = false,
40425             headerKey;
40426         for(headerKey in config.headers) {
40427             if (config.headers.hasOwnProperty( headerKey )) {
40428                 if (headerKey.toLowerCase() === 'x-requested-with') {
40429                     customRequestedWithHeader = true;
40430                 }
40431             }
40432         }
40433         if (customRequestedWithHeader === false) {
40434             // we did not have a custom "X-Requested-With" header
40435             config.headers['X-Requested-With'] = 'XMLHttpRequest';
40436         }
40437
40438         // create request, open, and set headers
40439         var request = new OpenLayers.Request.XMLHttpRequest();
40440         var url = OpenLayers.Util.urlAppend(config.url, 
40441             OpenLayers.Util.getParameterString(config.params || {}));
40442         url = OpenLayers.Request.makeSameOrigin(url, config.proxy);
40443         request.open(
40444             config.method, url, config.async, config.user, config.password
40445         );
40446         for(var header in config.headers) {
40447             request.setRequestHeader(header, config.headers[header]);
40448         }
40449
40450         var events = this.events;
40451
40452         // we want to execute runCallbacks with "this" as the
40453         // execution scope
40454         var self = this;
40455         
40456         request.onreadystatechange = function() {
40457             if(request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {
40458                 var proceed = events.triggerEvent(
40459                     "complete",
40460                     {request: request, config: config, requestUrl: url}
40461                 );
40462                 if(proceed !== false) {
40463                     self.runCallbacks(
40464                         {request: request, config: config, requestUrl: url}
40465                     );
40466                 }
40467             }
40468         };
40469         
40470         // send request (optionally with data) and return
40471         // call in a timeout for asynchronous requests so the return is
40472         // available before readyState == 4 for cached docs
40473         if(config.async === false) {
40474             request.send(config.data);
40475         } else {
40476             window.setTimeout(function(){
40477                 if (request.readyState !== 0) { // W3C: 0-UNSENT
40478                     request.send(config.data);
40479                 }
40480             }, 0);
40481         }
40482         return request;
40483     },
40484     
40485     /**
40486      * Method: runCallbacks
40487      * Calls the complete, success and failure callbacks. Application
40488      *    can listen to the "complete" event, have the listener 
40489      *    display a confirm window and always return false, and
40490      *    execute OpenLayers.Request.runCallbacks if the user
40491      *    hits "yes" in the confirm window.
40492      *
40493      * Parameters:
40494      * options - {Object} Hash containing request, config and requestUrl keys
40495      */
40496     runCallbacks: function(options) {
40497         var request = options.request;
40498         var config = options.config;
40499         
40500         // bind callbacks to readyState 4 (done)
40501         var complete = (config.scope) ?
40502             OpenLayers.Function.bind(config.callback, config.scope) :
40503             config.callback;
40504         
40505         // optional success callback
40506         var success;
40507         if(config.success) {
40508             success = (config.scope) ?
40509                 OpenLayers.Function.bind(config.success, config.scope) :
40510                 config.success;
40511         }
40512
40513         // optional failure callback
40514         var failure;
40515         if(config.failure) {
40516             failure = (config.scope) ?
40517                 OpenLayers.Function.bind(config.failure, config.scope) :
40518                 config.failure;
40519         }
40520
40521         if (OpenLayers.Util.createUrlObject(config.url).protocol == "file:" &&
40522                                                         request.responseText) {
40523             request.status = 200;
40524         }
40525         complete(request);
40526
40527         if (!request.status || (request.status >= 200 && request.status < 300)) {
40528             this.events.triggerEvent("success", options);
40529             if(success) {
40530                 success(request);
40531             }
40532         }
40533         if(request.status && (request.status < 200 || request.status >= 300)) {                    
40534             this.events.triggerEvent("failure", options);
40535             if(failure) {
40536                 failure(request);
40537             }
40538         }
40539     },
40540     
40541     /**
40542      * APIMethod: GET
40543      * Send an HTTP GET request.  Additional configuration properties are
40544      *     documented in the <issue> method, with the method property set
40545      *     to GET.
40546      *
40547      * Parameters:
40548      * config - {Object} Object with properties for configuring the request.
40549      *     See the <issue> method for documentation of allowed properties.
40550      *     This object is modified and should not be reused.
40551      * 
40552      * Returns:
40553      * {XMLHttpRequest} Request object.
40554      */
40555     GET: function(config) {
40556         config = OpenLayers.Util.extend(config, {method: "GET"});
40557         return OpenLayers.Request.issue(config);
40558     },
40559     
40560     /**
40561      * APIMethod: POST
40562      * Send a POST request.  Additional configuration properties are
40563      *     documented in the <issue> method, with the method property set
40564      *     to POST and "Content-Type" header set to "application/xml".
40565      *
40566      * Parameters:
40567      * config - {Object} Object with properties for configuring the request.
40568      *     See the <issue> method for documentation of allowed properties.  The
40569      *     default "Content-Type" header will be set to "application-xml" if
40570      *     none is provided.  This object is modified and should not be reused.
40571      * 
40572      * Returns:
40573      * {XMLHttpRequest} Request object.
40574      */
40575     POST: function(config) {
40576         config = OpenLayers.Util.extend(config, {method: "POST"});
40577         // set content type to application/xml if it isn't already set
40578         config.headers = config.headers ? config.headers : {};
40579         if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
40580             config.headers["Content-Type"] = "application/xml";
40581         }
40582         return OpenLayers.Request.issue(config);
40583     },
40584     
40585     /**
40586      * APIMethod: PUT
40587      * Send an HTTP PUT request.  Additional configuration properties are
40588      *     documented in the <issue> method, with the method property set
40589      *     to PUT and "Content-Type" header set to "application/xml".
40590      *
40591      * Parameters:
40592      * config - {Object} Object with properties for configuring the request.
40593      *     See the <issue> method for documentation of allowed properties.  The
40594      *     default "Content-Type" header will be set to "application-xml" if
40595      *     none is provided.  This object is modified and should not be reused.
40596      * 
40597      * Returns:
40598      * {XMLHttpRequest} Request object.
40599      */
40600     PUT: function(config) {
40601         config = OpenLayers.Util.extend(config, {method: "PUT"});
40602         // set content type to application/xml if it isn't already set
40603         config.headers = config.headers ? config.headers : {};
40604         if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
40605             config.headers["Content-Type"] = "application/xml";
40606         }
40607         return OpenLayers.Request.issue(config);
40608     },
40609     
40610     /**
40611      * APIMethod: DELETE
40612      * Send an HTTP DELETE request.  Additional configuration properties are
40613      *     documented in the <issue> method, with the method property set
40614      *     to DELETE.
40615      *
40616      * Parameters:
40617      * config - {Object} Object with properties for configuring the request.
40618      *     See the <issue> method for documentation of allowed properties.
40619      *     This object is modified and should not be reused.
40620      * 
40621      * Returns:
40622      * {XMLHttpRequest} Request object.
40623      */
40624     DELETE: function(config) {
40625         config = OpenLayers.Util.extend(config, {method: "DELETE"});
40626         return OpenLayers.Request.issue(config);
40627     },
40628   
40629     /**
40630      * APIMethod: HEAD
40631      * Send an HTTP HEAD request.  Additional configuration properties are
40632      *     documented in the <issue> method, with the method property set
40633      *     to HEAD.
40634      *
40635      * Parameters:
40636      * config - {Object} Object with properties for configuring the request.
40637      *     See the <issue> method for documentation of allowed properties.
40638      *     This object is modified and should not be reused.
40639      * 
40640      * Returns:
40641      * {XMLHttpRequest} Request object.
40642      */
40643     HEAD: function(config) {
40644         config = OpenLayers.Util.extend(config, {method: "HEAD"});
40645         return OpenLayers.Request.issue(config);
40646     },
40647     
40648     /**
40649      * APIMethod: OPTIONS
40650      * Send an HTTP OPTIONS request.  Additional configuration properties are
40651      *     documented in the <issue> method, with the method property set
40652      *     to OPTIONS.
40653      *
40654      * Parameters:
40655      * config - {Object} Object with properties for configuring the request.
40656      *     See the <issue> method for documentation of allowed properties.
40657      *     This object is modified and should not be reused.
40658      * 
40659      * Returns:
40660      * {XMLHttpRequest} Request object.
40661      */
40662     OPTIONS: function(config) {
40663         config = OpenLayers.Util.extend(config, {method: "OPTIONS"});
40664         return OpenLayers.Request.issue(config);
40665     }
40666
40667 });
40668 /* ======================================================================
40669     OpenLayers/Request/XMLHttpRequest.js
40670    ====================================================================== */
40671
40672 // XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com)
40673 //
40674 // Licensed under the Apache License, Version 2.0 (the "License");
40675 // you may not use this file except in compliance with the License.
40676 // You may obtain a copy of the License at
40677 //
40678 //   http://www.apache.org/licenses/LICENSE-2.0
40679 //
40680 // Unless required by applicable law or agreed to in writing, software
40681 // distributed under the License is distributed on an "AS IS" BASIS,
40682 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
40683 // See the License for the specific language governing permissions and
40684 // limitations under the License.
40685
40686 /**
40687  * @requires OpenLayers/Request.js
40688  */
40689
40690 (function () {
40691
40692     // Save reference to earlier defined object implementation (if any)
40693     var oXMLHttpRequest    = window.XMLHttpRequest;
40694
40695     // Define on browser type
40696     var bGecko    = !!window.controllers,
40697         bIE        = window.document.all && !window.opera,
40698         bIE7    = bIE && window.navigator.userAgent.match(/MSIE 7.0/);
40699
40700     // Enables "XMLHttpRequest()" call next to "new XMLHttpReques()"
40701     function fXMLHttpRequest() {
40702         this._object    = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP");
40703         this._listeners    = [];
40704     };
40705
40706     // Constructor
40707     function cXMLHttpRequest() {
40708         return new fXMLHttpRequest;
40709     };
40710     cXMLHttpRequest.prototype    = fXMLHttpRequest.prototype;
40711
40712     // BUGFIX: Firefox with Firebug installed would break pages if not executed
40713     if (bGecko && oXMLHttpRequest.wrapped)
40714         cXMLHttpRequest.wrapped    = oXMLHttpRequest.wrapped;
40715
40716     // Constants
40717     cXMLHttpRequest.UNSENT                = 0;
40718     cXMLHttpRequest.OPENED                = 1;
40719     cXMLHttpRequest.HEADERS_RECEIVED    = 2;
40720     cXMLHttpRequest.LOADING                = 3;
40721     cXMLHttpRequest.DONE                = 4;
40722
40723     // Public Properties
40724     cXMLHttpRequest.prototype.readyState    = cXMLHttpRequest.UNSENT;
40725     cXMLHttpRequest.prototype.responseText    = '';
40726     cXMLHttpRequest.prototype.responseXML    = null;
40727     cXMLHttpRequest.prototype.status        = 0;
40728     cXMLHttpRequest.prototype.statusText    = '';
40729
40730     // Priority proposal
40731     cXMLHttpRequest.prototype.priority        = "NORMAL";
40732
40733     // Instance-level Events Handlers
40734     cXMLHttpRequest.prototype.onreadystatechange    = null;
40735
40736     // Class-level Events Handlers
40737     cXMLHttpRequest.onreadystatechange    = null;
40738     cXMLHttpRequest.onopen                = null;
40739     cXMLHttpRequest.onsend                = null;
40740     cXMLHttpRequest.onabort                = null;
40741
40742     // Public Methods
40743     cXMLHttpRequest.prototype.open    = function(sMethod, sUrl, bAsync, sUser, sPassword) {
40744         // Delete headers, required when object is reused
40745         delete this._headers;
40746
40747         // When bAsync parameter value is omitted, use true as default
40748         if (arguments.length < 3)
40749             bAsync    = true;
40750
40751         // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests
40752         this._async        = bAsync;
40753
40754         // Set the onreadystatechange handler
40755         var oRequest    = this,
40756             nState        = this.readyState,
40757             fOnUnload;
40758
40759         // BUGFIX: IE - memory leak on page unload (inter-page leak)
40760         if (bIE && bAsync) {
40761             fOnUnload = function() {
40762                 if (nState != cXMLHttpRequest.DONE) {
40763                     fCleanTransport(oRequest);
40764                     // Safe to abort here since onreadystatechange handler removed
40765                     oRequest.abort();
40766                 }
40767             };
40768             window.attachEvent("onunload", fOnUnload);
40769         }
40770
40771         // Add method sniffer
40772         if (cXMLHttpRequest.onopen)
40773             cXMLHttpRequest.onopen.apply(this, arguments);
40774
40775         if (arguments.length > 4)
40776             this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
40777         else
40778         if (arguments.length > 3)
40779             this._object.open(sMethod, sUrl, bAsync, sUser);
40780         else
40781             this._object.open(sMethod, sUrl, bAsync);
40782
40783         this.readyState    = cXMLHttpRequest.OPENED;
40784         fReadyStateChange(this);
40785
40786         this._object.onreadystatechange    = function() {
40787             if (bGecko && !bAsync)
40788                 return;
40789
40790             // Synchronize state
40791             oRequest.readyState        = oRequest._object.readyState;
40792
40793             //
40794             fSynchronizeValues(oRequest);
40795
40796             // BUGFIX: Firefox fires unnecessary DONE when aborting
40797             if (oRequest._aborted) {
40798                 // Reset readyState to UNSENT
40799                 oRequest.readyState    = cXMLHttpRequest.UNSENT;
40800
40801                 // Return now
40802                 return;
40803             }
40804
40805             if (oRequest.readyState == cXMLHttpRequest.DONE) {
40806                 // Free up queue
40807                 delete oRequest._data;
40808 /*                if (bAsync)
40809                     fQueue_remove(oRequest);*/
40810                 //
40811                 fCleanTransport(oRequest);
40812 // Uncomment this block if you need a fix for IE cache
40813 /*
40814                 // BUGFIX: IE - cache issue
40815                 if (!oRequest._object.getResponseHeader("Date")) {
40816                     // Save object to cache
40817                     oRequest._cached    = oRequest._object;
40818
40819                     // Instantiate a new transport object
40820                     cXMLHttpRequest.call(oRequest);
40821
40822                     // Re-send request
40823                     if (sUser) {
40824                          if (sPassword)
40825                             oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
40826                         else
40827                             oRequest._object.open(sMethod, sUrl, bAsync, sUser);
40828                     }
40829                     else
40830                         oRequest._object.open(sMethod, sUrl, bAsync);
40831                     oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0));
40832                     // Copy headers set
40833                     if (oRequest._headers)
40834                         for (var sHeader in oRequest._headers)
40835                             if (typeof oRequest._headers[sHeader] == "string")    // Some frameworks prototype objects with functions
40836                                 oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);
40837
40838                     oRequest._object.onreadystatechange    = function() {
40839                         // Synchronize state
40840                         oRequest.readyState        = oRequest._object.readyState;
40841
40842                         if (oRequest._aborted) {
40843                             //
40844                             oRequest.readyState    = cXMLHttpRequest.UNSENT;
40845
40846                             // Return
40847                             return;
40848                         }
40849
40850                         if (oRequest.readyState == cXMLHttpRequest.DONE) {
40851                             // Clean Object
40852                             fCleanTransport(oRequest);
40853
40854                             // get cached request
40855                             if (oRequest.status == 304)
40856                                 oRequest._object    = oRequest._cached;
40857
40858                             //
40859                             delete oRequest._cached;
40860
40861                             //
40862                             fSynchronizeValues(oRequest);
40863
40864                             //
40865                             fReadyStateChange(oRequest);
40866
40867                             // BUGFIX: IE - memory leak in interrupted
40868                             if (bIE && bAsync)
40869                                 window.detachEvent("onunload", fOnUnload);
40870                         }
40871                     };
40872                     oRequest._object.send(null);
40873
40874                     // Return now - wait until re-sent request is finished
40875                     return;
40876                 };
40877 */
40878                 // BUGFIX: IE - memory leak in interrupted
40879                 if (bIE && bAsync)
40880                     window.detachEvent("onunload", fOnUnload);
40881             }
40882
40883             // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice
40884             if (nState != oRequest.readyState)
40885                 fReadyStateChange(oRequest);
40886
40887             nState    = oRequest.readyState;
40888         }
40889     };
40890     function fXMLHttpRequest_send(oRequest) {
40891         oRequest._object.send(oRequest._data);
40892
40893         // BUGFIX: Gecko - missing readystatechange calls in synchronous requests
40894         if (bGecko && !oRequest._async) {
40895             oRequest.readyState    = cXMLHttpRequest.OPENED;
40896
40897             // Synchronize state
40898             fSynchronizeValues(oRequest);
40899
40900             // Simulate missing states
40901             while (oRequest.readyState < cXMLHttpRequest.DONE) {
40902                 oRequest.readyState++;
40903                 fReadyStateChange(oRequest);
40904                 // Check if we are aborted
40905                 if (oRequest._aborted)
40906                     return;
40907             }
40908         }
40909     };
40910     cXMLHttpRequest.prototype.send    = function(vData) {
40911         // Add method sniffer
40912         if (cXMLHttpRequest.onsend)
40913             cXMLHttpRequest.onsend.apply(this, arguments);
40914
40915         if (!arguments.length)
40916             vData    = null;
40917
40918         // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required
40919         // BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent
40920         // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)
40921         if (vData && vData.nodeType) {
40922             vData    = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;
40923             if (!this._headers["Content-Type"])
40924                 this._object.setRequestHeader("Content-Type", "application/xml");
40925         }
40926
40927         this._data    = vData;
40928 /*
40929         // Add to queue
40930         if (this._async)
40931             fQueue_add(this);
40932         else*/
40933             fXMLHttpRequest_send(this);
40934     };
40935     cXMLHttpRequest.prototype.abort    = function() {
40936         // Add method sniffer
40937         if (cXMLHttpRequest.onabort)
40938             cXMLHttpRequest.onabort.apply(this, arguments);
40939
40940         // BUGFIX: Gecko - unnecessary DONE when aborting
40941         if (this.readyState > cXMLHttpRequest.UNSENT)
40942             this._aborted    = true;
40943
40944         this._object.abort();
40945
40946         // BUGFIX: IE - memory leak
40947         fCleanTransport(this);
40948
40949         this.readyState    = cXMLHttpRequest.UNSENT;
40950
40951         delete this._data;
40952 /*        if (this._async)
40953             fQueue_remove(this);*/
40954     };
40955     cXMLHttpRequest.prototype.getAllResponseHeaders    = function() {
40956         return this._object.getAllResponseHeaders();
40957     };
40958     cXMLHttpRequest.prototype.getResponseHeader    = function(sName) {
40959         return this._object.getResponseHeader(sName);
40960     };
40961     cXMLHttpRequest.prototype.setRequestHeader    = function(sName, sValue) {
40962         // BUGFIX: IE - cache issue
40963         if (!this._headers)
40964             this._headers    = {};
40965         this._headers[sName]    = sValue;
40966
40967         return this._object.setRequestHeader(sName, sValue);
40968     };
40969
40970     // EventTarget interface implementation
40971     cXMLHttpRequest.prototype.addEventListener    = function(sName, fHandler, bUseCapture) {
40972         for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
40973             if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
40974                 return;
40975         // Add listener
40976         this._listeners.push([sName, fHandler, bUseCapture]);
40977     };
40978
40979     cXMLHttpRequest.prototype.removeEventListener    = function(sName, fHandler, bUseCapture) {
40980         for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
40981             if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
40982                 break;
40983         // Remove listener
40984         if (oListener)
40985             this._listeners.splice(nIndex, 1);
40986     };
40987
40988     cXMLHttpRequest.prototype.dispatchEvent    = function(oEvent) {
40989         var oEventPseudo    = {
40990             'type':            oEvent.type,
40991             'target':        this,
40992             'currentTarget':this,
40993             'eventPhase':    2,
40994             'bubbles':        oEvent.bubbles,
40995             'cancelable':    oEvent.cancelable,
40996             'timeStamp':    oEvent.timeStamp,
40997             'stopPropagation':    function() {},    // There is no flow
40998             'preventDefault':    function() {},    // There is no default action
40999             'initEvent':        function() {}    // Original event object should be initialized
41000         };
41001
41002         // Execute onreadystatechange
41003         if (oEventPseudo.type == "readystatechange" && this.onreadystatechange)
41004             (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);
41005
41006         // Execute listeners
41007         for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
41008             if (oListener[0] == oEventPseudo.type && !oListener[2])
41009                 (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]);
41010     };
41011
41012     //
41013     cXMLHttpRequest.prototype.toString    = function() {
41014         return '[' + "object" + ' ' + "XMLHttpRequest" + ']';
41015     };
41016
41017     cXMLHttpRequest.toString    = function() {
41018         return '[' + "XMLHttpRequest" + ']';
41019     };
41020
41021     // Helper function
41022     function fReadyStateChange(oRequest) {
41023         // Sniffing code
41024         if (cXMLHttpRequest.onreadystatechange)
41025             cXMLHttpRequest.onreadystatechange.apply(oRequest);
41026
41027         // Fake event
41028         oRequest.dispatchEvent({
41029             'type':            "readystatechange",
41030             'bubbles':        false,
41031             'cancelable':    false,
41032             'timeStamp':    new Date + 0
41033         });
41034     };
41035
41036     function fGetDocument(oRequest) {
41037         var oDocument    = oRequest.responseXML,
41038             sResponse    = oRequest.responseText;
41039         // Try parsing responseText
41040         if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) {
41041             oDocument    = new window.ActiveXObject("Microsoft.XMLDOM");
41042             oDocument.async                = false;
41043             oDocument.validateOnParse    = false;
41044             oDocument.loadXML(sResponse);
41045         }
41046         // Check if there is no error in document
41047         if (oDocument)
41048             if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror"))
41049                 return null;
41050         return oDocument;
41051     };
41052
41053     function fSynchronizeValues(oRequest) {
41054         try {    oRequest.responseText    = oRequest._object.responseText;    } catch (e) {}
41055         try {    oRequest.responseXML    = fGetDocument(oRequest._object);    } catch (e) {}
41056         try {    oRequest.status            = oRequest._object.status;            } catch (e) {}
41057         try {    oRequest.statusText        = oRequest._object.statusText;        } catch (e) {}
41058     };
41059
41060     function fCleanTransport(oRequest) {
41061         // BUGFIX: IE - memory leak (on-page leak)
41062         oRequest._object.onreadystatechange    = new window.Function;
41063     };
41064 /*
41065     // Queue manager
41066     var oQueuePending    = {"CRITICAL":[],"HIGH":[],"NORMAL":[],"LOW":[],"LOWEST":[]},
41067         aQueueRunning    = [];
41068     function fQueue_add(oRequest) {
41069         oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : "NORMAL"].push(oRequest);
41070         //
41071         setTimeout(fQueue_process);
41072     };
41073
41074     function fQueue_remove(oRequest) {
41075         for (var nIndex = 0, bFound    = false; nIndex < aQueueRunning.length; nIndex++)
41076             if (bFound)
41077                 aQueueRunning[nIndex - 1]    = aQueueRunning[nIndex];
41078             else
41079             if (aQueueRunning[nIndex] == oRequest)
41080                 bFound    = true;
41081         if (bFound)
41082             aQueueRunning.length--;
41083         //
41084         setTimeout(fQueue_process);
41085     };
41086
41087     function fQueue_process() {
41088         if (aQueueRunning.length < 6) {
41089             for (var sPriority in oQueuePending) {
41090                 if (oQueuePending[sPriority].length) {
41091                     var oRequest    = oQueuePending[sPriority][0];
41092                     oQueuePending[sPriority]    = oQueuePending[sPriority].slice(1);
41093                     //
41094                     aQueueRunning.push(oRequest);
41095                     // Send request
41096                     fXMLHttpRequest_send(oRequest);
41097                     break;
41098                 }
41099             }
41100         }
41101     };
41102 */
41103     // Internet Explorer 5.0 (missing apply)
41104     if (!window.Function.prototype.apply) {
41105         window.Function.prototype.apply    = function(oRequest, oArguments) {
41106             if (!oArguments)
41107                 oArguments    = [];
41108             oRequest.__func    = this;
41109             oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);
41110             delete oRequest.__func;
41111         };
41112     };
41113
41114     // Register new object with window
41115     /**
41116      * Class: OpenLayers.Request.XMLHttpRequest
41117      * Standard-compliant (W3C) cross-browser implementation of the
41118      *     XMLHttpRequest object.  From
41119      *     http://code.google.com/p/xmlhttprequest/.
41120      */
41121     if (!OpenLayers.Request) {
41122         /**
41123          * This allows for OpenLayers/Request.js to be included
41124          * before or after this script.
41125          */
41126         OpenLayers.Request = {};
41127     }
41128     OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;
41129 })();
41130 /* ======================================================================
41131     OpenLayers/Lang/de.js
41132    ====================================================================== */
41133
41134 /* Translators (2009 onwards):
41135  *  - Grille chompa
41136  *  - Nikiwaibel
41137  *  - Umherirrender
41138  */
41139
41140 /**
41141  * @requires OpenLayers/Lang.js
41142  */
41143
41144 /**
41145  * Namespace: OpenLayers.Lang["de"]
41146  * Dictionary for Deutsch.  Keys for entries are used in calls to
41147  *     <OpenLayers.Lang.translate>.  Entry bodies are normal strings or
41148  *     strings formatted for use with <OpenLayers.String.format> calls.
41149  */
41150 OpenLayers.Lang["de"] = OpenLayers.Util.applyDefaults({
41151
41152     'unhandledRequest': "Unbehandelte Anfragerückmeldung ${statusText}",
41153
41154     'Permalink': "Permalink",
41155
41156     'Overlays': "Overlays",
41157
41158     'Base Layer': "Grundkarte",
41159
41160     'noFID': "Ein Feature, für das keine FID existiert, kann nicht aktualisiert werden.",
41161
41162     'browserNotSupported': "Ihr Browser unterstützt keine Vektordarstellung. Aktuell unterstützte Renderer:\n${renderers}",
41163
41164     'minZoomLevelError': "Die \x3ccode\x3eminZoomLevel\x3c/code\x3e-Eigenschaft ist nur für die Verwendung mit \x3ccode\x3eFixedZoomLevels\x3c/code\x3e-untergeordneten Layers vorgesehen. Das dieser \x3ctt\x3ewfs\x3c/tt\x3e-Layer die \x3ccode\x3eminZoomLevel\x3c/code\x3e-Eigenschaft Ã¼berprüft ist ein Relikt der Vergangenheit. Wir können diese Ãœberprüfung nicht entfernen, ohne das OL basierende Applikationen nicht mehr funktionieren. Daher markieren wir es als veraltet - die \x3ccode\x3eminZoomLevel\x3c/code\x3e-Überprüfung wird in Version 3.0 entfernt werden. Bitte verwenden Sie stattdessen die Min-/Max-Lösung, wie sie unter http://trac.openlayers.org/wiki/SettingZoomLevels beschrieben ist.",
41165
41166     'commitSuccess': "WFS-Transaktion: Erfolgreich ${response}",
41167
41168     'commitFailed': "WFS-Transaktion: Fehlgeschlagen ${response}",
41169
41170     'googleWarning': "Der Google-Layer konnte nicht korrekt geladen werden.\x3cbr\x3e\x3cbr\x3eUm diese Meldung nicht mehr zu erhalten, wählen Sie einen anderen Hintergrundlayer aus dem LayerSwitcher in der rechten oberen Ecke.\x3cbr\x3e\x3cbr\x3eSehr wahrscheinlich tritt dieser Fehler auf, weil das Skript der Google-Maps-Bibliothek nicht eingebunden wurde oder keinen gültigen API-Schlüssel für Ihre URL enthält.\x3cbr\x3e\x3cbr\x3eEntwickler: Besuche \x3ca href=\'http://trac.openlayers.org/wiki/Google\' target=\'_blank\'\x3edas Wiki\x3c/a\x3e für Hilfe zum korrekten Einbinden des Google-Layers",
41171
41172     'getLayerWarning': "Der ${layerType}-Layer konnte nicht korrekt geladen werden.\x3cbr\x3e\x3cbr\x3eUm diese Meldung nicht mehr zu erhalten, wählen Sie einen anderen Hintergrundlayer aus dem LayerSwitcher in der rechten oberen Ecke.\x3cbr\x3e\x3cbr\x3eSehr wahrscheinlich tritt dieser Fehler auf, weil das Skript der \'${layerLib}\'-Bibliothek nicht eingebunden wurde.\x3cbr\x3e\x3cbr\x3eEntwickler: Besuche \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3edas Wiki\x3c/a\x3e für Hilfe zum korrekten Einbinden von Layern",
41173
41174     'Scale = 1 : ${scaleDenom}': "Maßstab = 1 : ${scaleDenom}",
41175
41176     'W': "W",
41177
41178     'E': "O",
41179
41180     'N': "N",
41181
41182     'S': "S",
41183
41184     'reprojectDeprecated': "Sie verwenden die â€žReproject“-Option des Layers ${layerName}. Diese Option ist veraltet: Sie wurde entwickelt um die Anzeige von Daten auf kommerziellen Basiskarten zu unterstützen, aber diese Funktion sollte jetzt durch Unterstützung der â€žSpherical Mercator“ erreicht werden. Weitere Informationen sind unter http://trac.openlayers.org/wiki/SphericalMercator verfügbar.",
41185
41186     'methodDeprecated': "Die Methode ist veraltet und wird in 3.0 entfernt. Bitte verwende stattdessen ${newMethod}."
41187
41188 });
41189 /* ======================================================================
41190     OpenLayers/Popup/FramedCloud.js
41191    ====================================================================== */
41192
41193 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
41194  * full list of contributors). Published under the 2-clause BSD license.
41195  * See license.txt in the OpenLayers distribution or repository for the
41196  * full text of the license. */
41197
41198 /**
41199  * @requires OpenLayers/Popup/Framed.js
41200  * @requires OpenLayers/Util.js
41201  * @requires OpenLayers/BaseTypes/Bounds.js
41202  * @requires OpenLayers/BaseTypes/Pixel.js
41203  * @requires OpenLayers/BaseTypes/Size.js
41204  */
41205
41206 /**
41207  * Class: OpenLayers.Popup.FramedCloud
41208  * 
41209  * Inherits from: 
41210  *  - <OpenLayers.Popup.Framed>
41211  */
41212 OpenLayers.Popup.FramedCloud = 
41213   OpenLayers.Class(OpenLayers.Popup.Framed, {
41214
41215     /** 
41216      * Property: contentDisplayClass
41217      * {String} The CSS class of the popup content div.
41218      */
41219     contentDisplayClass: "olFramedCloudPopupContent",
41220
41221     /**
41222      * APIProperty: autoSize
41223      * {Boolean} Framed Cloud is autosizing by default.
41224      */
41225     autoSize: true,
41226
41227     /**
41228      * APIProperty: panMapIfOutOfView
41229      * {Boolean} Framed Cloud does pan into view by default.
41230      */
41231     panMapIfOutOfView: true,
41232
41233     /**
41234      * APIProperty: imageSize
41235      * {<OpenLayers.Size>}
41236      */
41237     imageSize: new OpenLayers.Size(1276, 736),
41238
41239     /**
41240      * APIProperty: isAlphaImage
41241      * {Boolean} The FramedCloud does not use an alpha image (in honor of the 
41242      *     good ie6 folk out there)
41243      */
41244     isAlphaImage: false,
41245
41246     /** 
41247      * APIProperty: fixedRelativePosition
41248      * {Boolean} The Framed Cloud popup works in just one fixed position.
41249      */
41250     fixedRelativePosition: false,
41251
41252     /**
41253      * Property: positionBlocks
41254      * {Object} Hash of differen position blocks, keyed by relativePosition
41255      *     two-character code string (ie "tl", "tr", "bl", "br")
41256      */
41257     positionBlocks: {
41258         "tl": {
41259             'offset': new OpenLayers.Pixel(44, 0),
41260             'padding': new OpenLayers.Bounds(8, 40, 8, 9),
41261             'blocks': [
41262                 { // top-left
41263                     size: new OpenLayers.Size('auto', 'auto'),
41264                     anchor: new OpenLayers.Bounds(0, 51, 22, 0),
41265                     position: new OpenLayers.Pixel(0, 0)
41266                 },
41267                 { //top-right
41268                     size: new OpenLayers.Size(22, 'auto'),
41269                     anchor: new OpenLayers.Bounds(null, 50, 0, 0),
41270                     position: new OpenLayers.Pixel(-1238, 0)
41271                 },
41272                 { //bottom-left
41273                     size: new OpenLayers.Size('auto', 19),
41274                     anchor: new OpenLayers.Bounds(0, 32, 22, null),
41275                     position: new OpenLayers.Pixel(0, -631)
41276                 },
41277                 { //bottom-right
41278                     size: new OpenLayers.Size(22, 18),
41279                     anchor: new OpenLayers.Bounds(null, 32, 0, null),
41280                     position: new OpenLayers.Pixel(-1238, -632)
41281                 },
41282                 { // stem
41283                     size: new OpenLayers.Size(81, 35),
41284                     anchor: new OpenLayers.Bounds(null, 0, 0, null),
41285                     position: new OpenLayers.Pixel(0, -688)
41286                 }
41287             ]
41288         },
41289         "tr": {
41290             'offset': new OpenLayers.Pixel(-45, 0),
41291             'padding': new OpenLayers.Bounds(8, 40, 8, 9),
41292             'blocks': [
41293                 { // top-left
41294                     size: new OpenLayers.Size('auto', 'auto'),
41295                     anchor: new OpenLayers.Bounds(0, 51, 22, 0),
41296                     position: new OpenLayers.Pixel(0, 0)
41297                 },
41298                 { //top-right
41299                     size: new OpenLayers.Size(22, 'auto'),
41300                     anchor: new OpenLayers.Bounds(null, 50, 0, 0),
41301                     position: new OpenLayers.Pixel(-1238, 0)
41302                 },
41303                 { //bottom-left
41304                     size: new OpenLayers.Size('auto', 19),
41305                     anchor: new OpenLayers.Bounds(0, 32, 22, null),
41306                     position: new OpenLayers.Pixel(0, -631)
41307                 },
41308                 { //bottom-right
41309                     size: new OpenLayers.Size(22, 19),
41310                     anchor: new OpenLayers.Bounds(null, 32, 0, null),
41311                     position: new OpenLayers.Pixel(-1238, -631)
41312                 },
41313                 { // stem
41314                     size: new OpenLayers.Size(81, 35),
41315                     anchor: new OpenLayers.Bounds(0, 0, null, null),
41316                     position: new OpenLayers.Pixel(-215, -687)
41317                 }
41318             ]
41319         },
41320         "bl": {
41321             'offset': new OpenLayers.Pixel(45, 0),
41322             'padding': new OpenLayers.Bounds(8, 9, 8, 40),
41323             'blocks': [
41324                 { // top-left
41325                     size: new OpenLayers.Size('auto', 'auto'),
41326                     anchor: new OpenLayers.Bounds(0, 21, 22, 32),
41327                     position: new OpenLayers.Pixel(0, 0)
41328                 },
41329                 { //top-right
41330                     size: new OpenLayers.Size(22, 'auto'),
41331                     anchor: new OpenLayers.Bounds(null, 21, 0, 32),
41332                     position: new OpenLayers.Pixel(-1238, 0)
41333                 },
41334                 { //bottom-left
41335                     size: new OpenLayers.Size('auto', 21),
41336                     anchor: new OpenLayers.Bounds(0, 0, 22, null),
41337                     position: new OpenLayers.Pixel(0, -629)
41338                 },
41339                 { //bottom-right
41340                     size: new OpenLayers.Size(22, 21),
41341                     anchor: new OpenLayers.Bounds(null, 0, 0, null),
41342                     position: new OpenLayers.Pixel(-1238, -629)
41343                 },
41344                 { // stem
41345                     size: new OpenLayers.Size(81, 33),
41346                     anchor: new OpenLayers.Bounds(null, null, 0, 0),
41347                     position: new OpenLayers.Pixel(-101, -674)
41348                 }
41349             ]
41350         },
41351         "br": {
41352             'offset': new OpenLayers.Pixel(-44, 0),
41353             'padding': new OpenLayers.Bounds(8, 9, 8, 40),
41354             'blocks': [
41355                 { // top-left
41356                     size: new OpenLayers.Size('auto', 'auto'),
41357                     anchor: new OpenLayers.Bounds(0, 21, 22, 32),
41358                     position: new OpenLayers.Pixel(0, 0)
41359                 },
41360                 { //top-right
41361                     size: new OpenLayers.Size(22, 'auto'),
41362                     anchor: new OpenLayers.Bounds(null, 21, 0, 32),
41363                     position: new OpenLayers.Pixel(-1238, 0)
41364                 },
41365                 { //bottom-left
41366                     size: new OpenLayers.Size('auto', 21),
41367                     anchor: new OpenLayers.Bounds(0, 0, 22, null),
41368                     position: new OpenLayers.Pixel(0, -629)
41369                 },
41370                 { //bottom-right
41371                     size: new OpenLayers.Size(22, 21),
41372                     anchor: new OpenLayers.Bounds(null, 0, 0, null),
41373                     position: new OpenLayers.Pixel(-1238, -629)
41374                 },
41375                 { // stem
41376                     size: new OpenLayers.Size(81, 33),
41377                     anchor: new OpenLayers.Bounds(0, null, null, 0),
41378                     position: new OpenLayers.Pixel(-311, -674)
41379                 }
41380             ]
41381         }
41382     },
41383
41384     /**
41385      * APIProperty: minSize
41386      * {<OpenLayers.Size>}
41387      */
41388     minSize: new OpenLayers.Size(105, 10),
41389
41390     /**
41391      * APIProperty: maxSize
41392      * {<OpenLayers.Size>}
41393      */
41394     maxSize: new OpenLayers.Size(1200, 660),
41395
41396     /** 
41397      * Constructor: OpenLayers.Popup.FramedCloud
41398      * 
41399      * Parameters:
41400      * id - {String}
41401      * lonlat - {<OpenLayers.LonLat>}
41402      * contentSize - {<OpenLayers.Size>}
41403      * contentHTML - {String}
41404      * anchor - {Object} Object to which we'll anchor the popup. Must expose 
41405      *     a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) 
41406      *     (Note that this is generally an <OpenLayers.Icon>).
41407      * closeBox - {Boolean}
41408      * closeBoxCallback - {Function} Function to be called on closeBox click.
41409      */
41410     initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox, 
41411                         closeBoxCallback) {
41412
41413         this.imageSrc = OpenLayers.Util.getImageLocation('cloud-popup-relative.png');
41414         OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);
41415         this.contentDiv.className = this.contentDisplayClass;
41416     },
41417
41418     CLASS_NAME: "OpenLayers.Popup.FramedCloud"
41419 });
41420 /* ======================================================================
41421     OpenLayers/Rule.js
41422    ====================================================================== */
41423
41424 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
41425  * full list of contributors). Published under the 2-clause BSD license.
41426  * See license.txt in the OpenLayers distribution or repository for the
41427  * full text of the license. */
41428
41429
41430 /**
41431  * @requires OpenLayers/BaseTypes/Class.js
41432  * @requires OpenLayers/Util.js
41433  * @requires OpenLayers/Style.js
41434  */
41435
41436 /**
41437  * Class: OpenLayers.Rule
41438  * This class represents an SLD Rule, as being used for rule-based SLD styling.
41439  */
41440 OpenLayers.Rule = OpenLayers.Class({
41441     
41442     /**
41443      * Property: id
41444      * {String} A unique id for this session.
41445      */
41446     id: null,
41447     
41448     /**
41449      * APIProperty: name
41450      * {String} name of this rule
41451      */
41452     name: null,
41453     
41454     /**
41455      * Property: title
41456      * {String} Title of this rule (set if included in SLD)
41457      */
41458     title: null,
41459     
41460     /**
41461      * Property: description
41462      * {String} Description of this rule (set if abstract is included in SLD)
41463      */
41464     description: null,
41465
41466     /**
41467      * Property: context
41468      * {Object} An optional object with properties that the rule should be
41469      * evaluated against. If no context is specified, feature.attributes will
41470      * be used.
41471      */
41472     context: null,
41473     
41474     /**
41475      * Property: filter
41476      * {<OpenLayers.Filter>} Optional filter for the rule.
41477      */
41478     filter: null,
41479
41480     /**
41481      * Property: elseFilter
41482      * {Boolean} Determines whether this rule is only to be applied only if
41483      * no other rules match (ElseFilter according to the SLD specification). 
41484      * Default is false.  For instances of OpenLayers.Rule, if elseFilter is
41485      * false, the rule will always apply.  For subclasses, the else property is 
41486      * ignored.
41487      */
41488     elseFilter: false,
41489     
41490     /**
41491      * Property: symbolizer
41492      * {Object} Symbolizer or hash of symbolizers for this rule. If hash of
41493      * symbolizers, keys are one or more of ["Point", "Line", "Polygon"]. The
41494      * latter if useful if it is required to style e.g. vertices of a line
41495      * with a point symbolizer. Note, however, that this is not implemented
41496      * yet in OpenLayers, but it is the way how symbolizers are defined in
41497      * SLD.
41498      */
41499     symbolizer: null,
41500     
41501     /**
41502      * Property: symbolizers
41503      * {Array} Collection of symbolizers associated with this rule.  If 
41504      *     provided at construction, the symbolizers array has precedence
41505      *     over the deprecated symbolizer property.  Note that multiple 
41506      *     symbolizers are not currently supported by the vector renderers.
41507      *     Rules with multiple symbolizers are currently only useful for
41508      *     maintaining elements in an SLD document.
41509      */
41510     symbolizers: null,
41511     
41512     /**
41513      * APIProperty: minScaleDenominator
41514      * {Number} or {String} minimum scale at which to draw the feature.
41515      * In the case of a String, this can be a combination of text and
41516      * propertyNames in the form "literal ${propertyName}"
41517      */
41518     minScaleDenominator: null,
41519
41520     /**
41521      * APIProperty: maxScaleDenominator
41522      * {Number} or {String} maximum scale at which to draw the feature.
41523      * In the case of a String, this can be a combination of text and
41524      * propertyNames in the form "literal ${propertyName}"
41525      */
41526     maxScaleDenominator: null,
41527     
41528     /** 
41529      * Constructor: OpenLayers.Rule
41530      * Creates a Rule.
41531      *
41532      * Parameters:
41533      * options - {Object} An optional object with properties to set on the
41534      *           rule
41535      * 
41536      * Returns:
41537      * {<OpenLayers.Rule>}
41538      */
41539     initialize: function(options) {
41540         this.symbolizer = {};
41541         OpenLayers.Util.extend(this, options);
41542         if (this.symbolizers) {
41543             delete this.symbolizer;
41544         }
41545         this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
41546     },
41547
41548     /** 
41549      * APIMethod: destroy
41550      * nullify references to prevent circular references and memory leaks
41551      */
41552     destroy: function() {
41553         for (var i in this.symbolizer) {
41554             this.symbolizer[i] = null;
41555         }
41556         this.symbolizer = null;
41557         delete this.symbolizers;
41558     },
41559     
41560     /**
41561      * APIMethod: evaluate
41562      * evaluates this rule for a specific feature
41563      * 
41564      * Parameters:
41565      * feature - {<OpenLayers.Feature>} feature to apply the rule to.
41566      * 
41567      * Returns:
41568      * {Boolean} true if the rule applies, false if it does not.
41569      * This rule is the default rule and always returns true.
41570      */
41571     evaluate: function(feature) {
41572         var context = this.getContext(feature);
41573         var applies = true;
41574
41575         if (this.minScaleDenominator || this.maxScaleDenominator) {
41576             var scale = feature.layer.map.getScale();
41577         }
41578         
41579         // check if within minScale/maxScale bounds
41580         if (this.minScaleDenominator) {
41581             applies = scale >= OpenLayers.Style.createLiteral(
41582                     this.minScaleDenominator, context);
41583         }
41584         if (applies && this.maxScaleDenominator) {
41585             applies = scale < OpenLayers.Style.createLiteral(
41586                     this.maxScaleDenominator, context);
41587         }
41588         
41589         // check if optional filter applies
41590         if(applies && this.filter) {
41591             // feature id filters get the feature, others get the context
41592             if(this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") {
41593                 applies = this.filter.evaluate(feature);
41594             } else {
41595                 applies = this.filter.evaluate(context);
41596             }
41597         }
41598
41599         return applies;
41600     },
41601     
41602     /**
41603      * Method: getContext
41604      * Gets the context for evaluating this rule
41605      * 
41606      * Paramters:
41607      * feature - {<OpenLayers.Feature>} feature to take the context from if
41608      *           none is specified.
41609      */
41610     getContext: function(feature) {
41611         var context = this.context;
41612         if (!context) {
41613             context = feature.attributes || feature.data;
41614         }
41615         if (typeof this.context == "function") {
41616             context = this.context(feature);
41617         }
41618         return context;
41619     },
41620     
41621     /**
41622      * APIMethod: clone
41623      * Clones this rule.
41624      * 
41625      * Returns:
41626      * {<OpenLayers.Rule>} Clone of this rule.
41627      */
41628     clone: function() {
41629         var options = OpenLayers.Util.extend({}, this);
41630         if (this.symbolizers) {
41631             // clone symbolizers
41632             var len = this.symbolizers.length;
41633             options.symbolizers = new Array(len);
41634             for (var i=0; i<len; ++i) {
41635                 options.symbolizers[i] = this.symbolizers[i].clone();
41636             }
41637         } else {
41638             // clone symbolizer
41639             options.symbolizer = {};
41640             var value, type;
41641             for(var key in this.symbolizer) {
41642                 value = this.symbolizer[key];
41643                 type = typeof value;
41644                 if(type === "object") {
41645                     options.symbolizer[key] = OpenLayers.Util.extend({}, value);
41646                 } else if(type === "string") {
41647                     options.symbolizer[key] = value;
41648                 }
41649             }
41650         }
41651         // clone filter
41652         options.filter = this.filter && this.filter.clone();
41653         // clone context
41654         options.context = this.context && OpenLayers.Util.extend({}, this.context);
41655         return new OpenLayers.Rule(options);
41656     },
41657         
41658     CLASS_NAME: "OpenLayers.Rule"
41659 });
41660 /* ======================================================================
41661     OpenLayers/Handler/Pinch.js
41662    ====================================================================== */
41663
41664 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
41665  * full list of contributors). Published under the 2-clause BSD license.
41666  * See license.txt in the OpenLayers distribution or repository for the
41667  * full text of the license. */
41668
41669 /**
41670  * @requires OpenLayers/Handler.js
41671  */
41672
41673 /**
41674  * Class: OpenLayers.Handler.Pinch
41675  * The pinch handler is used to deal with sequences of browser events related
41676  *     to pinch gestures. The handler is used by controls that want to know
41677  *     when a pinch sequence begins, when a pinch is happening, and when it has
41678  *     finished.
41679  *
41680  * Controls that use the pinch handler typically construct it with callbacks
41681  *     for 'start', 'move', and 'done'.  Callbacks for these keys are
41682  *     called when the pinch begins, with each change, and when the pinch is
41683  *     done.
41684  *
41685  * Create a new pinch handler with the <OpenLayers.Handler.Pinch> constructor.
41686  *
41687  * Inherits from:
41688  *  - <OpenLayers.Handler>
41689  */
41690 OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, {
41691
41692     /**
41693      * Property: started
41694      * {Boolean} When a touchstart event is received, we want to record it,
41695      *     but not set 'pinching' until the touchmove get started after
41696      *     starting.
41697      */
41698     started: false,
41699
41700     /**
41701      * Property: stopDown
41702      * {Boolean} Stop propagation of touchstart events from getting to
41703      *     listeners on the same element. Default is false.
41704      */
41705     stopDown: false,
41706
41707     /**
41708      * Property: pinching
41709      * {Boolean}
41710      */
41711     pinching: false,
41712
41713     /**
41714      * Property: last
41715      * {Object} Object that store informations related to pinch last touch.
41716      */
41717     last: null,
41718
41719     /**
41720      * Property: start
41721      * {Object} Object that store informations related to pinch touchstart.
41722      */
41723     start: null,
41724
41725     /**
41726      * Constructor: OpenLayers.Handler.Pinch
41727      * Returns OpenLayers.Handler.Pinch
41728      *
41729      * Parameters:
41730      * control - {<OpenLayers.Control>} The control that is making use of
41731      *     this handler.  If a handler is being used without a control, the
41732      *     handlers setMap method must be overridden to deal properly with
41733      *     the map.
41734      * callbacks - {Object} An object containing functions to be called when
41735      *     the pinch operation start, change, or is finished. The callbacks
41736      *     should expect to receive an object argument, which contains
41737      *     information about scale, distance, and position of touch points.
41738      * options - {Object}
41739      */
41740
41741     /**
41742      * Method: touchstart
41743      * Handle touchstart events
41744      *
41745      * Parameters:
41746      * evt - {Event}
41747      *
41748      * Returns:
41749      * {Boolean} Let the event propagate.
41750      */
41751     touchstart: function(evt) {
41752         var propagate = true;
41753         this.pinching = false;
41754         if (OpenLayers.Event.isMultiTouch(evt)) {
41755             this.started = true;
41756             this.last = this.start = {
41757                 distance: this.getDistance(evt.touches),
41758                 delta: 0,
41759                 scale: 1
41760             };
41761             this.callback("start", [evt, this.start]);
41762             propagate = !this.stopDown;
41763         } else if (this.started) {
41764             // Some webkit versions send fake single-touch events during
41765             // multitouch, which cause the drag handler to trigger
41766             return false;
41767         } else {
41768             this.started = false;
41769             this.start = null;
41770             this.last = null;
41771         }
41772         // prevent document dragging
41773         OpenLayers.Event.preventDefault(evt);
41774         return propagate;
41775     },
41776
41777     /**
41778      * Method: touchmove
41779      * Handle touchmove events
41780      *
41781      * Parameters:
41782      * evt - {Event}
41783      *
41784      * Returns:
41785      * {Boolean} Let the event propagate.
41786      */
41787     touchmove: function(evt) {
41788         if (this.started && OpenLayers.Event.isMultiTouch(evt)) {
41789             this.pinching = true;
41790             var current = this.getPinchData(evt);
41791             this.callback("move", [evt, current]);
41792             this.last = current;
41793             // prevent document dragging
41794             OpenLayers.Event.stop(evt);
41795         } else if (this.started) {
41796             // Some webkit versions send fake single-touch events during
41797             // multitouch, which cause the drag handler to trigger
41798             return false;
41799         }
41800         return true;
41801     },
41802
41803     /**
41804      * Method: touchend
41805      * Handle touchend events
41806      *
41807      * Parameters:
41808      * evt - {Event}
41809      *
41810      * Returns:
41811      * {Boolean} Let the event propagate.
41812      */
41813     touchend: function(evt) {
41814         if (this.started && !OpenLayers.Event.isMultiTouch(evt)) {
41815             this.started = false;
41816             this.pinching = false;
41817             this.callback("done", [evt, this.start, this.last]);
41818             this.start = null;
41819             this.last = null;
41820             return false;
41821         }
41822         return true;
41823     },
41824
41825     /**
41826      * Method: activate
41827      * Activate the handler.
41828      *
41829      * Returns:
41830      * {Boolean} The handler was successfully activated.
41831      */
41832     activate: function() {
41833         var activated = false;
41834         if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
41835             this.pinching = false;
41836             activated = true;
41837         }
41838         return activated;
41839     },
41840
41841     /**
41842      * Method: deactivate
41843      * Deactivate the handler.
41844      *
41845      * Returns:
41846      * {Boolean} The handler was successfully deactivated.
41847      */
41848     deactivate: function() {
41849         var deactivated = false;
41850         if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
41851             this.started = false;
41852             this.pinching = false;
41853             this.start = null;
41854             this.last = null;
41855             deactivated = true;
41856         }
41857         return deactivated;
41858     },
41859
41860     /**
41861      * Method: getDistance
41862      * Get the distance in pixels between two touches.
41863      *
41864      * Parameters:
41865      * touches - {Array(Object)}
41866      *
41867      * Returns:
41868      * {Number} The distance in pixels.
41869      */
41870     getDistance: function(touches) {
41871         var t0 = touches[0];
41872         var t1 = touches[1];
41873         return Math.sqrt(
41874             Math.pow(t0.olClientX - t1.olClientX, 2) +
41875             Math.pow(t0.olClientY - t1.olClientY, 2)
41876         );
41877     },
41878
41879
41880     /**
41881      * Method: getPinchData
41882      * Get informations about the pinch event.
41883      *
41884      * Parameters:
41885      * evt - {Event}
41886      *
41887      * Returns:
41888      * {Object} Object that contains data about the current pinch.
41889      */
41890     getPinchData: function(evt) {
41891         var distance = this.getDistance(evt.touches);
41892         var scale = distance / this.start.distance;
41893         return {
41894             distance: distance,
41895             delta: this.last.distance - distance,
41896             scale: scale
41897         };
41898     },
41899
41900     CLASS_NAME: "OpenLayers.Handler.Pinch"
41901 });
41902
41903 /* ======================================================================
41904     OpenLayers/Lang/en.js
41905    ====================================================================== */
41906
41907 /**
41908  * @requires OpenLayers/Lang.js
41909  */
41910
41911 /**
41912  * Namespace: OpenLayers.Lang["en"]
41913  * Dictionary for English.  Keys for entries are used in calls to
41914  *     <OpenLayers.Lang.translate>.  Entry bodies are normal strings or
41915  *     strings formatted for use with <OpenLayers.String.format> calls.
41916  */
41917 OpenLayers.Lang.en = {
41918
41919     'unhandledRequest': "Unhandled request return ${statusText}",
41920
41921     'Permalink': "Permalink",
41922
41923     'Overlays': "Overlays",
41924
41925     'Base Layer': "Base Layer",
41926
41927     'noFID': "Can't update a feature for which there is no FID.",
41928
41929     'browserNotSupported':
41930         "Your browser does not support vector rendering. Currently supported renderers are:\n${renderers}",
41931
41932     // console message
41933     'minZoomLevelError':
41934         "The minZoomLevel property is only intended for use " +
41935         "with the FixedZoomLevels-descendent layers. That this " +
41936         "wfs layer checks for minZoomLevel is a relic of the" +
41937         "past. We cannot, however, remove it without possibly " +
41938         "breaking OL based applications that may depend on it." +
41939         " Therefore we are deprecating it -- the minZoomLevel " +
41940         "check below will be removed at 3.0. Please instead " +
41941         "use min/max resolution setting as described here: " +
41942         "http://trac.openlayers.org/wiki/SettingZoomLevels",
41943
41944     'commitSuccess': "WFS Transaction: SUCCESS ${response}",
41945
41946     'commitFailed': "WFS Transaction: FAILED ${response}",
41947
41948     'googleWarning':
41949         "The Google Layer was unable to load correctly.<br><br>" +
41950         "To get rid of this message, select a new BaseLayer " +
41951         "in the layer switcher in the upper-right corner.<br><br>" +
41952         "Most likely, this is because the Google Maps library " +
41953         "script was either not included, or does not contain the " +
41954         "correct API key for your site.<br><br>" +
41955         "Developers: For help getting this working correctly, " +
41956         "<a href='http://trac.openlayers.org/wiki/Google' " +
41957         "target='_blank'>click here</a>",
41958
41959     'getLayerWarning':
41960         "The ${layerType} Layer was unable to load correctly.<br><br>" +
41961         "To get rid of this message, select a new BaseLayer " +
41962         "in the layer switcher in the upper-right corner.<br><br>" +
41963         "Most likely, this is because the ${layerLib} library " +
41964         "script was not correctly included.<br><br>" +
41965         "Developers: For help getting this working correctly, " +
41966         "<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
41967         "target='_blank'>click here</a>",
41968
41969     'Scale = 1 : ${scaleDenom}': "Scale = 1 : ${scaleDenom}",
41970     
41971     //labels for the graticule control
41972     'W': 'W',
41973     'E': 'E',
41974     'N': 'N',
41975     'S': 'S',
41976     'Graticule': 'Graticule',
41977
41978     // console message
41979     'reprojectDeprecated':
41980         "You are using the 'reproject' option " +
41981         "on the ${layerName} layer. This option is deprecated: " +
41982         "its use was designed to support displaying data over commercial " + 
41983         "basemaps, but that functionality should now be achieved by using " +
41984         "Spherical Mercator support. More information is available from " +
41985         "http://trac.openlayers.org/wiki/SphericalMercator.",
41986
41987     // console message
41988     'methodDeprecated':
41989         "This method has been deprecated and will be removed in 3.0. " +
41990         "Please use ${newMethod} instead.",
41991
41992     // **** end ****
41993     'end': ''
41994     
41995 };
41996 /* ======================================================================
41997     OpenLayers/Control/PinchZoom.js
41998    ====================================================================== */
41999
42000 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
42001  * full list of contributors). Published under the 2-clause BSD license.
42002  * See license.txt in the OpenLayers distribution or repository for the
42003  * full text of the license. */
42004
42005 /**
42006  * @requires OpenLayers/Handler/Pinch.js
42007  */
42008
42009 /**
42010  * Class: OpenLayers.Control.PinchZoom
42011  *
42012  * Inherits:
42013  *  - <OpenLayers.Control>
42014  */
42015 OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, {
42016
42017     /** 
42018      * Property: type
42019      * {OpenLayers.Control.TYPES}
42020      */
42021     type: OpenLayers.Control.TYPE_TOOL,
42022
42023     /**
42024      * Property: pinchOrigin
42025      * {Object} Cached object representing the pinch start (in pixels).
42026      */
42027     pinchOrigin: null,    
42028     
42029     /**
42030      * Property: currentCenter
42031      * {Object} Cached object representing the latest pinch center (in pixels).
42032      */
42033     currentCenter: null,    
42034
42035     /**
42036      * APIProperty: autoActivate
42037      * {Boolean} Activate the control when it is added to a map.  Default is
42038      *     true.
42039      */
42040     autoActivate: true,
42041
42042     /**
42043      * APIProperty: preserveCenter
42044      * {Boolean} Set this to true if you don't want the map center to change
42045      *     while pinching. For example you may want to set preserveCenter to
42046      *     true when the user location is being watched and you want to preserve
42047      *     the user location at the center of the map even if he zooms in or
42048      *     out using pinch. This property's value can be changed any time on an
42049      *     existing instance. Default is false.
42050      */
42051     preserveCenter: false,
42052     
42053     /**
42054      * APIProperty: handlerOptions
42055      * {Object} Used to set non-default properties on the pinch handler
42056      */
42057
42058     /**
42059      * Constructor: OpenLayers.Control.PinchZoom
42060      * Create a control for zooming with pinch gestures.  This works on devices
42061      *     with multi-touch support.
42062      *
42063      * Parameters:
42064      * options - {Object} An optional object whose properties will be set on
42065      *                    the control
42066      */
42067     initialize: function(options) {
42068         OpenLayers.Control.prototype.initialize.apply(this, arguments);
42069         this.handler = new OpenLayers.Handler.Pinch(this, {
42070             start: this.pinchStart,
42071             move: this.pinchMove,
42072             done: this.pinchDone
42073         }, this.handlerOptions);
42074     },
42075     
42076     /**
42077      * Method: pinchStart
42078      *
42079      * Parameters:
42080      * evt - {Event}
42081      * pinchData - {Object} pinch data object related to the current touchmove
42082      *     of the pinch gesture. This give us the current scale of the pinch.
42083      */
42084     pinchStart: function(evt, pinchData) {
42085         var xy = (this.preserveCenter) ?
42086             this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;
42087         this.pinchOrigin = xy;
42088         this.currentCenter = xy;
42089     },
42090     
42091     /**
42092      * Method: pinchMove
42093      *
42094      * Parameters:
42095      * evt - {Event}
42096      * pinchData - {Object} pinch data object related to the current touchmove
42097      *     of the pinch gesture. This give us the current scale of the pinch.
42098      */
42099     pinchMove: function(evt, pinchData) {
42100         var scale = pinchData.scale;
42101         var containerOrigin = this.map.layerContainerOriginPx;
42102         var pinchOrigin = this.pinchOrigin;
42103         var current = (this.preserveCenter) ?
42104             this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;
42105
42106         var dx = Math.round((containerOrigin.x + current.x - pinchOrigin.x) + (scale - 1) * (containerOrigin.x - pinchOrigin.x));
42107         var dy = Math.round((containerOrigin.y + current.y - pinchOrigin.y) + (scale - 1) * (containerOrigin.y - pinchOrigin.y));
42108
42109         this.map.applyTransform(dx, dy, scale);
42110         this.currentCenter = current;
42111     },
42112
42113     /**
42114      * Method: pinchDone
42115      *
42116      * Parameters:
42117      * evt - {Event}
42118      * start - {Object} pinch data object related to the touchstart event that
42119      *     started the pinch gesture.
42120      * last - {Object} pinch data object related to the last touchmove event
42121      *     of the pinch gesture. This give us the final scale of the pinch.
42122      */
42123     pinchDone: function(evt, start, last) {
42124         this.map.applyTransform();
42125         var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true);
42126         if (zoom !== this.map.getZoom() || !this.currentCenter.equals(this.pinchOrigin)) {
42127             var resolution = this.map.getResolutionForZoom(zoom);
42128
42129             var location = this.map.getLonLatFromPixel(this.pinchOrigin);
42130             var zoomPixel = this.currentCenter;        
42131             var size = this.map.getSize();
42132
42133             location.lon += resolution * ((size.w / 2) - zoomPixel.x);
42134             location.lat -= resolution * ((size.h / 2) - zoomPixel.y);
42135
42136             // Force a reflow before calling setCenter. This is to work
42137             // around an issue occuring in iOS.
42138             //
42139             // See https://github.com/openlayers/openlayers/pull/351.
42140             //
42141             // Without a reflow setting the layer container div's top left
42142             // style properties to "0px" - as done in Map.moveTo when zoom
42143             // is changed - won't actually correctly reposition the layer
42144             // container div.
42145             //
42146             // Also, we need to use a statement that the Google Closure
42147             // compiler won't optimize away.
42148             this.map.div.clientWidth = this.map.div.clientWidth;
42149
42150             this.map.setCenter(location, zoom);
42151         }
42152     },
42153
42154     CLASS_NAME: "OpenLayers.Control.PinchZoom"
42155
42156 });
42157 /* ======================================================================
42158     OpenLayers/Control/TouchNavigation.js
42159    ====================================================================== */
42160
42161 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
42162  * full list of contributors). Published under the 2-clause BSD license.
42163  * See license.txt in the OpenLayers distribution or repository for the
42164  * full text of the license. */
42165
42166 /**
42167  * @requires OpenLayers/Control/DragPan.js
42168  * @requires OpenLayers/Control/PinchZoom.js
42169  * @requires OpenLayers/Handler/Click.js
42170  */
42171
42172 /**
42173  * Class: OpenLayers.Control.TouchNavigation
42174  * The navigation control handles map browsing with touch events (dragging,
42175  *     double-tapping, tap with two fingers, and pinch zoom).  Create a new 
42176  *     control with the <OpenLayers.Control.TouchNavigation> constructor.
42177  *
42178  * If you’re only targeting touch enabled devices with your mapping application,
42179  *     you can create a map with only a TouchNavigation control. The 
42180  *     <OpenLayers.Control.Navigation> control is mobile ready by default, but 
42181  *     you can generate a smaller build of the library by only including this
42182  *     touch navigation control if you aren't concerned about mouse interaction.
42183  *
42184  * Inherits:
42185  *  - <OpenLayers.Control>
42186  */
42187 OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, {
42188
42189     /**
42190      * Property: dragPan
42191      * {<OpenLayers.Control.DragPan>}
42192      */
42193     dragPan: null,
42194
42195     /**
42196      * APIProperty: dragPanOptions
42197      * {Object} Options passed to the DragPan control.
42198      */
42199     dragPanOptions: null,
42200
42201     /**
42202      * Property: pinchZoom
42203      * {<OpenLayers.Control.PinchZoom>}
42204      */
42205     pinchZoom: null,
42206
42207     /**
42208      * APIProperty: pinchZoomOptions
42209      * {Object} Options passed to the PinchZoom control.
42210      */
42211     pinchZoomOptions: null,
42212
42213     /**
42214      * APIProperty: clickHandlerOptions
42215      * {Object} Options passed to the Click handler.
42216      */
42217     clickHandlerOptions: null,
42218
42219     /**
42220      * APIProperty: documentDrag
42221      * {Boolean} Allow panning of the map by dragging outside map viewport.
42222      *     Default is false.
42223      */
42224     documentDrag: false,
42225
42226     /**
42227      * APIProperty: autoActivate
42228      * {Boolean} Activate the control when it is added to a map.  Default is
42229      *     true.
42230      */
42231     autoActivate: true,
42232
42233     /**
42234      * Constructor: OpenLayers.Control.TouchNavigation
42235      * Create a new navigation control
42236      *
42237      * Parameters:
42238      * options - {Object} An optional object whose properties will be set on
42239      *                    the control
42240      */
42241     initialize: function(options) {
42242         this.handlers = {};
42243         OpenLayers.Control.prototype.initialize.apply(this, arguments);
42244     },
42245
42246     /**
42247      * Method: destroy
42248      * The destroy method is used to perform any clean up before the control
42249      * is dereferenced.  Typically this is where event listeners are removed
42250      * to prevent memory leaks.
42251      */
42252     destroy: function() {
42253         this.deactivate();
42254         if(this.dragPan) {
42255             this.dragPan.destroy();
42256         }
42257         this.dragPan = null;
42258         if (this.pinchZoom) {
42259             this.pinchZoom.destroy();
42260             delete this.pinchZoom;
42261         }
42262         OpenLayers.Control.prototype.destroy.apply(this,arguments);
42263     },
42264
42265     /**
42266      * Method: activate
42267      */
42268     activate: function() {
42269         if(OpenLayers.Control.prototype.activate.apply(this,arguments)) {
42270             this.dragPan.activate();
42271             this.handlers.click.activate();
42272             this.pinchZoom.activate();
42273             return true;
42274         }
42275         return false;
42276     },
42277
42278     /**
42279      * Method: deactivate
42280      */
42281     deactivate: function() {
42282         if(OpenLayers.Control.prototype.deactivate.apply(this,arguments)) {
42283             this.dragPan.deactivate();
42284             this.handlers.click.deactivate();
42285             this.pinchZoom.deactivate();
42286             return true;
42287         }
42288         return false;
42289     },
42290     
42291     /**
42292      * Method: draw
42293      */
42294     draw: function() {
42295         var clickCallbacks = {
42296             click: this.defaultClick,
42297             dblclick: this.defaultDblClick
42298         };
42299         var clickOptions = OpenLayers.Util.extend({
42300             "double": true,
42301             stopDouble: true,
42302             pixelTolerance: 2
42303         }, this.clickHandlerOptions);
42304         this.handlers.click = new OpenLayers.Handler.Click(
42305             this, clickCallbacks, clickOptions
42306         );
42307         this.dragPan = new OpenLayers.Control.DragPan(
42308             OpenLayers.Util.extend({
42309                 map: this.map,
42310                 documentDrag: this.documentDrag
42311             }, this.dragPanOptions)
42312         );
42313         this.dragPan.draw();
42314         this.pinchZoom = new OpenLayers.Control.PinchZoom(
42315             OpenLayers.Util.extend({map: this.map}, this.pinchZoomOptions)
42316         );
42317     },
42318
42319     /**
42320      * Method: defaultClick
42321      *
42322      * Parameters:
42323      * evt - {Event}
42324      */
42325     defaultClick: function (evt) {
42326         if(evt.lastTouches && evt.lastTouches.length == 2) {
42327             this.map.zoomOut();
42328         }
42329     },
42330
42331     /**
42332      * Method: defaultDblClick
42333      *
42334      * Parameters:
42335      * evt - {Event}
42336      */
42337     defaultDblClick: function (evt) {
42338         this.map.zoomTo(this.map.zoom + 1, evt.xy);
42339     },
42340
42341     CLASS_NAME: "OpenLayers.Control.TouchNavigation"
42342 });
42343 /* ======================================================================
42344     OpenLayers/Renderer/VML.js
42345    ====================================================================== */
42346
42347 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
42348  * full list of contributors). Published under the 2-clause BSD license.
42349  * See license.txt in the OpenLayers distribution or repository for the
42350  * full text of the license. */
42351
42352 /**
42353  * @requires OpenLayers/Renderer/Elements.js
42354  */
42355
42356 /**
42357  * Class: OpenLayers.Renderer.VML
42358  * Render vector features in browsers with VML capability.  Construct a new
42359  * VML renderer with the <OpenLayers.Renderer.VML> constructor.
42360  * 
42361  * Note that for all calculations in this class, we use (num | 0) to truncate a 
42362  * float value to an integer. This is done because it seems that VML doesn't 
42363  * support float values.
42364  *
42365  * Inherits from:
42366  *  - <OpenLayers.Renderer.Elements>
42367  */
42368 OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
42369
42370     /**
42371      * Property: xmlns
42372      * {String} XML Namespace URN
42373      */
42374     xmlns: "urn:schemas-microsoft-com:vml",
42375     
42376     /**
42377      * Property: symbolCache
42378      * {DOMElement} node holding symbols. This hash is keyed by symbol name,
42379      *     and each value is a hash with a "path" and an "extent" property.
42380      */
42381     symbolCache: {},
42382
42383     /**
42384      * Property: offset
42385      * {Object} Hash with "x" and "y" properties
42386      */
42387     offset: null,
42388     
42389     /**
42390      * Constructor: OpenLayers.Renderer.VML
42391      * Create a new VML renderer.
42392      *
42393      * Parameters:
42394      * containerID - {String} The id for the element that contains the renderer
42395      */
42396     initialize: function(containerID) {
42397         if (!this.supported()) { 
42398             return; 
42399         }
42400         if (!document.namespaces.olv) {
42401             document.namespaces.add("olv", this.xmlns);
42402             var style = document.createStyleSheet();
42403             var shapes = ['shape','rect', 'oval', 'fill', 'stroke', 'imagedata', 'group','textbox']; 
42404             for (var i = 0, len = shapes.length; i < len; i++) {
42405
42406                 style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " +
42407                               "position: absolute; display: inline-block;");
42408             }                  
42409         }
42410         
42411         OpenLayers.Renderer.Elements.prototype.initialize.apply(this, 
42412                                                                 arguments);
42413     },
42414
42415     /**
42416      * APIMethod: supported
42417      * Determine whether a browser supports this renderer.
42418      *
42419      * Returns:
42420      * {Boolean} The browser supports the VML renderer
42421      */
42422     supported: function() {
42423         return !!(document.namespaces);
42424     },    
42425
42426     /**
42427      * Method: setExtent
42428      * Set the renderer's extent
42429      *
42430      * Parameters:
42431      * extent - {<OpenLayers.Bounds>}
42432      * resolutionChanged - {Boolean}
42433      * 
42434      * Returns:
42435      * {Boolean} true to notify the layer that the new extent does not exceed
42436      *     the coordinate range, and the features will not need to be redrawn.
42437      */
42438     setExtent: function(extent, resolutionChanged) {
42439         var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);
42440         var resolution = this.getResolution();
42441     
42442         var left = (extent.left/resolution) | 0;
42443         var top = (extent.top/resolution - this.size.h) | 0;
42444         if (resolutionChanged || !this.offset) {
42445             this.offset = {x: left, y: top};
42446             left = 0;
42447             top = 0;
42448         } else {
42449             left = left - this.offset.x;
42450             top = top - this.offset.y;
42451         }
42452
42453         
42454         var org = (left - this.xOffset) + " " + top;
42455         this.root.coordorigin = org;
42456         var roots = [this.root, this.vectorRoot, this.textRoot];
42457         var root;
42458         for(var i=0, len=roots.length; i<len; ++i) {
42459             root = roots[i];
42460
42461             var size = this.size.w + " " + this.size.h;
42462             root.coordsize = size;
42463             
42464         }
42465         // flip the VML display Y axis upside down so it 
42466         // matches the display Y axis of the map
42467         this.root.style.flip = "y";
42468         
42469         return coordSysUnchanged;
42470     },
42471
42472
42473     /**
42474      * Method: setSize
42475      * Set the size of the drawing surface
42476      *
42477      * Parameters:
42478      * size - {<OpenLayers.Size>} the size of the drawing surface
42479      */
42480     setSize: function(size) {
42481         OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
42482         
42483         // setting width and height on all roots to avoid flicker which we
42484         // would get with 100% width and height on child roots
42485         var roots = [
42486             this.rendererRoot,
42487             this.root,
42488             this.vectorRoot,
42489             this.textRoot
42490         ];
42491         var w = this.size.w + "px";
42492         var h = this.size.h + "px";
42493         var root;
42494         for(var i=0, len=roots.length; i<len; ++i) {
42495             root = roots[i];
42496             root.style.width = w;
42497             root.style.height = h;
42498         }
42499     },
42500
42501     /**
42502      * Method: getNodeType
42503      * Get the node type for a geometry and style
42504      *
42505      * Parameters:
42506      * geometry - {<OpenLayers.Geometry>}
42507      * style - {Object}
42508      *
42509      * Returns:
42510      * {String} The corresponding node type for the specified geometry
42511      */
42512     getNodeType: function(geometry, style) {
42513         var nodeType = null;
42514         switch (geometry.CLASS_NAME) {
42515             case "OpenLayers.Geometry.Point":
42516                 if (style.externalGraphic) {
42517                     nodeType = "olv:rect";
42518                 } else if (this.isComplexSymbol(style.graphicName)) {
42519                     nodeType = "olv:shape";
42520                 } else {
42521                     nodeType = "olv:oval";
42522                 }
42523                 break;
42524             case "OpenLayers.Geometry.Rectangle":
42525                 nodeType = "olv:rect";
42526                 break;
42527             case "OpenLayers.Geometry.LineString":
42528             case "OpenLayers.Geometry.LinearRing":
42529             case "OpenLayers.Geometry.Polygon":
42530             case "OpenLayers.Geometry.Curve":
42531                 nodeType = "olv:shape";
42532                 break;
42533             default:
42534                 break;
42535         }
42536         return nodeType;
42537     },
42538
42539     /**
42540      * Method: setStyle
42541      * Use to set all the style attributes to a VML node.
42542      *
42543      * Parameters:
42544      * node - {DOMElement} An VML element to decorate
42545      * style - {Object}
42546      * options - {Object} Currently supported options include 
42547      *                              'isFilled' {Boolean} and
42548      *                              'isStroked' {Boolean}
42549      * geometry - {<OpenLayers.Geometry>}
42550      */
42551     setStyle: function(node, style, options, geometry) {
42552         style = style  || node._style;
42553         options = options || node._options;
42554         var fillColor = style.fillColor;
42555
42556         var title = style.title || style.graphicTitle;
42557         if (title) {
42558             node.title = title;
42559         } 
42560
42561         if (node._geometryClass === "OpenLayers.Geometry.Point") {
42562             if (style.externalGraphic) {
42563                 options.isFilled = true;
42564                 var width = style.graphicWidth || style.graphicHeight;
42565                 var height = style.graphicHeight || style.graphicWidth;
42566                 width = width ? width : style.pointRadius*2;
42567                 height = height ? height : style.pointRadius*2;
42568
42569                 var resolution = this.getResolution();
42570                 var xOffset = (style.graphicXOffset != undefined) ?
42571                     style.graphicXOffset : -(0.5 * width);
42572                 var yOffset = (style.graphicYOffset != undefined) ?
42573                     style.graphicYOffset : -(0.5 * height);
42574                 
42575                 node.style.left = ((((geometry.x - this.featureDx)/resolution - this.offset.x)+xOffset) | 0) + "px";
42576                 node.style.top = (((geometry.y/resolution - this.offset.y)-(yOffset+height)) | 0) + "px";
42577                 node.style.width = width + "px";
42578                 node.style.height = height + "px";
42579                 node.style.flip = "y";
42580                 
42581                 // modify fillColor and options for stroke styling below
42582                 fillColor = "none";
42583                 options.isStroked = false;
42584             } else if (this.isComplexSymbol(style.graphicName)) {
42585                 var cache = this.importSymbol(style.graphicName);
42586                 node.path = cache.path;
42587                 node.coordorigin = cache.left + "," + cache.bottom;
42588                 var size = cache.size;
42589                 node.coordsize = size + "," + size;        
42590                 this.drawCircle(node, geometry, style.pointRadius);
42591                 node.style.flip = "y";
42592             } else {
42593                 this.drawCircle(node, geometry, style.pointRadius);
42594             }
42595         }
42596
42597         // fill 
42598         if (options.isFilled) { 
42599             node.fillcolor = fillColor; 
42600         } else { 
42601             node.filled = "false"; 
42602         }
42603         var fills = node.getElementsByTagName("fill");
42604         var fill = (fills.length == 0) ? null : fills[0];
42605         if (!options.isFilled) {
42606             if (fill) {
42607                 node.removeChild(fill);
42608             }
42609         } else {
42610             if (!fill) {
42611                 fill = this.createNode('olv:fill', node.id + "_fill");
42612             }
42613             fill.opacity = style.fillOpacity;
42614
42615             if (node._geometryClass === "OpenLayers.Geometry.Point" &&
42616                     style.externalGraphic) {
42617
42618                 // override fillOpacity
42619                 if (style.graphicOpacity) {
42620                     fill.opacity = style.graphicOpacity;
42621                 }
42622                 
42623                 fill.src = style.externalGraphic;
42624                 fill.type = "frame";
42625                 
42626                 if (!(style.graphicWidth && style.graphicHeight)) {
42627                   fill.aspect = "atmost";
42628                 }                
42629             }
42630             if (fill.parentNode != node) {
42631                 node.appendChild(fill);
42632             }
42633         }
42634
42635         // additional rendering for rotated graphics or symbols
42636         var rotation = style.rotation;
42637         if ((rotation !== undefined || node._rotation !== undefined)) {
42638             node._rotation = rotation;
42639             if (style.externalGraphic) {
42640                 this.graphicRotate(node, xOffset, yOffset, style);
42641                 // make the fill fully transparent, because we now have
42642                 // the graphic as imagedata element. We cannot just remove
42643                 // the fill, because this is part of the hack described
42644                 // in graphicRotate
42645                 fill.opacity = 0;
42646             } else if(node._geometryClass === "OpenLayers.Geometry.Point") {
42647                 node.style.rotation = rotation || 0;
42648             }
42649         }
42650
42651         // stroke 
42652         var strokes = node.getElementsByTagName("stroke");
42653         var stroke = (strokes.length == 0) ? null : strokes[0];
42654         if (!options.isStroked) {
42655             node.stroked = false;
42656             if (stroke) {
42657                 stroke.on = false;
42658             }
42659         } else {
42660             if (!stroke) {
42661                 stroke = this.createNode('olv:stroke', node.id + "_stroke");
42662                 node.appendChild(stroke);
42663             }
42664             stroke.on = true;
42665             stroke.color = style.strokeColor; 
42666             stroke.weight = style.strokeWidth + "px"; 
42667             stroke.opacity = style.strokeOpacity;
42668             stroke.endcap = style.strokeLinecap == 'butt' ? 'flat' :
42669                 (style.strokeLinecap || 'round');
42670             if (style.strokeDashstyle) {
42671                 stroke.dashstyle = this.dashStyle(style);
42672             }
42673         }
42674         
42675         if (style.cursor != "inherit" && style.cursor != null) {
42676             node.style.cursor = style.cursor;
42677         }
42678         return node;
42679     },
42680
42681     /**
42682      * Method: graphicRotate
42683      * If a point is to be styled with externalGraphic and rotation, VML fills
42684      * cannot be used to display the graphic, because rotation of graphic
42685      * fills is not supported by the VML implementation of Internet Explorer.
42686      * This method creates a olv:imagedata element inside the VML node,
42687      * DXImageTransform.Matrix and BasicImage filters for rotation and
42688      * opacity, and a 3-step hack to remove rendering artefacts from the
42689      * graphic and preserve the ability of graphics to trigger events.
42690      * Finally, OpenLayers methods are used to determine the correct
42691      * insertion point of the rotated image, because DXImageTransform.Matrix
42692      * does the rotation without the ability to specify a rotation center
42693      * point.
42694      * 
42695      * Parameters:
42696      * node    - {DOMElement}
42697      * xOffset - {Number} rotation center relative to image, x coordinate
42698      * yOffset - {Number} rotation center relative to image, y coordinate
42699      * style   - {Object}
42700      */
42701     graphicRotate: function(node, xOffset, yOffset, style) {
42702         var style = style || node._style;
42703         var rotation = style.rotation || 0;
42704         
42705         var aspectRatio, size;
42706         if (!(style.graphicWidth && style.graphicHeight)) {
42707             // load the image to determine its size
42708             var img = new Image();
42709             img.onreadystatechange = OpenLayers.Function.bind(function() {
42710                 if(img.readyState == "complete" ||
42711                         img.readyState == "interactive") {
42712                     aspectRatio = img.width / img.height;
42713                     size = Math.max(style.pointRadius * 2, 
42714                         style.graphicWidth || 0,
42715                         style.graphicHeight || 0);
42716                     xOffset = xOffset * aspectRatio;
42717                     style.graphicWidth = size * aspectRatio;
42718                     style.graphicHeight = size;
42719                     this.graphicRotate(node, xOffset, yOffset, style);
42720                 }
42721             }, this);
42722             img.src = style.externalGraphic;
42723             
42724             // will be called again by the onreadystate handler
42725             return;
42726         } else {
42727             size = Math.max(style.graphicWidth, style.graphicHeight);
42728             aspectRatio = style.graphicWidth / style.graphicHeight;
42729         }
42730         
42731         var width = Math.round(style.graphicWidth || size * aspectRatio);
42732         var height = Math.round(style.graphicHeight || size);
42733         node.style.width = width + "px";
42734         node.style.height = height + "px";
42735         
42736         // Three steps are required to remove artefacts for images with
42737         // transparent backgrounds (resulting from using DXImageTransform
42738         // filters on svg objects), while preserving awareness for browser
42739         // events on images:
42740         // - Use the fill as usual (like for unrotated images) to handle
42741         //   events
42742         // - specify an imagedata element with the same src as the fill
42743         // - style the imagedata element with an AlphaImageLoader filter
42744         //   with empty src
42745         var image = document.getElementById(node.id + "_image");
42746         if (!image) {
42747             image = this.createNode("olv:imagedata", node.id + "_image");
42748             node.appendChild(image);
42749         }
42750         image.style.width = width + "px";
42751         image.style.height = height + "px";
42752         image.src = style.externalGraphic;
42753         image.style.filter =
42754             "progid:DXImageTransform.Microsoft.AlphaImageLoader(" + 
42755             "src='', sizingMethod='scale')";
42756
42757         var rot = rotation * Math.PI / 180;
42758         var sintheta = Math.sin(rot);
42759         var costheta = Math.cos(rot);
42760
42761         // do the rotation on the image
42762         var filter =
42763             "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta +
42764             ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta +
42765             ",SizingMethod='auto expand')\n";
42766
42767         // set the opacity (needed for the imagedata)
42768         var opacity = style.graphicOpacity || style.fillOpacity;
42769         if (opacity && opacity != 1) {
42770             filter += 
42771                 "progid:DXImageTransform.Microsoft.BasicImage(opacity=" + 
42772                 opacity+")\n";
42773         }
42774         node.style.filter = filter;
42775
42776         // do the rotation again on a box, so we know the insertion point
42777         var centerPoint = new OpenLayers.Geometry.Point(-xOffset, -yOffset);
42778         var imgBox = new OpenLayers.Bounds(0, 0, width, height).toGeometry();
42779         imgBox.rotate(style.rotation, centerPoint);
42780         var imgBounds = imgBox.getBounds();
42781
42782         node.style.left = Math.round(
42783             parseInt(node.style.left) + imgBounds.left) + "px";
42784         node.style.top = Math.round(
42785             parseInt(node.style.top) - imgBounds.bottom) + "px";
42786     },
42787
42788     /**
42789      * Method: postDraw
42790      * Does some node postprocessing to work around browser issues:
42791      * - Some versions of Internet Explorer seem to be unable to set fillcolor
42792      *   and strokecolor to "none" correctly before the fill node is appended
42793      *   to a visible vml node. This method takes care of that and sets
42794      *   fillcolor and strokecolor again if needed.
42795      * - In some cases, a node won't become visible after being drawn. Setting
42796      *   style.visibility to "visible" works around that.
42797      * 
42798      * Parameters:
42799      * node - {DOMElement}
42800      */
42801     postDraw: function(node) {
42802         node.style.visibility = "visible";
42803         var fillColor = node._style.fillColor;
42804         var strokeColor = node._style.strokeColor;
42805         if (fillColor == "none" &&
42806                 node.fillcolor != fillColor) {
42807             node.fillcolor = fillColor;
42808         }
42809         if (strokeColor == "none" &&
42810                 node.strokecolor != strokeColor) {
42811             node.strokecolor = strokeColor;
42812         }
42813     },
42814
42815
42816     /**
42817      * Method: setNodeDimension
42818      * Get the geometry's bounds, convert it to our vml coordinate system, 
42819      * then set the node's position, size, and local coordinate system.
42820      *   
42821      * Parameters:
42822      * node - {DOMElement}
42823      * geometry - {<OpenLayers.Geometry>}
42824      */
42825     setNodeDimension: function(node, geometry) {
42826
42827         var bbox = geometry.getBounds();
42828         if(bbox) {
42829             var resolution = this.getResolution();
42830         
42831             var scaledBox = 
42832                 new OpenLayers.Bounds(((bbox.left - this.featureDx)/resolution - this.offset.x) | 0,
42833                                       (bbox.bottom/resolution - this.offset.y) | 0,
42834                                       ((bbox.right - this.featureDx)/resolution - this.offset.x) | 0,
42835                                       (bbox.top/resolution - this.offset.y) | 0);
42836             
42837             // Set the internal coordinate system to draw the path
42838             node.style.left = scaledBox.left + "px";
42839             node.style.top = scaledBox.top + "px";
42840             node.style.width = scaledBox.getWidth() + "px";
42841             node.style.height = scaledBox.getHeight() + "px";
42842     
42843             node.coordorigin = scaledBox.left + " " + scaledBox.top;
42844             node.coordsize = scaledBox.getWidth()+ " " + scaledBox.getHeight();
42845         }
42846     },
42847     
42848     /** 
42849      * Method: dashStyle
42850      * 
42851      * Parameters:
42852      * style - {Object}
42853      * 
42854      * Returns:
42855      * {String} A VML compliant 'stroke-dasharray' value
42856      */
42857     dashStyle: function(style) {
42858         var dash = style.strokeDashstyle;
42859         switch (dash) {
42860             case 'solid':
42861             case 'dot':
42862             case 'dash':
42863             case 'dashdot':
42864             case 'longdash':
42865             case 'longdashdot':
42866                 return dash;
42867             default:
42868                 // very basic guessing of dash style patterns
42869                 var parts = dash.split(/[ ,]/);
42870                 if (parts.length == 2) {
42871                     if (1*parts[0] >= 2*parts[1]) {
42872                         return "longdash";
42873                     }
42874                     return (parts[0] == 1 || parts[1] == 1) ? "dot" : "dash";
42875                 } else if (parts.length == 4) {
42876                     return (1*parts[0] >= 2*parts[1]) ? "longdashdot" :
42877                         "dashdot";
42878                 }
42879                 return "solid";
42880         }
42881     },
42882
42883     /**
42884      * Method: createNode
42885      * Create a new node
42886      *
42887      * Parameters:
42888      * type - {String} Kind of node to draw
42889      * id - {String} Id for node
42890      *
42891      * Returns:
42892      * {DOMElement} A new node of the given type and id
42893      */
42894     createNode: function(type, id) {
42895         var node = document.createElement(type);
42896         if (id) {
42897             node.id = id;
42898         }
42899         
42900         // IE hack to make elements unselectable, to prevent 'blue flash'
42901         // while dragging vectors; #1410
42902         node.unselectable = 'on';
42903         node.onselectstart = OpenLayers.Function.False;
42904         
42905         return node;    
42906     },
42907     
42908     /**
42909      * Method: nodeTypeCompare
42910      * Determine whether a node is of a given type
42911      *
42912      * Parameters:
42913      * node - {DOMElement} An VML element
42914      * type - {String} Kind of node
42915      *
42916      * Returns:
42917      * {Boolean} Whether or not the specified node is of the specified type
42918      */
42919     nodeTypeCompare: function(node, type) {
42920
42921         //split type
42922         var subType = type;
42923         var splitIndex = subType.indexOf(":");
42924         if (splitIndex != -1) {
42925             subType = subType.substr(splitIndex+1);
42926         }
42927
42928         //split nodeName
42929         var nodeName = node.nodeName;
42930         splitIndex = nodeName.indexOf(":");
42931         if (splitIndex != -1) {
42932             nodeName = nodeName.substr(splitIndex+1);
42933         }
42934
42935         return (subType == nodeName);
42936     },
42937
42938     /**
42939      * Method: createRenderRoot
42940      * Create the renderer root
42941      *
42942      * Returns:
42943      * {DOMElement} The specific render engine's root element
42944      */
42945     createRenderRoot: function() {
42946         return this.nodeFactory(this.container.id + "_vmlRoot", "div");
42947     },
42948
42949     /**
42950      * Method: createRoot
42951      * Create the main root element
42952      * 
42953      * Parameters:
42954      * suffix - {String} suffix to append to the id
42955      *
42956      * Returns:
42957      * {DOMElement}
42958      */
42959     createRoot: function(suffix) {
42960         return this.nodeFactory(this.container.id + suffix, "olv:group");
42961     },
42962     
42963     /**************************************
42964      *                                    *
42965      *     GEOMETRY DRAWING FUNCTIONS     *
42966      *                                    *
42967      **************************************/
42968     
42969     /**
42970      * Method: drawPoint
42971      * Render a point
42972      * 
42973      * Parameters:
42974      * node - {DOMElement}
42975      * geometry - {<OpenLayers.Geometry>}
42976      * 
42977      * Returns:
42978      * {DOMElement} or false if the point could not be drawn
42979      */
42980     drawPoint: function(node, geometry) {
42981         return this.drawCircle(node, geometry, 1);
42982     },
42983
42984     /**
42985      * Method: drawCircle
42986      * Render a circle.
42987      * Size and Center a circle given geometry (x,y center) and radius
42988      * 
42989      * Parameters:
42990      * node - {DOMElement}
42991      * geometry - {<OpenLayers.Geometry>}
42992      * radius - {float}
42993      * 
42994      * Returns:
42995      * {DOMElement} or false if the circle could not ne drawn
42996      */
42997     drawCircle: function(node, geometry, radius) {
42998         if(!isNaN(geometry.x)&& !isNaN(geometry.y)) {
42999             var resolution = this.getResolution();
43000
43001             node.style.left = ((((geometry.x - this.featureDx) /resolution - this.offset.x) | 0) - radius) + "px";
43002             node.style.top = (((geometry.y /resolution - this.offset.y) | 0) - radius) + "px";
43003     
43004             var diameter = radius * 2;
43005             
43006             node.style.width = diameter + "px";
43007             node.style.height = diameter + "px";
43008             return node;
43009         }
43010         return false;
43011     },
43012
43013
43014     /**
43015      * Method: drawLineString
43016      * Render a linestring.
43017      * 
43018      * Parameters:
43019      * node - {DOMElement}
43020      * geometry - {<OpenLayers.Geometry>}
43021      * 
43022      * Returns:
43023      * {DOMElement}
43024      */
43025     drawLineString: function(node, geometry) {
43026         return this.drawLine(node, geometry, false);
43027     },
43028
43029     /**
43030      * Method: drawLinearRing
43031      * Render a linearring
43032      * 
43033      * Parameters:
43034      * node - {DOMElement}
43035      * geometry - {<OpenLayers.Geometry>}
43036      * 
43037      * Returns:
43038      * {DOMElement}
43039      */
43040     drawLinearRing: function(node, geometry) {
43041         return this.drawLine(node, geometry, true);
43042     },
43043
43044     /**
43045      * Method: DrawLine
43046      * Render a line.
43047      * 
43048      * Parameters:
43049      * node - {DOMElement}
43050      * geometry - {<OpenLayers.Geometry>}
43051      * closeLine - {Boolean} Close the line? (make it a ring?)
43052      * 
43053      * Returns:
43054      * {DOMElement}
43055      */
43056     drawLine: function(node, geometry, closeLine) {
43057
43058         this.setNodeDimension(node, geometry);
43059
43060         var resolution = this.getResolution();
43061         var numComponents = geometry.components.length;
43062         var parts = new Array(numComponents);
43063
43064         var comp, x, y;
43065         for (var i = 0; i < numComponents; i++) {
43066             comp = geometry.components[i];
43067             x = ((comp.x - this.featureDx)/resolution - this.offset.x) | 0;
43068             y = (comp.y/resolution - this.offset.y) | 0;
43069             parts[i] = " " + x + "," + y + " l ";
43070         }
43071         var end = (closeLine) ? " x e" : " e";
43072         node.path = "m" + parts.join("") + end;
43073         return node;
43074     },
43075
43076     /**
43077      * Method: drawPolygon
43078      * Render a polygon
43079      * 
43080      * Parameters:
43081      * node - {DOMElement}
43082      * geometry - {<OpenLayers.Geometry>}
43083      * 
43084      * Returns:
43085      * {DOMElement}
43086      */
43087     drawPolygon: function(node, geometry) {
43088         this.setNodeDimension(node, geometry);
43089
43090         var resolution = this.getResolution();
43091     
43092         var path = [];
43093         var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y;
43094         for (j=0, jj=geometry.components.length; j<jj; j++) {
43095             path.push("m");
43096             points = geometry.components[j].components;
43097             // we only close paths of interior rings with area
43098             area = (j === 0);
43099             first = null;
43100             second = null;
43101             for (i=0, ii=points.length; i<ii; i++) {
43102                 comp = points[i];
43103                 x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0;
43104                 y = (comp.y / resolution - this.offset.y) | 0;
43105                 pathComp = " " + x + "," + y;
43106                 path.push(pathComp);
43107                 if (i==0) {
43108                     path.push(" l");
43109                 }
43110                 if (!area) {
43111                     // IE improperly renders sub-paths that have no area.
43112                     // Instead of checking the area of every ring, we confirm
43113                     // the ring has at least three distinct points.  This does
43114                     // not catch all non-zero area cases, but it greatly improves
43115                     // interior ring digitizing and is a minor performance hit
43116                     // when rendering rings with many points.
43117                     if (!first) {
43118                         first = pathComp;
43119                     } else if (first != pathComp) {
43120                         if (!second) {
43121                             second = pathComp;
43122                         } else if (second != pathComp) {
43123                             // stop looking
43124                             area = true;
43125                         }
43126                     }
43127                 }
43128             }
43129             path.push(area ? " x " : " ");
43130         }
43131         path.push("e");
43132         node.path = path.join("");
43133         return node;
43134     },
43135
43136     /**
43137      * Method: drawRectangle
43138      * Render a rectangle
43139      * 
43140      * Parameters:
43141      * node - {DOMElement}
43142      * geometry - {<OpenLayers.Geometry>}
43143      * 
43144      * Returns:
43145      * {DOMElement}
43146      */
43147     drawRectangle: function(node, geometry) {
43148         var resolution = this.getResolution();
43149     
43150         node.style.left = (((geometry.x - this.featureDx)/resolution - this.offset.x) | 0) + "px";
43151         node.style.top = ((geometry.y/resolution - this.offset.y) | 0) + "px";
43152         node.style.width = ((geometry.width/resolution) | 0) + "px";
43153         node.style.height = ((geometry.height/resolution) | 0) + "px";
43154         
43155         return node;
43156     },
43157     
43158     /**
43159      * Method: drawText
43160      * This method is only called by the renderer itself.
43161      * 
43162      * Parameters: 
43163      * featureId - {String}
43164      * style -
43165      * location - {<OpenLayers.Geometry.Point>}
43166      */
43167     drawText: function(featureId, style, location) {
43168         var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect");
43169         var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox");
43170         
43171         var resolution = this.getResolution();
43172         label.style.left = (((location.x - this.featureDx)/resolution - this.offset.x) | 0) + "px";
43173         label.style.top = ((location.y/resolution - this.offset.y) | 0) + "px";
43174         label.style.flip = "y";
43175
43176         textbox.innerText = style.label;
43177
43178         if (style.cursor != "inherit" && style.cursor != null) {
43179             textbox.style.cursor = style.cursor;
43180         }
43181         if (style.fontColor) {
43182             textbox.style.color = style.fontColor;
43183         }
43184         if (style.fontOpacity) {
43185             textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')';
43186         }
43187         if (style.fontFamily) {
43188             textbox.style.fontFamily = style.fontFamily;
43189         }
43190         if (style.fontSize) {
43191             textbox.style.fontSize = style.fontSize;
43192         }
43193         if (style.fontWeight) {
43194             textbox.style.fontWeight = style.fontWeight;
43195         }
43196         if (style.fontStyle) {
43197             textbox.style.fontStyle = style.fontStyle;
43198         }
43199         if(style.labelSelect === true) {
43200             label._featureId = featureId;
43201             textbox._featureId = featureId;
43202             textbox._geometry = location;
43203             textbox._geometryClass = location.CLASS_NAME;
43204         }
43205         textbox.style.whiteSpace = "nowrap";
43206         // fun with IE: IE7 in standards compliant mode does not display any
43207         // text with a left inset of 0. So we set this to 1px and subtract one
43208         // pixel later when we set label.style.left
43209         textbox.inset = "1px,0px,0px,0px";
43210
43211         if(!label.parentNode) {
43212             label.appendChild(textbox);
43213             this.textRoot.appendChild(label);
43214         }
43215
43216         var align = style.labelAlign || "cm";
43217         if (align.length == 1) {
43218             align += "m";
43219         }
43220         var xshift = textbox.clientWidth *
43221             (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0,1)]);
43222         var yshift = textbox.clientHeight *
43223             (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1,1)]);
43224         label.style.left = parseInt(label.style.left)-xshift-1+"px";
43225         label.style.top = parseInt(label.style.top)+yshift+"px";
43226         
43227     },
43228     
43229     /**
43230      * Method: moveRoot
43231      * moves this renderer's root to a different renderer.
43232      * 
43233      * Parameters:
43234      * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
43235      * root - {DOMElement} optional root node. To be used when this renderer
43236      *     holds roots from multiple layers to tell this method which one to
43237      *     detach
43238      * 
43239      * Returns:
43240      * {Boolean} true if successful, false otherwise
43241      */
43242     moveRoot: function(renderer) {
43243         var layer = this.map.getLayer(renderer.container.id);
43244         if(layer instanceof OpenLayers.Layer.Vector.RootContainer) {
43245             layer = this.map.getLayer(this.container.id);
43246         }
43247         layer && layer.renderer.clear();
43248         OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments);
43249         layer && layer.redraw();
43250     },
43251     
43252     /**
43253      * Method: importSymbol
43254      * add a new symbol definition from the rendererer's symbol hash
43255      * 
43256      * Parameters:
43257      * graphicName - {String} name of the symbol to import
43258      * 
43259      * Returns:
43260      * {Object} - hash of {DOMElement} "symbol" and {Number} "size"
43261      */      
43262     importSymbol: function (graphicName)  {
43263         var id = this.container.id + "-" + graphicName;
43264         
43265         // check if symbol already exists in the cache
43266         var cache = this.symbolCache[id];
43267         if (cache) {
43268             return cache;
43269         }
43270         
43271         var symbol = OpenLayers.Renderer.symbol[graphicName];
43272         if (!symbol) {
43273             throw new Error(graphicName + ' is not a valid symbol name');
43274         }
43275
43276         var symbolExtent = new OpenLayers.Bounds(
43277                                     Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
43278         
43279         var pathitems = ["m"];
43280         for (var i=0; i<symbol.length; i=i+2) {
43281             var x = symbol[i];
43282             var y = symbol[i+1];
43283             symbolExtent.left = Math.min(symbolExtent.left, x);
43284             symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
43285             symbolExtent.right = Math.max(symbolExtent.right, x);
43286             symbolExtent.top = Math.max(symbolExtent.top, y);
43287
43288             pathitems.push(x);
43289             pathitems.push(y);
43290             if (i == 0) {
43291                 pathitems.push("l");
43292             }
43293         }
43294         pathitems.push("x e");
43295         var path = pathitems.join(" ");
43296
43297         var diff = (symbolExtent.getWidth() - symbolExtent.getHeight()) / 2;
43298         if(diff > 0) {
43299             symbolExtent.bottom = symbolExtent.bottom - diff;
43300             symbolExtent.top = symbolExtent.top + diff;
43301         } else {
43302             symbolExtent.left = symbolExtent.left + diff;
43303             symbolExtent.right = symbolExtent.right - diff;
43304         }
43305         
43306         cache = {
43307             path: path,
43308             size: symbolExtent.getWidth(), // equals getHeight() now
43309             left: symbolExtent.left,
43310             bottom: symbolExtent.bottom
43311         };
43312         this.symbolCache[id] = cache;
43313         
43314         return cache;
43315     },
43316     
43317     CLASS_NAME: "OpenLayers.Renderer.VML"
43318 });
43319
43320 /**
43321  * Constant: OpenLayers.Renderer.VML.LABEL_SHIFT
43322  * {Object}
43323  */
43324 OpenLayers.Renderer.VML.LABEL_SHIFT = {
43325     "l": 0,
43326     "c": .5,
43327     "r": 1,
43328     "t": 0,
43329     "m": .5,
43330     "b": 1
43331 };
43332 /* ======================================================================
43333     OpenLayers/Protocol/WFS/v1_0_0.js
43334    ====================================================================== */
43335
43336 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
43337  * full list of contributors). Published under the 2-clause BSD license.
43338  * See license.txt in the OpenLayers distribution or repository for the
43339  * full text of the license. */
43340
43341 /**
43342  * @requires OpenLayers/Protocol/WFS/v1.js
43343  * @requires OpenLayers/Format/WFST/v1_0_0.js
43344  */
43345
43346 /**
43347  * Class: OpenLayers.Protocol.WFS.v1_0_0
43348  * A WFS v1.0.0 protocol for vector layers.  Create a new instance with the
43349  *     <OpenLayers.Protocol.WFS.v1_0_0> constructor.
43350  *
43351  * Inherits from:
43352  *  - <OpenLayers.Protocol.WFS.v1>
43353  */
43354 OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {
43355     
43356     /**
43357      * Property: version
43358      * {String} WFS version number.
43359      */
43360     version: "1.0.0",
43361     
43362     /**
43363      * Constructor: OpenLayers.Protocol.WFS.v1_0_0
43364      * A class for giving layers WFS v1.0.0 protocol.
43365      *
43366      * Parameters:
43367      * options - {Object} Optional object whose properties will be set on the
43368      *     instance.
43369      *
43370      * Valid options properties:
43371      * featureType - {String} Local (without prefix) feature typeName (required).
43372      * featureNS - {String} Feature namespace (optional).
43373      * featurePrefix - {String} Feature namespace alias (optional - only used
43374      *     if featureNS is provided).  Default is 'feature'.
43375      * geometryName - {String} Name of geometry attribute.  Default is 'the_geom'.
43376      */
43377    
43378     CLASS_NAME: "OpenLayers.Protocol.WFS.v1_0_0" 
43379 });