]> ToastFreeware Gitweb - philipp/winterrodeln/mediawiki_extensions/wrmap.git/blob - openlayers/OpenLayers.js
The line width is checked now.
[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/Renderer.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  * @requires OpenLayers/BaseTypes/Class.js
18334  */
18335
18336 /**
18337  * Class: OpenLayers.Renderer 
18338  * This is the base class for all renderers.
18339  *
18340  * This is based on a merger code written by Paul Spencer and Bertil Chapuis.
18341  * It is largely composed of virtual functions that are to be implemented
18342  * in technology-specific subclasses, but there is some generic code too.
18343  * 
18344  * The functions that *are* implemented here merely deal with the maintenance
18345  *  of the size and extent variables, as well as the cached 'resolution' 
18346  *  value. 
18347  * 
18348  * A note to the user that all subclasses should use getResolution() instead
18349  *  of directly accessing this.resolution in order to correctly use the 
18350  *  cacheing system.
18351  *
18352  */
18353 OpenLayers.Renderer = OpenLayers.Class({
18354
18355     /** 
18356      * Property: container
18357      * {DOMElement} 
18358      */
18359     container: null,
18360     
18361     /**
18362      * Property: root
18363      * {DOMElement}
18364      */
18365     root: null,
18366
18367     /** 
18368      * Property: extent
18369      * {<OpenLayers.Bounds>}
18370      */
18371     extent: null,
18372
18373     /**
18374      * Property: locked
18375      * {Boolean} If the renderer is currently in a state where many things
18376      *     are changing, the 'locked' property is set to true. This means 
18377      *     that renderers can expect at least one more drawFeature event to be
18378      *     called with the 'locked' property set to 'true': In some renderers,
18379      *     this might make sense to use as a 'only update local information'
18380      *     flag. 
18381      */  
18382     locked: false,
18383     
18384     /** 
18385      * Property: size
18386      * {<OpenLayers.Size>} 
18387      */
18388     size: null,
18389     
18390     /**
18391      * Property: resolution
18392      * {Float} cache of current map resolution
18393      */
18394     resolution: null,
18395     
18396     /**
18397      * Property: map  
18398      * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap()
18399      */
18400     map: null,
18401     
18402     /**
18403      * Property: featureDx
18404      * {Number} Feature offset in x direction. Will be calculated for and
18405      * applied to the current feature while rendering (see
18406      * <calculateFeatureDx>).
18407      */
18408     featureDx: 0,
18409     
18410     /**
18411      * Constructor: OpenLayers.Renderer 
18412      *
18413      * Parameters:
18414      * containerID - {<String>} 
18415      * options - {Object} options for this renderer. See sublcasses for
18416      *     supported options.
18417      */
18418     initialize: function(containerID, options) {
18419         this.container = OpenLayers.Util.getElement(containerID);
18420         OpenLayers.Util.extend(this, options);
18421     },
18422     
18423     /**
18424      * APIMethod: destroy
18425      */
18426     destroy: function() {
18427         this.container = null;
18428         this.extent = null;
18429         this.size =  null;
18430         this.resolution = null;
18431         this.map = null;
18432     },
18433
18434     /**
18435      * APIMethod: supported
18436      * This should be overridden by specific subclasses
18437      * 
18438      * Returns:
18439      * {Boolean} Whether or not the browser supports the renderer class
18440      */
18441     supported: function() {
18442         return false;
18443     },    
18444     
18445     /**
18446      * Method: setExtent
18447      * Set the visible part of the layer.
18448      *
18449      * Resolution has probably changed, so we nullify the resolution 
18450      * cache (this.resolution) -- this way it will be re-computed when 
18451      * next it is needed.
18452      * We nullify the resolution cache (this.resolution) if resolutionChanged
18453      * is set to true - this way it will be re-computed on the next
18454      * getResolution() request.
18455      *
18456      * Parameters:
18457      * extent - {<OpenLayers.Bounds>}
18458      * resolutionChanged - {Boolean}
18459      *
18460      * Returns:
18461      * {Boolean} true to notify the layer that the new extent does not exceed
18462      *     the coordinate range, and the features will not need to be redrawn.
18463      *     False otherwise.
18464      */
18465     setExtent: function(extent, resolutionChanged) {
18466         this.extent = extent.clone();
18467         if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
18468             var ratio = extent.getWidth() / this.map.getExtent().getWidth(),
18469                 extent = extent.scale(1 / ratio);
18470             this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio);
18471         }
18472         if (resolutionChanged) {
18473             this.resolution = null;
18474         }
18475         return true;
18476     },
18477     
18478     /**
18479      * Method: setSize
18480      * Sets the size of the drawing surface.
18481      * 
18482      * Resolution has probably changed, so we nullify the resolution 
18483      * cache (this.resolution) -- this way it will be re-computed when 
18484      * next it is needed.
18485      *
18486      * Parameters:
18487      * size - {<OpenLayers.Size>} 
18488      */
18489     setSize: function(size) {
18490         this.size = size.clone();
18491         this.resolution = null;
18492     },
18493     
18494     /** 
18495      * Method: getResolution
18496      * Uses cached copy of resolution if available to minimize computing
18497      * 
18498      * Returns:
18499      * {Float} The current map's resolution
18500      */
18501     getResolution: function() {
18502         this.resolution = this.resolution || this.map.getResolution();
18503         return this.resolution;
18504     },
18505     
18506     /**
18507      * Method: drawFeature
18508      * Draw the feature.  The optional style argument can be used
18509      * to override the feature's own style.  This method should only
18510      * be called from layer.drawFeature().
18511      *
18512      * Parameters:
18513      * feature - {<OpenLayers.Feature.Vector>} 
18514      * style - {<Object>}
18515      * 
18516      * Returns:
18517      * {Boolean} true if the feature has been drawn completely, false if not,
18518      *     undefined if the feature had no geometry
18519      */
18520     drawFeature: function(feature, style) {
18521         if(style == null) {
18522             style = feature.style;
18523         }
18524         if (feature.geometry) {
18525             var bounds = feature.geometry.getBounds();
18526             if(bounds) {
18527                 var worldBounds;
18528                 if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
18529                     worldBounds = this.map.getMaxExtent();
18530                 }
18531                 if (!bounds.intersectsBounds(this.extent, {worldBounds: worldBounds})) {
18532                     style = {display: "none"};
18533                 } else {
18534                     this.calculateFeatureDx(bounds, worldBounds);
18535                 }
18536                 var rendered = this.drawGeometry(feature.geometry, style, feature.id);
18537                 if(style.display != "none" && style.label && rendered !== false) {
18538
18539                     var location = feature.geometry.getCentroid(); 
18540                     if(style.labelXOffset || style.labelYOffset) {
18541                         var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;
18542                         var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;
18543                         var res = this.getResolution();
18544                         location.move(xOffset*res, yOffset*res);
18545                     }
18546                     this.drawText(feature.id, style, location);
18547                 } else {
18548                     this.removeText(feature.id);
18549                 }
18550                 return rendered;
18551             }
18552         }
18553     },
18554
18555     /**
18556      * Method: calculateFeatureDx
18557      * {Number} Calculates the feature offset in x direction. Looking at the
18558      * center of the feature bounds and the renderer extent, we calculate how
18559      * many world widths the two are away from each other. This distance is
18560      * used to shift the feature as close as possible to the center of the
18561      * current enderer extent, which ensures that the feature is visible in the
18562      * current viewport.
18563      *
18564      * Parameters:
18565      * bounds - {<OpenLayers.Bounds>} Bounds of the feature
18566      * worldBounds - {<OpenLayers.Bounds>} Bounds of the world
18567      */
18568     calculateFeatureDx: function(bounds, worldBounds) {
18569         this.featureDx = 0;
18570         if (worldBounds) {
18571             var worldWidth = worldBounds.getWidth(),
18572                 rendererCenterX = (this.extent.left + this.extent.right) / 2,
18573                 featureCenterX = (bounds.left + bounds.right) / 2,
18574                 worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth);
18575             this.featureDx = worldsAway * worldWidth;
18576         }
18577     },
18578
18579     /** 
18580      * Method: drawGeometry
18581      * 
18582      * Draw a geometry.  This should only be called from the renderer itself.
18583      * Use layer.drawFeature() from outside the renderer.
18584      * virtual function
18585      *
18586      * Parameters:
18587      * geometry - {<OpenLayers.Geometry>} 
18588      * style - {Object} 
18589      * featureId - {<String>} 
18590      */
18591     drawGeometry: function(geometry, style, featureId) {},
18592         
18593     /**
18594      * Method: drawText
18595      * Function for drawing text labels.
18596      * This method is only called by the renderer itself.
18597      * 
18598      * Parameters: 
18599      * featureId - {String}
18600      * style -
18601      * location - {<OpenLayers.Geometry.Point>}
18602      */
18603     drawText: function(featureId, style, location) {},
18604
18605     /**
18606      * Method: removeText
18607      * Function for removing text labels.
18608      * This method is only called by the renderer itself.
18609      * 
18610      * Parameters: 
18611      * featureId - {String}
18612      */
18613     removeText: function(featureId) {},
18614     
18615     /**
18616      * Method: clear
18617      * Clear all vectors from the renderer.
18618      * virtual function.
18619      */    
18620     clear: function() {},
18621
18622     /**
18623      * Method: getFeatureIdFromEvent
18624      * Returns a feature id from an event on the renderer.  
18625      * How this happens is specific to the renderer.  This should be
18626      * called from layer.getFeatureFromEvent().
18627      * Virtual function.
18628      * 
18629      * Parameters:
18630      * evt - {<OpenLayers.Event>} 
18631      *
18632      * Returns:
18633      * {String} A feature id or undefined.
18634      */
18635     getFeatureIdFromEvent: function(evt) {},
18636     
18637     /**
18638      * Method: eraseFeatures 
18639      * This is called by the layer to erase features
18640      * 
18641      * Parameters:
18642      * features - {Array(<OpenLayers.Feature.Vector>)} 
18643      */
18644     eraseFeatures: function(features) {
18645         if(!(OpenLayers.Util.isArray(features))) {
18646             features = [features];
18647         }
18648         for(var i=0, len=features.length; i<len; ++i) {
18649             var feature = features[i];
18650             this.eraseGeometry(feature.geometry, feature.id);
18651             this.removeText(feature.id);
18652         }
18653     },
18654     
18655     /**
18656      * Method: eraseGeometry
18657      * Remove a geometry from the renderer (by id).
18658      * virtual function.
18659      * 
18660      * Parameters:
18661      * geometry - {<OpenLayers.Geometry>} 
18662      * featureId - {String}
18663      */
18664     eraseGeometry: function(geometry, featureId) {},
18665     
18666     /**
18667      * Method: moveRoot
18668      * moves this renderer's root to a (different) renderer.
18669      * To be implemented by subclasses that require a common renderer root for
18670      * feature selection.
18671      * 
18672      * Parameters:
18673      * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
18674      */
18675     moveRoot: function(renderer) {},
18676
18677     /**
18678      * Method: getRenderLayerId
18679      * Gets the layer that this renderer's output appears on. If moveRoot was
18680      * used, this will be different from the id of the layer containing the
18681      * features rendered by this renderer.
18682      * 
18683      * Returns:
18684      * {String} the id of the output layer.
18685      */
18686     getRenderLayerId: function() {
18687         return this.container.id;
18688     },
18689     
18690     /**
18691      * Method: applyDefaultSymbolizer
18692      * 
18693      * Parameters:
18694      * symbolizer - {Object}
18695      * 
18696      * Returns:
18697      * {Object}
18698      */
18699     applyDefaultSymbolizer: function(symbolizer) {
18700         var result = OpenLayers.Util.extend({},
18701             OpenLayers.Renderer.defaultSymbolizer);
18702         if(symbolizer.stroke === false) {
18703             delete result.strokeWidth;
18704             delete result.strokeColor;
18705         }
18706         if(symbolizer.fill === false) {
18707             delete result.fillColor;
18708         }
18709         OpenLayers.Util.extend(result, symbolizer);
18710         return result;
18711     },
18712
18713     CLASS_NAME: "OpenLayers.Renderer"
18714 });
18715
18716 /**
18717  * Constant: OpenLayers.Renderer.defaultSymbolizer
18718  * {Object} Properties from this symbolizer will be applied to symbolizers
18719  *     with missing properties. This can also be used to set a global
18720  *     symbolizer default in OpenLayers. To be SLD 1.x compliant, add the
18721  *     following code before rendering any vector features:
18722  * (code)
18723  * OpenLayers.Renderer.defaultSymbolizer = {
18724  *     fillColor: "#808080",
18725  *     fillOpacity: 1,
18726  *     strokeColor: "#000000",
18727  *     strokeOpacity: 1,
18728  *     strokeWidth: 1,
18729  *     pointRadius: 3,
18730  *     graphicName: "square"
18731  * };
18732  * (end)
18733  */
18734 OpenLayers.Renderer.defaultSymbolizer = {
18735     fillColor: "#000000",
18736     strokeColor: "#000000",
18737     strokeWidth: 2,
18738     fillOpacity: 1,
18739     strokeOpacity: 1,
18740     pointRadius: 0,
18741     labelAlign: 'cm'
18742 };
18743     
18744
18745
18746 /**
18747  * Constant: OpenLayers.Renderer.symbol
18748  * Coordinate arrays for well known (named) symbols.
18749  */
18750 OpenLayers.Renderer.symbol = {
18751     "star": [350,75, 379,161, 469,161, 397,215, 423,301, 350,250, 277,301,
18752             303,215, 231,161, 321,161, 350,75],
18753     "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,
18754             4,0],
18755     "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],
18756     "square": [0,0, 0,1, 1,1, 1,0, 0,0],
18757     "triangle": [0,10, 10,10, 5,0, 0,10]
18758 };
18759 /* ======================================================================
18760     OpenLayers/Renderer/Canvas.js
18761    ====================================================================== */
18762
18763 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
18764  * full list of contributors). Published under the 2-clause BSD license.
18765  * See license.txt in the OpenLayers distribution or repository for the
18766  * full text of the license. */
18767
18768 /**
18769  * @requires OpenLayers/Renderer.js
18770  */
18771
18772 /**
18773  * Class: OpenLayers.Renderer.Canvas 
18774  * A renderer based on the 2D 'canvas' drawing element.
18775  * 
18776  * Inherits:
18777  *  - <OpenLayers.Renderer>
18778  */
18779 OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
18780     
18781     /**
18782      * APIProperty: hitDetection
18783      * {Boolean} Allow for hit detection of features.  Default is true.
18784      */
18785     hitDetection: true,
18786     
18787     /**
18788      * Property: hitOverflow
18789      * {Number} The method for converting feature identifiers to color values
18790      *     supports 16777215 sequential values.  Two features cannot be 
18791      *     predictably detected if their identifiers differ by more than this
18792      *     value.  The hitOverflow allows for bigger numbers (but the 
18793      *     difference in values is still limited).
18794      */
18795     hitOverflow: 0,
18796
18797     /**
18798      * Property: canvas
18799      * {Canvas} The canvas context object.
18800      */
18801     canvas: null, 
18802     
18803     /**
18804      * Property: features
18805      * {Object} Internal object of feature/style pairs for use in redrawing the layer.
18806      */
18807     features: null,
18808     
18809     /**
18810      * Property: pendingRedraw
18811      * {Boolean} The renderer needs a redraw call to render features added while
18812      *     the renderer was locked.
18813      */
18814     pendingRedraw: false,
18815     
18816     /**
18817      * Property: cachedSymbolBounds
18818      * {Object} Internal cache of calculated symbol extents.
18819      */
18820     cachedSymbolBounds: {},
18821     
18822     /**
18823      * Constructor: OpenLayers.Renderer.Canvas
18824      *
18825      * Parameters:
18826      * containerID - {<String>}
18827      * options - {Object} Optional properties to be set on the renderer.
18828      */
18829     initialize: function(containerID, options) {
18830         OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
18831         this.root = document.createElement("canvas");
18832         this.container.appendChild(this.root);
18833         this.canvas = this.root.getContext("2d");
18834         this.features = {};
18835         if (this.hitDetection) {
18836             this.hitCanvas = document.createElement("canvas");
18837             this.hitContext = this.hitCanvas.getContext("2d");
18838         }
18839     },
18840     
18841     /**
18842      * Method: setExtent
18843      * Set the visible part of the layer.
18844      *
18845      * Parameters:
18846      * extent - {<OpenLayers.Bounds>}
18847      * resolutionChanged - {Boolean}
18848      *
18849      * Returns:
18850      * {Boolean} true to notify the layer that the new extent does not exceed
18851      *     the coordinate range, and the features will not need to be redrawn.
18852      *     False otherwise.
18853      */
18854     setExtent: function() {
18855         OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);
18856         // always redraw features
18857         return false;
18858     },
18859     
18860     /** 
18861      * Method: eraseGeometry
18862      * Erase a geometry from the renderer. Because the Canvas renderer has
18863      *     'memory' of the features that it has drawn, we have to remove the
18864      *     feature so it doesn't redraw.   
18865      * 
18866      * Parameters:
18867      * geometry - {<OpenLayers.Geometry>}
18868      * featureId - {String}
18869      */
18870     eraseGeometry: function(geometry, featureId) {
18871         this.eraseFeatures(this.features[featureId][0]);
18872     },
18873
18874     /**
18875      * APIMethod: supported
18876      * 
18877      * Returns:
18878      * {Boolean} Whether or not the browser supports the renderer class
18879      */
18880     supported: function() {
18881         return OpenLayers.CANVAS_SUPPORTED;
18882     },    
18883     
18884     /**
18885      * Method: setSize
18886      * Sets the size of the drawing surface.
18887      *
18888      * Once the size is updated, redraw the canvas.
18889      *
18890      * Parameters:
18891      * size - {<OpenLayers.Size>} 
18892      */
18893     setSize: function(size) {
18894         this.size = size.clone();
18895         var root = this.root;
18896         root.style.width = size.w + "px";
18897         root.style.height = size.h + "px";
18898         root.width = size.w;
18899         root.height = size.h;
18900         this.resolution = null;
18901         if (this.hitDetection) {
18902             var hitCanvas = this.hitCanvas;
18903             hitCanvas.style.width = size.w + "px";
18904             hitCanvas.style.height = size.h + "px";
18905             hitCanvas.width = size.w;
18906             hitCanvas.height = size.h;
18907         }
18908     },
18909     
18910     /**
18911      * Method: drawFeature
18912      * Draw the feature. Stores the feature in the features list,
18913      * then redraws the layer. 
18914      *
18915      * Parameters:
18916      * feature - {<OpenLayers.Feature.Vector>} 
18917      * style - {<Object>} 
18918      *
18919      * Returns:
18920      * {Boolean} The feature has been drawn completely.  If the feature has no
18921      *     geometry, undefined will be returned.  If the feature is not rendered
18922      *     for other reasons, false will be returned.
18923      */
18924     drawFeature: function(feature, style) {
18925         var rendered;
18926         if (feature.geometry) {
18927             style = this.applyDefaultSymbolizer(style || feature.style);
18928             // don't render if display none or feature outside extent
18929             var bounds = feature.geometry.getBounds();
18930
18931             var worldBounds;
18932             if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
18933                 worldBounds = this.map.getMaxExtent();
18934             }
18935
18936             var intersects = bounds && bounds.intersectsBounds(this.extent, {worldBounds: worldBounds});
18937
18938             rendered = (style.display !== "none") && !!bounds && intersects;
18939             if (rendered) {
18940                 // keep track of what we have rendered for redraw
18941                 this.features[feature.id] = [feature, style];
18942             }
18943             else {
18944                 // remove from features tracked for redraw
18945                 delete(this.features[feature.id]);
18946             }
18947             this.pendingRedraw = true;
18948         }
18949         if (this.pendingRedraw && !this.locked) {
18950             this.redraw();
18951             this.pendingRedraw = false;
18952         }
18953         return rendered;
18954     },
18955
18956     /** 
18957      * Method: drawGeometry
18958      * Used when looping (in redraw) over the features; draws
18959      * the canvas. 
18960      *
18961      * Parameters:
18962      * geometry - {<OpenLayers.Geometry>} 
18963      * style - {Object} 
18964      */
18965     drawGeometry: function(geometry, style, featureId) {
18966         var className = geometry.CLASS_NAME;
18967         if ((className == "OpenLayers.Geometry.Collection") ||
18968             (className == "OpenLayers.Geometry.MultiPoint") ||
18969             (className == "OpenLayers.Geometry.MultiLineString") ||
18970             (className == "OpenLayers.Geometry.MultiPolygon")) {
18971             for (var i = 0; i < geometry.components.length; i++) {
18972                 this.drawGeometry(geometry.components[i], style, featureId);
18973             }
18974             return;
18975         }
18976         switch (geometry.CLASS_NAME) {
18977             case "OpenLayers.Geometry.Point":
18978                 this.drawPoint(geometry, style, featureId);
18979                 break;
18980             case "OpenLayers.Geometry.LineString":
18981                 this.drawLineString(geometry, style, featureId);
18982                 break;
18983             case "OpenLayers.Geometry.LinearRing":
18984                 this.drawLinearRing(geometry, style, featureId);
18985                 break;
18986             case "OpenLayers.Geometry.Polygon":
18987                 this.drawPolygon(geometry, style, featureId);
18988                 break;
18989             default:
18990                 break;
18991         }
18992     },
18993
18994     /**
18995      * Method: drawExternalGraphic
18996      * Called to draw External graphics. 
18997      * 
18998      * Parameters: 
18999      * geometry - {<OpenLayers.Geometry>}
19000      * style    - {Object}
19001      * featureId - {String}
19002      */ 
19003     drawExternalGraphic: function(geometry, style, featureId) {
19004         var img = new Image();
19005
19006         var title = style.title || style.graphicTitle;        
19007         if (title) {
19008             img.title = title;           
19009         }
19010
19011         var width = style.graphicWidth || style.graphicHeight;
19012         var height = style.graphicHeight || style.graphicWidth;
19013         width = width ? width : style.pointRadius * 2;
19014         height = height ? height : style.pointRadius * 2;
19015         var xOffset = (style.graphicXOffset != undefined) ?
19016            style.graphicXOffset : -(0.5 * width);
19017         var yOffset = (style.graphicYOffset != undefined) ?
19018            style.graphicYOffset : -(0.5 * height);
19019
19020         var opacity = style.graphicOpacity || style.fillOpacity;
19021         
19022         var onLoad = function() {
19023             if(!this.features[featureId]) {
19024                 return;
19025             }
19026             var pt = this.getLocalXY(geometry);
19027             var p0 = pt[0];
19028             var p1 = pt[1];
19029             if(!isNaN(p0) && !isNaN(p1)) {
19030                 var x = (p0 + xOffset) | 0;
19031                 var y = (p1 + yOffset) | 0;
19032                 var canvas = this.canvas;
19033                 canvas.globalAlpha = opacity;
19034                 var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor ||
19035                     (OpenLayers.Renderer.Canvas.drawImageScaleFactor =
19036                         /android 2.1/.test(navigator.userAgent.toLowerCase()) ?
19037                             // 320 is the screen width of the G1 phone, for
19038                             // which drawImage works out of the box.
19039                             320 / window.screen.width : 1
19040                     );
19041                 canvas.drawImage(
19042                     img, x*factor, y*factor, width*factor, height*factor
19043                 );
19044                 if (this.hitDetection) {
19045                     this.setHitContextStyle("fill", featureId);
19046                     this.hitContext.fillRect(x, y, width, height);
19047                 }
19048             }
19049         };
19050
19051         img.onload = OpenLayers.Function.bind(onLoad, this);
19052         img.src = style.externalGraphic;
19053     },
19054
19055     /**
19056      * Method: drawNamedSymbol
19057      * Called to draw Well Known Graphic Symbol Name. 
19058      * This method is only called by the renderer itself.
19059      * 
19060      * Parameters: 
19061      * geometry - {<OpenLayers.Geometry>}
19062      * style    - {Object}
19063      * featureId - {String}
19064      */ 
19065     drawNamedSymbol: function(geometry, style, featureId) {
19066         var x, y, cx, cy, i, symbolBounds, scaling, angle;
19067         var unscaledStrokeWidth;
19068         var deg2rad = Math.PI / 180.0;
19069         
19070         var symbol = OpenLayers.Renderer.symbol[style.graphicName];
19071          
19072         if (!symbol) {
19073             throw new Error(style.graphicName + ' is not a valid symbol name');
19074         }
19075         
19076         if (!symbol.length || symbol.length < 2) return;
19077         
19078         var pt = this.getLocalXY(geometry);
19079         var p0 = pt[0];
19080         var p1 = pt[1];
19081        
19082         if (isNaN(p0) || isNaN(p1)) return;
19083         
19084         // Use rounded line caps
19085         this.canvas.lineCap = "round";
19086         this.canvas.lineJoin = "round";
19087         
19088         if (this.hitDetection) {
19089             this.hitContext.lineCap = "round";
19090             this.hitContext.lineJoin = "round";
19091         }
19092         
19093         // Scale and rotate symbols, using precalculated bounds whenever possible.
19094         if (style.graphicName in this.cachedSymbolBounds) {
19095             symbolBounds = this.cachedSymbolBounds[style.graphicName];
19096         } else {
19097             symbolBounds = new OpenLayers.Bounds();
19098             for(i = 0; i < symbol.length; i+=2) {
19099                 symbolBounds.extend(new OpenLayers.LonLat(symbol[i], symbol[i+1]));
19100             }
19101             this.cachedSymbolBounds[style.graphicName] = symbolBounds;
19102         }
19103         
19104         // Push symbol scaling, translation and rotation onto the transformation stack in reverse order.
19105         // Don't forget to apply all canvas transformations to the hitContext canvas as well(!)
19106         this.canvas.save();
19107         if (this.hitDetection) { this.hitContext.save(); }
19108         
19109         // Step 3: place symbol at the desired location
19110         this.canvas.translate(p0,p1);
19111         if (this.hitDetection) { this.hitContext.translate(p0,p1); }
19112         
19113         // Step 2a. rotate the symbol if necessary
19114         angle = deg2rad * style.rotation; // will be NaN when style.rotation is undefined.
19115         if (!isNaN(angle)) {
19116             this.canvas.rotate(angle);
19117             if (this.hitDetection) { this.hitContext.rotate(angle); }
19118         }
19119                 
19120         // // Step 2: scale symbol such that pointRadius equals half the maximum symbol dimension.
19121         scaling = 2.0 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight());
19122         this.canvas.scale(scaling,scaling);
19123         if (this.hitDetection) { this.hitContext.scale(scaling,scaling); }
19124         
19125         // Step 1: center the symbol at the origin        
19126         cx = symbolBounds.getCenterLonLat().lon;
19127         cy = symbolBounds.getCenterLonLat().lat;
19128         this.canvas.translate(-cx,-cy);
19129         if (this.hitDetection) { this.hitContext.translate(-cx,-cy); }        
19130
19131         // Don't forget to scale stroke widths, because they are affected by canvas scale transformations as well(!)
19132         // Alternative: scale symbol coordinates manually, so stroke width scaling is not needed anymore.
19133         unscaledStrokeWidth = style.strokeWidth;
19134         style.strokeWidth = unscaledStrokeWidth / scaling;
19135             
19136         if (style.fill !== false) {
19137             this.setCanvasStyle("fill", style);
19138             this.canvas.beginPath();
19139             for (i=0; i<symbol.length; i=i+2) {
19140                 x = symbol[i];
19141                 y = symbol[i+1];
19142                 if (i == 0) this.canvas.moveTo(x,y);
19143                 this.canvas.lineTo(x,y);
19144             }
19145             this.canvas.closePath();
19146             this.canvas.fill();
19147
19148             if (this.hitDetection) {
19149                 this.setHitContextStyle("fill", featureId, style);
19150                 this.hitContext.beginPath();
19151                 for (i=0; i<symbol.length; i=i+2) {
19152                     x = symbol[i];
19153                     y = symbol[i+1];
19154                     if (i == 0) this.canvas.moveTo(x,y);
19155                     this.hitContext.lineTo(x,y);
19156                 }
19157                 this.hitContext.closePath();
19158                 this.hitContext.fill();
19159             }
19160         }  
19161         
19162         if (style.stroke !== false) {
19163             this.setCanvasStyle("stroke", style);
19164             this.canvas.beginPath();
19165             for (i=0; i<symbol.length; i=i+2) {
19166                 x = symbol[i];
19167                 y = symbol[i+1];
19168                 if (i == 0) this.canvas.moveTo(x,y);
19169                 this.canvas.lineTo(x,y);
19170             }
19171             this.canvas.closePath();
19172             this.canvas.stroke();
19173             
19174             
19175             if (this.hitDetection) {
19176                 this.setHitContextStyle("stroke", featureId, style, scaling);
19177                 this.hitContext.beginPath();
19178                 for (i=0; i<symbol.length; i=i+2) {
19179                     x = symbol[i];
19180                     y = symbol[i+1];
19181                     if (i == 0) this.hitContext.moveTo(x,y);
19182                     this.hitContext.lineTo(x,y);
19183                 }
19184                 this.hitContext.closePath();
19185                 this.hitContext.stroke();
19186             }
19187             
19188         }
19189         
19190         style.strokeWidth = unscaledStrokeWidth;
19191         this.canvas.restore();
19192         if (this.hitDetection) { this.hitContext.restore(); }
19193         this.setCanvasStyle("reset");  
19194     },
19195
19196     /**
19197      * Method: setCanvasStyle
19198      * Prepare the canvas for drawing by setting various global settings.
19199      *
19200      * Parameters:
19201      * type - {String} one of 'stroke', 'fill', or 'reset'
19202      * style - {Object} Symbolizer hash
19203      */
19204     setCanvasStyle: function(type, style) {
19205         if (type === "fill") {     
19206             this.canvas.globalAlpha = style['fillOpacity'];
19207             this.canvas.fillStyle = style['fillColor'];
19208         } else if (type === "stroke") {  
19209             this.canvas.globalAlpha = style['strokeOpacity'];
19210             this.canvas.strokeStyle = style['strokeColor'];
19211             this.canvas.lineWidth = style['strokeWidth'];
19212         } else {
19213             this.canvas.globalAlpha = 0;
19214             this.canvas.lineWidth = 1;
19215         }
19216     },
19217     
19218     /**
19219      * Method: featureIdToHex
19220      * Convert a feature ID string into an RGB hex string.
19221      *
19222      * Parameters:
19223      * featureId - {String} Feature id
19224      *
19225      * Returns:
19226      * {String} RGB hex string.
19227      */
19228     featureIdToHex: function(featureId) {
19229         var id = Number(featureId.split("_").pop()) + 1; // zero for no feature
19230         if (id >= 16777216) {
19231             this.hitOverflow = id - 16777215;
19232             id = id % 16777216 + 1;
19233         }
19234         var hex = "000000" + id.toString(16);
19235         var len = hex.length;
19236         hex = "#" + hex.substring(len-6, len);
19237         return hex;
19238     },
19239     
19240     /**
19241      * Method: setHitContextStyle
19242      * Prepare the hit canvas for drawing by setting various global settings.
19243      *
19244      * Parameters:
19245      * type - {String} one of 'stroke', 'fill', or 'reset'
19246      * featureId - {String} The feature id.
19247      * symbolizer - {<OpenLayers.Symbolizer>} The symbolizer.
19248      */
19249     setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) {
19250         var hex = this.featureIdToHex(featureId);
19251         if (type == "fill") {
19252             this.hitContext.globalAlpha = 1.0;
19253             this.hitContext.fillStyle = hex;
19254         } else if (type == "stroke") {  
19255             this.hitContext.globalAlpha = 1.0;
19256             this.hitContext.strokeStyle = hex;
19257             // bump up stroke width to deal with antialiasing. If strokeScaling is defined, we're rendering a symbol 
19258             // on a transformed canvas, so the antialias width bump has to scale as well.
19259             if (typeof strokeScaling === "undefined") {
19260                 this.hitContext.lineWidth = symbolizer.strokeWidth + 2;
19261             } else {
19262                 if (!isNaN(strokeScaling)) { this.hitContext.lineWidth = symbolizer.strokeWidth + 2.0 / strokeScaling; }
19263             }
19264         } else {
19265             this.hitContext.globalAlpha = 0;
19266             this.hitContext.lineWidth = 1;
19267         }
19268     },
19269
19270     /**
19271      * Method: drawPoint
19272      * This method is only called by the renderer itself.
19273      * 
19274      * Parameters: 
19275      * geometry - {<OpenLayers.Geometry>}
19276      * style    - {Object}
19277      * featureId - {String}
19278      */ 
19279     drawPoint: function(geometry, style, featureId) {
19280         if(style.graphic !== false) {
19281             if(style.externalGraphic) {
19282                 this.drawExternalGraphic(geometry, style, featureId);
19283             } else if (style.graphicName && (style.graphicName != "circle")) {
19284                 this.drawNamedSymbol(geometry, style, featureId);
19285             } else {
19286                 var pt = this.getLocalXY(geometry);
19287                 var p0 = pt[0];
19288                 var p1 = pt[1];
19289                 if(!isNaN(p0) && !isNaN(p1)) {
19290                     var twoPi = Math.PI*2;
19291                     var radius = style.pointRadius;
19292                     if(style.fill !== false) {
19293                         this.setCanvasStyle("fill", style);
19294                         this.canvas.beginPath();
19295                         this.canvas.arc(p0, p1, radius, 0, twoPi, true);
19296                         this.canvas.fill();
19297                         if (this.hitDetection) {
19298                             this.setHitContextStyle("fill", featureId, style);
19299                             this.hitContext.beginPath();
19300                             this.hitContext.arc(p0, p1, radius, 0, twoPi, true);
19301                             this.hitContext.fill();
19302                         }
19303                     }
19304
19305                     if(style.stroke !== false) {
19306                         this.setCanvasStyle("stroke", style);
19307                         this.canvas.beginPath();
19308                         this.canvas.arc(p0, p1, radius, 0, twoPi, true);
19309                         this.canvas.stroke();
19310                         if (this.hitDetection) {
19311                             this.setHitContextStyle("stroke", featureId, style);
19312                             this.hitContext.beginPath();
19313                             this.hitContext.arc(p0, p1, radius, 0, twoPi, true);
19314                             this.hitContext.stroke();
19315                         }
19316                         this.setCanvasStyle("reset");
19317                     }
19318                 }
19319             }
19320         }
19321     },
19322     
19323     /**
19324      * Method: drawLineString
19325      * This method is only called by the renderer itself.
19326      * 
19327      * Parameters: 
19328      * geometry - {<OpenLayers.Geometry>}
19329      * style    - {Object}
19330      * featureId - {String}
19331      */ 
19332     drawLineString: function(geometry, style, featureId) {
19333         style = OpenLayers.Util.applyDefaults({fill: false}, style);
19334         this.drawLinearRing(geometry, style, featureId);
19335     },    
19336     
19337     /**
19338      * Method: drawLinearRing
19339      * This method is only called by the renderer itself.
19340      * 
19341      * Parameters: 
19342      * geometry - {<OpenLayers.Geometry>}
19343      * style    - {Object}
19344      * featureId - {String}
19345      */ 
19346     drawLinearRing: function(geometry, style, featureId) {
19347         if (style.fill !== false) {
19348             this.setCanvasStyle("fill", style);
19349             this.renderPath(this.canvas, geometry, style, featureId, "fill");
19350             if (this.hitDetection) {
19351                 this.setHitContextStyle("fill", featureId, style);
19352                 this.renderPath(this.hitContext, geometry, style, featureId, "fill");
19353             }
19354         }
19355         if (style.stroke !== false) {
19356             this.setCanvasStyle("stroke", style);
19357             this.renderPath(this.canvas, geometry, style, featureId, "stroke");
19358             if (this.hitDetection) {
19359                 this.setHitContextStyle("stroke", featureId, style);
19360                 this.renderPath(this.hitContext, geometry, style, featureId, "stroke");
19361             }
19362         }
19363         this.setCanvasStyle("reset");
19364     },
19365     
19366     /**
19367      * Method: renderPath
19368      * Render a path with stroke and optional fill.
19369      */
19370     renderPath: function(context, geometry, style, featureId, type) {
19371         var components = geometry.components;
19372         var len = components.length;
19373         context.beginPath();
19374         var start = this.getLocalXY(components[0]);
19375         var x = start[0];
19376         var y = start[1];
19377         if (!isNaN(x) && !isNaN(y)) {
19378             context.moveTo(start[0], start[1]);
19379             for (var i=1; i<len; ++i) {
19380                 var pt = this.getLocalXY(components[i]);
19381                 context.lineTo(pt[0], pt[1]);
19382             }
19383             if (type === "fill") {
19384                 context.fill();
19385             } else {
19386                 context.stroke();
19387             }
19388         }
19389     },
19390     
19391     /**
19392      * Method: drawPolygon
19393      * This method is only called by the renderer itself.
19394      * 
19395      * Parameters: 
19396      * geometry - {<OpenLayers.Geometry>}
19397      * style    - {Object}
19398      * featureId - {String}
19399      */ 
19400     drawPolygon: function(geometry, style, featureId) {
19401         var components = geometry.components;
19402         var len = components.length;
19403         this.drawLinearRing(components[0], style, featureId);
19404         // erase inner rings
19405         for (var i=1; i<len; ++i) {
19406             /** 
19407              * Note that this is overly agressive.  Here we punch holes through 
19408              * all previously rendered features on the same canvas.  A better 
19409              * solution for polygons with interior rings would be to draw the 
19410              * polygon on a sketch canvas first.  We could erase all holes 
19411              * there and then copy the drawing to the layer canvas. 
19412              * TODO: http://trac.osgeo.org/openlayers/ticket/3130 
19413              */
19414             this.canvas.globalCompositeOperation = "destination-out";
19415             if (this.hitDetection) {
19416                 this.hitContext.globalCompositeOperation = "destination-out";
19417             }
19418             this.drawLinearRing(
19419                 components[i], 
19420                 OpenLayers.Util.applyDefaults({stroke: false, fillOpacity: 1.0}, style),
19421                 featureId
19422             );
19423             this.canvas.globalCompositeOperation = "source-over";
19424             if (this.hitDetection) {
19425                 this.hitContext.globalCompositeOperation = "source-over";
19426             }
19427             this.drawLinearRing(
19428                 components[i], 
19429                 OpenLayers.Util.applyDefaults({fill: false}, style),
19430                 featureId
19431             );
19432         }
19433     },
19434     
19435     /**
19436      * Method: drawText
19437      * This method is only called by the renderer itself.
19438      *
19439      * Parameters:
19440      * location - {<OpenLayers.Point>}
19441      * style    - {Object}
19442      */
19443     drawText: function(location, style) {
19444         var pt = this.getLocalXY(location);
19445
19446         this.setCanvasStyle("reset");
19447         this.canvas.fillStyle = style.fontColor;
19448         this.canvas.globalAlpha = style.fontOpacity || 1.0;
19449         var fontStyle = [style.fontStyle ? style.fontStyle : "normal",
19450                          "normal", // "font-variant" not supported
19451                          style.fontWeight ? style.fontWeight : "normal",
19452                          style.fontSize ? style.fontSize : "1em",
19453                          style.fontFamily ? style.fontFamily : "sans-serif"].join(" ");
19454         var labelRows = style.label.split('\n');
19455         var numRows = labelRows.length;
19456         if (this.canvas.fillText) {
19457             // HTML5
19458             this.canvas.font = fontStyle;
19459             this.canvas.textAlign =
19460                 OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] ||
19461                 "center";
19462             this.canvas.textBaseline =
19463                 OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] ||
19464                 "middle";
19465             var vfactor =
19466                 OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
19467             if (vfactor == null) {
19468                 vfactor = -.5;
19469             }
19470             var lineHeight =
19471                 this.canvas.measureText('Mg').height ||
19472                 this.canvas.measureText('xx').width;
19473             pt[1] += lineHeight*vfactor*(numRows-1);
19474             for (var i = 0; i < numRows; i++) {
19475                 if (style.labelOutlineWidth) {
19476                     this.canvas.save();
19477                     this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1.0;
19478                     this.canvas.strokeStyle = style.labelOutlineColor;
19479                     this.canvas.lineWidth = style.labelOutlineWidth;
19480                     this.canvas.strokeText(labelRows[i], pt[0], pt[1] + (lineHeight*i) + 1);
19481                     this.canvas.restore();
19482                 }
19483                 this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight*i));
19484             }
19485         } else if (this.canvas.mozDrawText) {
19486             // Mozilla pre-Gecko1.9.1 (<FF3.1)
19487             this.canvas.mozTextStyle = fontStyle;
19488             // No built-in text alignment, so we measure and adjust the position
19489             var hfactor =
19490                 OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];
19491             if (hfactor == null) {
19492                 hfactor = -.5;
19493             }
19494             var vfactor =
19495                 OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
19496             if (vfactor == null) {
19497                 vfactor = -.5;
19498             }
19499             var lineHeight = this.canvas.mozMeasureText('xx');
19500             pt[1] += lineHeight*(1 + (vfactor*numRows));
19501             for (var i = 0; i < numRows; i++) {
19502                 var x = pt[0] + (hfactor*this.canvas.mozMeasureText(labelRows[i]));
19503                 var y = pt[1] + (i*lineHeight);
19504                 this.canvas.translate(x, y);
19505                 this.canvas.mozDrawText(labelRows[i]);
19506                 this.canvas.translate(-x, -y);
19507             }
19508         }
19509         this.setCanvasStyle("reset");
19510     },
19511     
19512     /**
19513      * Method: getLocalXY
19514      * transform geographic xy into pixel xy
19515      *
19516      * Parameters: 
19517      * point - {<OpenLayers.Geometry.Point>}
19518      */
19519     getLocalXY: function(point) {
19520         var resolution = this.getResolution();
19521         var extent = this.extent;
19522         var x = ((point.x - this.featureDx) / resolution + (-extent.left / resolution));
19523         var y = ((extent.top / resolution) - point.y / resolution);
19524         return [x, y];
19525     },
19526
19527     /**
19528      * Method: clear
19529      * Clear all vectors from the renderer.
19530      */    
19531     clear: function() {
19532         var height = this.root.height;
19533         var width = this.root.width;
19534         this.canvas.clearRect(0, 0, width, height);
19535         this.features = {};
19536         if (this.hitDetection) {
19537             this.hitContext.clearRect(0, 0, width, height);
19538         }
19539     },
19540
19541     /**
19542      * Method: getFeatureIdFromEvent
19543      * Returns a feature id from an event on the renderer.  
19544      * 
19545      * Parameters:
19546      * evt - {<OpenLayers.Event>} 
19547      *
19548      * Returns:
19549      * {<OpenLayers.Feature.Vector} A feature or undefined.  This method returns a 
19550      *     feature instead of a feature id to avoid an unnecessary lookup on the
19551      *     layer.
19552      */
19553     getFeatureIdFromEvent: function(evt) {
19554         var featureId, feature;
19555         
19556         if (this.hitDetection && this.root.style.display !== "none") {
19557             // this dragging check should go in the feature handler
19558             if (!this.map.dragging) {
19559                 var xy = evt.xy;
19560                 var x = xy.x | 0;
19561                 var y = xy.y | 0;
19562                 var data = this.hitContext.getImageData(x, y, 1, 1).data;
19563                 if (data[3] === 255) { // antialiased
19564                     var id = data[2] + (256 * (data[1] + (256 * data[0])));
19565                     if (id) {
19566                         featureId = "OpenLayers_Feature_Vector_" + (id - 1 + this.hitOverflow);
19567                         try {
19568                             feature = this.features[featureId][0];
19569                         } catch(err) {
19570                             // Because of antialiasing on the canvas, when the hit location is at a point where the edge of
19571                             // one symbol intersects the interior of another symbol, a wrong hit color (and therefore id) results.
19572                             // todo: set Antialiasing = 'off' on the hitContext as soon as browsers allow it.
19573                         }
19574                     }
19575                 }
19576             }
19577         }
19578         return feature;
19579     },
19580     
19581     /**
19582      * Method: eraseFeatures 
19583      * This is called by the layer to erase features; removes the feature from
19584      *     the list, then redraws the layer.
19585      * 
19586      * Parameters:
19587      * features - {Array(<OpenLayers.Feature.Vector>)} 
19588      */
19589     eraseFeatures: function(features) {
19590         if(!(OpenLayers.Util.isArray(features))) {
19591             features = [features];
19592         }
19593         for(var i=0; i<features.length; ++i) {
19594             delete this.features[features[i].id];
19595         }
19596         this.redraw();
19597     },
19598
19599     /**
19600      * Method: redraw
19601      * The real 'meat' of the function: any time things have changed,
19602      *     redraw() can be called to loop over all the data and (you guessed
19603      *     it) redraw it.  Unlike Elements-based Renderers, we can't interact
19604      *     with things once they're drawn, to remove them, for example, so
19605      *     instead we have to just clear everything and draw from scratch.
19606      */
19607     redraw: function() {
19608         if (!this.locked) {
19609             var height = this.root.height;
19610             var width = this.root.width;
19611             this.canvas.clearRect(0, 0, width, height);
19612             if (this.hitDetection) {
19613                 this.hitContext.clearRect(0, 0, width, height);
19614             }
19615             var labelMap = [];
19616             var feature, geometry, style;
19617             var worldBounds = (this.map.baseLayer && this.map.baseLayer.wrapDateLine) && this.map.getMaxExtent();
19618             for (var id in this.features) {
19619                 if (!this.features.hasOwnProperty(id)) { continue; }
19620                 feature = this.features[id][0];
19621                 geometry = feature.geometry;
19622                 this.calculateFeatureDx(geometry.getBounds(), worldBounds);
19623                 style = this.features[id][1];
19624                 this.drawGeometry(geometry, style, feature.id);
19625                 if(style.label) {
19626                     labelMap.push([feature, style]);
19627                 }
19628             }
19629             var item;
19630             for (var i=0, len=labelMap.length; i<len; ++i) {
19631                 item = labelMap[i];
19632                 this.drawText(item[0].geometry.getCentroid(), item[1]);
19633             }
19634         }    
19635     },
19636
19637     CLASS_NAME: "OpenLayers.Renderer.Canvas"
19638 });
19639
19640 /**
19641  * Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN
19642  * {Object}
19643  */
19644 OpenLayers.Renderer.Canvas.LABEL_ALIGN = {
19645     "l": "left",
19646     "r": "right",
19647     "t": "top",
19648     "b": "bottom"
19649 };
19650
19651 /**
19652  * Constant: OpenLayers.Renderer.Canvas.LABEL_FACTOR
19653  * {Object}
19654  */
19655 OpenLayers.Renderer.Canvas.LABEL_FACTOR = {
19656     "l": 0,
19657     "r": -1,
19658     "t": 0,
19659     "b": -1
19660 };
19661
19662 /**
19663  * Constant: OpenLayers.Renderer.Canvas.drawImageScaleFactor
19664  * {Number} Scale factor to apply to the canvas drawImage arguments. This
19665  *     is always 1 except for Android 2.1 devices, to work around
19666  *     http://code.google.com/p/android/issues/detail?id=5141.
19667  */
19668 OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;
19669 /* ======================================================================
19670     OpenLayers/Handler.js
19671    ====================================================================== */
19672
19673 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
19674  * full list of contributors). Published under the 2-clause BSD license.
19675  * See license.txt in the OpenLayers distribution or repository for the
19676  * full text of the license. */
19677
19678 /**
19679  * @requires OpenLayers/BaseTypes/Class.js
19680  * @requires OpenLayers/Events.js
19681  */
19682
19683 /**
19684  * Class: OpenLayers.Handler
19685  * Base class to construct a higher-level handler for event sequences.  All
19686  *     handlers have activate and deactivate methods.  In addition, they have
19687  *     methods named like browser events.  When a handler is activated, any
19688  *     additional methods named like a browser event is registered as a
19689  *     listener for the corresponding event.  When a handler is deactivated,
19690  *     those same methods are unregistered as event listeners.
19691  *
19692  * Handlers also typically have a callbacks object with keys named like
19693  *     the abstracted events or event sequences that they are in charge of
19694  *     handling.  The controls that wrap handlers define the methods that
19695  *     correspond to these abstract events - so instead of listening for
19696  *     individual browser events, they only listen for the abstract events
19697  *     defined by the handler.
19698  *     
19699  * Handlers are created by controls, which ultimately have the responsibility
19700  *     of making changes to the the state of the application.  Handlers
19701  *     themselves may make temporary changes, but in general are expected to
19702  *     return the application in the same state that they found it.
19703  */
19704 OpenLayers.Handler = OpenLayers.Class({
19705
19706     /**
19707      * Property: id
19708      * {String}
19709      */
19710     id: null,
19711         
19712     /**
19713      * APIProperty: control
19714      * {<OpenLayers.Control>}. The control that initialized this handler.  The
19715      *     control is assumed to have a valid map property - that map is used
19716      *     in the handler's own setMap method.
19717      */
19718     control: null,
19719
19720     /**
19721      * Property: map
19722      * {<OpenLayers.Map>}
19723      */
19724     map: null,
19725
19726     /**
19727      * APIProperty: keyMask
19728      * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler
19729      *     constants to construct a keyMask.  The keyMask is used by
19730      *     <checkModifiers>.  If the keyMask matches the combination of keys
19731      *     down on an event, checkModifiers returns true.
19732      *
19733      * Example:
19734      * (code)
19735      *     // handler only responds if the Shift key is down
19736      *     handler.keyMask = OpenLayers.Handler.MOD_SHIFT;
19737      *
19738      *     // handler only responds if Ctrl-Shift is down
19739      *     handler.keyMask = OpenLayers.Handler.MOD_SHIFT |
19740      *                       OpenLayers.Handler.MOD_CTRL;
19741      * (end)
19742      */
19743     keyMask: null,
19744
19745     /**
19746      * Property: active
19747      * {Boolean}
19748      */
19749     active: false,
19750     
19751     /**
19752      * Property: evt
19753      * {Event} This property references the last event handled by the handler.
19754      *     Note that this property is not part of the stable API.  Use of the
19755      *     evt property should be restricted to controls in the library
19756      *     or other applications that are willing to update with changes to
19757      *     the OpenLayers code.
19758      */
19759     evt: null,
19760     
19761     /**
19762      * Property: touch
19763      * {Boolean} Indicates the support of touch events. When touch events are 
19764      *     started touch will be true and all mouse related listeners will do 
19765      *     nothing.
19766      */
19767     touch: false,
19768
19769     /**
19770      * Constructor: OpenLayers.Handler
19771      * Construct a handler.
19772      *
19773      * Parameters:
19774      * control - {<OpenLayers.Control>} The control that initialized this
19775      *     handler.  The control is assumed to have a valid map property; that
19776      *     map is used in the handler's own setMap method.  If a map property
19777      *     is present in the options argument it will be used instead.
19778      * callbacks - {Object} An object whose properties correspond to abstracted
19779      *     events or sequences of browser events.  The values for these
19780      *     properties are functions defined by the control that get called by
19781      *     the handler.
19782      * options - {Object} An optional object whose properties will be set on
19783      *     the handler.
19784      */
19785     initialize: function(control, callbacks, options) {
19786         OpenLayers.Util.extend(this, options);
19787         this.control = control;
19788         this.callbacks = callbacks;
19789
19790         var map = this.map || control.map;
19791         if (map) {
19792             this.setMap(map); 
19793         }
19794         
19795         this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
19796     },
19797     
19798     /**
19799      * Method: setMap
19800      */
19801     setMap: function (map) {
19802         this.map = map;
19803     },
19804
19805     /**
19806      * Method: checkModifiers
19807      * Check the keyMask on the handler.  If no <keyMask> is set, this always
19808      *     returns true.  If a <keyMask> is set and it matches the combination
19809      *     of keys down on an event, this returns true.
19810      *
19811      * Returns:
19812      * {Boolean} The keyMask matches the keys down on an event.
19813      */
19814     checkModifiers: function (evt) {
19815         if(this.keyMask == null) {
19816             return true;
19817         }
19818         /* calculate the keyboard modifier mask for this event */
19819         var keyModifiers =
19820             (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) |
19821             (evt.ctrlKey  ? OpenLayers.Handler.MOD_CTRL  : 0) |
19822             (evt.altKey   ? OpenLayers.Handler.MOD_ALT   : 0) |
19823             (evt.metaKey  ? OpenLayers.Handler.MOD_META  : 0);
19824     
19825         /* if it differs from the handler object's key mask,
19826            bail out of the event handler */
19827         return (keyModifiers == this.keyMask);
19828     },
19829
19830     /**
19831      * APIMethod: activate
19832      * Turn on the handler.  Returns false if the handler was already active.
19833      * 
19834      * Returns: 
19835      * {Boolean} The handler was activated.
19836      */
19837     activate: function() {
19838         if(this.active) {
19839             return false;
19840         }
19841         // register for event handlers defined on this class.
19842         var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
19843         for (var i=0, len=events.length; i<len; i++) {
19844             if (this[events[i]]) {
19845                 this.register(events[i], this[events[i]]); 
19846             }
19847         } 
19848         this.active = true;
19849         return true;
19850     },
19851     
19852     /**
19853      * APIMethod: deactivate
19854      * Turn off the handler.  Returns false if the handler was already inactive.
19855      * 
19856      * Returns:
19857      * {Boolean} The handler was deactivated.
19858      */
19859     deactivate: function() {
19860         if(!this.active) {
19861             return false;
19862         }
19863         // unregister event handlers defined on this class.
19864         var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
19865         for (var i=0, len=events.length; i<len; i++) {
19866             if (this[events[i]]) {
19867                 this.unregister(events[i], this[events[i]]); 
19868             }
19869         } 
19870         this.touch = false;
19871         this.active = false;
19872         return true;
19873     },
19874
19875     /**
19876      * Method: startTouch
19877      * Start touch events, this method must be called by subclasses in 
19878      *     "touchstart" method. When touch events are started <touch> will be
19879      *     true and all mouse related listeners will do nothing.
19880      */
19881     startTouch: function() {
19882         if (!this.touch) {
19883             this.touch = true;
19884             var events = [
19885                 "mousedown", "mouseup", "mousemove", "click", "dblclick",
19886                 "mouseout"
19887             ];
19888             for (var i=0, len=events.length; i<len; i++) {
19889                 if (this[events[i]]) {
19890                     this.unregister(events[i], this[events[i]]); 
19891                 }
19892             } 
19893         }
19894     },
19895
19896     /**
19897     * Method: callback
19898     * Trigger the control's named callback with the given arguments
19899     *
19900     * Parameters:
19901     * name - {String} The key for the callback that is one of the properties
19902     *     of the handler's callbacks object.
19903     * args - {Array(*)} An array of arguments (any type) with which to call 
19904     *     the callback (defined by the control).
19905     */
19906     callback: function (name, args) {
19907         if (name && this.callbacks[name]) {
19908             this.callbacks[name].apply(this.control, args);
19909         }
19910     },
19911
19912     /**
19913     * Method: register
19914     * register an event on the map
19915     */
19916     register: function (name, method) {
19917         // TODO: deal with registerPriority in 3.0
19918         this.map.events.registerPriority(name, this, method);
19919         this.map.events.registerPriority(name, this, this.setEvent);
19920     },
19921
19922     /**
19923     * Method: unregister
19924     * unregister an event from the map
19925     */
19926     unregister: function (name, method) {
19927         this.map.events.unregister(name, this, method);   
19928         this.map.events.unregister(name, this, this.setEvent);
19929     },
19930     
19931     /**
19932      * Method: setEvent
19933      * With each registered browser event, the handler sets its own evt
19934      *     property.  This property can be accessed by controls if needed
19935      *     to get more information about the event that the handler is
19936      *     processing.
19937      *
19938      * This allows modifier keys on the event to be checked (alt, shift, ctrl,
19939      *     and meta cannot be checked with the keyboard handler).  For a
19940      *     control to determine which modifier keys are associated with the
19941      *     event that a handler is currently processing, it should access
19942      *     (code)handler.evt.altKey || handler.evt.shiftKey ||
19943      *     handler.evt.ctrlKey || handler.evt.metaKey(end).
19944      *
19945      * Parameters:
19946      * evt - {Event} The browser event.
19947      */
19948     setEvent: function(evt) {
19949         this.evt = evt;
19950         return true;
19951     },
19952
19953     /**
19954      * Method: destroy
19955      * Deconstruct the handler.
19956      */
19957     destroy: function () {
19958         // unregister event listeners
19959         this.deactivate();
19960         // eliminate circular references
19961         this.control = this.map = null;        
19962     },
19963
19964     CLASS_NAME: "OpenLayers.Handler"
19965 });
19966
19967 /**
19968  * Constant: OpenLayers.Handler.MOD_NONE
19969  * If set as the <keyMask>, <checkModifiers> returns false if any key is down.
19970  */
19971 OpenLayers.Handler.MOD_NONE  = 0;
19972
19973 /**
19974  * Constant: OpenLayers.Handler.MOD_SHIFT
19975  * If set as the <keyMask>, <checkModifiers> returns false if Shift is down.
19976  */
19977 OpenLayers.Handler.MOD_SHIFT = 1;
19978
19979 /**
19980  * Constant: OpenLayers.Handler.MOD_CTRL
19981  * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down.
19982  */
19983 OpenLayers.Handler.MOD_CTRL  = 2;
19984
19985 /**
19986  * Constant: OpenLayers.Handler.MOD_ALT
19987  * If set as the <keyMask>, <checkModifiers> returns false if Alt is down.
19988  */
19989 OpenLayers.Handler.MOD_ALT   = 4;
19990
19991 /**
19992  * Constant: OpenLayers.Handler.MOD_META
19993  * If set as the <keyMask>, <checkModifiers> returns false if Cmd is down.
19994  */
19995 OpenLayers.Handler.MOD_META  = 8;
19996
19997
19998 /* ======================================================================
19999     OpenLayers/Handler/MouseWheel.js
20000    ====================================================================== */
20001
20002 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
20003  * full list of contributors). Published under the 2-clause BSD license.
20004  * See license.txt in the OpenLayers distribution or repository for the
20005  * full text of the license. */
20006
20007 /**
20008  * @requires OpenLayers/Handler.js
20009  */
20010
20011 /**
20012  * Class: OpenLayers.Handler.MouseWheel
20013  * Handler for wheel up/down events.
20014  * 
20015  * Inherits from:
20016  *  - <OpenLayers.Handler>
20017  */
20018 OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {
20019     /** 
20020      * Property: wheelListener 
20021      * {function} 
20022      */
20023     wheelListener: null,
20024
20025     /**
20026      * Property: interval
20027      * {Integer} In order to increase server performance, an interval (in 
20028      *     milliseconds) can be set to reduce the number of up/down events 
20029      *     called. If set, a new up/down event will not be set until the 
20030      *     interval has passed. 
20031      *     Defaults to 0, meaning no interval. 
20032      */
20033     interval: 0,
20034     
20035     /**
20036      * Property: maxDelta
20037      * {Integer} Maximum delta to collect before breaking from the current
20038      *    interval. In cumulative mode, this also limits the maximum delta
20039      *    returned from the handler. Default is Number.POSITIVE_INFINITY.
20040      */
20041     maxDelta: Number.POSITIVE_INFINITY,
20042     
20043     /**
20044      * Property: delta
20045      * {Integer} When interval is set, delta collects the mousewheel z-deltas
20046      *     of the events that occur within the interval.
20047      *      See also the cumulative option
20048      */
20049     delta: 0,
20050     
20051     /**
20052      * Property: cumulative
20053      * {Boolean} When interval is set: true to collect all the mousewheel 
20054      *     z-deltas, false to only record the delta direction (positive or
20055      *     negative)
20056      */
20057     cumulative: true,
20058     
20059     /**
20060      * Constructor: OpenLayers.Handler.MouseWheel
20061      *
20062      * Parameters:
20063      * control - {<OpenLayers.Control>} 
20064      * callbacks - {Object} An object containing a single function to be
20065      *                          called when the drag operation is finished.
20066      *                          The callback should expect to recieve a single
20067      *                          argument, the point geometry.
20068      * options - {Object} 
20069      */
20070     initialize: function(control, callbacks, options) {
20071         OpenLayers.Handler.prototype.initialize.apply(this, arguments);
20072         this.wheelListener = OpenLayers.Function.bindAsEventListener(
20073             this.onWheelEvent, this
20074         );
20075     },
20076
20077     /**
20078      * Method: destroy
20079      */    
20080     destroy: function() {
20081         OpenLayers.Handler.prototype.destroy.apply(this, arguments);
20082         this.wheelListener = null;
20083     },
20084
20085     /**
20086      *  Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
20087      */
20088
20089     /** 
20090      * Method: onWheelEvent
20091      * Catch the wheel event and handle it xbrowserly
20092      * 
20093      * Parameters:
20094      * e - {Event} 
20095      */
20096     onWheelEvent: function(e){
20097         
20098         // make sure we have a map and check keyboard modifiers
20099         if (!this.map || !this.checkModifiers(e)) {
20100             return;
20101         }
20102         
20103         // Ride up the element's DOM hierarchy to determine if it or any of 
20104         //  its ancestors was: 
20105         //   * specifically marked as scrollable (CSS overflow property)
20106         //   * one of our layer divs or a div marked as scrollable
20107         //     ('olScrollable' CSS class)
20108         //   * the map div
20109         //
20110         var overScrollableDiv = false;
20111         var allowScroll = false;
20112         var overMapDiv = false;
20113         
20114         var elem = OpenLayers.Event.element(e);
20115         while((elem != null) && !overMapDiv && !overScrollableDiv) {
20116
20117             if (!overScrollableDiv) {
20118                 try {
20119                     var overflow;
20120                     if (elem.currentStyle) {
20121                         overflow = elem.currentStyle["overflow"];
20122                     } else {
20123                         var style = 
20124                             document.defaultView.getComputedStyle(elem, null);
20125                         overflow = style.getPropertyValue("overflow");
20126                     }
20127                     overScrollableDiv = ( overflow && 
20128                         (overflow == "auto") || (overflow == "scroll") );
20129                 } catch(err) {
20130                     //sometimes when scrolling in a popup, this causes 
20131                     // obscure browser error
20132                 }
20133             }
20134
20135             if (!allowScroll) {
20136                 allowScroll = OpenLayers.Element.hasClass(elem, 'olScrollable');
20137                 if (!allowScroll) {
20138                     for (var i = 0, len = this.map.layers.length; i < len; i++) {
20139                         // Are we in the layer div? Note that we have two cases
20140                         // here: one is to catch EventPane layers, which have a
20141                         // pane above the layer (layer.pane)
20142                         var layer = this.map.layers[i];
20143                         if (elem == layer.div || elem == layer.pane) {
20144                             allowScroll = true;
20145                             break;
20146                         }
20147                     }
20148                 }
20149             }
20150             overMapDiv = (elem == this.map.div);
20151
20152             elem = elem.parentNode;
20153         }
20154         
20155         // Logic below is the following:
20156         //
20157         // If we are over a scrollable div or not over the map div:
20158         //  * do nothing (let the browser handle scrolling)
20159         //
20160         //    otherwise 
20161         // 
20162         //    If we are over the layer div or a 'olScrollable' div:
20163         //     * zoom/in out
20164         //     then
20165         //     * kill event (so as not to also scroll the page after zooming)
20166         //
20167         //       otherwise
20168         //
20169         //       Kill the event (dont scroll the page if we wheel over the 
20170         //        layerswitcher or the pan/zoom control)
20171         //
20172         if (!overScrollableDiv && overMapDiv) {
20173             if (allowScroll) {
20174                 var delta = 0;
20175                 
20176                 if (e.wheelDelta) {
20177                     delta = e.wheelDelta;
20178                     if (delta % 160 === 0) {
20179                         // opera have steps of 160 instead of 120
20180                         delta = delta * 0.75;
20181                     }
20182                     delta = delta / 120;
20183                 } else if (e.detail) {
20184                     // detail in Firefox on OS X is 1/3 of Windows
20185                     // so force delta 1 / -1
20186                     delta = - (e.detail / Math.abs(e.detail));
20187                 }
20188                 this.delta += delta;
20189
20190                 window.clearTimeout(this._timeoutId);
20191                 if(this.interval && Math.abs(this.delta) < this.maxDelta) {
20192                     // store e because window.event might change during delay
20193                     var evt = OpenLayers.Util.extend({}, e);
20194                     this._timeoutId = window.setTimeout(
20195                         OpenLayers.Function.bind(function(){
20196                             this.wheelZoom(evt);
20197                         }, this),
20198                         this.interval
20199                     );
20200                 } else {
20201                     this.wheelZoom(e);
20202                 }
20203             }
20204             OpenLayers.Event.stop(e);
20205         }
20206     },
20207
20208     /**
20209      * Method: wheelZoom
20210      * Given the wheel event, we carry out the appropriate zooming in or out,
20211      *     based on the 'wheelDelta' or 'detail' property of the event.
20212      * 
20213      * Parameters:
20214      * e - {Event}
20215      */
20216     wheelZoom: function(e) {
20217         var delta = this.delta;
20218         this.delta = 0;
20219         
20220         if (delta) {
20221             e.xy = this.map.events.getMousePosition(e);
20222             if (delta < 0) {
20223                 this.callback("down",
20224                     [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]);
20225             } else {
20226                 this.callback("up",
20227                     [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]);
20228             }
20229         }
20230     },
20231     
20232     /**
20233      * Method: activate 
20234      */
20235     activate: function (evt) {
20236         if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
20237             //register mousewheel events specifically on the window and document
20238             var wheelListener = this.wheelListener;
20239             OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener);
20240             OpenLayers.Event.observe(window, "mousewheel", wheelListener);
20241             OpenLayers.Event.observe(document, "mousewheel", wheelListener);
20242             return true;
20243         } else {
20244             return false;
20245         }
20246     },
20247
20248     /**
20249      * Method: deactivate 
20250      */
20251     deactivate: function (evt) {
20252         if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
20253             // unregister mousewheel events specifically on the window and document
20254             var wheelListener = this.wheelListener;
20255             OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener);
20256             OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener);
20257             OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener);
20258             return true;
20259         } else {
20260             return false;
20261         }
20262     },
20263
20264     CLASS_NAME: "OpenLayers.Handler.MouseWheel"
20265 });
20266 /* ======================================================================
20267     OpenLayers/Geometry/MultiLineString.js
20268    ====================================================================== */
20269
20270 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
20271  * full list of contributors). Published under the 2-clause BSD license.
20272  * See license.txt in the OpenLayers distribution or repository for the
20273  * full text of the license. */
20274
20275 /**
20276  * @requires OpenLayers/Geometry/Collection.js
20277  * @requires OpenLayers/Geometry/LineString.js
20278  */
20279
20280 /**
20281  * Class: OpenLayers.Geometry.MultiLineString
20282  * A MultiLineString is a geometry with multiple <OpenLayers.Geometry.LineString>
20283  * components.
20284  * 
20285  * Inherits from:
20286  *  - <OpenLayers.Geometry.Collection>
20287  *  - <OpenLayers.Geometry> 
20288  */
20289 OpenLayers.Geometry.MultiLineString = OpenLayers.Class(
20290   OpenLayers.Geometry.Collection, {
20291
20292     /**
20293      * Property: componentTypes
20294      * {Array(String)} An array of class names representing the types of
20295      * components that the collection can include.  A null value means the
20296      * component types are not restricted.
20297      */
20298     componentTypes: ["OpenLayers.Geometry.LineString"],
20299
20300     /**
20301      * Constructor: OpenLayers.Geometry.MultiLineString
20302      * Constructor for a MultiLineString Geometry.
20303      *
20304      * Parameters: 
20305      * components - {Array(<OpenLayers.Geometry.LineString>)} 
20306      *
20307      */
20308     
20309     /**
20310      * Method: split
20311      * Use this geometry (the source) to attempt to split a target geometry.
20312      * 
20313      * Parameters:
20314      * geometry - {<OpenLayers.Geometry>} The target geometry.
20315      * options - {Object} Properties of this object will be used to determine
20316      *     how the split is conducted.
20317      *
20318      * Valid options:
20319      * mutual - {Boolean} Split the source geometry in addition to the target
20320      *     geometry.  Default is false.
20321      * edge - {Boolean} Allow splitting when only edges intersect.  Default is
20322      *     true.  If false, a vertex on the source must be within the tolerance
20323      *     distance of the intersection to be considered a split.
20324      * tolerance - {Number} If a non-null value is provided, intersections
20325      *     within the tolerance distance of an existing vertex on the source
20326      *     will be assumed to occur at the vertex.
20327      * 
20328      * Returns:
20329      * {Array} A list of geometries (of this same type as the target) that
20330      *     result from splitting the target with the source geometry.  The
20331      *     source and target geometry will remain unmodified.  If no split
20332      *     results, null will be returned.  If mutual is true and a split
20333      *     results, return will be an array of two arrays - the first will be
20334      *     all geometries that result from splitting the source geometry and
20335      *     the second will be all geometries that result from splitting the
20336      *     target geometry.
20337      */
20338     split: function(geometry, options) {
20339         var results = null;
20340         var mutual = options && options.mutual;
20341         var splits, sourceLine, sourceLines, sourceSplit, targetSplit;
20342         var sourceParts = [];
20343         var targetParts = [geometry];
20344         for(var i=0, len=this.components.length; i<len; ++i) {
20345             sourceLine = this.components[i];
20346             sourceSplit = false;
20347             for(var j=0; j < targetParts.length; ++j) { 
20348                 splits = sourceLine.split(targetParts[j], options);
20349                 if(splits) {
20350                     if(mutual) {
20351                         sourceLines = splits[0];
20352                         for(var k=0, klen=sourceLines.length; k<klen; ++k) {
20353                             if(k===0 && sourceParts.length) {
20354                                 sourceParts[sourceParts.length-1].addComponent(
20355                                     sourceLines[k]
20356                                 );
20357                             } else {
20358                                 sourceParts.push(
20359                                     new OpenLayers.Geometry.MultiLineString([
20360                                         sourceLines[k]
20361                                     ])
20362                                 );
20363                             }
20364                         }
20365                         sourceSplit = true;
20366                         splits = splits[1];
20367                     }
20368                     if(splits.length) {
20369                         // splice in new target parts
20370                         splits.unshift(j, 1);
20371                         Array.prototype.splice.apply(targetParts, splits);
20372                         break;
20373                     }
20374                 }
20375             }
20376             if(!sourceSplit) {
20377                 // source line was not hit
20378                 if(sourceParts.length) {
20379                     // add line to existing multi
20380                     sourceParts[sourceParts.length-1].addComponent(
20381                         sourceLine.clone()
20382                     );
20383                 } else {
20384                     // create a fresh multi
20385                     sourceParts = [
20386                         new OpenLayers.Geometry.MultiLineString(
20387                             sourceLine.clone()
20388                         )
20389                     ];
20390                 }
20391             }
20392         }
20393         if(sourceParts && sourceParts.length > 1) {
20394             sourceSplit = true;
20395         } else {
20396             sourceParts = [];
20397         }
20398         if(targetParts && targetParts.length > 1) {
20399             targetSplit = true;
20400         } else {
20401             targetParts = [];
20402         }
20403         if(sourceSplit || targetSplit) {
20404             if(mutual) {
20405                 results = [sourceParts, targetParts];
20406             } else {
20407                 results = targetParts;
20408             }
20409         }
20410         return results;
20411     },
20412     
20413     /**
20414      * Method: splitWith
20415      * Split this geometry (the target) with the given geometry (the source).
20416      *
20417      * Parameters:
20418      * geometry - {<OpenLayers.Geometry>} A geometry used to split this
20419      *     geometry (the source).
20420      * options - {Object} Properties of this object will be used to determine
20421      *     how the split is conducted.
20422      *
20423      * Valid options:
20424      * mutual - {Boolean} Split the source geometry in addition to the target
20425      *     geometry.  Default is false.
20426      * edge - {Boolean} Allow splitting when only edges intersect.  Default is
20427      *     true.  If false, a vertex on the source must be within the tolerance
20428      *     distance of the intersection to be considered a split.
20429      * tolerance - {Number} If a non-null value is provided, intersections
20430      *     within the tolerance distance of an existing vertex on the source
20431      *     will be assumed to occur at the vertex.
20432      * 
20433      * Returns:
20434      * {Array} A list of geometries (of this same type as the target) that
20435      *     result from splitting the target with the source geometry.  The
20436      *     source and target geometry will remain unmodified.  If no split
20437      *     results, null will be returned.  If mutual is true and a split
20438      *     results, return will be an array of two arrays - the first will be
20439      *     all geometries that result from splitting the source geometry and
20440      *     the second will be all geometries that result from splitting the
20441      *     target geometry.
20442      */
20443     splitWith: function(geometry, options) {
20444         var results = null;
20445         var mutual = options && options.mutual;
20446         var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;
20447         if(geometry instanceof OpenLayers.Geometry.LineString) {
20448             targetParts = [];
20449             sourceParts = [geometry];
20450             for(var i=0, len=this.components.length; i<len; ++i) {
20451                 targetSplit = false;
20452                 targetLine = this.components[i];
20453                 for(var j=0; j<sourceParts.length; ++j) {
20454                     splits = sourceParts[j].split(targetLine, options);
20455                     if(splits) {
20456                         if(mutual) {
20457                             sourceLines = splits[0];
20458                             if(sourceLines.length) {
20459                                 // splice in new source parts
20460                                 sourceLines.unshift(j, 1);
20461                                 Array.prototype.splice.apply(sourceParts, sourceLines);
20462                                 j += sourceLines.length - 2;
20463                             }
20464                             splits = splits[1];
20465                             if(splits.length === 0) {
20466                                 splits = [targetLine.clone()];
20467                             }
20468                         }
20469                         for(var k=0, klen=splits.length; k<klen; ++k) {
20470                             if(k===0 && targetParts.length) {
20471                                 targetParts[targetParts.length-1].addComponent(
20472                                     splits[k]
20473                                 );
20474                             } else {
20475                                 targetParts.push(
20476                                     new OpenLayers.Geometry.MultiLineString([
20477                                         splits[k]
20478                                     ])
20479                                 );
20480                             }
20481                         }
20482                         targetSplit = true;                    
20483                     }
20484                 }
20485                 if(!targetSplit) {
20486                     // target component was not hit
20487                     if(targetParts.length) {
20488                         // add it to any existing multi-line
20489                         targetParts[targetParts.length-1].addComponent(
20490                             targetLine.clone()
20491                         );
20492                     } else {
20493                         // or start with a fresh multi-line
20494                         targetParts = [
20495                             new OpenLayers.Geometry.MultiLineString([
20496                                 targetLine.clone()
20497                             ])
20498                         ];
20499                     }
20500                     
20501                 }
20502             }
20503         } else {
20504             results = geometry.split(this);
20505         }
20506         if(sourceParts && sourceParts.length > 1) {
20507             sourceSplit = true;
20508         } else {
20509             sourceParts = [];
20510         }
20511         if(targetParts && targetParts.length > 1) {
20512             targetSplit = true;
20513         } else {
20514             targetParts = [];
20515         }
20516         if(sourceSplit || targetSplit) {
20517             if(mutual) {
20518                 results = [sourceParts, targetParts];
20519             } else {
20520                 results = targetParts;
20521             }
20522         }
20523         return results;
20524     },
20525
20526     CLASS_NAME: "OpenLayers.Geometry.MultiLineString"
20527 });
20528 /* ======================================================================
20529     OpenLayers/Format.js
20530    ====================================================================== */
20531
20532 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
20533  * full list of contributors). Published under the 2-clause BSD license.
20534  * See license.txt in the OpenLayers distribution or repository for the
20535  * full text of the license. */
20536
20537 /**
20538  * @requires OpenLayers/BaseTypes/Class.js
20539  * @requires OpenLayers/Util.js
20540  */
20541
20542 /**
20543  * Class: OpenLayers.Format
20544  * Base class for format reading/writing a variety of formats.  Subclasses
20545  *     of OpenLayers.Format are expected to have read and write methods.
20546  */
20547 OpenLayers.Format = OpenLayers.Class({
20548     
20549     /**
20550      * Property: options
20551      * {Object} A reference to options passed to the constructor.
20552      */
20553     options: null,
20554     
20555     /**
20556      * APIProperty: externalProjection
20557      * {<OpenLayers.Projection>} When passed a externalProjection and
20558      *     internalProjection, the format will reproject the geometries it
20559      *     reads or writes. The externalProjection is the projection used by
20560      *     the content which is passed into read or which comes out of write.
20561      *     In order to reproject, a projection transformation function for the
20562      *     specified projections must be available. This support may be 
20563      *     provided via proj4js or via a custom transformation function. See
20564      *     {<OpenLayers.Projection.addTransform>} for more information on
20565      *     custom transformations.
20566      */
20567     externalProjection: null,
20568
20569     /**
20570      * APIProperty: internalProjection
20571      * {<OpenLayers.Projection>} When passed a externalProjection and
20572      *     internalProjection, the format will reproject the geometries it
20573      *     reads or writes. The internalProjection is the projection used by
20574      *     the geometries which are returned by read or which are passed into
20575      *     write.  In order to reproject, a projection transformation function
20576      *     for the specified projections must be available. This support may be
20577      *     provided via proj4js or via a custom transformation function. See
20578      *     {<OpenLayers.Projection.addTransform>} for more information on
20579      *     custom transformations.
20580      */
20581     internalProjection: null,
20582
20583     /**
20584      * APIProperty: data
20585      * {Object} When <keepData> is true, this is the parsed string sent to
20586      *     <read>.
20587      */
20588     data: null,
20589
20590     /**
20591      * APIProperty: keepData
20592      * {Object} Maintain a reference (<data>) to the most recently read data.
20593      *     Default is false.
20594      */
20595     keepData: false,
20596
20597     /**
20598      * Constructor: OpenLayers.Format
20599      * Instances of this class are not useful.  See one of the subclasses.
20600      *
20601      * Parameters:
20602      * options - {Object} An optional object with properties to set on the
20603      *           format
20604      *
20605      * Valid options:
20606      * keepData - {Boolean} If true, upon <read>, the data property will be
20607      *     set to the parsed object (e.g. the json or xml object).
20608      *
20609      * Returns:
20610      * An instance of OpenLayers.Format
20611      */
20612     initialize: function(options) {
20613         OpenLayers.Util.extend(this, options);
20614         this.options = options;
20615     },
20616     
20617     /**
20618      * APIMethod: destroy
20619      * Clean up.
20620      */
20621     destroy: function() {
20622     },
20623
20624     /**
20625      * Method: read
20626      * Read data from a string, and return an object whose type depends on the
20627      * subclass. 
20628      * 
20629      * Parameters:
20630      * data - {string} Data to read/parse.
20631      *
20632      * Returns:
20633      * Depends on the subclass
20634      */
20635     read: function(data) {
20636         throw new Error('Read not implemented.');
20637     },
20638     
20639     /**
20640      * Method: write
20641      * Accept an object, and return a string. 
20642      *
20643      * Parameters:
20644      * object - {Object} Object to be serialized
20645      *
20646      * Returns:
20647      * {String} A string representation of the object.
20648      */
20649     write: function(object) {
20650         throw new Error('Write not implemented.');
20651     },
20652
20653     CLASS_NAME: "OpenLayers.Format"
20654 });     
20655 /* ======================================================================
20656     OpenLayers/Format/XML.js
20657    ====================================================================== */
20658
20659 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
20660  * full list of contributors). Published under the 2-clause BSD license.
20661  * See license.txt in the OpenLayers distribution or repository for the
20662  * full text of the license. */
20663
20664 /**
20665  * @requires OpenLayers/Format.js
20666  */
20667
20668 /**
20669  * Class: OpenLayers.Format.XML
20670  * Read and write XML.  For cross-browser XML generation, use methods on an
20671  *     instance of the XML format class instead of on <code>document<end>.
20672  *     The DOM creation and traversing methods exposed here all mimic the
20673  *     W3C XML DOM methods.  Create a new parser with the
20674  *     <OpenLayers.Format.XML> constructor.
20675  *
20676  * Inherits from:
20677  *  - <OpenLayers.Format>
20678  */
20679 OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
20680     
20681     /**
20682      * Property: namespaces
20683      * {Object} Mapping of namespace aliases to namespace URIs.  Properties
20684      *     of this object should not be set individually.  Read-only.  All
20685      *     XML subclasses should have their own namespaces object.  Use
20686      *     <setNamespace> to add or set a namespace alias after construction.
20687      */
20688     namespaces: null,
20689     
20690     /**
20691      * Property: namespaceAlias
20692      * {Object} Mapping of namespace URI to namespace alias.  This object
20693      *     is read-only.  Use <setNamespace> to add or set a namespace alias.
20694      */
20695     namespaceAlias: null,
20696     
20697     /**
20698      * Property: defaultPrefix
20699      * {String} The default namespace alias for creating element nodes.
20700      */
20701     defaultPrefix: null,
20702     
20703     /**
20704      * Property: readers
20705      * Contains public functions, grouped by namespace prefix, that will
20706      *     be applied when a namespaced node is found matching the function
20707      *     name.  The function will be applied in the scope of this parser
20708      *     with two arguments: the node being read and a context object passed
20709      *     from the parent.
20710      */
20711     readers: {},
20712     
20713     /**
20714      * Property: writers
20715      * As a compliment to the <readers> property, this structure contains public
20716      *     writing functions grouped by namespace alias and named like the
20717      *     node names they produce.
20718      */
20719     writers: {},
20720
20721     /**
20722      * Property: xmldom
20723      * {XMLDom} If this browser uses ActiveX, this will be set to a XMLDOM
20724      *     object.  It is not intended to be a browser sniffing property.
20725      *     Instead, the xmldom property is used instead of <code>document<end>
20726      *     where namespaced node creation methods are not supported. In all
20727      *     other browsers, this remains null.
20728      */
20729     xmldom: null,
20730
20731     /**
20732      * Constructor: OpenLayers.Format.XML
20733      * Construct an XML parser.  The parser is used to read and write XML.
20734      *     Reading XML from a string returns a DOM element.  Writing XML from
20735      *     a DOM element returns a string.
20736      *
20737      * Parameters:
20738      * options - {Object} Optional object whose properties will be set on
20739      *     the object.
20740      */
20741     initialize: function(options) {
20742         if(window.ActiveXObject) {
20743             this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
20744         }
20745         OpenLayers.Format.prototype.initialize.apply(this, [options]);
20746         // clone the namespace object and set all namespace aliases
20747         this.namespaces = OpenLayers.Util.extend({}, this.namespaces);
20748         this.namespaceAlias = {};
20749         for(var alias in this.namespaces) {
20750             this.namespaceAlias[this.namespaces[alias]] = alias;
20751         }
20752     },
20753     
20754     /**
20755      * APIMethod: destroy
20756      * Clean up.
20757      */
20758     destroy: function() {
20759         this.xmldom = null;
20760         OpenLayers.Format.prototype.destroy.apply(this, arguments);
20761     },
20762     
20763     /**
20764      * Method: setNamespace
20765      * Set a namespace alias and URI for the format.
20766      *
20767      * Parameters:
20768      * alias - {String} The namespace alias (prefix).
20769      * uri - {String} The namespace URI.
20770      */
20771     setNamespace: function(alias, uri) {
20772         this.namespaces[alias] = uri;
20773         this.namespaceAlias[uri] = alias;
20774     },
20775
20776     /**
20777      * APIMethod: read
20778      * Deserialize a XML string and return a DOM node.
20779      *
20780      * Parameters:
20781      * text - {String} A XML string
20782      
20783      * Returns:
20784      * {DOMElement} A DOM node
20785      */
20786     read: function(text) {
20787         var index = text.indexOf('<');
20788         if(index > 0) {
20789             text = text.substring(index);
20790         }
20791         var node = OpenLayers.Util.Try(
20792             OpenLayers.Function.bind((
20793                 function() {
20794                     var xmldom;
20795                     /**
20796                      * Since we want to be able to call this method on the prototype
20797                      * itself, this.xmldom may not exist even if in IE.
20798                      */
20799                     if(window.ActiveXObject && !this.xmldom) {
20800                         xmldom = new ActiveXObject("Microsoft.XMLDOM");
20801                     } else {
20802                         xmldom = this.xmldom;
20803                         
20804                     }
20805                     xmldom.loadXML(text);
20806                     return xmldom;
20807                 }
20808             ), this),
20809             function() {
20810                 return new DOMParser().parseFromString(text, 'text/xml');
20811             },
20812             function() {
20813                 var req = new XMLHttpRequest();
20814                 req.open("GET", "data:" + "text/xml" +
20815                          ";charset=utf-8," + encodeURIComponent(text), false);
20816                 if(req.overrideMimeType) {
20817                     req.overrideMimeType("text/xml");
20818                 }
20819                 req.send(null);
20820                 return req.responseXML;
20821             }
20822         );
20823
20824         if(this.keepData) {
20825             this.data = node;
20826         }
20827
20828         return node;
20829     },
20830
20831     /**
20832      * APIMethod: write
20833      * Serialize a DOM node into a XML string.
20834      * 
20835      * Parameters:
20836      * node - {DOMElement} A DOM node.
20837      *
20838      * Returns:
20839      * {String} The XML string representation of the input node.
20840      */
20841     write: function(node) {
20842         var data;
20843         if(this.xmldom) {
20844             data = node.xml;
20845         } else {
20846             var serializer = new XMLSerializer();
20847             if (node.nodeType == 1) {
20848                 // Add nodes to a document before serializing. Everything else
20849                 // is serialized as is. This may need more work. See #1218 .
20850                 var doc = document.implementation.createDocument("", "", null);
20851                 if (doc.importNode) {
20852                     node = doc.importNode(node, true);
20853                 }
20854                 doc.appendChild(node);
20855                 data = serializer.serializeToString(doc);
20856             } else {
20857                 data = serializer.serializeToString(node);
20858             }
20859         }
20860         return data;
20861     },
20862
20863     /**
20864      * APIMethod: createElementNS
20865      * Create a new element with namespace.  This node can be appended to
20866      *     another node with the standard node.appendChild method.  For
20867      *     cross-browser support, this method must be used instead of
20868      *     document.createElementNS.
20869      *
20870      * Parameters:
20871      * uri - {String} Namespace URI for the element.
20872      * name - {String} The qualified name of the element (prefix:localname).
20873      * 
20874      * Returns:
20875      * {Element} A DOM element with namespace.
20876      */
20877     createElementNS: function(uri, name) {
20878         var element;
20879         if(this.xmldom) {
20880             if(typeof uri == "string") {
20881                 element = this.xmldom.createNode(1, name, uri);
20882             } else {
20883                 element = this.xmldom.createNode(1, name, "");
20884             }
20885         } else {
20886             element = document.createElementNS(uri, name);
20887         }
20888         return element;
20889     },
20890
20891     /**
20892      * APIMethod: createDocumentFragment
20893      * Create a document fragment node that can be appended to another node
20894      *     created by createElementNS.  This will call 
20895      *     document.createDocumentFragment outside of IE.  In IE, the ActiveX
20896      *     object's createDocumentFragment method is used.
20897      *
20898      * Returns:
20899      * {Element} A document fragment.
20900      */
20901     createDocumentFragment: function() {
20902         var element;
20903         if (this.xmldom) {
20904             element = this.xmldom.createDocumentFragment();
20905         } else {
20906             element = document.createDocumentFragment();
20907         }
20908         return element;
20909     },
20910
20911     /**
20912      * APIMethod: createTextNode
20913      * Create a text node.  This node can be appended to another node with
20914      *     the standard node.appendChild method.  For cross-browser support,
20915      *     this method must be used instead of document.createTextNode.
20916      * 
20917      * Parameters:
20918      * text - {String} The text of the node.
20919      * 
20920      * Returns: 
20921      * {DOMElement} A DOM text node.
20922      */
20923     createTextNode: function(text) {
20924         var node;
20925         if (typeof text !== "string") {
20926             text = String(text);
20927         }
20928         if(this.xmldom) {
20929             node = this.xmldom.createTextNode(text);
20930         } else {
20931             node = document.createTextNode(text);
20932         }
20933         return node;
20934     },
20935
20936     /**
20937      * APIMethod: getElementsByTagNameNS
20938      * Get a list of elements on a node given the namespace URI and local name.
20939      *     To return all nodes in a given namespace, use '*' for the name
20940      *     argument.  To return all nodes of a given (local) name, regardless
20941      *     of namespace, use '*' for the uri argument.
20942      * 
20943      * Parameters:
20944      * node - {Element} Node on which to search for other nodes.
20945      * uri - {String} Namespace URI.
20946      * name - {String} Local name of the tag (without the prefix).
20947      * 
20948      * Returns:
20949      * {NodeList} A node list or array of elements.
20950      */
20951     getElementsByTagNameNS: function(node, uri, name) {
20952         var elements = [];
20953         if(node.getElementsByTagNameNS) {
20954             elements = node.getElementsByTagNameNS(uri, name);
20955         } else {
20956             // brute force method
20957             var allNodes = node.getElementsByTagName("*");
20958             var potentialNode, fullName;
20959             for(var i=0, len=allNodes.length; i<len; ++i) {
20960                 potentialNode = allNodes[i];
20961                 fullName = (potentialNode.prefix) ?
20962                            (potentialNode.prefix + ":" + name) : name;
20963                 if((name == "*") || (fullName == potentialNode.nodeName)) {
20964                     if((uri == "*") || (uri == potentialNode.namespaceURI)) {
20965                         elements.push(potentialNode);
20966                     }
20967                 }
20968             }
20969         }
20970         return elements;
20971     },
20972
20973     /**
20974      * APIMethod: getAttributeNodeNS
20975      * Get an attribute node given the namespace URI and local name.
20976      * 
20977      * Parameters:
20978      * node - {Element} Node on which to search for attribute nodes.
20979      * uri - {String} Namespace URI.
20980      * name - {String} Local name of the attribute (without the prefix).
20981      * 
20982      * Returns:
20983      * {DOMElement} An attribute node or null if none found.
20984      */
20985     getAttributeNodeNS: function(node, uri, name) {
20986         var attributeNode = null;
20987         if(node.getAttributeNodeNS) {
20988             attributeNode = node.getAttributeNodeNS(uri, name);
20989         } else {
20990             var attributes = node.attributes;
20991             var potentialNode, fullName;
20992             for(var i=0, len=attributes.length; i<len; ++i) {
20993                 potentialNode = attributes[i];
20994                 if(potentialNode.namespaceURI == uri) {
20995                     fullName = (potentialNode.prefix) ?
20996                                (potentialNode.prefix + ":" + name) : name;
20997                     if(fullName == potentialNode.nodeName) {
20998                         attributeNode = potentialNode;
20999                         break;
21000                     }
21001                 }
21002             }
21003         }
21004         return attributeNode;
21005     },
21006
21007     /**
21008      * APIMethod: getAttributeNS
21009      * Get an attribute value given the namespace URI and local name.
21010      * 
21011      * Parameters:
21012      * node - {Element} Node on which to search for an attribute.
21013      * uri - {String} Namespace URI.
21014      * name - {String} Local name of the attribute (without the prefix).
21015      * 
21016      * Returns:
21017      * {String} An attribute value or and empty string if none found.
21018      */
21019     getAttributeNS: function(node, uri, name) {
21020         var attributeValue = "";
21021         if(node.getAttributeNS) {
21022             attributeValue = node.getAttributeNS(uri, name) || "";
21023         } else {
21024             var attributeNode = this.getAttributeNodeNS(node, uri, name);
21025             if(attributeNode) {
21026                 attributeValue = attributeNode.nodeValue;
21027             }
21028         }
21029         return attributeValue;
21030     },
21031     
21032     /**
21033      * APIMethod: getChildValue
21034      * Get the textual value of the node if it exists, or return an
21035      *     optional default string.  Returns an empty string if no first child
21036      *     exists and no default value is supplied.
21037      *
21038      * Parameters:
21039      * node - {DOMElement} The element used to look for a first child value.
21040      * def - {String} Optional string to return in the event that no
21041      *     first child value exists.
21042      *
21043      * Returns:
21044      * {String} The value of the first child of the given node.
21045      */
21046     getChildValue: function(node, def) {
21047         var value = def || "";
21048         if(node) {
21049             for(var child=node.firstChild; child; child=child.nextSibling) {
21050                 switch(child.nodeType) {
21051                     case 3: // text node
21052                     case 4: // cdata section
21053                         value += child.nodeValue;
21054                 }
21055             }
21056         }
21057         return value;
21058     },
21059
21060     /**
21061      * APIMethod: isSimpleContent
21062      * Test if the given node has only simple content (i.e. no child element
21063      *     nodes).
21064      *
21065      * Parameters:
21066      * node - {DOMElement} An element node.
21067      *
21068      * Returns:
21069      * {Boolean} The node has no child element nodes (nodes of type 1). 
21070      */
21071     isSimpleContent: function(node) {
21072         var simple = true;
21073         for(var child=node.firstChild; child; child=child.nextSibling) {
21074             if(child.nodeType === 1) {
21075                 simple = false;
21076                 break;
21077             }
21078         }
21079         return simple;
21080     },
21081     
21082     /**
21083      * APIMethod: contentType
21084      * Determine the content type for a given node.
21085      *
21086      * Parameters:
21087      * node - {DOMElement}
21088      *
21089      * Returns:
21090      * {Integer} One of OpenLayers.Format.XML.CONTENT_TYPE.{EMPTY,SIMPLE,COMPLEX,MIXED}
21091      *     if the node has no, simple, complex, or mixed content.
21092      */
21093     contentType: function(node) {
21094         var simple = false,
21095             complex = false;
21096             
21097         var type = OpenLayers.Format.XML.CONTENT_TYPE.EMPTY;
21098
21099         for(var child=node.firstChild; child; child=child.nextSibling) {
21100             switch(child.nodeType) {
21101                 case 1: // element
21102                     complex = true;
21103                     break;
21104                 case 8: // comment
21105                     break;
21106                 default:
21107                     simple = true;
21108             }
21109             if(complex && simple) {
21110                 break;
21111             }
21112         }
21113         
21114         if(complex && simple) {
21115             type = OpenLayers.Format.XML.CONTENT_TYPE.MIXED;
21116         } else if(complex) {
21117             return OpenLayers.Format.XML.CONTENT_TYPE.COMPLEX;
21118         } else if(simple) {
21119             return OpenLayers.Format.XML.CONTENT_TYPE.SIMPLE;
21120         }
21121         return type;
21122     },
21123
21124     /**
21125      * APIMethod: hasAttributeNS
21126      * Determine whether a node has a particular attribute matching the given
21127      *     name and namespace.
21128      * 
21129      * Parameters:
21130      * node - {Element} Node on which to search for an attribute.
21131      * uri - {String} Namespace URI.
21132      * name - {String} Local name of the attribute (without the prefix).
21133      * 
21134      * Returns:
21135      * {Boolean} The node has an attribute matching the name and namespace.
21136      */
21137     hasAttributeNS: function(node, uri, name) {
21138         var found = false;
21139         if(node.hasAttributeNS) {
21140             found = node.hasAttributeNS(uri, name);
21141         } else {
21142             found = !!this.getAttributeNodeNS(node, uri, name);
21143         }
21144         return found;
21145     },
21146     
21147     /**
21148      * APIMethod: setAttributeNS
21149      * Adds a new attribute or changes the value of an attribute with the given
21150      *     namespace and name.
21151      *
21152      * Parameters:
21153      * node - {Element} Element node on which to set the attribute.
21154      * uri - {String} Namespace URI for the attribute.
21155      * name - {String} Qualified name (prefix:localname) for the attribute.
21156      * value - {String} Attribute value.
21157      */
21158     setAttributeNS: function(node, uri, name, value) {
21159         if(node.setAttributeNS) {
21160             node.setAttributeNS(uri, name, value);
21161         } else {
21162             if(this.xmldom) {
21163                 if(uri) {
21164                     var attribute = node.ownerDocument.createNode(
21165                         2, name, uri
21166                     );
21167                     attribute.nodeValue = value;
21168                     node.setAttributeNode(attribute);
21169                 } else {
21170                     node.setAttribute(name, value);
21171                 }
21172             } else {
21173                 throw "setAttributeNS not implemented";
21174             }
21175         }
21176     },
21177
21178     /**
21179      * Method: createElementNSPlus
21180      * Shorthand for creating namespaced elements with optional attributes and
21181      *     child text nodes.
21182      *
21183      * Parameters:
21184      * name - {String} The qualified node name.
21185      * options - {Object} Optional object for node configuration.
21186      *
21187      * Valid options:
21188      * uri - {String} Optional namespace uri for the element - supply a prefix
21189      *     instead if the namespace uri is a property of the format's namespace
21190      *     object.
21191      * attributes - {Object} Optional attributes to be set using the
21192      *     <setAttributes> method.
21193      * value - {String} Optional text to be appended as a text node.
21194      *
21195      * Returns:
21196      * {Element} An element node.
21197      */
21198     createElementNSPlus: function(name, options) {
21199         options = options || {};
21200         // order of prefix preference
21201         // 1. in the uri option
21202         // 2. in the prefix option
21203         // 3. in the qualified name
21204         // 4. from the defaultPrefix
21205         var uri = options.uri || this.namespaces[options.prefix];
21206         if(!uri) {
21207             var loc = name.indexOf(":");
21208             uri = this.namespaces[name.substring(0, loc)];
21209         }
21210         if(!uri) {
21211             uri = this.namespaces[this.defaultPrefix];
21212         }
21213         var node = this.createElementNS(uri, name);
21214         if(options.attributes) {
21215             this.setAttributes(node, options.attributes);
21216         }
21217         var value = options.value;
21218         if(value != null) {
21219             node.appendChild(this.createTextNode(value));
21220         }
21221         return node;
21222     },
21223     
21224     /**
21225      * Method: setAttributes
21226      * Set multiple attributes given key value pairs from an object.
21227      *
21228      * Parameters:
21229      * node - {Element} An element node.
21230      * obj - {Object || Array} An object whose properties represent attribute
21231      *     names and values represent attribute values.  If an attribute name
21232      *     is a qualified name ("prefix:local"), the prefix will be looked up
21233      *     in the parsers {namespaces} object.  If the prefix is found,
21234      *     setAttributeNS will be used instead of setAttribute.
21235      */
21236     setAttributes: function(node, obj) {
21237         var value, uri;
21238         for(var name in obj) {
21239             if(obj[name] != null && obj[name].toString) {
21240                 value = obj[name].toString();
21241                 // check for qualified attribute name ("prefix:local")
21242                 uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null;
21243                 this.setAttributeNS(node, uri, name, value);
21244             }
21245         }
21246     },
21247
21248     /**
21249      * Method: readNode
21250      * Shorthand for applying one of the named readers given the node
21251      *     namespace and local name.  Readers take two args (node, obj) and
21252      *     generally extend or modify the second.
21253      *
21254      * Parameters:
21255      * node - {DOMElement} The node to be read (required).
21256      * obj - {Object} The object to be modified (optional).
21257      *
21258      * Returns:
21259      * {Object} The input object, modified (or a new one if none was provided).
21260      */
21261     readNode: function(node, obj) {
21262         if(!obj) {
21263             obj = {};
21264         }
21265         var group = this.readers[node.namespaceURI ? this.namespaceAlias[node.namespaceURI]: this.defaultPrefix];
21266         if(group) {
21267             var local = node.localName || node.nodeName.split(":").pop();
21268             var reader = group[local] || group["*"];
21269             if(reader) {
21270                 reader.apply(this, [node, obj]);
21271             }
21272         }
21273         return obj;
21274     },
21275
21276     /**
21277      * Method: readChildNodes
21278      * Shorthand for applying the named readers to all children of a node.
21279      *     For each child of type 1 (element), <readSelf> is called.
21280      *
21281      * Parameters:
21282      * node - {DOMElement} The node to be read (required).
21283      * obj - {Object} The object to be modified (optional).
21284      *
21285      * Returns:
21286      * {Object} The input object, modified.
21287      */
21288     readChildNodes: function(node, obj) {
21289         if(!obj) {
21290             obj = {};
21291         }
21292         var children = node.childNodes;
21293         var child;
21294         for(var i=0, len=children.length; i<len; ++i) {
21295             child = children[i];
21296             if(child.nodeType == 1) {
21297                 this.readNode(child, obj);
21298             }
21299         }
21300         return obj;
21301     },
21302
21303     /**
21304      * Method: writeNode
21305      * Shorthand for applying one of the named writers and appending the
21306      *     results to a node.  If a qualified name is not provided for the
21307      *     second argument (and a local name is used instead), the namespace
21308      *     of the parent node will be assumed.
21309      *
21310      * Parameters:
21311      * name - {String} The name of a node to generate.  If a qualified name
21312      *     (e.g. "pre:Name") is used, the namespace prefix is assumed to be
21313      *     in the <writers> group.  If a local name is used (e.g. "Name") then
21314      *     the namespace of the parent is assumed.  If a local name is used
21315      *     and no parent is supplied, then the default namespace is assumed.
21316      * obj - {Object} Structure containing data for the writer.
21317      * parent - {DOMElement} Result will be appended to this node.  If no parent
21318      *     is supplied, the node will not be appended to anything.
21319      *
21320      * Returns:
21321      * {DOMElement} The child node.
21322      */
21323     writeNode: function(name, obj, parent) {
21324         var prefix, local;
21325         var split = name.indexOf(":");
21326         if(split > 0) {
21327             prefix = name.substring(0, split);
21328             local = name.substring(split + 1);
21329         } else {
21330             if(parent) {
21331                 prefix = this.namespaceAlias[parent.namespaceURI];
21332             } else {
21333                 prefix = this.defaultPrefix;
21334             }
21335             local = name;
21336         }
21337         var child = this.writers[prefix][local].apply(this, [obj]);
21338         if(parent) {
21339             parent.appendChild(child);
21340         }
21341         return child;
21342     },
21343
21344     /**
21345      * APIMethod: getChildEl
21346      * Get the first child element.  Optionally only return the first child
21347      *     if it matches the given name and namespace URI.
21348      *
21349      * Parameters:
21350      * node - {DOMElement} The parent node.
21351      * name - {String} Optional node name (local) to search for.
21352      * uri - {String} Optional namespace URI to search for.
21353      *
21354      * Returns:
21355      * {DOMElement} The first child.  Returns null if no element is found, if
21356      *     something significant besides an element is found, or if the element
21357      *     found does not match the optional name and uri.
21358      */
21359     getChildEl: function(node, name, uri) {
21360         return node && this.getThisOrNextEl(node.firstChild, name, uri);
21361     },
21362     
21363     /**
21364      * APIMethod: getNextEl
21365      * Get the next sibling element.  Optionally get the first sibling only
21366      *     if it matches the given local name and namespace URI.
21367      *
21368      * Parameters:
21369      * node - {DOMElement} The node.
21370      * name - {String} Optional local name of the sibling to search for.
21371      * uri - {String} Optional namespace URI of the sibling to search for.
21372      *
21373      * Returns:
21374      * {DOMElement} The next sibling element.  Returns null if no element is
21375      *     found, something significant besides an element is found, or the
21376      *     found element does not match the optional name and uri.
21377      */
21378     getNextEl: function(node, name, uri) {
21379         return node && this.getThisOrNextEl(node.nextSibling, name, uri);
21380     },
21381     
21382     /**
21383      * Method: getThisOrNextEl
21384      * Return this node or the next element node.  Optionally get the first
21385      *     sibling with the given local name or namespace URI.
21386      *
21387      * Parameters:
21388      * node - {DOMElement} The node.
21389      * name - {String} Optional local name of the sibling to search for.
21390      * uri - {String} Optional namespace URI of the sibling to search for.
21391      *
21392      * Returns:
21393      * {DOMElement} The next sibling element.  Returns null if no element is
21394      *     found, something significant besides an element is found, or the
21395      *     found element does not match the query.
21396      */
21397     getThisOrNextEl: function(node, name, uri) {
21398         outer: for(var sibling=node; sibling; sibling=sibling.nextSibling) {
21399             switch(sibling.nodeType) {
21400                 case 1: // Element
21401                     if((!name || name === (sibling.localName || sibling.nodeName.split(":").pop())) &&
21402                        (!uri || uri === sibling.namespaceURI)) {
21403                         // matches
21404                         break outer;
21405                     }
21406                     sibling = null;
21407                     break outer;
21408                 case 3: // Text
21409                     if(/^\s*$/.test(sibling.nodeValue)) {
21410                         break;
21411                     }
21412                 case 4: // CDATA
21413                 case 6: // ENTITY_NODE
21414                 case 12: // NOTATION_NODE
21415                 case 10: // DOCUMENT_TYPE_NODE
21416                 case 11: // DOCUMENT_FRAGMENT_NODE
21417                     sibling = null;
21418                     break outer;
21419             } // ignore comments and processing instructions
21420         }
21421         return sibling || null;
21422     },
21423     
21424     /**
21425      * APIMethod: lookupNamespaceURI
21426      * Takes a prefix and returns the namespace URI associated with it on the given
21427      *     node if found (and null if not). Supplying null for the prefix will
21428      *     return the default namespace.
21429      *
21430      * For browsers that support it, this calls the native lookupNamesapceURI
21431      *     function.  In other browsers, this is an implementation of
21432      *     http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
21433      *
21434      * For browsers that don't support the attribute.ownerElement property, this
21435      *     method cannot be called on attribute nodes.
21436      *     
21437      * Parameters:
21438      * node - {DOMElement} The node from which to start looking.
21439      * prefix - {String} The prefix to lookup or null to lookup the default namespace.
21440      * 
21441      * Returns:
21442      * {String} The namespace URI for the given prefix.  Returns null if the prefix
21443      *     cannot be found or the node is the wrong type.
21444      */
21445     lookupNamespaceURI: function(node, prefix) {
21446         var uri = null;
21447         if(node) {
21448             if(node.lookupNamespaceURI) {
21449                 uri = node.lookupNamespaceURI(prefix);
21450             } else {
21451                 outer: switch(node.nodeType) {
21452                     case 1: // ELEMENT_NODE
21453                         if(node.namespaceURI !== null && node.prefix === prefix) {
21454                             uri = node.namespaceURI;
21455                             break outer;
21456                         }
21457                         var len = node.attributes.length;
21458                         if(len) {
21459                             var attr;
21460                             for(var i=0; i<len; ++i) {
21461                                 attr = node.attributes[i];
21462                                 if(attr.prefix === "xmlns" && attr.name === "xmlns:" + prefix) {
21463                                     uri = attr.value || null;
21464                                     break outer;
21465                                 } else if(attr.name === "xmlns" && prefix === null) {
21466                                     uri = attr.value || null;
21467                                     break outer;
21468                                 }
21469                             }
21470                         }
21471                         uri = this.lookupNamespaceURI(node.parentNode, prefix);
21472                         break outer;
21473                     case 2: // ATTRIBUTE_NODE
21474                         uri = this.lookupNamespaceURI(node.ownerElement, prefix);
21475                         break outer;
21476                     case 9: // DOCUMENT_NODE
21477                         uri = this.lookupNamespaceURI(node.documentElement, prefix);
21478                         break outer;
21479                     case 6: // ENTITY_NODE
21480                     case 12: // NOTATION_NODE
21481                     case 10: // DOCUMENT_TYPE_NODE
21482                     case 11: // DOCUMENT_FRAGMENT_NODE
21483                         break outer;
21484                     default: 
21485                         // TEXT_NODE (3), CDATA_SECTION_NODE (4), ENTITY_REFERENCE_NODE (5),
21486                         // PROCESSING_INSTRUCTION_NODE (7), COMMENT_NODE (8)
21487                         uri =  this.lookupNamespaceURI(node.parentNode, prefix);
21488                         break outer;
21489                 }
21490             }
21491         }
21492         return uri;
21493     },
21494     
21495     /**
21496      * Method: getXMLDoc
21497      * Get an XML document for nodes that are not supported in HTML (e.g.
21498      * createCDATASection). On IE, this will either return an existing or
21499      * create a new <xmldom> on the instance. On other browsers, this will
21500      * either return an existing or create a new shared document (see
21501      * <OpenLayers.Format.XML.document>).
21502      *
21503      * Returns:
21504      * {XMLDocument}
21505      */
21506     getXMLDoc: function() {
21507         if (!OpenLayers.Format.XML.document && !this.xmldom) {
21508             if (document.implementation && document.implementation.createDocument) {
21509                 OpenLayers.Format.XML.document =
21510                     document.implementation.createDocument("", "", null);
21511             } else if (!this.xmldom && window.ActiveXObject) {
21512                 this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
21513             }
21514         }
21515         return OpenLayers.Format.XML.document || this.xmldom;
21516     },
21517
21518     CLASS_NAME: "OpenLayers.Format.XML" 
21519
21520 });     
21521
21522 OpenLayers.Format.XML.CONTENT_TYPE = {EMPTY: 0, SIMPLE: 1, COMPLEX: 2, MIXED: 3};
21523
21524 /**
21525  * APIFunction: OpenLayers.Format.XML.lookupNamespaceURI
21526  * Takes a prefix and returns the namespace URI associated with it on the given
21527  *     node if found (and null if not). Supplying null for the prefix will
21528  *     return the default namespace.
21529  *
21530  * For browsers that support it, this calls the native lookupNamesapceURI
21531  *     function.  In other browsers, this is an implementation of
21532  *     http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI.
21533  *
21534  * For browsers that don't support the attribute.ownerElement property, this
21535  *     method cannot be called on attribute nodes.
21536  *     
21537  * Parameters:
21538  * node - {DOMElement} The node from which to start looking.
21539  * prefix - {String} The prefix to lookup or null to lookup the default namespace.
21540  * 
21541  * Returns:
21542  * {String} The namespace URI for the given prefix.  Returns null if the prefix
21543  *     cannot be found or the node is the wrong type.
21544  */
21545 OpenLayers.Format.XML.lookupNamespaceURI = OpenLayers.Function.bind(
21546     OpenLayers.Format.XML.prototype.lookupNamespaceURI,
21547     OpenLayers.Format.XML.prototype
21548 );
21549
21550 /**
21551  * Property: OpenLayers.Format.XML.document
21552  * {XMLDocument} XML document to reuse for creating non-HTML compliant nodes,
21553  * like document.createCDATASection.
21554  */
21555 OpenLayers.Format.XML.document = null;
21556 /* ======================================================================
21557     OpenLayers/Format/OGCExceptionReport.js
21558    ====================================================================== */
21559
21560 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
21561  * full list of contributors). Published under the 2-clause BSD license.
21562  * See license.txt in the OpenLayers distribution or repository for the
21563  * full text of the license. */
21564
21565 /**
21566  * @requires OpenLayers/Format/XML.js
21567  */
21568
21569 /**
21570  * Class: OpenLayers.Format.OGCExceptionReport
21571  * Class to read exception reports for various OGC services and versions.
21572  *
21573  * Inherits from:
21574  *  - <OpenLayers.Format.XML>
21575  */
21576 OpenLayers.Format.OGCExceptionReport = OpenLayers.Class(OpenLayers.Format.XML, {
21577
21578     /**
21579      * Property: namespaces
21580      * {Object} Mapping of namespace aliases to namespace URIs.
21581      */
21582     namespaces: {
21583         ogc: "http://www.opengis.net/ogc"
21584     },
21585
21586     /**
21587      * Property: regExes
21588      * Compiled regular expressions for manipulating strings.
21589      */
21590     regExes: {
21591         trimSpace: (/^\s*|\s*$/g),
21592         removeSpace: (/\s*/g),
21593         splitSpace: (/\s+/),
21594         trimComma: (/\s*,\s*/g)
21595     },
21596
21597     /**
21598      * Property: defaultPrefix
21599      */
21600     defaultPrefix: "ogc",
21601
21602     /**
21603      * Constructor: OpenLayers.Format.OGCExceptionReport
21604      * Create a new parser for OGC exception reports.
21605      *
21606      * Parameters:
21607      * options - {Object} An optional object whose properties will be set on
21608      *     this instance.
21609      */
21610
21611     /**
21612      * APIMethod: read
21613      * Read OGC exception report data from a string, and return an object with
21614      * information about the exceptions.
21615      *
21616      * Parameters:
21617      * data - {String} or {DOMElement} data to read/parse.
21618      *
21619      * Returns:
21620      * {Object} Information about the exceptions that occurred.
21621      */
21622     read: function(data) {
21623         var result;
21624         if(typeof data == "string") {
21625             data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
21626         }
21627         var root = data.documentElement;
21628         var exceptionInfo = {exceptionReport: null}; 
21629         if (root) {
21630             this.readChildNodes(data, exceptionInfo);
21631             if (exceptionInfo.exceptionReport === null) {
21632                 // fall-back to OWSCommon since this is a common output format for exceptions
21633                 // we cannot easily use the ows readers directly since they differ for 1.0 and 1.1
21634                 exceptionInfo = new OpenLayers.Format.OWSCommon().read(data);
21635             }
21636         }
21637         return exceptionInfo;
21638     },
21639
21640     /**
21641      * Property: readers
21642      * Contains public functions, grouped by namespace prefix, that will
21643      *     be applied when a namespaced node is found matching the function
21644      *     name.  The function will be applied in the scope of this parser
21645      *     with two arguments: the node being read and a context object passed
21646      *     from the parent.
21647      */
21648     readers: {
21649         "ogc": {
21650             "ServiceExceptionReport": function(node, obj) {
21651                 obj.exceptionReport = {exceptions: []};
21652                 this.readChildNodes(node, obj.exceptionReport);
21653             },
21654             "ServiceException": function(node, exceptionReport) {
21655                 var exception = {
21656                     code: node.getAttribute("code"),
21657                     locator: node.getAttribute("locator"),
21658                     text: this.getChildValue(node)
21659                 };
21660                 exceptionReport.exceptions.push(exception);
21661             }
21662         }
21663     },
21664     
21665     CLASS_NAME: "OpenLayers.Format.OGCExceptionReport"
21666     
21667 });
21668 /* ======================================================================
21669     OpenLayers/Format/XML/VersionedOGC.js
21670    ====================================================================== */
21671
21672 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
21673  * full list of contributors). Published under the 2-clause BSD license.
21674  * See license.txt in the OpenLayers distribution or repository for the
21675  * full text of the license. */
21676
21677 /**
21678  * @requires OpenLayers/Format/XML.js
21679  * @requires OpenLayers/Format/OGCExceptionReport.js
21680  */
21681
21682 /**
21683  * Class: OpenLayers.Format.XML.VersionedOGC
21684  * Base class for versioned formats, i.e. a format which supports multiple
21685  * versions.
21686  *
21687  * To enable checking if parsing succeeded, you will need to define a property
21688  * called errorProperty on the parser you want to check. The parser will then
21689  * check the returned object to see if that property is present. If it is, it
21690  * assumes the parsing was successful. If it is not present (or is null), it will
21691  * pass the document through an OGCExceptionReport parser.
21692  * 
21693  * If errorProperty is undefined for the parser, this error checking mechanism
21694  * will be disabled.
21695  *
21696  *
21697  * 
21698  * Inherits from:
21699  *  - <OpenLayers.Format.XML>
21700  */
21701 OpenLayers.Format.XML.VersionedOGC = OpenLayers.Class(OpenLayers.Format.XML, {
21702     
21703     /**
21704      * APIProperty: defaultVersion
21705      * {String} Version number to assume if none found.
21706      */
21707     defaultVersion: null,
21708     
21709     /**
21710      * APIProperty: version
21711      * {String} Specify a version string if one is known.
21712      */
21713     version: null,
21714
21715     /**
21716      * APIProperty: profile
21717      * {String} If provided, use a custom profile.
21718      */
21719     profile: null,
21720
21721     /**
21722      * APIProperty: allowFallback
21723      * {Boolean} If a profiled parser cannot be found for the returned version,
21724      * use a non-profiled parser as the fallback. Application code using this
21725      * should take into account that the return object structure might be
21726      * missing the specifics of the profile. Defaults to false.
21727      */
21728     allowFallback: false,
21729
21730     /**
21731      * Property: name
21732      * {String} The name of this parser, this is the part of the CLASS_NAME
21733      * except for "OpenLayers.Format."
21734      */
21735     name: null,
21736
21737     /**
21738      * APIProperty: stringifyOutput
21739      * {Boolean} If true, write will return a string otherwise a DOMElement.
21740      * Default is false.
21741      */
21742     stringifyOutput: false,
21743
21744     /**
21745      * Property: parser
21746      * {Object} Instance of the versioned parser.  Cached for multiple read and
21747      *     write calls of the same version.
21748      */
21749     parser: null,
21750
21751     /**
21752      * Constructor: OpenLayers.Format.XML.VersionedOGC.
21753      * Constructor.
21754      *
21755      * Parameters:
21756      * options - {Object} Optional object whose properties will be set on
21757      *     the object.
21758      */
21759     initialize: function(options) {
21760         OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
21761         var className = this.CLASS_NAME;
21762         this.name = className.substring(className.lastIndexOf(".")+1);
21763     },
21764
21765     /**
21766      * Method: getVersion
21767      * Returns the version to use. Subclasses can override this function
21768      * if a different version detection is needed.
21769      *
21770      * Parameters:
21771      * root - {DOMElement}
21772      * options - {Object} Optional configuration object.
21773      *
21774      * Returns:
21775      * {String} The version to use.
21776      */
21777     getVersion: function(root, options) {
21778         var version;
21779         // read
21780         if (root) {
21781             version = this.version;
21782             if(!version) {
21783                 version = root.getAttribute("version");
21784                 if(!version) {
21785                     version = this.defaultVersion;
21786                 }
21787             }
21788         } else { // write
21789             version = (options && options.version) || 
21790                 this.version || this.defaultVersion;
21791         }
21792         return version;
21793     },
21794
21795     /**
21796      * Method: getParser
21797      * Get an instance of the cached parser if available, otherwise create one.
21798      *
21799      * Parameters:
21800      * version - {String}
21801      *
21802      * Returns:
21803      * {<OpenLayers.Format>}
21804      */
21805     getParser: function(version) {
21806         version = version || this.defaultVersion;
21807         var profile = this.profile ? "_" + this.profile : "";
21808         if(!this.parser || this.parser.VERSION != version) {
21809             var format = OpenLayers.Format[this.name][
21810                 "v" + version.replace(/\./g, "_") + profile
21811             ];
21812             if(!format) {
21813                 if (profile !== "" && this.allowFallback) {
21814                     // fallback to the non-profiled version of the parser
21815                     profile = "";
21816                     format = OpenLayers.Format[this.name][
21817                         "v" + version.replace(/\./g, "_")
21818                     ];
21819                 }
21820                 if (!format) {
21821                     throw "Can't find a " + this.name + " parser for version " +
21822                           version + profile;
21823                 }
21824             }
21825             this.parser = new format(this.options);
21826         }
21827         return this.parser;
21828     },
21829
21830     /**
21831      * APIMethod: write
21832      * Write a document.
21833      *
21834      * Parameters:
21835      * obj - {Object} An object representing the document.
21836      * options - {Object} Optional configuration object.
21837      *
21838      * Returns:
21839      * {String} The document as a string
21840      */
21841     write: function(obj, options) {
21842         var version = this.getVersion(null, options);
21843         this.parser = this.getParser(version);
21844         var root = this.parser.write(obj, options);
21845         if (this.stringifyOutput === false) {
21846             return root;
21847         } else {
21848             return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
21849         }
21850     },
21851
21852     /**
21853      * APIMethod: read
21854      * Read a doc and return an object representing the document.
21855      *
21856      * Parameters:
21857      * data - {String | DOMElement} Data to read.
21858      * options - {Object} Options for the reader.
21859      *
21860      * Returns:
21861      * {Object} An object representing the document.
21862      */
21863     read: function(data, options) {
21864         if(typeof data == "string") {
21865             data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
21866         }
21867         var root = data.documentElement;
21868         var version = this.getVersion(root);
21869         this.parser = this.getParser(version);          // Select the parser
21870         var obj = this.parser.read(data, options);      // Parse the data
21871
21872         var errorProperty = this.parser.errorProperty || null;
21873         if (errorProperty !== null && obj[errorProperty] === undefined) {
21874             // an error must have happened, so parse it and report back
21875             var format = new OpenLayers.Format.OGCExceptionReport();
21876             obj.error = format.read(data);
21877         }
21878         obj.version = version;
21879         return obj;
21880     },
21881
21882     CLASS_NAME: "OpenLayers.Format.XML.VersionedOGC"
21883 });
21884 /* ======================================================================
21885     OpenLayers/Filter/FeatureId.js
21886    ====================================================================== */
21887
21888 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
21889  * full list of contributors). Published under the 2-clause BSD license.
21890  * See license.txt in the OpenLayers distribution or repository for the
21891  * full text of the license. */
21892
21893
21894 /**
21895  * @requires OpenLayers/Filter.js
21896  */
21897
21898 /**
21899  * Class: OpenLayers.Filter.FeatureId
21900  * This class represents a ogc:FeatureId Filter, as being used for rule-based SLD
21901  * styling
21902  * 
21903  * Inherits from:
21904  * - <OpenLayers.Filter>
21905  */
21906 OpenLayers.Filter.FeatureId = OpenLayers.Class(OpenLayers.Filter, {
21907
21908     /** 
21909      * APIProperty: fids
21910      * {Array(String)} Feature Ids to evaluate this rule against. 
21911      *     To be passed inside the params object.
21912      */
21913     fids: null,
21914     
21915     /** 
21916      * Property: type
21917      * {String} Type to identify this filter.
21918      */
21919     type: "FID",
21920     
21921     /** 
21922      * Constructor: OpenLayers.Filter.FeatureId
21923      * Creates an ogc:FeatureId rule.
21924      *
21925      * Parameters:
21926      * options - {Object} An optional object with properties to set on the
21927      *           rule
21928      * 
21929      * Returns:
21930      * {<OpenLayers.Filter.FeatureId>}
21931      */
21932     initialize: function(options) {
21933         this.fids = [];
21934         OpenLayers.Filter.prototype.initialize.apply(this, [options]);
21935     },
21936
21937     /**
21938      * APIMethod: evaluate
21939      * evaluates this rule for a specific feature
21940      * 
21941      * Parameters:
21942      * feature - {<OpenLayers.Feature>} feature to apply the rule to.
21943      *           For vector features, the check is run against the fid,
21944      *           for plain features against the id.
21945      * 
21946      * Returns:
21947      * {Boolean} true if the rule applies, false if it does not
21948      */
21949     evaluate: function(feature) {
21950         for (var i=0, len=this.fids.length; i<len; i++) {
21951             var fid = feature.fid || feature.id;
21952             if (fid == this.fids[i]) {
21953                 return true;
21954             }
21955         }
21956         return false;
21957     },
21958     
21959     /**
21960      * APIMethod: clone
21961      * Clones this filter.
21962      * 
21963      * Returns:
21964      * {<OpenLayers.Filter.FeatureId>} Clone of this filter.
21965      */
21966     clone: function() {
21967         var filter = new OpenLayers.Filter.FeatureId();
21968         OpenLayers.Util.extend(filter, this);
21969         filter.fids = this.fids.slice();
21970         return filter;
21971     },
21972     
21973     CLASS_NAME: "OpenLayers.Filter.FeatureId"
21974 });
21975 /* ======================================================================
21976     OpenLayers/Filter/Logical.js
21977    ====================================================================== */
21978
21979 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
21980  * full list of contributors). Published under the 2-clause BSD license.
21981  * See license.txt in the OpenLayers distribution or repository for the
21982  * full text of the license. */
21983
21984
21985 /**
21986  * @requires OpenLayers/Filter.js
21987  */
21988
21989 /**
21990  * Class: OpenLayers.Filter.Logical
21991  * This class represents ogc:And, ogc:Or and ogc:Not rules.
21992  * 
21993  * Inherits from:
21994  * - <OpenLayers.Filter>
21995  */
21996 OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {
21997
21998     /**
21999      * APIProperty: filters
22000      * {Array(<OpenLayers.Filter>)} Child filters for this filter.
22001      */
22002     filters: null, 
22003      
22004     /**
22005      * APIProperty: type
22006      * {String} type of logical operator. Available types are:
22007      * - OpenLayers.Filter.Logical.AND = "&&";
22008      * - OpenLayers.Filter.Logical.OR  = "||";
22009      * - OpenLayers.Filter.Logical.NOT = "!";
22010      */
22011     type: null,
22012
22013     /** 
22014      * Constructor: OpenLayers.Filter.Logical
22015      * Creates a logical filter (And, Or, Not).
22016      *
22017      * Parameters:
22018      * options - {Object} An optional object with properties to set on the
22019      *     filter.
22020      * 
22021      * Returns:
22022      * {<OpenLayers.Filter.Logical>}
22023      */
22024     initialize: function(options) {
22025         this.filters = [];
22026         OpenLayers.Filter.prototype.initialize.apply(this, [options]);
22027     },
22028     
22029     /** 
22030      * APIMethod: destroy
22031      * Remove reference to child filters.
22032      */
22033     destroy: function() {
22034         this.filters = null;
22035         OpenLayers.Filter.prototype.destroy.apply(this);
22036     },
22037
22038     /**
22039      * APIMethod: evaluate
22040      * Evaluates this filter in a specific context.
22041      * 
22042      * Parameters:
22043      * context - {Object} Context to use in evaluating the filter.  A vector
22044      *     feature may also be provided to evaluate feature attributes in 
22045      *     comparison filters or geometries in spatial filters.
22046      * 
22047      * Returns:
22048      * {Boolean} The filter applies.
22049      */
22050     evaluate: function(context) {
22051         var i, len;
22052         switch(this.type) {
22053             case OpenLayers.Filter.Logical.AND:
22054                 for (i=0, len=this.filters.length; i<len; i++) {
22055                     if (this.filters[i].evaluate(context) == false) {
22056                         return false;
22057                     }
22058                 }
22059                 return true;
22060                 
22061             case OpenLayers.Filter.Logical.OR:
22062                 for (i=0, len=this.filters.length; i<len; i++) {
22063                     if (this.filters[i].evaluate(context) == true) {
22064                         return true;
22065                     }
22066                 }
22067                 return false;
22068             
22069             case OpenLayers.Filter.Logical.NOT:
22070                 return (!this.filters[0].evaluate(context));
22071         }
22072         return undefined;
22073     },
22074     
22075     /**
22076      * APIMethod: clone
22077      * Clones this filter.
22078      * 
22079      * Returns:
22080      * {<OpenLayers.Filter.Logical>} Clone of this filter.
22081      */
22082     clone: function() {
22083         var filters = [];        
22084         for(var i=0, len=this.filters.length; i<len; ++i) {
22085             filters.push(this.filters[i].clone());
22086         }
22087         return new OpenLayers.Filter.Logical({
22088             type: this.type,
22089             filters: filters
22090         });
22091     },
22092     
22093     CLASS_NAME: "OpenLayers.Filter.Logical"
22094 });
22095
22096
22097 OpenLayers.Filter.Logical.AND = "&&";
22098 OpenLayers.Filter.Logical.OR  = "||";
22099 OpenLayers.Filter.Logical.NOT = "!";
22100 /* ======================================================================
22101     OpenLayers/Filter/Comparison.js
22102    ====================================================================== */
22103
22104 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22105  * full list of contributors). Published under the 2-clause BSD license.
22106  * See license.txt in the OpenLayers distribution or repository for the
22107  * full text of the license. */
22108
22109 /**
22110  * @requires OpenLayers/Filter.js
22111  */
22112
22113 /**
22114  * Class: OpenLayers.Filter.Comparison
22115  * This class represents a comparison filter.
22116  * 
22117  * Inherits from:
22118  * - <OpenLayers.Filter>
22119  */
22120 OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {
22121
22122     /**
22123      * APIProperty: type
22124      * {String} type: type of the comparison. This is one of
22125      * - OpenLayers.Filter.Comparison.EQUAL_TO                 = "==";
22126      * - OpenLayers.Filter.Comparison.NOT_EQUAL_TO             = "!=";
22127      * - OpenLayers.Filter.Comparison.LESS_THAN                = "<";
22128      * - OpenLayers.Filter.Comparison.GREATER_THAN             = ">";
22129      * - OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO    = "<=";
22130      * - OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
22131      * - OpenLayers.Filter.Comparison.BETWEEN                  = "..";
22132      * - OpenLayers.Filter.Comparison.LIKE                     = "~";
22133      * - OpenLayers.Filter.Comparison.IS_NULL                  = "NULL";
22134      */
22135     type: null,
22136     
22137     /**
22138      * APIProperty: property
22139      * {String}
22140      * name of the context property to compare
22141      */
22142     property: null,
22143     
22144     /**
22145      * APIProperty: value
22146      * {Number} or {String}
22147      * comparison value for binary comparisons. In the case of a String, this
22148      * can be a combination of text and propertyNames in the form
22149      * "literal ${propertyName}"
22150      */
22151     value: null,
22152     
22153     /**
22154      * Property: matchCase
22155      * {Boolean} Force case sensitive searches for EQUAL_TO and NOT_EQUAL_TO
22156      *     comparisons.  The Filter Encoding 1.1 specification added a matchCase
22157      *     attribute to ogc:PropertyIsEqualTo and ogc:PropertyIsNotEqualTo
22158      *     elements.  This property will be serialized with those elements only
22159      *     if using the v1.1.0 filter format. However, when evaluating filters
22160      *     here, the matchCase property will always be respected (for EQUAL_TO
22161      *     and NOT_EQUAL_TO).  Default is true. 
22162      */
22163     matchCase: true,
22164     
22165     /**
22166      * APIProperty: lowerBoundary
22167      * {Number} or {String}
22168      * lower boundary for between comparisons. In the case of a String, this
22169      * can be a combination of text and propertyNames in the form
22170      * "literal ${propertyName}"
22171      */
22172     lowerBoundary: null,
22173     
22174     /**
22175      * APIProperty: upperBoundary
22176      * {Number} or {String}
22177      * upper boundary for between comparisons. In the case of a String, this
22178      * can be a combination of text and propertyNames in the form
22179      * "literal ${propertyName}"
22180      */
22181     upperBoundary: null,
22182
22183     /** 
22184      * Constructor: OpenLayers.Filter.Comparison
22185      * Creates a comparison rule.
22186      *
22187      * Parameters:
22188      * options - {Object} An optional object with properties to set on the
22189      *           rule
22190      * 
22191      * Returns:
22192      * {<OpenLayers.Filter.Comparison>}
22193      */
22194     initialize: function(options) {
22195         OpenLayers.Filter.prototype.initialize.apply(this, [options]);
22196         // since matchCase on PropertyIsLike is not schema compliant, we only
22197         // want to use this if explicitly asked for
22198         if (this.type === OpenLayers.Filter.Comparison.LIKE 
22199             && options.matchCase === undefined) {
22200                 this.matchCase = null;
22201         }
22202     },
22203
22204     /**
22205      * APIMethod: evaluate
22206      * Evaluates this filter in a specific context.
22207      * 
22208      * Parameters:
22209      * context - {Object} Context to use in evaluating the filter.  If a vector
22210      *     feature is provided, the feature.attributes will be used as context.
22211      * 
22212      * Returns:
22213      * {Boolean} The filter applies.
22214      */
22215     evaluate: function(context) {
22216         if (context instanceof OpenLayers.Feature.Vector) {
22217             context = context.attributes;
22218         }
22219         var result = false;
22220         var got = context[this.property];
22221         var exp;
22222         switch(this.type) {
22223             case OpenLayers.Filter.Comparison.EQUAL_TO:
22224                 exp = this.value;
22225                 if(!this.matchCase &&
22226                    typeof got == "string" && typeof exp == "string") {
22227                     result = (got.toUpperCase() == exp.toUpperCase());
22228                 } else {
22229                     result = (got == exp);
22230                 }
22231                 break;
22232             case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:
22233                 exp = this.value;
22234                 if(!this.matchCase &&
22235                    typeof got == "string" && typeof exp == "string") {
22236                     result = (got.toUpperCase() != exp.toUpperCase());
22237                 } else {
22238                     result = (got != exp);
22239                 }
22240                 break;
22241             case OpenLayers.Filter.Comparison.LESS_THAN:
22242                 result = got < this.value;
22243                 break;
22244             case OpenLayers.Filter.Comparison.GREATER_THAN:
22245                 result = got > this.value;
22246                 break;
22247             case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:
22248                 result = got <= this.value;
22249                 break;
22250             case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:
22251                 result = got >= this.value;
22252                 break;
22253             case OpenLayers.Filter.Comparison.BETWEEN:
22254                 result = (got >= this.lowerBoundary) &&
22255                     (got <= this.upperBoundary);
22256                 break;
22257             case OpenLayers.Filter.Comparison.LIKE:
22258                 var regexp = new RegExp(this.value, "gi");
22259                 result = regexp.test(got);
22260                 break;
22261             case OpenLayers.Filter.Comparison.IS_NULL:
22262                 result = (got === null);
22263                 break;
22264         }
22265         return result;
22266     },
22267     
22268     /**
22269      * APIMethod: value2regex
22270      * Converts the value of this rule into a regular expression string,
22271      * according to the wildcard characters specified. This method has to
22272      * be called after instantiation of this class, if the value is not a
22273      * regular expression already.
22274      * 
22275      * Parameters:
22276      * wildCard   - {Char} wildcard character in the above value, default
22277      *              is "*"
22278      * singleChar - {Char} single-character wildcard in the above value
22279      *              default is "."
22280      * escapeChar - {Char} escape character in the above value, default is
22281      *              "!"
22282      * 
22283      * Returns:
22284      * {String} regular expression string
22285      */
22286     value2regex: function(wildCard, singleChar, escapeChar) {
22287         if (wildCard == ".") {
22288             throw new Error("'.' is an unsupported wildCard character for " +
22289                             "OpenLayers.Filter.Comparison");
22290         }
22291         
22292
22293         // set UMN MapServer defaults for unspecified parameters
22294         wildCard = wildCard ? wildCard : "*";
22295         singleChar = singleChar ? singleChar : ".";
22296         escapeChar = escapeChar ? escapeChar : "!";
22297         
22298         this.value = this.value.replace(
22299                 new RegExp("\\"+escapeChar+"(.|$)", "g"), "\\$1");
22300         this.value = this.value.replace(
22301                 new RegExp("\\"+singleChar, "g"), ".");
22302         this.value = this.value.replace(
22303                 new RegExp("\\"+wildCard, "g"), ".*");
22304         this.value = this.value.replace(
22305                 new RegExp("\\\\.\\*", "g"), "\\"+wildCard);
22306         this.value = this.value.replace(
22307                 new RegExp("\\\\\\.", "g"), "\\"+singleChar);
22308         
22309         return this.value;
22310     },
22311     
22312     /**
22313      * Method: regex2value
22314      * Convert the value of this rule from a regular expression string into an
22315      *     ogc literal string using a wildCard of *, a singleChar of ., and an
22316      *     escape of !.  Leaves the <value> property unmodified.
22317      * 
22318      * Returns:
22319      * {String} A string value.
22320      */
22321     regex2value: function() {
22322         
22323         var value = this.value;
22324         
22325         // replace ! with !!
22326         value = value.replace(/!/g, "!!");
22327
22328         // replace \. with !. (watching out for \\.)
22329         value = value.replace(/(\\)?\\\./g, function($0, $1) {
22330             return $1 ? $0 : "!.";
22331         });
22332         
22333         // replace \* with #* (watching out for \\*)
22334         value = value.replace(/(\\)?\\\*/g, function($0, $1) {
22335             return $1 ? $0 : "!*";
22336         });
22337         
22338         // replace \\ with \
22339         value = value.replace(/\\\\/g, "\\");
22340
22341         // convert .* to * (the sequence #.* is not allowed)
22342         value = value.replace(/\.\*/g, "*");
22343         
22344         return value;
22345     },
22346     
22347     /**
22348      * APIMethod: clone
22349      * Clones this filter.
22350      * 
22351      * Returns:
22352      * {<OpenLayers.Filter.Comparison>} Clone of this filter.
22353      */
22354     clone: function() {
22355         return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this);
22356     },
22357     
22358     CLASS_NAME: "OpenLayers.Filter.Comparison"
22359 });
22360
22361
22362 OpenLayers.Filter.Comparison.EQUAL_TO                 = "==";
22363 OpenLayers.Filter.Comparison.NOT_EQUAL_TO             = "!=";
22364 OpenLayers.Filter.Comparison.LESS_THAN                = "<";
22365 OpenLayers.Filter.Comparison.GREATER_THAN             = ">";
22366 OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO    = "<=";
22367 OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
22368 OpenLayers.Filter.Comparison.BETWEEN                  = "..";
22369 OpenLayers.Filter.Comparison.LIKE                     = "~";
22370 OpenLayers.Filter.Comparison.IS_NULL                  = "NULL";
22371 /* ======================================================================
22372     OpenLayers/Format/Filter.js
22373    ====================================================================== */
22374
22375 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22376  * full list of contributors). Published under the 2-clause BSD license.
22377  * See license.txt in the OpenLayers distribution or repository for the
22378  * full text of the license. */
22379
22380 /**
22381  * @requires OpenLayers/Format/XML/VersionedOGC.js
22382  * @requires OpenLayers/Filter/FeatureId.js
22383  * @requires OpenLayers/Filter/Logical.js
22384  * @requires OpenLayers/Filter/Comparison.js
22385  */
22386
22387 /**
22388  * Class: OpenLayers.Format.Filter
22389  * Read/Write ogc:Filter. Create a new instance with the <OpenLayers.Format.Filter>
22390  *     constructor.
22391  * 
22392  * Inherits from:
22393  *  - <OpenLayers.Format.XML.VersionedOGC>
22394  */
22395 OpenLayers.Format.Filter = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
22396     
22397     /**
22398      * APIProperty: defaultVersion
22399      * {String} Version number to assume if none found.  Default is "1.0.0".
22400      */
22401     defaultVersion: "1.0.0",
22402     
22403     /**
22404      * APIMethod: write
22405      * Write an ogc:Filter given a filter object.
22406      *
22407      * Parameters:
22408      * filter - {<OpenLayers.Filter>} An filter.
22409      * options - {Object} Optional configuration object.
22410      *
22411      * Returns:
22412      * {Elment} An ogc:Filter element node.
22413      */
22414     
22415     /**
22416      * APIMethod: read
22417      * Read and Filter doc and return an object representing the Filter.
22418      *
22419      * Parameters:
22420      * data - {String | DOMElement} Data to read.
22421      *
22422      * Returns:
22423      * {<OpenLayers.Filter>} A filter object.
22424      */
22425
22426     CLASS_NAME: "OpenLayers.Format.Filter" 
22427 });
22428 /* ======================================================================
22429     OpenLayers/Format/WFST.js
22430    ====================================================================== */
22431
22432 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22433  * full list of contributors). Published under the 2-clause BSD license.
22434  * See license.txt in the OpenLayers distribution or repository for the
22435  * full text of the license. */
22436
22437 /**
22438  * @requires OpenLayers/Format.js
22439  */
22440
22441 /**
22442  * Function: OpenLayers.Format.WFST
22443  * Used to create a versioned WFS protocol.  Default version is 1.0.0.
22444  *
22445  * Returns:
22446  * {<OpenLayers.Format>} A WFST format of the given version.
22447  */
22448 OpenLayers.Format.WFST = function(options) {
22449     options = OpenLayers.Util.applyDefaults(
22450         options, OpenLayers.Format.WFST.DEFAULTS
22451     );
22452     var cls = OpenLayers.Format.WFST["v"+options.version.replace(/\./g, "_")];
22453     if(!cls) {
22454         throw "Unsupported WFST version: " + options.version;
22455     }
22456     return new cls(options);
22457 };
22458
22459 /**
22460  * Constant: OpenLayers.Format.WFST.DEFAULTS
22461  * {Object} Default properties for the WFST format.
22462  */
22463 OpenLayers.Format.WFST.DEFAULTS = {
22464     "version": "1.0.0"
22465 };
22466 /* ======================================================================
22467     OpenLayers/Filter/Spatial.js
22468    ====================================================================== */
22469
22470 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22471  * full list of contributors). Published under the 2-clause BSD license.
22472  * See license.txt in the OpenLayers distribution or repository for the
22473  * full text of the license. */
22474
22475 /**
22476  * @requires OpenLayers/Filter.js
22477  */
22478
22479 /**
22480  * Class: OpenLayers.Filter.Spatial
22481  * This class represents a spatial filter.
22482  * Currently implemented: BBOX, DWithin and Intersects
22483  * 
22484  * Inherits from:
22485  * - <OpenLayers.Filter>
22486  */
22487 OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, {
22488
22489     /**
22490      * APIProperty: type
22491      * {String} Type of spatial filter.
22492      *
22493      * The type should be one of:
22494      * - OpenLayers.Filter.Spatial.BBOX
22495      * - OpenLayers.Filter.Spatial.INTERSECTS
22496      * - OpenLayers.Filter.Spatial.DWITHIN
22497      * - OpenLayers.Filter.Spatial.WITHIN
22498      * - OpenLayers.Filter.Spatial.CONTAINS
22499      */
22500     type: null,
22501     
22502     /**
22503      * APIProperty: property
22504      * {String} Name of the context property to compare.
22505      */
22506     property: null,
22507     
22508     /**
22509      * APIProperty: value
22510      * {<OpenLayers.Bounds> || <OpenLayers.Geometry>} The bounds or geometry
22511      *     to be used by the filter.  Use bounds for BBOX filters and geometry
22512      *     for INTERSECTS or DWITHIN filters.
22513      */
22514     value: null,
22515
22516     /**
22517      * APIProperty: distance
22518      * {Number} The distance to use in a DWithin spatial filter.
22519      */
22520     distance: null,
22521
22522     /**
22523      * APIProperty: distanceUnits
22524      * {String} The units to use for the distance, e.g. 'm'.
22525      */
22526     distanceUnits: null,
22527     
22528     /** 
22529      * Constructor: OpenLayers.Filter.Spatial
22530      * Creates a spatial filter.
22531      *
22532      * Parameters:
22533      * options - {Object} An optional object with properties to set on the
22534      *     filter.
22535      * 
22536      * Returns:
22537      * {<OpenLayers.Filter.Spatial>}
22538      */
22539
22540    /**
22541     * Method: evaluate
22542     * Evaluates this filter for a specific feature.
22543     * 
22544     * Parameters:
22545     * feature - {<OpenLayers.Feature.Vector>} feature to apply the filter to.
22546     * 
22547     * Returns:
22548     * {Boolean} The feature meets filter criteria.
22549     */
22550     evaluate: function(feature) {
22551         var intersect = false;
22552         switch(this.type) {
22553             case OpenLayers.Filter.Spatial.BBOX:
22554             case OpenLayers.Filter.Spatial.INTERSECTS:
22555                 if(feature.geometry) {
22556                     var geom = this.value;
22557                     if(this.value.CLASS_NAME == "OpenLayers.Bounds") {
22558                         geom = this.value.toGeometry();
22559                     }
22560                     if(feature.geometry.intersects(geom)) {
22561                         intersect = true;
22562                     }
22563                 }
22564                 break;
22565             default:
22566                 throw new Error('evaluate is not implemented for this filter type.');
22567         }
22568         return intersect;
22569     },
22570
22571     /**
22572      * APIMethod: clone
22573      * Clones this filter.
22574      * 
22575      * Returns:
22576      * {<OpenLayers.Filter.Spatial>} Clone of this filter.
22577      */
22578     clone: function() {
22579         var options = OpenLayers.Util.applyDefaults({
22580             value: this.value && this.value.clone && this.value.clone()
22581         }, this);
22582         return new OpenLayers.Filter.Spatial(options);
22583     },
22584     CLASS_NAME: "OpenLayers.Filter.Spatial"
22585 });
22586
22587 OpenLayers.Filter.Spatial.BBOX = "BBOX";
22588 OpenLayers.Filter.Spatial.INTERSECTS = "INTERSECTS";
22589 OpenLayers.Filter.Spatial.DWITHIN = "DWITHIN";
22590 OpenLayers.Filter.Spatial.WITHIN = "WITHIN";
22591 OpenLayers.Filter.Spatial.CONTAINS = "CONTAINS";
22592 /* ======================================================================
22593     OpenLayers/Format/WFST/v1.js
22594    ====================================================================== */
22595
22596 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
22597  * full list of contributors). Published under the 2-clause BSD license.
22598  * See license.txt in the OpenLayers distribution or repository for the
22599  * full text of the license. */
22600
22601 /**
22602  * @requires OpenLayers/Format/XML.js
22603  * @requires OpenLayers/Format/WFST.js
22604  * @requires OpenLayers/Filter/Spatial.js
22605  * @requires OpenLayers/Filter/FeatureId.js
22606  */
22607
22608 /**
22609  * Class: OpenLayers.Format.WFST.v1
22610  * Superclass for WFST parsers.
22611  *
22612  * Inherits from:
22613  *  - <OpenLayers.Format.XML>
22614  */
22615 OpenLayers.Format.WFST.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
22616     
22617     /**
22618      * Property: namespaces
22619      * {Object} Mapping of namespace aliases to namespace URIs.
22620      */
22621     namespaces: {
22622         xlink: "http://www.w3.org/1999/xlink",
22623         xsi: "http://www.w3.org/2001/XMLSchema-instance",
22624         wfs: "http://www.opengis.net/wfs",
22625         gml: "http://www.opengis.net/gml",
22626         ogc: "http://www.opengis.net/ogc",
22627         ows: "http://www.opengis.net/ows"
22628     },
22629     
22630     /**
22631      * Property: defaultPrefix
22632      */
22633     defaultPrefix: "wfs",
22634
22635     /**
22636      * Property: version
22637      * {String} WFS version number.
22638      */
22639     version: null,
22640
22641     /**
22642      * Property: schemaLocation
22643      * {String} Schema location for a particular minor version.
22644      */
22645     schemaLocations: null,
22646     
22647     /**
22648      * APIProperty: srsName
22649      * {String} URI for spatial reference system.
22650      */
22651     srsName: null,
22652
22653     /**
22654      * APIProperty: extractAttributes
22655      * {Boolean} Extract attributes from GML.  Default is true.
22656      */
22657     extractAttributes: true,
22658     
22659     /**
22660      * APIProperty: xy
22661      * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
22662      * Changing is not recommended, a new Format should be instantiated.
22663      */ 
22664     xy: true,
22665
22666     /**
22667      * Property: stateName
22668      * {Object} Maps feature states to node names.
22669      */
22670     stateName: null,
22671     
22672     /**
22673      * Constructor: OpenLayers.Format.WFST.v1
22674      * Instances of this class are not created directly.  Use the
22675      *     <OpenLayers.Format.WFST.v1_0_0> or <OpenLayers.Format.WFST.v1_1_0>
22676      *     constructor instead.
22677      *
22678      * Parameters:
22679      * options - {Object} An optional object whose properties will be set on
22680      *     this instance.
22681      */
22682     initialize: function(options) {
22683         // set state name mapping
22684         this.stateName = {};
22685         this.stateName[OpenLayers.State.INSERT] = "wfs:Insert";
22686         this.stateName[OpenLayers.State.UPDATE] = "wfs:Update";
22687         this.stateName[OpenLayers.State.DELETE] = "wfs:Delete";
22688         OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
22689     },
22690     
22691     /**
22692      * Method: getSrsName
22693      */
22694     getSrsName: function(feature, options) {
22695         var srsName = options && options.srsName;
22696         if(!srsName) {
22697             if(feature && feature.layer) {
22698                 srsName = feature.layer.projection.getCode();
22699             } else {
22700                 srsName = this.srsName;
22701             }
22702         }
22703         return srsName;
22704     },
22705
22706     /**
22707      * APIMethod: read
22708      * Parse the response from a transaction.  Because WFS is split into
22709      *     Transaction requests (create, update, and delete) and GetFeature
22710      *     requests (read), this method handles parsing of both types of
22711      *     responses.
22712      *
22713      * Parameters:
22714      * data - {String | Document} The WFST document to read
22715      * options - {Object} Options for the reader
22716      *
22717      * Valid options properties:
22718      * output - {String} either "features" or "object". The default is
22719      *     "features", which means that the method will return an array of
22720      *     features. If set to "object", an object with a "features" property
22721      *     and other properties read by the parser will be returned.
22722      *
22723      * Returns:
22724      * {Array | Object} Output depending on the output option.
22725      */
22726     read: function(data, options) {
22727         options = options || {};
22728         OpenLayers.Util.applyDefaults(options, {
22729             output: "features"
22730         });
22731         
22732         if(typeof data == "string") { 
22733             data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
22734         }
22735         if(data && data.nodeType == 9) {
22736             data = data.documentElement;
22737         }
22738         var obj = {};
22739         if(data) {
22740             this.readNode(data, obj, true);
22741         }
22742         if(obj.features && options.output === "features") {
22743             obj = obj.features;
22744         }
22745         return obj;
22746     },
22747     
22748     /**
22749      * Property: readers
22750      * Contains public functions, grouped by namespace prefix, that will
22751      *     be applied when a namespaced node is found matching the function
22752      *     name.  The function will be applied in the scope of this parser
22753      *     with two arguments: the node being read and a context object passed
22754      *     from the parent.
22755      */
22756     readers: {
22757         "wfs": {
22758             "FeatureCollection": function(node, obj) {
22759                 obj.features = [];
22760                 this.readChildNodes(node, obj);
22761             }
22762         }
22763     },
22764     
22765     /**
22766      * Method: write
22767      * Given an array of features, write a WFS transaction.  This assumes
22768      *     the features have a state property that determines the operation
22769      *     type - insert, update, or delete.
22770      *
22771      * Parameters:
22772      * features - {Array(<OpenLayers.Feature.Vector>)} A list of features. See
22773      *     below for a more detailed description of the influence of the
22774      *     feature's *modified* property.
22775      * options - {Object}
22776      *
22777      * feature.modified rules:
22778      * If a feature has a modified property set, the following checks will be
22779      * made before a feature's geometry or attribute is included in an Update
22780      * transaction:
22781      * - *modified* is not set at all: The geometry and all attributes will be
22782      *     included.
22783      * - *modified.geometry* is set (null or a geometry): The geometry will be
22784      *     included. If *modified.attributes* is not set, all attributes will
22785      *     be included.
22786      * - *modified.attributes* is set: Only the attributes set (i.e. to null or
22787      *     a value) in *modified.attributes* will be included. 
22788      *     If *modified.geometry* is not set, the geometry will not be included.
22789      *
22790      * Valid options include:
22791      * - *multi* {Boolean} If set to true, geometries will be casted to
22792      *   Multi geometries before writing.
22793      *
22794      * Returns:
22795      * {String} A serialized WFS transaction.
22796      */
22797     write: function(features, options) {
22798         var node = this.writeNode("wfs:Transaction", {
22799             features:features,
22800             options: options
22801         });
22802         var value = this.schemaLocationAttr();
22803         if(value) {
22804             this.setAttributeNS(
22805                 node, this.namespaces["xsi"], "xsi:schemaLocation",  value
22806             );
22807         }
22808         return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
22809     },
22810     
22811     /**
22812      * Property: writers
22813      * As a compliment to the readers property, this structure contains public
22814      *     writing functions grouped by namespace alias and named like the
22815      *     node names they produce.
22816      */
22817     writers: {
22818         "wfs": {
22819             "GetFeature": function(options) {
22820                 var node = this.createElementNSPlus("wfs:GetFeature", {
22821                     attributes: {
22822                         service: "WFS",
22823                         version: this.version,
22824                         handle: options && options.handle,
22825                         outputFormat: options && options.outputFormat,
22826                         maxFeatures: options && options.maxFeatures,
22827                         "xsi:schemaLocation": this.schemaLocationAttr(options)
22828                     }
22829                 });
22830                 if (typeof this.featureType == "string") {
22831                     this.writeNode("Query", options, node);
22832                 } else {
22833                     for (var i=0,len = this.featureType.length; i<len; i++) { 
22834                         options.featureType = this.featureType[i]; 
22835                         this.writeNode("Query", options, node); 
22836                     } 
22837                 }
22838                 return node;
22839             },
22840             "Transaction": function(obj) {
22841                 obj = obj || {};
22842                 var options = obj.options || {};
22843                 var node = this.createElementNSPlus("wfs:Transaction", {
22844                     attributes: {
22845                         service: "WFS",
22846                         version: this.version,
22847                         handle: options.handle
22848                     }
22849                 });
22850                 var i, len;
22851                 var features = obj.features;
22852                 if(features) {
22853                     // temporarily re-assigning geometry types
22854                     if (options.multi === true) {
22855                         OpenLayers.Util.extend(this.geometryTypes, {
22856                             "OpenLayers.Geometry.Point": "MultiPoint",
22857                             "OpenLayers.Geometry.LineString": (this.multiCurve === true) ? "MultiCurve": "MultiLineString",
22858                             "OpenLayers.Geometry.Polygon": (this.multiSurface === true) ? "MultiSurface" : "MultiPolygon"
22859                         });
22860                     }
22861                     var name, feature;
22862                     for(i=0, len=features.length; i<len; ++i) {
22863                         feature = features[i];
22864                         name = this.stateName[feature.state];
22865                         if(name) {
22866                             this.writeNode(name, {
22867                                 feature: feature, 
22868                                 options: options
22869                             }, node);
22870                         }
22871                     }
22872                     // switch back to original geometry types assignment
22873                     if (options.multi === true) {
22874                         this.setGeometryTypes();
22875                     }
22876                 }
22877                 if (options.nativeElements) {
22878                     for (i=0, len=options.nativeElements.length; i<len; ++i) {
22879                         this.writeNode("wfs:Native", 
22880                             options.nativeElements[i], node);
22881                     }
22882                 }
22883                 return node;
22884             },
22885             "Native": function(nativeElement) {
22886                 var node = this.createElementNSPlus("wfs:Native", {
22887                     attributes: {
22888                         vendorId: nativeElement.vendorId,
22889                         safeToIgnore: nativeElement.safeToIgnore
22890                     },
22891                     value: nativeElement.value
22892                 });
22893                 return node;
22894             },
22895             "Insert": function(obj) {
22896                 var feature = obj.feature;
22897                 var options = obj.options;
22898                 var node = this.createElementNSPlus("wfs:Insert", {
22899                     attributes: {
22900                         handle: options && options.handle
22901                     }
22902                 });
22903                 this.srsName = this.getSrsName(feature);
22904                 this.writeNode("feature:_typeName", feature, node);
22905                 return node;
22906             },
22907             "Update": function(obj) {
22908                 var feature = obj.feature;
22909                 var options = obj.options;
22910                 var node = this.createElementNSPlus("wfs:Update", {
22911                     attributes: {
22912                         handle: options && options.handle,
22913                         typeName: (this.featureNS ? this.featurePrefix + ":" : "") +
22914                             this.featureType
22915                     }
22916                 });
22917                 if(this.featureNS) {
22918                     node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
22919                 }
22920                 
22921                 // add in geometry
22922                 var modified = feature.modified;
22923                 if (this.geometryName !== null && (!modified || modified.geometry !== undefined)) {
22924                     this.srsName = this.getSrsName(feature);
22925                     this.writeNode(
22926                         "Property", {name: this.geometryName, value: feature.geometry}, node
22927                     );
22928                 }
22929         
22930                 // add in attributes
22931                 for(var key in feature.attributes) {
22932                     if(feature.attributes[key] !== undefined &&
22933                                 (!modified || !modified.attributes ||
22934                                 (modified.attributes && modified.attributes[key] !== undefined))) {
22935                         this.writeNode(
22936                             "Property", {name: key, value: feature.attributes[key]}, node
22937                         );
22938                     }
22939                 }
22940                 
22941                 // add feature id filter
22942                 this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({
22943                     fids: [feature.fid]
22944                 }), node);
22945         
22946                 return node;
22947             },
22948             "Property": function(obj) {
22949                 var node = this.createElementNSPlus("wfs:Property");
22950                 this.writeNode("Name", obj.name, node);
22951                 if(obj.value !== null) {
22952                     this.writeNode("Value", obj.value, node);
22953                 }
22954                 return node;
22955             },
22956             "Name": function(name) {
22957                 return this.createElementNSPlus("wfs:Name", {value: name});
22958             },
22959             "Value": function(obj) {
22960                 var node;
22961                 if(obj instanceof OpenLayers.Geometry) {
22962                     node = this.createElementNSPlus("wfs:Value");
22963                     var geom = this.writeNode("feature:_geometry", obj).firstChild;
22964                     node.appendChild(geom);
22965                 } else {
22966                     node = this.createElementNSPlus("wfs:Value", {value: obj});                
22967                 }
22968                 return node;
22969             },
22970             "Delete": function(obj) {
22971                 var feature = obj.feature;
22972                 var options = obj.options;
22973                 var node = this.createElementNSPlus("wfs:Delete", {
22974                     attributes: {
22975                         handle: options && options.handle,
22976                         typeName: (this.featureNS ? this.featurePrefix + ":" : "") +
22977                             this.featureType
22978                     }
22979                 });
22980                 if(this.featureNS) {
22981                     node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
22982                 }
22983                 this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({
22984                     fids: [feature.fid]
22985                 }), node);
22986                 return node;
22987             }
22988         }
22989     },
22990
22991     /**
22992      * Method: schemaLocationAttr
22993      * Generate the xsi:schemaLocation attribute value.
22994      *
22995      * Returns:
22996      * {String} The xsi:schemaLocation attribute or undefined if none.
22997      */
22998     schemaLocationAttr: function(options) {
22999         options = OpenLayers.Util.extend({
23000             featurePrefix: this.featurePrefix,
23001             schema: this.schema
23002         }, options);
23003         var schemaLocations = OpenLayers.Util.extend({}, this.schemaLocations);
23004         if(options.schema) {
23005             schemaLocations[options.featurePrefix] = options.schema;
23006         }
23007         var parts = [];
23008         var uri;
23009         for(var key in schemaLocations) {
23010             uri = this.namespaces[key];
23011             if(uri) {
23012                 parts.push(uri + " " + schemaLocations[key]);
23013             }
23014         }
23015         var value = parts.join(" ") || undefined;
23016         return value;
23017     },
23018     
23019     /**
23020      * Method: setFilterProperty
23021      * Set the property of each spatial filter.
23022      *
23023      * Parameters:
23024      * filter - {<OpenLayers.Filter>}
23025      */
23026     setFilterProperty: function(filter) {
23027         if(filter.filters) {
23028             for(var i=0, len=filter.filters.length; i<len; ++i) {
23029                 OpenLayers.Format.WFST.v1.prototype.setFilterProperty.call(this, filter.filters[i]);
23030             }
23031         } else {
23032             if(filter instanceof OpenLayers.Filter.Spatial && !filter.property) {
23033                 // got a spatial filter without property, so set it
23034                 filter.property = this.geometryName;
23035             }
23036         }
23037     },
23038
23039     CLASS_NAME: "OpenLayers.Format.WFST.v1" 
23040
23041 });
23042 /* ======================================================================
23043     OpenLayers/Geometry/Polygon.js
23044    ====================================================================== */
23045
23046 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
23047  * full list of contributors). Published under the 2-clause BSD license.
23048  * See license.txt in the OpenLayers distribution or repository for the
23049  * full text of the license. */
23050
23051 /**
23052  * @requires OpenLayers/Geometry/Collection.js
23053  * @requires OpenLayers/Geometry/LinearRing.js
23054  */
23055
23056 /**
23057  * Class: OpenLayers.Geometry.Polygon 
23058  * Polygon is a collection of Geometry.LinearRings. 
23059  * 
23060  * Inherits from:
23061  *  - <OpenLayers.Geometry.Collection> 
23062  *  - <OpenLayers.Geometry> 
23063  */
23064 OpenLayers.Geometry.Polygon = OpenLayers.Class(
23065   OpenLayers.Geometry.Collection, {
23066
23067     /**
23068      * Property: componentTypes
23069      * {Array(String)} An array of class names representing the types of
23070      * components that the collection can include.  A null value means the
23071      * component types are not restricted.
23072      */
23073     componentTypes: ["OpenLayers.Geometry.LinearRing"],
23074
23075     /**
23076      * Constructor: OpenLayers.Geometry.Polygon
23077      * Constructor for a Polygon geometry. 
23078      * The first ring (this.component[0])is the outer bounds of the polygon and 
23079      * all subsequent rings (this.component[1-n]) are internal holes.
23080      *
23081      *
23082      * Parameters:
23083      * components - {Array(<OpenLayers.Geometry.LinearRing>)} 
23084      */
23085
23086     /** 
23087      * APIMethod: getArea
23088      * Calculated by subtracting the areas of the internal holes from the 
23089      *   area of the outer hole.
23090      * 
23091      * Returns:
23092      * {float} The area of the geometry
23093      */
23094     getArea: function() {
23095         var area = 0.0;
23096         if ( this.components && (this.components.length > 0)) {
23097             area += Math.abs(this.components[0].getArea());
23098             for (var i=1, len=this.components.length; i<len; i++) {
23099                 area -= Math.abs(this.components[i].getArea());
23100             }
23101         }
23102         return area;
23103     },
23104
23105     /** 
23106      * APIMethod: getGeodesicArea
23107      * Calculate the approximate area of the polygon were it projected onto
23108      *     the earth.
23109      *
23110      * Parameters:
23111      * projection - {<OpenLayers.Projection>} The spatial reference system
23112      *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
23113      *     assumed.
23114      * 
23115      * Reference:
23116      * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
23117      *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
23118      *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
23119      *
23120      * Returns:
23121      * {float} The approximate geodesic area of the polygon in square meters.
23122      */
23123     getGeodesicArea: function(projection) {
23124         var area = 0.0;
23125         if(this.components && (this.components.length > 0)) {
23126             area += Math.abs(this.components[0].getGeodesicArea(projection));
23127             for(var i=1, len=this.components.length; i<len; i++) {
23128                 area -= Math.abs(this.components[i].getGeodesicArea(projection));
23129             }
23130         }
23131         return area;
23132     },
23133
23134     /**
23135      * Method: containsPoint
23136      * Test if a point is inside a polygon.  Points on a polygon edge are
23137      *     considered inside.
23138      *
23139      * Parameters:
23140      * point - {<OpenLayers.Geometry.Point>}
23141      *
23142      * Returns:
23143      * {Boolean | Number} The point is inside the polygon.  Returns 1 if the
23144      *     point is on an edge.  Returns boolean otherwise.
23145      */
23146     containsPoint: function(point) {
23147         var numRings = this.components.length;
23148         var contained = false;
23149         if(numRings > 0) {
23150             // check exterior ring - 1 means on edge, boolean otherwise
23151             contained = this.components[0].containsPoint(point);
23152             if(contained !== 1) {
23153                 if(contained && numRings > 1) {
23154                     // check interior rings
23155                     var hole;
23156                     for(var i=1; i<numRings; ++i) {
23157                         hole = this.components[i].containsPoint(point);
23158                         if(hole) {
23159                             if(hole === 1) {
23160                                 // on edge
23161                                 contained = 1;
23162                             } else {
23163                                 // in hole
23164                                 contained = false;
23165                             }                            
23166                             break;
23167                         }
23168                     }
23169                 }
23170             }
23171         }
23172         return contained;
23173     },
23174
23175     /**
23176      * APIMethod: intersects
23177      * Determine if the input geometry intersects this one.
23178      *
23179      * Parameters:
23180      * geometry - {<OpenLayers.Geometry>} Any type of geometry.
23181      *
23182      * Returns:
23183      * {Boolean} The input geometry intersects this one.
23184      */
23185     intersects: function(geometry) {
23186         var intersect = false;
23187         var i, len;
23188         if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
23189             intersect = this.containsPoint(geometry);
23190         } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" ||
23191                   geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
23192             // check if rings/linestrings intersect
23193             for(i=0, len=this.components.length; i<len; ++i) {
23194                 intersect = geometry.intersects(this.components[i]);
23195                 if(intersect) {
23196                     break;
23197                 }
23198             }
23199             if(!intersect) {
23200                 // check if this poly contains points of the ring/linestring
23201                 for(i=0, len=geometry.components.length; i<len; ++i) {
23202                     intersect = this.containsPoint(geometry.components[i]);
23203                     if(intersect) {
23204                         break;
23205                     }
23206                 }
23207             }
23208         } else {
23209             for(i=0, len=geometry.components.length; i<len; ++ i) {
23210                 intersect = this.intersects(geometry.components[i]);
23211                 if(intersect) {
23212                     break;
23213                 }
23214             }
23215         }
23216         // check case where this poly is wholly contained by another
23217         if(!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
23218             // exterior ring points will be contained in the other geometry
23219             var ring = this.components[0];
23220             for(i=0, len=ring.components.length; i<len; ++i) {
23221                 intersect = geometry.containsPoint(ring.components[i]);
23222                 if(intersect) {
23223                     break;
23224                 }
23225             }
23226         }
23227         return intersect;
23228     },
23229
23230     /**
23231      * APIMethod: distanceTo
23232      * Calculate the closest distance between two geometries (on the x-y plane).
23233      *
23234      * Parameters:
23235      * geometry - {<OpenLayers.Geometry>} The target geometry.
23236      * options - {Object} Optional properties for configuring the distance
23237      *     calculation.
23238      *
23239      * Valid options:
23240      * details - {Boolean} Return details from the distance calculation.
23241      *     Default is false.
23242      * edge - {Boolean} Calculate the distance from this geometry to the
23243      *     nearest edge of the target geometry.  Default is true.  If true,
23244      *     calling distanceTo from a geometry that is wholly contained within
23245      *     the target will result in a non-zero distance.  If false, whenever
23246      *     geometries intersect, calling distanceTo will return 0.  If false,
23247      *     details cannot be returned.
23248      *
23249      * Returns:
23250      * {Number | Object} The distance between this geometry and the target.
23251      *     If details is true, the return will be an object with distance,
23252      *     x0, y0, x1, and y1 properties.  The x0 and y0 properties represent
23253      *     the coordinates of the closest point on this geometry. The x1 and y1
23254      *     properties represent the coordinates of the closest point on the
23255      *     target geometry.
23256      */
23257     distanceTo: function(geometry, options) {
23258         var edge = !(options && options.edge === false);
23259         var result;
23260         // this is the case where we might not be looking for distance to edge
23261         if(!edge && this.intersects(geometry)) {
23262             result = 0;
23263         } else {
23264             result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply(
23265                 this, [geometry, options]
23266             );
23267         }
23268         return result;
23269     },
23270
23271     CLASS_NAME: "OpenLayers.Geometry.Polygon"
23272 });
23273
23274 /**
23275  * APIMethod: createRegularPolygon
23276  * Create a regular polygon around a radius. Useful for creating circles 
23277  * and the like.
23278  *
23279  * Parameters:
23280  * origin - {<OpenLayers.Geometry.Point>} center of polygon.
23281  * radius - {Float} distance to vertex, in map units.
23282  * sides - {Integer} Number of sides. 20 approximates a circle.
23283  * rotation - {Float} original angle of rotation, in degrees.
23284  */
23285 OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {  
23286     var angle = Math.PI * ((1/sides) - (1/2));
23287     if(rotation) {
23288         angle += (rotation / 180) * Math.PI;
23289     }
23290     var rotatedAngle, x, y;
23291     var points = [];
23292     for(var i=0; i<sides; ++i) {
23293         rotatedAngle = angle + (i * 2 * Math.PI / sides);
23294         x = origin.x + (radius * Math.cos(rotatedAngle));
23295         y = origin.y + (radius * Math.sin(rotatedAngle));
23296         points.push(new OpenLayers.Geometry.Point(x, y));
23297     }
23298     var ring = new OpenLayers.Geometry.LinearRing(points);
23299     return new OpenLayers.Geometry.Polygon([ring]);
23300 };
23301 /* ======================================================================
23302     OpenLayers/Geometry/MultiPolygon.js
23303    ====================================================================== */
23304
23305 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
23306  * full list of contributors). Published under the 2-clause BSD license.
23307  * See license.txt in the OpenLayers distribution or repository for the
23308  * full text of the license. */
23309
23310 /**
23311  * @requires OpenLayers/Geometry/Collection.js
23312  * @requires OpenLayers/Geometry/Polygon.js
23313  */
23314
23315 /**
23316  * Class: OpenLayers.Geometry.MultiPolygon
23317  * MultiPolygon is a geometry with multiple <OpenLayers.Geometry.Polygon>
23318  * components.  Create a new instance with the <OpenLayers.Geometry.MultiPolygon>
23319  * constructor.
23320  * 
23321  * Inherits from:
23322  *  - <OpenLayers.Geometry.Collection>
23323  */
23324 OpenLayers.Geometry.MultiPolygon = OpenLayers.Class(
23325   OpenLayers.Geometry.Collection, {
23326
23327     /**
23328      * Property: componentTypes
23329      * {Array(String)} An array of class names representing the types of
23330      * components that the collection can include.  A null value means the
23331      * component types are not restricted.
23332      */
23333     componentTypes: ["OpenLayers.Geometry.Polygon"],
23334
23335     /**
23336      * Constructor: OpenLayers.Geometry.MultiPolygon
23337      * Create a new MultiPolygon geometry
23338      *
23339      * Parameters:
23340      * components - {Array(<OpenLayers.Geometry.Polygon>)} An array of polygons
23341      *              used to generate the MultiPolygon
23342      *
23343      */
23344
23345     CLASS_NAME: "OpenLayers.Geometry.MultiPolygon"
23346 });
23347 /* ======================================================================
23348     OpenLayers/Format/GML.js
23349    ====================================================================== */
23350
23351 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
23352  * full list of contributors). Published under the 2-clause BSD license.
23353  * See license.txt in the OpenLayers distribution or repository for the
23354  * full text of the license. */
23355
23356 /**
23357  * @requires OpenLayers/Format/XML.js
23358  * @requires OpenLayers/Feature/Vector.js
23359  * @requires OpenLayers/Geometry/Point.js
23360  * @requires OpenLayers/Geometry/MultiPoint.js
23361  * @requires OpenLayers/Geometry/LineString.js
23362  * @requires OpenLayers/Geometry/MultiLineString.js
23363  * @requires OpenLayers/Geometry/Polygon.js
23364  * @requires OpenLayers/Geometry/MultiPolygon.js
23365  */
23366
23367 /**
23368  * Class: OpenLayers.Format.GML
23369  * Read/Write GML. Create a new instance with the <OpenLayers.Format.GML>
23370  *     constructor.  Supports the GML simple features profile.
23371  * 
23372  * Inherits from:
23373  *  - <OpenLayers.Format.XML>
23374  */
23375 OpenLayers.Format.GML = OpenLayers.Class(OpenLayers.Format.XML, {
23376     
23377     /**
23378      * APIProperty: featureNS
23379      * {String} Namespace used for feature attributes.  Default is
23380      *     "http://mapserver.gis.umn.edu/mapserver".
23381      */
23382     featureNS: "http://mapserver.gis.umn.edu/mapserver",
23383     
23384     /**
23385      * APIProperty: featurePrefix
23386      * {String} Namespace alias (or prefix) for feature nodes.  Default is
23387      *     "feature".
23388      */
23389     featurePrefix: "feature",
23390     
23391     /**
23392      * APIProperty: featureName
23393      * {String} Element name for features. Default is "featureMember".
23394      */
23395     featureName: "featureMember", 
23396     
23397     /**
23398      * APIProperty: layerName
23399      * {String} Name of data layer. Default is "features".
23400      */
23401     layerName: "features",
23402     
23403     /**
23404      * APIProperty: geometryName
23405      * {String} Name of geometry element.  Defaults to "geometry".
23406      */
23407     geometryName: "geometry",
23408     
23409     /** 
23410      * APIProperty: collectionName
23411      * {String} Name of featureCollection element.
23412      */
23413     collectionName: "FeatureCollection",
23414     
23415     /**
23416      * APIProperty: gmlns
23417      * {String} GML Namespace.
23418      */
23419     gmlns: "http://www.opengis.net/gml",
23420
23421     /**
23422      * APIProperty: extractAttributes
23423      * {Boolean} Extract attributes from GML.
23424      */
23425     extractAttributes: true,
23426     
23427     /**
23428      * APIProperty: xy
23429      * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
23430      * Changing is not recommended, a new Format should be instantiated.
23431      */ 
23432     xy: true,
23433     
23434     /**
23435      * Constructor: OpenLayers.Format.GML
23436      * Create a new parser for GML.
23437      *
23438      * Parameters:
23439      * options - {Object} An optional object whose properties will be set on
23440      *     this instance.
23441      */
23442     initialize: function(options) {
23443         // compile regular expressions once instead of every time they are used
23444         this.regExes = {
23445             trimSpace: (/^\s*|\s*$/g),
23446             removeSpace: (/\s*/g),
23447             splitSpace: (/\s+/),
23448             trimComma: (/\s*,\s*/g)
23449         };
23450         OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
23451     },
23452
23453     /**
23454      * APIMethod: read
23455      * Read data from a string, and return a list of features. 
23456      * 
23457      * Parameters:
23458      * data - {String} or {DOMElement} data to read/parse.
23459      *
23460      * Returns:
23461      * {Array(<OpenLayers.Feature.Vector>)} An array of features.
23462      */
23463     read: function(data) {
23464         if(typeof data == "string") { 
23465             data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
23466         }
23467         var featureNodes = this.getElementsByTagNameNS(data.documentElement,
23468                                                        this.gmlns,
23469                                                        this.featureName);
23470         var features = [];
23471         for(var i=0; i<featureNodes.length; i++) {
23472             var feature = this.parseFeature(featureNodes[i]);
23473             if(feature) {
23474                 features.push(feature);
23475             }
23476         }
23477         return features;
23478     },
23479     
23480     /**
23481      * Method: parseFeature
23482      * This function is the core of the GML parsing code in OpenLayers.
23483      *    It creates the geometries that are then attached to the returned
23484      *    feature, and calls parseAttributes() to get attribute data out.
23485      *    
23486      * Parameters:
23487      * node - {DOMElement} A GML feature node. 
23488      */
23489     parseFeature: function(node) {
23490         // only accept one geometry per feature - look for highest "order"
23491         var order = ["MultiPolygon", "Polygon",
23492                      "MultiLineString", "LineString",
23493                      "MultiPoint", "Point", "Envelope"];
23494         // FIXME: In case we parse a feature with no geometry, but boundedBy an Envelope,
23495         // this code creates a geometry derived from the Envelope. This is not correct.
23496         var type, nodeList, geometry, parser;
23497         for(var i=0; i<order.length; ++i) {
23498             type = order[i];
23499             nodeList = this.getElementsByTagNameNS(node, this.gmlns, type);
23500             if(nodeList.length > 0) {
23501                 // only deal with first geometry of this type
23502                 parser = this.parseGeometry[type.toLowerCase()];
23503                 if(parser) {
23504                     geometry = parser.apply(this, [nodeList[0]]);
23505                     if (this.internalProjection && this.externalProjection) {
23506                         geometry.transform(this.externalProjection, 
23507                                            this.internalProjection); 
23508                     }                       
23509                 } else {
23510                     throw new TypeError("Unsupported geometry type: " + type);
23511                 }
23512                 // stop looking for different geometry types
23513                 break;
23514             }
23515         }
23516
23517         var bounds;
23518         var boxNodes = this.getElementsByTagNameNS(node, this.gmlns, "Box");
23519         for(i=0; i<boxNodes.length; ++i) {
23520             var boxNode = boxNodes[i];
23521             var box = this.parseGeometry["box"].apply(this, [boxNode]);
23522             var parentNode = boxNode.parentNode;
23523             var parentName = parentNode.localName ||
23524                              parentNode.nodeName.split(":").pop();
23525             if(parentName === "boundedBy") {
23526                 bounds = box;
23527             } else {
23528                 geometry = box.toGeometry();
23529             }
23530         }
23531         
23532         // construct feature (optionally with attributes)
23533         var attributes;
23534         if(this.extractAttributes) {
23535             attributes = this.parseAttributes(node);
23536         }
23537         var feature = new OpenLayers.Feature.Vector(geometry, attributes);
23538         feature.bounds = bounds;
23539         
23540         feature.gml = {
23541             featureType: node.firstChild.nodeName.split(":")[1],
23542             featureNS: node.firstChild.namespaceURI,
23543             featureNSPrefix: node.firstChild.prefix
23544         };
23545                 
23546         // assign fid - this can come from a "fid" or "id" attribute
23547         var childNode = node.firstChild;
23548         var fid;
23549         while(childNode) {
23550             if(childNode.nodeType == 1) {
23551                 fid = childNode.getAttribute("fid") ||
23552                       childNode.getAttribute("id");
23553                 if(fid) {
23554                     break;
23555                 }
23556             }
23557             childNode = childNode.nextSibling;
23558         }
23559         feature.fid = fid;
23560         return feature;
23561     },
23562     
23563     /**
23564      * Property: parseGeometry
23565      * Properties of this object are the functions that parse geometries based
23566      *     on their type.
23567      */
23568     parseGeometry: {
23569         
23570         /**
23571          * Method: parseGeometry.point
23572          * Given a GML node representing a point geometry, create an OpenLayers
23573          *     point geometry.
23574          *
23575          * Parameters:
23576          * node - {DOMElement} A GML node.
23577          *
23578          * Returns:
23579          * {<OpenLayers.Geometry.Point>} A point geometry.
23580          */
23581         point: function(node) {
23582             /**
23583              * Three coordinate variations to consider:
23584              * 1) <gml:pos>x y z</gml:pos>
23585              * 2) <gml:coordinates>x, y, z</gml:coordinates>
23586              * 3) <gml:coord><gml:X>x</gml:X><gml:Y>y</gml:Y></gml:coord>
23587              */
23588             var nodeList, coordString;
23589             var coords = [];
23590
23591             // look for <gml:pos>
23592             var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "pos");
23593             if(nodeList.length > 0) {
23594                 coordString = nodeList[0].firstChild.nodeValue;
23595                 coordString = coordString.replace(this.regExes.trimSpace, "");
23596                 coords = coordString.split(this.regExes.splitSpace);
23597             }
23598
23599             // look for <gml:coordinates>
23600             if(coords.length == 0) {
23601                 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
23602                                                        "coordinates");
23603                 if(nodeList.length > 0) {
23604                     coordString = nodeList[0].firstChild.nodeValue;
23605                     coordString = coordString.replace(this.regExes.removeSpace,
23606                                                       "");
23607                     coords = coordString.split(",");
23608                 }
23609             }
23610
23611             // look for <gml:coord>
23612             if(coords.length == 0) {
23613                 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
23614                                                        "coord");
23615                 if(nodeList.length > 0) {
23616                     var xList = this.getElementsByTagNameNS(nodeList[0],
23617                                                             this.gmlns, "X");
23618                     var yList = this.getElementsByTagNameNS(nodeList[0],
23619                                                             this.gmlns, "Y");
23620                     if(xList.length > 0 && yList.length > 0) {
23621                         coords = [xList[0].firstChild.nodeValue,
23622                                   yList[0].firstChild.nodeValue];
23623                     }
23624                 }
23625             }
23626                 
23627             // preserve third dimension
23628             if(coords.length == 2) {
23629                 coords[2] = null;
23630             }
23631             
23632             if (this.xy) {
23633                 return new OpenLayers.Geometry.Point(coords[0], coords[1],
23634                                                  coords[2]);
23635             }
23636             else{
23637                 return new OpenLayers.Geometry.Point(coords[1], coords[0],
23638                                                  coords[2]);
23639             }
23640         },
23641         
23642         /**
23643          * Method: parseGeometry.multipoint
23644          * Given a GML node representing a multipoint geometry, create an
23645          *     OpenLayers multipoint geometry.
23646          *
23647          * Parameters:
23648          * node - {DOMElement} A GML node.
23649          *
23650          * Returns:
23651          * {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
23652          */
23653         multipoint: function(node) {
23654             var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
23655                                                        "Point");
23656             var components = [];
23657             if(nodeList.length > 0) {
23658                 var point;
23659                 for(var i=0; i<nodeList.length; ++i) {
23660                     point = this.parseGeometry.point.apply(this, [nodeList[i]]);
23661                     if(point) {
23662                         components.push(point);
23663                     }
23664                 }
23665             }
23666             return new OpenLayers.Geometry.MultiPoint(components);
23667         },
23668         
23669         /**
23670          * Method: parseGeometry.linestring
23671          * Given a GML node representing a linestring geometry, create an
23672          *     OpenLayers linestring geometry.
23673          *
23674          * Parameters:
23675          * node - {DOMElement} A GML node.
23676          *
23677          * Returns:
23678          * {<OpenLayers.Geometry.LineString>} A linestring geometry.
23679          */
23680         linestring: function(node, ring) {
23681             /**
23682              * Two coordinate variations to consider:
23683              * 1) <gml:posList dimension="d">x0 y0 z0 x1 y1 z1</gml:posList>
23684              * 2) <gml:coordinates>x0, y0, z0 x1, y1, z1</gml:coordinates>
23685              */
23686             var nodeList, coordString;
23687             var coords = [];
23688             var points = [];
23689
23690             // look for <gml:posList>
23691             nodeList = this.getElementsByTagNameNS(node, this.gmlns, "posList");
23692             if(nodeList.length > 0) {
23693                 coordString = this.getChildValue(nodeList[0]);
23694                 coordString = coordString.replace(this.regExes.trimSpace, "");
23695                 coords = coordString.split(this.regExes.splitSpace);
23696                 var dim = parseInt(nodeList[0].getAttribute("dimension"));
23697                 var j, x, y, z;
23698                 for(var i=0; i<coords.length/dim; ++i) {
23699                     j = i * dim;
23700                     x = coords[j];
23701                     y = coords[j+1];
23702                     z = (dim == 2) ? null : coords[j+2];
23703                     if (this.xy) {
23704                         points.push(new OpenLayers.Geometry.Point(x, y, z));
23705                     } else {
23706                         points.push(new OpenLayers.Geometry.Point(y, x, z));
23707                     }
23708                 }
23709             }
23710
23711             // look for <gml:coordinates>
23712             if(coords.length == 0) {
23713                 nodeList = this.getElementsByTagNameNS(node, this.gmlns,
23714                                                        "coordinates");
23715                 if(nodeList.length > 0) {
23716                     coordString = this.getChildValue(nodeList[0]);
23717                     coordString = coordString.replace(this.regExes.trimSpace,
23718                                                       "");
23719                     coordString = coordString.replace(this.regExes.trimComma,
23720                                                       ",");
23721                     var pointList = coordString.split(this.regExes.splitSpace);
23722                     for(var i=0; i<pointList.length; ++i) {
23723                         coords = pointList[i].split(",");
23724                         if(coords.length == 2) {
23725                             coords[2] = null;
23726                         }
23727                         if (this.xy) {
23728                             points.push(new OpenLayers.Geometry.Point(coords[0],
23729                                                                   coords[1],
23730                                                                   coords[2]));
23731                         } else {
23732                             points.push(new OpenLayers.Geometry.Point(coords[1],
23733                                                                   coords[0],
23734                                                                   coords[2]));
23735                         }
23736                     }
23737                 }
23738             }
23739
23740             var line = null;
23741             if(points.length != 0) {
23742                 if(ring) {
23743                     line = new OpenLayers.Geometry.LinearRing(points);
23744                 } else {
23745                     line = new OpenLayers.Geometry.LineString(points);
23746                 }
23747             }
23748             return line;
23749         },
23750         
23751         /**
23752          * Method: parseGeometry.multilinestring
23753          * Given a GML node representing a multilinestring geometry, create an
23754          *     OpenLayers multilinestring geometry.
23755          *
23756          * Parameters:
23757          * node - {DOMElement} A GML node.
23758          *
23759          * Returns:
23760          * {<OpenLayers.Geometry.MultiLineString>} A multilinestring geometry.
23761          */
23762         multilinestring: function(node) {
23763             var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
23764                                                        "LineString");
23765             var components = [];
23766             if(nodeList.length > 0) {
23767                 var line;
23768                 for(var i=0; i<nodeList.length; ++i) {
23769                     line = this.parseGeometry.linestring.apply(this,
23770                                                                [nodeList[i]]);
23771                     if(line) {
23772                         components.push(line);
23773                     }
23774                 }
23775             }
23776             return new OpenLayers.Geometry.MultiLineString(components);
23777         },
23778         
23779         /**
23780          * Method: parseGeometry.polygon
23781          * Given a GML node representing a polygon geometry, create an
23782          *     OpenLayers polygon geometry.
23783          *
23784          * Parameters:
23785          * node - {DOMElement} A GML node.
23786          *
23787          * Returns:
23788          * {<OpenLayers.Geometry.Polygon>} A polygon geometry.
23789          */
23790         polygon: function(node) {
23791             var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
23792                                                        "LinearRing");
23793             var components = [];
23794             if(nodeList.length > 0) {
23795                 // this assumes exterior ring first, inner rings after
23796                 var ring;
23797                 for(var i=0; i<nodeList.length; ++i) {
23798                     ring = this.parseGeometry.linestring.apply(this,
23799                                                         [nodeList[i], true]);
23800                     if(ring) {
23801                         components.push(ring);
23802                     }
23803                 }
23804             }
23805             return new OpenLayers.Geometry.Polygon(components);
23806         },
23807         
23808         /**
23809          * Method: parseGeometry.multipolygon
23810          * Given a GML node representing a multipolygon geometry, create an
23811          *     OpenLayers multipolygon geometry.
23812          *
23813          * Parameters:
23814          * node - {DOMElement} A GML node.
23815          *
23816          * Returns:
23817          * {<OpenLayers.Geometry.MultiPolygon>} A multipolygon geometry.
23818          */
23819         multipolygon: function(node) {
23820             var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
23821                                                        "Polygon");
23822             var components = [];
23823             if(nodeList.length > 0) {
23824                 var polygon;
23825                 for(var i=0; i<nodeList.length; ++i) {
23826                     polygon = this.parseGeometry.polygon.apply(this,
23827                                                                [nodeList[i]]);
23828                     if(polygon) {
23829                         components.push(polygon);
23830                     }
23831                 }
23832             }
23833             return new OpenLayers.Geometry.MultiPolygon(components);
23834         },
23835         
23836         envelope: function(node) {
23837             var components = [];
23838             var coordString;
23839             var envelope;
23840             
23841             var lpoint = this.getElementsByTagNameNS(node, this.gmlns, "lowerCorner");
23842             if (lpoint.length > 0) {
23843                 var coords = [];
23844                 
23845                 if(lpoint.length > 0) {
23846                     coordString = lpoint[0].firstChild.nodeValue;
23847                     coordString = coordString.replace(this.regExes.trimSpace, "");
23848                     coords = coordString.split(this.regExes.splitSpace);
23849                 }
23850                 
23851                 if(coords.length == 2) {
23852                     coords[2] = null;
23853                 }
23854                 if (this.xy) {
23855                     var lowerPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
23856                 } else {
23857                     var lowerPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
23858                 }
23859             }
23860             
23861             var upoint = this.getElementsByTagNameNS(node, this.gmlns, "upperCorner");
23862             if (upoint.length > 0) {
23863                 var coords = [];
23864                 
23865                 if(upoint.length > 0) {
23866                     coordString = upoint[0].firstChild.nodeValue;
23867                     coordString = coordString.replace(this.regExes.trimSpace, "");
23868                     coords = coordString.split(this.regExes.splitSpace);
23869                 }
23870                 
23871                 if(coords.length == 2) {
23872                     coords[2] = null;
23873                 }
23874                 if (this.xy) {
23875                     var upperPoint = new OpenLayers.Geometry.Point(coords[0], coords[1],coords[2]);
23876                 } else {
23877                     var upperPoint = new OpenLayers.Geometry.Point(coords[1], coords[0],coords[2]);
23878                 }
23879             }
23880             
23881             if (lowerPoint && upperPoint) {
23882                 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
23883                 components.push(new OpenLayers.Geometry.Point(upperPoint.x, lowerPoint.y));
23884                 components.push(new OpenLayers.Geometry.Point(upperPoint.x, upperPoint.y));
23885                 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, upperPoint.y));
23886                 components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
23887                 
23888                 var ring = new OpenLayers.Geometry.LinearRing(components);
23889                 envelope = new OpenLayers.Geometry.Polygon([ring]);
23890             }
23891             return envelope; 
23892         },
23893
23894         /**
23895          * Method: parseGeometry.box
23896          * Given a GML node representing a box geometry, create an
23897          *     OpenLayers.Bounds.
23898          *
23899          * Parameters:
23900          * node - {DOMElement} A GML node.
23901          *
23902          * Returns:
23903          * {<OpenLayers.Bounds>} A bounds representing the box.
23904          */
23905         box: function(node) {
23906             var nodeList = this.getElementsByTagNameNS(node, this.gmlns,
23907                                                    "coordinates");
23908             var coordString;
23909             var coords, beginPoint = null, endPoint = null;
23910             if (nodeList.length > 0) {
23911                 coordString = nodeList[0].firstChild.nodeValue;
23912                 coords = coordString.split(" ");
23913                 if (coords.length == 2) {
23914                     beginPoint = coords[0].split(",");
23915                     endPoint = coords[1].split(",");
23916                 }
23917             }
23918             if (beginPoint !== null && endPoint !== null) {
23919                 return new OpenLayers.Bounds(parseFloat(beginPoint[0]),
23920                     parseFloat(beginPoint[1]),
23921                     parseFloat(endPoint[0]),
23922                     parseFloat(endPoint[1]) );
23923             }
23924         }
23925         
23926     },
23927     
23928     /**
23929      * Method: parseAttributes
23930      *
23931      * Parameters:
23932      * node - {DOMElement}
23933      *
23934      * Returns:
23935      * {Object} An attributes object.
23936      */
23937     parseAttributes: function(node) {
23938         var attributes = {};
23939         // assume attributes are children of the first type 1 child
23940         var childNode = node.firstChild;
23941         var children, i, child, grandchildren, grandchild, name, value;
23942         while(childNode) {
23943             if(childNode.nodeType == 1) {
23944                 // attributes are type 1 children with one type 3 child
23945                 children = childNode.childNodes;
23946                 for(i=0; i<children.length; ++i) {
23947                     child = children[i];
23948                     if(child.nodeType == 1) {
23949                         grandchildren = child.childNodes;
23950                         if(grandchildren.length == 1) {
23951                             grandchild = grandchildren[0];
23952                             if(grandchild.nodeType == 3 ||
23953                                grandchild.nodeType == 4) {
23954                                 name = (child.prefix) ?
23955                                         child.nodeName.split(":")[1] :
23956                                         child.nodeName;
23957                                 value = grandchild.nodeValue.replace(
23958                                                 this.regExes.trimSpace, "");
23959                                 attributes[name] = value;
23960                             }
23961                         } else {
23962                             // If child has no childNodes (grandchildren),
23963                             // set an attribute with null value.
23964                             // e.g. <prefix:fieldname/> becomes
23965                             // {fieldname: null}
23966                             attributes[child.nodeName.split(":").pop()] = null;
23967                         }
23968                     }
23969                 }
23970                 break;
23971             }
23972             childNode = childNode.nextSibling;
23973         }
23974         return attributes;
23975     },
23976     
23977     /**
23978      * APIMethod: write
23979      * Generate a GML document string given a list of features. 
23980      * 
23981      * Parameters:
23982      * features - {Array(<OpenLayers.Feature.Vector>)} List of features to
23983      *     serialize into a string.
23984      *
23985      * Returns:
23986      * {String} A string representing the GML document.
23987      */
23988     write: function(features) {
23989         if(!(OpenLayers.Util.isArray(features))) {
23990             features = [features];
23991         }
23992         var gml = this.createElementNS("http://www.opengis.net/wfs",
23993                                        "wfs:" + this.collectionName);
23994         for(var i=0; i<features.length; i++) {
23995             gml.appendChild(this.createFeatureXML(features[i]));
23996         }
23997         return OpenLayers.Format.XML.prototype.write.apply(this, [gml]);
23998     },
23999
24000     /** 
24001      * Method: createFeatureXML
24002      * Accept an OpenLayers.Feature.Vector, and build a GML node for it.
24003      *
24004      * Parameters:
24005      * feature - {<OpenLayers.Feature.Vector>} The feature to be built as GML.
24006      *
24007      * Returns:
24008      * {DOMElement} A node reprensting the feature in GML.
24009      */
24010     createFeatureXML: function(feature) {
24011         var geometry = feature.geometry;
24012         var geometryNode = this.buildGeometryNode(geometry);
24013         var geomContainer = this.createElementNS(this.featureNS,
24014                                                  this.featurePrefix + ":" +
24015                                                  this.geometryName);
24016         geomContainer.appendChild(geometryNode);
24017         var featureNode = this.createElementNS(this.gmlns,
24018                                                "gml:" + this.featureName);
24019         var featureContainer = this.createElementNS(this.featureNS,
24020                                                     this.featurePrefix + ":" +
24021                                                     this.layerName);
24022         var fid = feature.fid || feature.id;
24023         featureContainer.setAttribute("fid", fid);
24024         featureContainer.appendChild(geomContainer);
24025         for(var attr in feature.attributes) {
24026             var attrText = this.createTextNode(feature.attributes[attr]); 
24027             var nodename = attr.substring(attr.lastIndexOf(":") + 1);
24028             var attrContainer = this.createElementNS(this.featureNS,
24029                                                      this.featurePrefix + ":" +
24030                                                      nodename);
24031             attrContainer.appendChild(attrText);
24032             featureContainer.appendChild(attrContainer);
24033         }    
24034         featureNode.appendChild(featureContainer);
24035         return featureNode;
24036     },
24037     
24038     /**
24039      * APIMethod: buildGeometryNode
24040      */
24041     buildGeometryNode: function(geometry) {
24042         if (this.externalProjection && this.internalProjection) {
24043             geometry = geometry.clone();
24044             geometry.transform(this.internalProjection, 
24045                                this.externalProjection);
24046         }    
24047         var className = geometry.CLASS_NAME;
24048         var type = className.substring(className.lastIndexOf(".") + 1);
24049         var builder = this.buildGeometry[type.toLowerCase()];
24050         return builder.apply(this, [geometry]);
24051     },
24052
24053     /**
24054      * Property: buildGeometry
24055      * Object containing methods to do the actual geometry node building
24056      *     based on geometry type.
24057      */
24058     buildGeometry: {
24059         // TBD retrieve the srs from layer
24060         // srsName is non-standard, so not including it until it's right.
24061         // gml.setAttribute("srsName",
24062         //                  "http://www.opengis.net/gml/srs/epsg.xml#4326");
24063
24064         /**
24065          * Method: buildGeometry.point
24066          * Given an OpenLayers point geometry, create a GML point.
24067          *
24068          * Parameters:
24069          * geometry - {<OpenLayers.Geometry.Point>} A point geometry.
24070          *
24071          * Returns:
24072          * {DOMElement} A GML point node.
24073          */
24074         point: function(geometry) {
24075             var gml = this.createElementNS(this.gmlns, "gml:Point");
24076             gml.appendChild(this.buildCoordinatesNode(geometry));
24077             return gml;
24078         },
24079         
24080         /**
24081          * Method: buildGeometry.multipoint
24082          * Given an OpenLayers multipoint geometry, create a GML multipoint.
24083          *
24084          * Parameters:
24085          * geometry - {<OpenLayers.Geometry.MultiPoint>} A multipoint geometry.
24086          *
24087          * Returns:
24088          * {DOMElement} A GML multipoint node.
24089          */
24090         multipoint: function(geometry) {
24091             var gml = this.createElementNS(this.gmlns, "gml:MultiPoint");
24092             var points = geometry.components;
24093             var pointMember, pointGeom;
24094             for(var i=0; i<points.length; i++) { 
24095                 pointMember = this.createElementNS(this.gmlns,
24096                                                    "gml:pointMember");
24097                 pointGeom = this.buildGeometry.point.apply(this,
24098                                                                [points[i]]);
24099                 pointMember.appendChild(pointGeom);
24100                 gml.appendChild(pointMember);
24101             }
24102             return gml;            
24103         },
24104         
24105         /**
24106          * Method: buildGeometry.linestring
24107          * Given an OpenLayers linestring geometry, create a GML linestring.
24108          *
24109          * Parameters:
24110          * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry.
24111          *
24112          * Returns:
24113          * {DOMElement} A GML linestring node.
24114          */
24115         linestring: function(geometry) {
24116             var gml = this.createElementNS(this.gmlns, "gml:LineString");
24117             gml.appendChild(this.buildCoordinatesNode(geometry));
24118             return gml;
24119         },
24120         
24121         /**
24122          * Method: buildGeometry.multilinestring
24123          * Given an OpenLayers multilinestring geometry, create a GML
24124          *     multilinestring.
24125          *
24126          * Parameters:
24127          * geometry - {<OpenLayers.Geometry.MultiLineString>} A multilinestring
24128          *     geometry.
24129          *
24130          * Returns:
24131          * {DOMElement} A GML multilinestring node.
24132          */
24133         multilinestring: function(geometry) {
24134             var gml = this.createElementNS(this.gmlns, "gml:MultiLineString");
24135             var lines = geometry.components;
24136             var lineMember, lineGeom;
24137             for(var i=0; i<lines.length; ++i) {
24138                 lineMember = this.createElementNS(this.gmlns,
24139                                                   "gml:lineStringMember");
24140                 lineGeom = this.buildGeometry.linestring.apply(this,
24141                                                                    [lines[i]]);
24142                 lineMember.appendChild(lineGeom);
24143                 gml.appendChild(lineMember);
24144             }
24145             return gml;
24146         },
24147         
24148         /**
24149          * Method: buildGeometry.linearring
24150          * Given an OpenLayers linearring geometry, create a GML linearring.
24151          *
24152          * Parameters:
24153          * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry.
24154          *
24155          * Returns:
24156          * {DOMElement} A GML linearring node.
24157          */
24158         linearring: function(geometry) {
24159             var gml = this.createElementNS(this.gmlns, "gml:LinearRing");
24160             gml.appendChild(this.buildCoordinatesNode(geometry));
24161             return gml;
24162         },
24163         
24164         /**
24165          * Method: buildGeometry.polygon
24166          * Given an OpenLayers polygon geometry, create a GML polygon.
24167          *
24168          * Parameters:
24169          * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry.
24170          *
24171          * Returns:
24172          * {DOMElement} A GML polygon node.
24173          */
24174         polygon: function(geometry) {
24175             var gml = this.createElementNS(this.gmlns, "gml:Polygon");
24176             var rings = geometry.components;
24177             var ringMember, ringGeom, type;
24178             for(var i=0; i<rings.length; ++i) {
24179                 type = (i==0) ? "outerBoundaryIs" : "innerBoundaryIs";
24180                 ringMember = this.createElementNS(this.gmlns,
24181                                                   "gml:" + type);
24182                 ringGeom = this.buildGeometry.linearring.apply(this,
24183                                                                    [rings[i]]);
24184                 ringMember.appendChild(ringGeom);
24185                 gml.appendChild(ringMember);
24186             }
24187             return gml;
24188         },
24189         
24190         /**
24191          * Method: buildGeometry.multipolygon
24192          * Given an OpenLayers multipolygon geometry, create a GML multipolygon.
24193          *
24194          * Parameters:
24195          * geometry - {<OpenLayers.Geometry.MultiPolygon>} A multipolygon
24196          *     geometry.
24197          *
24198          * Returns:
24199          * {DOMElement} A GML multipolygon node.
24200          */
24201         multipolygon: function(geometry) {
24202             var gml = this.createElementNS(this.gmlns, "gml:MultiPolygon");
24203             var polys = geometry.components;
24204             var polyMember, polyGeom;
24205             for(var i=0; i<polys.length; ++i) {
24206                 polyMember = this.createElementNS(this.gmlns,
24207                                                   "gml:polygonMember");
24208                 polyGeom = this.buildGeometry.polygon.apply(this,
24209                                                                 [polys[i]]);
24210                 polyMember.appendChild(polyGeom);
24211                 gml.appendChild(polyMember);
24212             }
24213             return gml;
24214
24215         },
24216  
24217         /**
24218          * Method: buildGeometry.bounds
24219          * Given an OpenLayers bounds, create a GML box.
24220          *
24221          * Parameters:
24222          * bounds - {<OpenLayers.Geometry.Bounds>} A bounds object.
24223          *
24224          * Returns:
24225          * {DOMElement} A GML box node.
24226          */
24227         bounds: function(bounds) {
24228             var gml = this.createElementNS(this.gmlns, "gml:Box");
24229             gml.appendChild(this.buildCoordinatesNode(bounds));
24230             return gml;
24231         }
24232     },
24233
24234     /**
24235      * Method: buildCoordinates
24236      * builds the coordinates XmlNode
24237      * (code)
24238      * <gml:coordinates decimal="." cs="," ts=" ">...</gml:coordinates>
24239      * (end)
24240      *
24241      * Parameters: 
24242      * geometry - {<OpenLayers.Geometry>} 
24243      *
24244      * Returns:
24245      * {XmlNode} created xmlNode
24246      */
24247     buildCoordinatesNode: function(geometry) {
24248         var coordinatesNode = this.createElementNS(this.gmlns,
24249                                                    "gml:coordinates");
24250         coordinatesNode.setAttribute("decimal", ".");
24251         coordinatesNode.setAttribute("cs", ",");
24252         coordinatesNode.setAttribute("ts", " ");
24253
24254         var parts = [];
24255
24256         if(geometry instanceof OpenLayers.Bounds){
24257             parts.push(geometry.left + "," + geometry.bottom);
24258             parts.push(geometry.right + "," + geometry.top);
24259         } else {
24260             var points = (geometry.components) ? geometry.components : [geometry];
24261             for(var i=0; i<points.length; i++) {
24262                 parts.push(points[i].x + "," + points[i].y);                
24263             }            
24264         }
24265
24266         var txtNode = this.createTextNode(parts.join(" "));
24267         coordinatesNode.appendChild(txtNode);
24268         
24269         return coordinatesNode;
24270     },
24271
24272     CLASS_NAME: "OpenLayers.Format.GML" 
24273 });
24274 /* ======================================================================
24275     OpenLayers/Format/GML/Base.js
24276    ====================================================================== */
24277
24278 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24279  * full list of contributors). Published under the 2-clause BSD license.
24280  * See license.txt in the OpenLayers distribution or repository for the
24281  * full text of the license. */
24282
24283 /**
24284  * @requires OpenLayers/Format/XML.js
24285  * @requires OpenLayers/Format/GML.js
24286  */
24287
24288 /**
24289  * Though required in the full build, if the GML format is excluded, we set
24290  * the namespace here.
24291  */
24292 if(!OpenLayers.Format.GML) {
24293     OpenLayers.Format.GML = {};
24294 }
24295
24296 /**
24297  * Class: OpenLayers.Format.GML.Base
24298  * Superclass for GML parsers.
24299  *
24300  * Inherits from:
24301  *  - <OpenLayers.Format.XML>
24302  */
24303 OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, {
24304     
24305     /**
24306      * Property: namespaces
24307      * {Object} Mapping of namespace aliases to namespace URIs.
24308      */
24309     namespaces: {
24310         gml: "http://www.opengis.net/gml",
24311         xlink: "http://www.w3.org/1999/xlink",
24312         xsi: "http://www.w3.org/2001/XMLSchema-instance",
24313         wfs: "http://www.opengis.net/wfs" // this is a convenience for reading wfs:FeatureCollection
24314     },
24315     
24316     /**
24317      * Property: defaultPrefix
24318      */
24319     defaultPrefix: "gml",
24320
24321     /**
24322      * Property: schemaLocation
24323      * {String} Schema location for a particular minor version.
24324      */
24325     schemaLocation: null,
24326     
24327     /**
24328      * APIProperty: featureType
24329      * {Array(String) or String} The local (without prefix) feature typeName(s).
24330      */
24331     featureType: null,
24332     
24333     /**
24334      * APIProperty: featureNS
24335      * {String} The feature namespace.  Must be set in the options at
24336      *     construction.
24337      */
24338     featureNS: null,
24339
24340     /**
24341      * APIProperty: geometry
24342      * {String} Name of geometry element.  Defaults to "geometry". If null, it
24343      * will be set on <read> when the first geometry is parsed.
24344      */
24345     geometryName: "geometry",
24346
24347     /**
24348      * APIProperty: extractAttributes
24349      * {Boolean} Extract attributes from GML.  Default is true.
24350      */
24351     extractAttributes: true,
24352     
24353     /**
24354      * APIProperty: srsName
24355      * {String} URI for spatial reference system.  This is optional for
24356      *     single part geometries and mandatory for collections and multis.
24357      *     If set, the srsName attribute will be written for all geometries.
24358      *     Default is null.
24359      */
24360     srsName: null,
24361
24362     /**
24363      * APIProperty: xy
24364      * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x)
24365      * Changing is not recommended, a new Format should be instantiated.
24366      */ 
24367     xy: true,
24368
24369     /**
24370      * Property: geometryTypes
24371      * {Object} Maps OpenLayers geometry class names to GML element names.
24372      *     Use <setGeometryTypes> before accessing this property.
24373      */
24374     geometryTypes: null,
24375
24376     /**
24377      * Property: singleFeatureType
24378      * {Boolean} True if there is only 1 featureType, and not an array
24379      *     of featuretypes.
24380      */
24381     singleFeatureType: null,
24382     
24383     /**
24384      * Property: autoConfig
24385      * {Boolean} Indicates if the format was configured without a <featureNS>,
24386      * but auto-configured <featureNS> and <featureType> during read.
24387      * Subclasses making use of <featureType> auto-configuration should make
24388      * the first call to the <readNode> method (usually in the read method)
24389      * with true as 3rd argument, so the auto-configured featureType can be
24390      * reset and the format can be reused for subsequent reads with data from
24391      * different featureTypes. Set to false after read if you want to keep the
24392      * auto-configured values.
24393      */
24394
24395     /**
24396      * Property: regExes
24397      * Compiled regular expressions for manipulating strings.
24398      */
24399     regExes: {
24400         trimSpace: (/^\s*|\s*$/g),
24401         removeSpace: (/\s*/g),
24402         splitSpace: (/\s+/),
24403         trimComma: (/\s*,\s*/g),
24404         featureMember: (/^(.*:)?featureMembers?$/)
24405     },
24406
24407     /**
24408      * Constructor: OpenLayers.Format.GML.Base
24409      * Instances of this class are not created directly.  Use the
24410      *     <OpenLayers.Format.GML.v2> or <OpenLayers.Format.GML.v3> constructor
24411      *     instead.
24412      *
24413      * Parameters:
24414      * options - {Object} An optional object whose properties will be set on
24415      *     this instance.
24416      *
24417      * Valid options properties:
24418      * featureType - {Array(String) or String} Local (without prefix) feature 
24419      *     typeName(s) (required for write).
24420      * featureNS - {String} Feature namespace (required for write).
24421      * geometryName - {String} Geometry element name (required for write).
24422      */
24423     initialize: function(options) {
24424         OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
24425         this.setGeometryTypes();
24426         if(options && options.featureNS) {
24427             this.setNamespace("feature", options.featureNS);
24428         }
24429         this.singleFeatureType = !options || (typeof options.featureType === "string");
24430     },
24431     
24432     /**
24433      * Method: read
24434      *
24435      * Parameters:
24436      * data - {DOMElement} A gml:featureMember element, a gml:featureMembers
24437      *     element, or an element containing either of the above at any level.
24438      *
24439      * Returns:
24440      * {Array(<OpenLayers.Feature.Vector>)} An array of features.
24441      */
24442     read: function(data) {
24443         if(typeof data == "string") { 
24444             data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
24445         }
24446         if(data && data.nodeType == 9) {
24447             data = data.documentElement;
24448         }
24449         var features = [];
24450         this.readNode(data, {features: features}, true);
24451         if(features.length == 0) {
24452             // look for gml:featureMember elements
24453             var elements = this.getElementsByTagNameNS(
24454                 data, this.namespaces.gml, "featureMember"
24455             );
24456             if(elements.length) {
24457                 for(var i=0, len=elements.length; i<len; ++i) {
24458                     this.readNode(elements[i], {features: features}, true);
24459                 }
24460             } else {
24461                 // look for gml:featureMembers elements (this is v3, but does no harm here)
24462                 var elements = this.getElementsByTagNameNS(
24463                     data, this.namespaces.gml, "featureMembers"
24464                 );
24465                 if(elements.length) {
24466                     // there can be only one
24467                     this.readNode(elements[0], {features: features}, true);
24468                 }
24469             }
24470         }
24471         return features;
24472     },
24473     
24474     /**
24475      * Method: readNode
24476      * Shorthand for applying one of the named readers given the node
24477      *     namespace and local name.  Readers take two args (node, obj) and
24478      *     generally extend or modify the second.
24479      *
24480      * Parameters:
24481      * node - {DOMElement} The node to be read (required).
24482      * obj - {Object} The object to be modified (optional).
24483      * first - {Boolean} Should be set to true for the first node read. This
24484      *     is usually the readNode call in the read method. Without this being
24485      *     set, auto-configured properties will stick on subsequent reads.
24486      *
24487      * Returns:
24488      * {Object} The input object, modified (or a new one if none was provided).
24489      */
24490     readNode: function(node, obj, first) {
24491         // on subsequent calls of format.read(), we want to reset auto-
24492         // configured properties and auto-configure again.
24493         if (first === true && this.autoConfig === true) {
24494             this.featureType = null;
24495             delete this.namespaceAlias[this.featureNS];
24496             delete this.namespaces["feature"];
24497             this.featureNS = null;
24498         }
24499         // featureType auto-configuration
24500         if (!this.featureNS && (!(node.prefix in this.namespaces) &&
24501                 node.parentNode.namespaceURI == this.namespaces["gml"] &&
24502                 this.regExes.featureMember.test(node.parentNode.nodeName))) {
24503             this.featureType = node.nodeName.split(":").pop();
24504             this.setNamespace("feature", node.namespaceURI);
24505             this.featureNS = node.namespaceURI;
24506             this.autoConfig = true;
24507         }
24508         return OpenLayers.Format.XML.prototype.readNode.apply(this, [node, obj]);
24509     },
24510     
24511     /**
24512      * Property: readers
24513      * Contains public functions, grouped by namespace prefix, that will
24514      *     be applied when a namespaced node is found matching the function
24515      *     name.  The function will be applied in the scope of this parser
24516      *     with two arguments: the node being read and a context object passed
24517      *     from the parent.
24518      */
24519     readers: {
24520         "gml": {
24521             "_inherit": function(node, obj, container) {
24522                 // To be implemented by version specific parsers
24523             },
24524             "featureMember": function(node, obj) {
24525                 this.readChildNodes(node, obj);
24526             },
24527             "featureMembers": function(node, obj) {
24528                 this.readChildNodes(node, obj);                
24529             },
24530             "name": function(node, obj) {
24531                 obj.name = this.getChildValue(node);
24532             },
24533             "boundedBy": function(node, obj) {
24534                 var container = {};
24535                 this.readChildNodes(node, container);
24536                 if(container.components && container.components.length > 0) {
24537                     obj.bounds = container.components[0];
24538                 }
24539             },
24540             "Point": function(node, container) {
24541                 var obj = {points: []};
24542                 this.readChildNodes(node, obj);
24543                 if(!container.components) {
24544                     container.components = [];
24545                 }
24546                 container.components.push(obj.points[0]);
24547             },
24548             "coordinates": function(node, obj) {
24549                 var str = this.getChildValue(node).replace(
24550                     this.regExes.trimSpace, ""
24551                 );
24552                 str = str.replace(this.regExes.trimComma, ",");
24553                 var pointList = str.split(this.regExes.splitSpace);
24554                 var coords;
24555                 var numPoints = pointList.length;
24556                 var points = new Array(numPoints);
24557                 for(var i=0; i<numPoints; ++i) {
24558                     coords = pointList[i].split(",");
24559                     if (this.xy) {
24560                         points[i] = new OpenLayers.Geometry.Point(
24561                             coords[0], coords[1], coords[2]
24562                         );
24563                     } else {
24564                         points[i] = new OpenLayers.Geometry.Point(
24565                             coords[1], coords[0], coords[2]
24566                         );
24567                     }
24568                 }
24569                 obj.points = points;
24570             },
24571             "coord": function(node, obj) {
24572                 var coord = {};
24573                 this.readChildNodes(node, coord);
24574                 if(!obj.points) {
24575                     obj.points = [];
24576                 }
24577                 obj.points.push(new OpenLayers.Geometry.Point(
24578                     coord.x, coord.y, coord.z
24579                 ));
24580             },
24581             "X": function(node, coord) {
24582                 coord.x = this.getChildValue(node);
24583             },
24584             "Y": function(node, coord) {
24585                 coord.y = this.getChildValue(node);
24586             },
24587             "Z": function(node, coord) {
24588                 coord.z = this.getChildValue(node);
24589             },
24590             "MultiPoint": function(node, container) {
24591                 var obj = {components: []};
24592                 this.readers.gml._inherit.apply(this, [node, obj, container]);
24593                 this.readChildNodes(node, obj);
24594                 container.components = [
24595                     new OpenLayers.Geometry.MultiPoint(obj.components)
24596                 ];
24597             },
24598             "pointMember": function(node, obj) {
24599                 this.readChildNodes(node, obj);
24600             },
24601             "LineString": function(node, container) {
24602                 var obj = {};
24603                 this.readers.gml._inherit.apply(this, [node, obj, container]);
24604                 this.readChildNodes(node, obj);
24605                 if(!container.components) {
24606                     container.components = [];
24607                 }
24608                 container.components.push(
24609                     new OpenLayers.Geometry.LineString(obj.points)
24610                 );
24611             },
24612             "MultiLineString": function(node, container) {
24613                 var obj = {components: []};
24614                 this.readers.gml._inherit.apply(this, [node, obj, container]);
24615                 this.readChildNodes(node, obj);
24616                 container.components = [
24617                     new OpenLayers.Geometry.MultiLineString(obj.components)
24618                 ];
24619             },
24620             "lineStringMember": function(node, obj) {
24621                 this.readChildNodes(node, obj);
24622             },
24623             "Polygon": function(node, container) {
24624                 var obj = {outer: null, inner: []};
24625                 this.readers.gml._inherit.apply(this, [node, obj, container]);
24626                 this.readChildNodes(node, obj);
24627                 obj.inner.unshift(obj.outer);
24628                 if(!container.components) {
24629                     container.components = [];
24630                 }
24631                 container.components.push(
24632                     new OpenLayers.Geometry.Polygon(obj.inner)
24633                 );
24634             },
24635             "LinearRing": function(node, obj) {
24636                 var container = {};
24637                 this.readers.gml._inherit.apply(this, [node, container]);
24638                 this.readChildNodes(node, container);
24639                 obj.components = [new OpenLayers.Geometry.LinearRing(
24640                     container.points
24641                 )];
24642             },
24643             "MultiPolygon": function(node, container) {
24644                 var obj = {components: []};
24645                 this.readers.gml._inherit.apply(this, [node, obj, container]);
24646                 this.readChildNodes(node, obj);
24647                 container.components = [
24648                     new OpenLayers.Geometry.MultiPolygon(obj.components)
24649                 ];
24650             },
24651             "polygonMember": function(node, obj) {
24652                 this.readChildNodes(node, obj);
24653             },
24654             "GeometryCollection": function(node, container) {
24655                 var obj = {components: []};
24656                 this.readers.gml._inherit.apply(this, [node, obj, container]);
24657                 this.readChildNodes(node, obj);
24658                 container.components = [
24659                     new OpenLayers.Geometry.Collection(obj.components)
24660                 ];
24661             },
24662             "geometryMember": function(node, obj) {
24663                 this.readChildNodes(node, obj);
24664             }
24665         },
24666         "feature": {
24667             "*": function(node, obj) {
24668                 // The node can either be named like the featureType, or it
24669                 // can be a child of the feature:featureType.  Children can be
24670                 // geometry or attributes.
24671                 var name;
24672                 var local = node.localName || node.nodeName.split(":").pop();
24673                 // Since an attribute can have the same name as the feature type
24674                 // we only want to read the node as a feature if the parent
24675                 // node can have feature nodes as children.  In this case, the
24676                 // obj.features property is set.
24677                 if (obj.features) {
24678                     if (!this.singleFeatureType &&
24679                         (OpenLayers.Util.indexOf(this.featureType, local) !== -1)) {
24680                         name = "_typeName";
24681                     } else if(local === this.featureType) {
24682                         name = "_typeName";
24683                     }
24684                 } else {
24685                     // Assume attribute elements have one child node and that the child
24686                     // is a text node.  Otherwise assume it is a geometry node.
24687                     if(node.childNodes.length == 0 ||
24688                        (node.childNodes.length == 1 && node.firstChild.nodeType == 3)) {
24689                         if(this.extractAttributes) {
24690                             name = "_attribute";
24691                         }
24692                     } else {
24693                         name = "_geometry";
24694                     }
24695                 }
24696                 if(name) {
24697                     this.readers.feature[name].apply(this, [node, obj]);
24698                 }
24699             },
24700             "_typeName": function(node, obj) {
24701                 var container = {components: [], attributes: {}};
24702                 this.readChildNodes(node, container);
24703                 // look for common gml namespaced elements
24704                 if(container.name) {
24705                     container.attributes.name = container.name;
24706                 }
24707                 var feature = new OpenLayers.Feature.Vector(
24708                     container.components[0], container.attributes
24709                 );
24710                 if (!this.singleFeatureType) {
24711                     feature.type = node.nodeName.split(":").pop();
24712                     feature.namespace = node.namespaceURI;
24713                 }
24714                 var fid = node.getAttribute("fid") ||
24715                     this.getAttributeNS(node, this.namespaces["gml"], "id");
24716                 if(fid) {
24717                     feature.fid = fid;
24718                 }
24719                 if(this.internalProjection && this.externalProjection &&
24720                    feature.geometry) {
24721                     feature.geometry.transform(
24722                         this.externalProjection, this.internalProjection
24723                     );
24724                 }
24725                 if(container.bounds) {
24726                     feature.bounds = container.bounds;
24727                 }
24728                 obj.features.push(feature);
24729             },
24730             "_geometry": function(node, obj) {
24731                 if (!this.geometryName) {
24732                     this.geometryName = node.nodeName.split(":").pop();
24733                 }
24734                 this.readChildNodes(node, obj);
24735             },
24736             "_attribute": function(node, obj) {
24737                 var local = node.localName || node.nodeName.split(":").pop();
24738                 var value = this.getChildValue(node);
24739                 obj.attributes[local] = value;
24740             }
24741         },
24742         "wfs": {
24743             "FeatureCollection": function(node, obj) {
24744                 this.readChildNodes(node, obj);
24745             }
24746         }
24747     },
24748     
24749     /**
24750      * Method: write
24751      *
24752      * Parameters:
24753      * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
24754      *     An array of features or a single feature.
24755      *
24756      * Returns:
24757      * {String} Given an array of features, a doc with a gml:featureMembers
24758      *     element will be returned.  Given a single feature, a doc with a
24759      *     gml:featureMember element will be returned.
24760      */
24761     write: function(features) {
24762         var name;
24763         if(OpenLayers.Util.isArray(features)) {
24764             name = "featureMembers";
24765         } else {
24766             name = "featureMember";
24767         }
24768         var root = this.writeNode("gml:" + name, features);
24769         this.setAttributeNS(
24770             root, this.namespaces["xsi"],
24771             "xsi:schemaLocation", this.schemaLocation
24772         );
24773
24774         return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
24775     },
24776     
24777     /**
24778      * Property: writers
24779      * As a compliment to the readers property, this structure contains public
24780      *     writing functions grouped by namespace alias and named like the
24781      *     node names they produce.
24782      */
24783     writers: {
24784         "gml": {
24785             "featureMember": function(feature) {
24786                 var node = this.createElementNSPlus("gml:featureMember");
24787                 this.writeNode("feature:_typeName", feature, node);
24788                 return node;
24789             },
24790             "MultiPoint": function(geometry) {
24791                 var node = this.createElementNSPlus("gml:MultiPoint");
24792                 var components = geometry.components || [geometry];
24793                 for(var i=0, ii=components.length; i<ii; ++i) {
24794                     this.writeNode("pointMember", components[i], node);
24795                 }
24796                 return node;
24797             },
24798             "pointMember": function(geometry) {
24799                 var node = this.createElementNSPlus("gml:pointMember");
24800                 this.writeNode("Point", geometry, node);
24801                 return node;
24802             },
24803             "MultiLineString": function(geometry) {
24804                 var node = this.createElementNSPlus("gml:MultiLineString");
24805                 var components = geometry.components || [geometry];
24806                 for(var i=0, ii=components.length; i<ii; ++i) {
24807                     this.writeNode("lineStringMember", components[i], node);
24808                 }
24809                 return node;
24810             },
24811             "lineStringMember": function(geometry) {
24812                 var node = this.createElementNSPlus("gml:lineStringMember");
24813                 this.writeNode("LineString", geometry, node);
24814                 return node;
24815             },
24816             "MultiPolygon": function(geometry) {
24817                 var node = this.createElementNSPlus("gml:MultiPolygon");
24818                 var components = geometry.components || [geometry];
24819                 for(var i=0, ii=components.length; i<ii; ++i) {
24820                     this.writeNode(
24821                         "polygonMember", components[i], node
24822                     );
24823                 }
24824                 return node;
24825             },
24826             "polygonMember": function(geometry) {
24827                 var node = this.createElementNSPlus("gml:polygonMember");
24828                 this.writeNode("Polygon", geometry, node);
24829                 return node;
24830             },
24831             "GeometryCollection": function(geometry) {
24832                 var node = this.createElementNSPlus("gml:GeometryCollection");
24833                 for(var i=0, len=geometry.components.length; i<len; ++i) {
24834                     this.writeNode("geometryMember", geometry.components[i], node);
24835                 }
24836                 return node;
24837             },
24838             "geometryMember": function(geometry) {
24839                 var node = this.createElementNSPlus("gml:geometryMember");
24840                 var child = this.writeNode("feature:_geometry", geometry);
24841                 node.appendChild(child.firstChild);
24842                 return node;
24843             }
24844         },
24845         "feature": {
24846             "_typeName": function(feature) {
24847                 var node = this.createElementNSPlus("feature:" + this.featureType, {
24848                     attributes: {fid: feature.fid}
24849                 });
24850                 if(feature.geometry) {
24851                     this.writeNode("feature:_geometry", feature.geometry, node);
24852                 }
24853                 for(var name in feature.attributes) {
24854                     var value = feature.attributes[name];
24855                     if(value != null) {
24856                         this.writeNode(
24857                             "feature:_attribute",
24858                             {name: name, value: value}, node
24859                         );
24860                     }
24861                 }
24862                 return node;
24863             },
24864             "_geometry": function(geometry) {
24865                 if(this.externalProjection && this.internalProjection) {
24866                     geometry = geometry.clone().transform(
24867                         this.internalProjection, this.externalProjection
24868                     );
24869                 }    
24870                 var node = this.createElementNSPlus(
24871                     "feature:" + this.geometryName
24872                 );
24873                 var type = this.geometryTypes[geometry.CLASS_NAME];
24874                 var child = this.writeNode("gml:" + type, geometry, node);
24875                 if(this.srsName) {
24876                     child.setAttribute("srsName", this.srsName);
24877                 }
24878                 return node;
24879             },
24880             "_attribute": function(obj) {
24881                 return this.createElementNSPlus("feature:" + obj.name, {
24882                     value: obj.value
24883                 });
24884             }
24885         },
24886         "wfs": {
24887             "FeatureCollection": function(features) {
24888                 /**
24889                  * This is only here because GML2 only describes abstract
24890                  * feature collections.  Typically, you would not be using
24891                  * the GML format to write wfs elements.  This just provides
24892                  * some way to write out lists of features.  GML3 defines the
24893                  * featureMembers element, so that is used by default instead.
24894                  */
24895                 var node = this.createElementNSPlus("wfs:FeatureCollection");
24896                 for(var i=0, len=features.length; i<len; ++i) {
24897                     this.writeNode("gml:featureMember", features[i], node);
24898                 }
24899                 return node;
24900             }
24901         }
24902     },
24903     
24904     /**
24905      * Method: setGeometryTypes
24906      * Sets the <geometryTypes> mapping.
24907      */
24908     setGeometryTypes: function() {
24909         this.geometryTypes = {
24910             "OpenLayers.Geometry.Point": "Point",
24911             "OpenLayers.Geometry.MultiPoint": "MultiPoint",
24912             "OpenLayers.Geometry.LineString": "LineString",
24913             "OpenLayers.Geometry.MultiLineString": "MultiLineString",
24914             "OpenLayers.Geometry.Polygon": "Polygon",
24915             "OpenLayers.Geometry.MultiPolygon": "MultiPolygon",
24916             "OpenLayers.Geometry.Collection": "GeometryCollection"
24917         };
24918     },
24919
24920     CLASS_NAME: "OpenLayers.Format.GML.Base" 
24921
24922 });
24923 /* ======================================================================
24924     OpenLayers/Format/GML/v2.js
24925    ====================================================================== */
24926
24927 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
24928  * full list of contributors). Published under the 2-clause BSD license.
24929  * See license.txt in the OpenLayers distribution or repository for the
24930  * full text of the license. */
24931
24932 /**
24933  * @requires OpenLayers/Format/GML/Base.js
24934  */
24935
24936 /**
24937  * Class: OpenLayers.Format.GML.v2
24938  * Parses GML version 2.
24939  *
24940  * Inherits from:
24941  *  - <OpenLayers.Format.GML.Base>
24942  */
24943 OpenLayers.Format.GML.v2 = OpenLayers.Class(OpenLayers.Format.GML.Base, {
24944     
24945     /**
24946      * Property: schemaLocation
24947      * {String} Schema location for a particular minor version.
24948      */
24949     schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd",
24950
24951     /**
24952      * Constructor: OpenLayers.Format.GML.v2
24953      * Create a parser for GML v2.
24954      *
24955      * Parameters:
24956      * options - {Object} An optional object whose properties will be set on
24957      *     this instance.
24958      *
24959      * Valid options properties:
24960      * featureType - {String} Local (without prefix) feature typeName (required).
24961      * featureNS - {String} Feature namespace (required).
24962      * geometryName - {String} Geometry element name.
24963      */
24964     initialize: function(options) {
24965         OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]);
24966     },
24967
24968     /**
24969      * Property: readers
24970      * Contains public functions, grouped by namespace prefix, that will
24971      *     be applied when a namespaced node is found matching the function
24972      *     name.  The function will be applied in the scope of this parser
24973      *     with two arguments: the node being read and a context object passed
24974      *     from the parent.
24975      */
24976     readers: {
24977         "gml": OpenLayers.Util.applyDefaults({
24978             "outerBoundaryIs": function(node, container) {
24979                 var obj = {};
24980                 this.readChildNodes(node, obj);
24981                 container.outer = obj.components[0];
24982             },
24983             "innerBoundaryIs": function(node, container) {
24984                 var obj = {};
24985                 this.readChildNodes(node, obj);
24986                 container.inner.push(obj.components[0]);
24987             },
24988             "Box": function(node, container) {
24989                 var obj = {};
24990                 this.readChildNodes(node, obj);
24991                 if(!container.components) {
24992                     container.components = [];
24993                 }
24994                 var min = obj.points[0];
24995                 var max = obj.points[1];
24996                 container.components.push(
24997                     new OpenLayers.Bounds(min.x, min.y, max.x, max.y)
24998                 );
24999             }
25000         }, OpenLayers.Format.GML.Base.prototype.readers["gml"]),
25001         "feature": OpenLayers.Format.GML.Base.prototype.readers["feature"],
25002         "wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"]
25003     },
25004
25005     /**
25006      * Method: write
25007      *
25008      * Parameters:
25009      * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector}
25010      *     An array of features or a single feature.
25011      *
25012      * Returns:
25013      * {String} Given an array of features, a doc with a gml:featureMembers
25014      *     element will be returned.  Given a single feature, a doc with a
25015      *     gml:featureMember element will be returned.
25016      */
25017     write: function(features) {
25018         var name;
25019         if(OpenLayers.Util.isArray(features)) {
25020             // GML2 only has abstract feature collections
25021             // wfs provides a feature collection from a well-known schema
25022             name = "wfs:FeatureCollection";
25023         } else {
25024             name = "gml:featureMember";
25025         }
25026         var root = this.writeNode(name, features);
25027         this.setAttributeNS(
25028             root, this.namespaces["xsi"],
25029             "xsi:schemaLocation", this.schemaLocation
25030         );
25031
25032         return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
25033     },
25034
25035     /**
25036      * Property: writers
25037      * As a compliment to the readers property, this structure contains public
25038      *     writing functions grouped by namespace alias and named like the
25039      *     node names they produce.
25040      */
25041     writers: {
25042         "gml": OpenLayers.Util.applyDefaults({
25043             "Point": function(geometry) {
25044                 var node = this.createElementNSPlus("gml:Point");
25045                 this.writeNode("coordinates", [geometry], node);
25046                 return node;
25047             },
25048             "coordinates": function(points) {
25049                 var numPoints = points.length;
25050                 var parts = new Array(numPoints);
25051                 var point;
25052                 for(var i=0; i<numPoints; ++i) {
25053                     point = points[i];
25054                     if(this.xy) {
25055                         parts[i] = point.x + "," + point.y;
25056                     } else {
25057                         parts[i] = point.y + "," + point.x;
25058                     }
25059                     if(point.z != undefined) { // allow null or undefined
25060                         parts[i] += "," + point.z;
25061                     }
25062                 }
25063                 return this.createElementNSPlus("gml:coordinates", {
25064                     attributes: {
25065                         decimal: ".", cs: ",", ts: " "
25066                     },
25067                     value: (numPoints == 1) ? parts[0] : parts.join(" ")
25068                 });
25069             },
25070             "LineString": function(geometry) {
25071                 var node = this.createElementNSPlus("gml:LineString");
25072                 this.writeNode("coordinates", geometry.components, node);
25073                 return node;
25074             },
25075             "Polygon": function(geometry) {
25076                 var node = this.createElementNSPlus("gml:Polygon");
25077                 this.writeNode("outerBoundaryIs", geometry.components[0], node);
25078                 for(var i=1; i<geometry.components.length; ++i) {
25079                     this.writeNode(
25080                         "innerBoundaryIs", geometry.components[i], node
25081                     );
25082                 }
25083                 return node;
25084             },
25085             "outerBoundaryIs": function(ring) {
25086                 var node = this.createElementNSPlus("gml:outerBoundaryIs");
25087                 this.writeNode("LinearRing", ring, node);
25088                 return node;
25089             },
25090             "innerBoundaryIs": function(ring) {
25091                 var node = this.createElementNSPlus("gml:innerBoundaryIs");
25092                 this.writeNode("LinearRing", ring, node);
25093                 return node;
25094             },
25095             "LinearRing": function(ring) {
25096                 var node = this.createElementNSPlus("gml:LinearRing");
25097                 this.writeNode("coordinates", ring.components, node);
25098                 return node;
25099             },
25100             "Box": function(bounds) {
25101                 var node = this.createElementNSPlus("gml:Box");
25102                 this.writeNode("coordinates", [
25103                     {x: bounds.left, y: bounds.bottom},
25104                     {x: bounds.right, y: bounds.top}
25105                 ], node);
25106                 // srsName attribute is optional for gml:Box
25107                 if(this.srsName) {
25108                     node.setAttribute("srsName", this.srsName);
25109                 }
25110                 return node;
25111             }
25112         }, OpenLayers.Format.GML.Base.prototype.writers["gml"]),
25113         "feature": OpenLayers.Format.GML.Base.prototype.writers["feature"],
25114         "wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"]
25115     },
25116     
25117     CLASS_NAME: "OpenLayers.Format.GML.v2" 
25118
25119 });
25120 /* ======================================================================
25121     OpenLayers/Filter/Function.js
25122    ====================================================================== */
25123
25124 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
25125  * full list of contributors). Published under the 2-clause BSD license.
25126  * See license.txt in the OpenLayers distribution or repository for the
25127  * full text of the license. */
25128
25129 /**
25130  * @requires OpenLayers/Filter.js
25131  */
25132
25133 /**
25134  * Class: OpenLayers.Filter.Function
25135  * This class represents a filter function.
25136  * We are using this class for creation of complex 
25137  * filters that can contain filter functions as values.
25138  * Nesting function as other functions parameter is supported.
25139  * 
25140  * Inherits from:
25141  * - <OpenLayers.Filter>
25142  */
25143 OpenLayers.Filter.Function = OpenLayers.Class(OpenLayers.Filter, {
25144
25145     /**
25146      * APIProperty: name
25147      * {String} Name of the function.
25148      */
25149     name: null,
25150     
25151     /**
25152      * APIProperty: params
25153      * {Array(<OpenLayers.Filter.Function> || String || Number)} Function parameters
25154      * For now support only other Functions, String or Number
25155      */
25156     params: null,  
25157     
25158     /** 
25159      * Constructor: OpenLayers.Filter.Function
25160      * Creates a filter function.
25161      *
25162      * Parameters:
25163      * options - {Object} An optional object with properties to set on the
25164      *     function.
25165      * 
25166      * Returns:
25167      * {<OpenLayers.Filter.Function>}
25168      */
25169
25170     CLASS_NAME: "OpenLayers.Filter.Function"
25171 });
25172
25173 /* ======================================================================
25174     OpenLayers/BaseTypes/Date.js
25175    ====================================================================== */
25176
25177 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
25178  * full list of contributors). Published under the 2-clause BSD license.
25179  * See license.txt in the OpenLayers distribution or repository for the
25180  * full text of the license. */
25181
25182 /**
25183  * @requires OpenLayers/SingleFile.js
25184  */
25185
25186 /**
25187  * Namespace: OpenLayers.Date
25188  * Contains implementations of Date.parse and date.toISOString that match the
25189  *     ECMAScript 5 specification for parsing RFC 3339 dates.
25190  *     http://tools.ietf.org/html/rfc3339
25191  */
25192 OpenLayers.Date = {
25193
25194     /** 
25195      * APIProperty: dateRegEx
25196      * The regex to be used for validating dates. You can provide your own
25197      * regex for instance for adding support for years before BC. Default
25198      * value is: /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/
25199      */
25200     dateRegEx: /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))|Z)?$/,
25201
25202     /**
25203      * APIMethod: toISOString
25204      * Generates a string representing a date.  The format of the string follows
25205      *     the profile of ISO 8601 for date and time on the Internet (see
25206      *     http://tools.ietf.org/html/rfc3339).  If the toISOString method is
25207      *     available on the Date prototype, that is used.  The toISOString
25208      *     method for Date instances is defined in ECMA-262.
25209      *
25210      * Parameters:
25211      * date - {Date} A date object.
25212      *
25213      * Returns:
25214      * {String} A string representing the date (e.g.
25215      *     "2010-08-07T16:58:23.123Z").  If the date does not have a valid time
25216      *     (i.e. isNaN(date.getTime())) this method returns the string "Invalid
25217      *     Date".  The ECMA standard says the toISOString method should throw
25218      *     RangeError in this case, but Firefox returns a string instead.  For
25219      *     best results, use isNaN(date.getTime()) to determine date validity
25220      *     before generating date strings.
25221      */
25222     toISOString: (function() {
25223         if ("toISOString" in Date.prototype) {
25224             return function(date) {
25225                 return date.toISOString();
25226             };
25227         } else {
25228             return function(date) {
25229                 var str;
25230                 if (isNaN(date.getTime())) {
25231                     // ECMA-262 says throw RangeError, Firefox returns
25232                     // "Invalid Date"
25233                     str = "Invalid Date";
25234                 } else {
25235                     str =
25236                         date.getUTCFullYear() + "-" +
25237                         OpenLayers.Number.zeroPad(date.getUTCMonth() + 1, 2) + "-" +
25238                         OpenLayers.Number.zeroPad(date.getUTCDate(), 2) + "T" +
25239                         OpenLayers.Number.zeroPad(date.getUTCHours(), 2) + ":" +
25240                         OpenLayers.Number.zeroPad(date.getUTCMinutes(), 2) + ":" +
25241                         OpenLayers.Number.zeroPad(date.getUTCSeconds(), 2) + "." +
25242                         OpenLayers.Number.zeroPad(date.getUTCMilliseconds(), 3) + "Z";
25243                 }
25244                 return str;
25245             };
25246         }
25247
25248     })(),
25249
25250     /**
25251      * APIMethod: parse
25252      * Generate a date object from a string.  The format for the string follows
25253      *     the profile of ISO 8601 for date and time on the Internet (see
25254      *     http://tools.ietf.org/html/rfc3339).  We don't call the native
25255      *     Date.parse because of inconsistency between implmentations.  In
25256      *     Chrome, calling Date.parse with a string that doesn't contain any
25257      *     indication of the timezone (e.g. "2011"), the date is interpreted
25258      *     in local time.  On Firefox, the assumption is UTC.
25259      *
25260      * Parameters:
25261      * str - {String} A string representing the date (e.g.
25262      *     "2010", "2010-08", "2010-08-07", "2010-08-07T16:58:23.123Z",
25263      *     "2010-08-07T11:58:23.123-06").
25264      *
25265      * Returns:
25266      * {Date} A date object.  If the string could not be parsed, an invalid
25267      *     date is returned (i.e. isNaN(date.getTime())).
25268      */
25269     parse: function(str) {
25270         var date;
25271         var match = str.match(this.dateRegEx);
25272         if (match && (match[1] || match[7])) { // must have at least year or time
25273             var year = parseInt(match[1], 10) || 0;
25274             var month = (parseInt(match[2], 10) - 1) || 0;
25275             var day = parseInt(match[3], 10) || 1;
25276             date = new Date(Date.UTC(year, month, day));
25277             // optional time
25278             var type = match[7];
25279             if (type) {
25280                 var hours = parseInt(match[4], 10);
25281                 var minutes = parseInt(match[5], 10);
25282                 var secFrac = parseFloat(match[6]);
25283                 var seconds = secFrac | 0;
25284                 var milliseconds = Math.round(1000 * (secFrac - seconds));
25285                 date.setUTCHours(hours, minutes, seconds, milliseconds);
25286                 // check offset
25287                 if (type !== "Z") {
25288                     var hoursOffset = parseInt(type, 10);
25289                     var minutesOffset = parseInt(match[8], 10) || 0;
25290                     var offset = -1000 * (60 * (hoursOffset * 60) + minutesOffset * 60);
25291                     date = new Date(date.getTime() + offset);
25292                 }
25293             }
25294         } else {
25295             date = new Date("invalid");
25296         }
25297         return date;
25298     }
25299 };
25300 /* ======================================================================
25301     OpenLayers/Format/Filter/v1.js
25302    ====================================================================== */
25303
25304 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
25305  * full list of contributors). Published under the 2-clause BSD license.
25306  * See license.txt in the OpenLayers distribution or repository for the
25307  * full text of the license. */
25308 /**
25309  * @requires OpenLayers/Format/Filter.js
25310  * @requires OpenLayers/Format/XML.js
25311  * @requires OpenLayers/Filter/Function.js
25312  * @requires OpenLayers/BaseTypes/Date.js
25313  */
25314
25315 /**
25316  * Class: OpenLayers.Format.Filter.v1
25317  * Superclass for Filter version 1 parsers.
25318  *
25319  * Inherits from:
25320  *  - <OpenLayers.Format.XML>
25321  */
25322 OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
25323     
25324     /**
25325      * Property: namespaces
25326      * {Object} Mapping of namespace aliases to namespace URIs.
25327      */
25328     namespaces: {
25329         ogc: "http://www.opengis.net/ogc",
25330         gml: "http://www.opengis.net/gml",
25331         xlink: "http://www.w3.org/1999/xlink",
25332         xsi: "http://www.w3.org/2001/XMLSchema-instance"
25333     },
25334
25335     /**
25336      * Property: defaultPrefix
25337      */
25338     defaultPrefix: "ogc",
25339
25340     /**
25341      * Property: schemaLocation
25342      * {String} Schema location for a particular minor version.
25343      */
25344     schemaLocation: null,
25345     
25346     /**
25347      * Constructor: OpenLayers.Format.Filter.v1
25348      * Instances of this class are not created directly.  Use the
25349      *     <OpenLayers.Format.Filter> constructor instead.
25350      *
25351      * Parameters:
25352      * options - {Object} An optional object whose properties will be set on
25353      *     this instance.
25354      */
25355     initialize: function(options) {
25356         OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
25357     },
25358     
25359     /**
25360      * Method: read
25361      *
25362      * Parameters:
25363      * data - {DOMElement} A Filter document element.
25364      *
25365      * Returns:
25366      * {<OpenLayers.Filter>} A filter object.
25367      */
25368     read: function(data) {
25369         var obj = {};
25370         this.readers.ogc["Filter"].apply(this, [data, obj]);
25371         return obj.filter;
25372     },
25373     
25374     /**
25375      * Property: readers
25376      * Contains public functions, grouped by namespace prefix, that will
25377      *     be applied when a namespaced node is found matching the function
25378      *     name.  The function will be applied in the scope of this parser
25379      *     with two arguments: the node being read and a context object passed
25380      *     from the parent.
25381      */
25382     readers: {
25383         "ogc": {
25384             "_expression": function(node) {
25385                 // only the simplest of ogc:expression handled
25386                 // "some text and an <PropertyName>attribute</PropertyName>"}
25387                 var obj, value = "";
25388                 for(var child=node.firstChild; child; child=child.nextSibling) {
25389                     switch(child.nodeType) {
25390                         case 1:
25391                             obj = this.readNode(child);
25392                             if (obj.property) {
25393                                 value += "${" + obj.property + "}";
25394                             } else if (obj.value !== undefined) {
25395                                 value += obj.value;
25396                             }
25397                             break;
25398                         case 3: // text node
25399                         case 4: // cdata section
25400                             value += child.nodeValue;
25401                     }
25402                 }
25403                 return value;
25404             },
25405             "Filter": function(node, parent) {
25406                 // Filters correspond to subclasses of OpenLayers.Filter.
25407                 // Since they contain information we don't persist, we
25408                 // create a temporary object and then pass on the filter
25409                 // (ogc:Filter) to the parent obj.
25410                 var obj = {
25411                     fids: [],
25412                     filters: []
25413                 };
25414                 this.readChildNodes(node, obj);
25415                 if(obj.fids.length > 0) {
25416                     parent.filter = new OpenLayers.Filter.FeatureId({
25417                         fids: obj.fids
25418                     });
25419                 } else if(obj.filters.length > 0) {
25420                     parent.filter = obj.filters[0];
25421                 }
25422             },
25423             "FeatureId": function(node, obj) {
25424                 var fid = node.getAttribute("fid");
25425                 if(fid) {
25426                     obj.fids.push(fid);
25427                 }
25428             },
25429             "And": function(node, obj) {
25430                 var filter = new OpenLayers.Filter.Logical({
25431                     type: OpenLayers.Filter.Logical.AND
25432                 });
25433                 this.readChildNodes(node, filter);
25434                 obj.filters.push(filter);
25435             },
25436             "Or": function(node, obj) {
25437                 var filter = new OpenLayers.Filter.Logical({
25438                     type: OpenLayers.Filter.Logical.OR
25439                 });
25440                 this.readChildNodes(node, filter);
25441                 obj.filters.push(filter);
25442             },
25443             "Not": function(node, obj) {
25444                 var filter = new OpenLayers.Filter.Logical({
25445                     type: OpenLayers.Filter.Logical.NOT
25446                 });
25447                 this.readChildNodes(node, filter);
25448                 obj.filters.push(filter);
25449             },
25450             "PropertyIsLessThan": function(node, obj) {
25451                 var filter = new OpenLayers.Filter.Comparison({
25452                     type: OpenLayers.Filter.Comparison.LESS_THAN
25453                 });
25454                 this.readChildNodes(node, filter);
25455                 obj.filters.push(filter);
25456             },
25457             "PropertyIsGreaterThan": function(node, obj) {
25458                 var filter = new OpenLayers.Filter.Comparison({
25459                     type: OpenLayers.Filter.Comparison.GREATER_THAN
25460                 });
25461                 this.readChildNodes(node, filter);
25462                 obj.filters.push(filter);
25463             },
25464             "PropertyIsLessThanOrEqualTo": function(node, obj) {
25465                 var filter = new OpenLayers.Filter.Comparison({
25466                     type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO
25467                 });
25468                 this.readChildNodes(node, filter);
25469                 obj.filters.push(filter);
25470             },
25471             "PropertyIsGreaterThanOrEqualTo": function(node, obj) {
25472                 var filter = new OpenLayers.Filter.Comparison({
25473                     type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO
25474                 });
25475                 this.readChildNodes(node, filter);
25476                 obj.filters.push(filter);
25477             },
25478             "PropertyIsBetween": function(node, obj) {
25479                 var filter = new OpenLayers.Filter.Comparison({
25480                     type: OpenLayers.Filter.Comparison.BETWEEN
25481                 });
25482                 this.readChildNodes(node, filter);
25483                 obj.filters.push(filter);
25484             },
25485             "Literal": function(node, obj) {
25486                 obj.value = OpenLayers.String.numericIf(
25487                     this.getChildValue(node), true);
25488             },
25489             "PropertyName": function(node, filter) {
25490                 filter.property = this.getChildValue(node);
25491             },
25492             "LowerBoundary": function(node, filter) {
25493                 filter.lowerBoundary = OpenLayers.String.numericIf(
25494                     this.readers.ogc._expression.call(this, node), true);
25495             },
25496             "UpperBoundary": function(node, filter) {
25497                 filter.upperBoundary = OpenLayers.String.numericIf(
25498                     this.readers.ogc._expression.call(this, node), true);
25499             },
25500             "Intersects": function(node, obj) {
25501                 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.INTERSECTS);
25502             },
25503             "Within": function(node, obj) {
25504                 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.WITHIN);
25505             },
25506             "Contains": function(node, obj) {
25507                 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.CONTAINS);
25508             },
25509             "DWithin": function(node, obj) {
25510                 this.readSpatial(node, obj, OpenLayers.Filter.Spatial.DWITHIN);
25511             },
25512             "Distance": function(node, obj) {
25513                 obj.distance = parseInt(this.getChildValue(node));
25514                 obj.distanceUnits = node.getAttribute("units");
25515             },
25516             "Function": function(node, obj) {
25517                 //TODO write decoder for it
25518                 return;
25519             },
25520             "PropertyIsNull": function(node, obj) {
25521                 var filter = new OpenLayers.Filter.Comparison({
25522                     type: OpenLayers.Filter.Comparison.IS_NULL
25523                 });
25524                 this.readChildNodes(node, filter);
25525                 obj.filters.push(filter);
25526             }
25527         }
25528     },
25529     
25530     /**
25531      * Method: readSpatial
25532      *
25533      * Read a {<OpenLayers.Filter.Spatial>} filter.
25534      * 
25535      * Parameters:
25536      * node - {DOMElement} A DOM element that contains an ogc:expression.
25537      * obj - {Object} The target object.
25538      * type - {String} One of the OpenLayers.Filter.Spatial.* constants.
25539      *
25540      * Returns:
25541      * {<OpenLayers.Filter.Spatial>} The created filter.
25542      */
25543     readSpatial: function(node, obj, type) {
25544         var filter = new OpenLayers.Filter.Spatial({
25545             type: type
25546         });
25547         this.readChildNodes(node, filter);
25548         filter.value = filter.components[0];
25549         delete filter.components;
25550         obj.filters.push(filter);
25551     },
25552
25553     /**
25554      * APIMethod: encodeLiteral
25555      * Generates the string representation of a value for use in <Literal> 
25556      *     elements.  The default encoder writes Date values as ISO 8601 
25557      *     strings.
25558      *
25559      * Parameters:
25560      * value - {Object} Literal value to encode
25561      *
25562      * Returns:
25563      * {String} String representation of the provided value.
25564      */
25565     encodeLiteral: function(value) {
25566         if (value instanceof Date) {
25567             value = OpenLayers.Date.toISOString(value);
25568         }
25569         return value;
25570     },
25571
25572     /**
25573      * Method: writeOgcExpression
25574      * Limited support for writing OGC expressions. Currently it supports
25575      * (<OpenLayers.Filter.Function> || String || Number)
25576      *
25577      * Parameters:
25578      * value - (<OpenLayers.Filter.Function> || String || Number)
25579      * node - {DOMElement} A parent DOM element 
25580      *
25581      * Returns:
25582      * {DOMElement} Updated node element.
25583      */
25584     writeOgcExpression: function(value, node) {
25585         if (value instanceof OpenLayers.Filter.Function){
25586             this.writeNode("Function", value, node);
25587         } else {
25588             this.writeNode("Literal", value, node);
25589         }
25590         return node;
25591     },    
25592     
25593     /**
25594      * Method: write
25595      *
25596      * Parameters:
25597      * filter - {<OpenLayers.Filter>} A filter object.
25598      *
25599      * Returns:
25600      * {DOMElement} An ogc:Filter element.
25601      */
25602     write: function(filter) {
25603         return this.writers.ogc["Filter"].apply(this, [filter]);
25604     },
25605     
25606     /**
25607      * Property: writers
25608      * As a compliment to the readers property, this structure contains public
25609      *     writing functions grouped by namespace alias and named like the
25610      *     node names they produce.
25611      */
25612     writers: {
25613         "ogc": {
25614             "Filter": function(filter) {
25615                 var node = this.createElementNSPlus("ogc:Filter");
25616                 this.writeNode(this.getFilterType(filter), filter, node);
25617                 return node;
25618             },
25619             "_featureIds": function(filter) {
25620                 var node = this.createDocumentFragment();
25621                 for (var i=0, ii=filter.fids.length; i<ii; ++i) {
25622                     this.writeNode("ogc:FeatureId", filter.fids[i], node);
25623                 }
25624                 return node;
25625             },
25626             "FeatureId": function(fid) {
25627                 return this.createElementNSPlus("ogc:FeatureId", {
25628                     attributes: {fid: fid}
25629                 });
25630             },
25631             "And": function(filter) {
25632                 var node = this.createElementNSPlus("ogc:And");
25633                 var childFilter;
25634                 for (var i=0, ii=filter.filters.length; i<ii; ++i) {
25635                     childFilter = filter.filters[i];
25636                     this.writeNode(
25637                         this.getFilterType(childFilter), childFilter, node
25638                     );
25639                 }
25640                 return node;
25641             },
25642             "Or": function(filter) {
25643                 var node = this.createElementNSPlus("ogc:Or");
25644                 var childFilter;
25645                 for (var i=0, ii=filter.filters.length; i<ii; ++i) {
25646                     childFilter = filter.filters[i];
25647                     this.writeNode(
25648                         this.getFilterType(childFilter), childFilter, node
25649                     );
25650                 }
25651                 return node;
25652             },
25653             "Not": function(filter) {
25654                 var node = this.createElementNSPlus("ogc:Not");
25655                 var childFilter = filter.filters[0];
25656                 this.writeNode(
25657                     this.getFilterType(childFilter), childFilter, node
25658                 );
25659                 return node;
25660             },
25661             "PropertyIsLessThan": function(filter) {
25662                 var node = this.createElementNSPlus("ogc:PropertyIsLessThan");
25663                 // no ogc:expression handling for PropertyName for now
25664                 this.writeNode("PropertyName", filter, node);
25665                 // handle Literals or Functions for now
25666                 this.writeOgcExpression(filter.value, node);
25667                 return node;
25668             },
25669             "PropertyIsGreaterThan": function(filter) {
25670                 var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan");
25671                 // no ogc:expression handling for PropertyName for now
25672                 this.writeNode("PropertyName", filter, node);
25673                 // handle Literals or Functions for now
25674                 this.writeOgcExpression(filter.value, node);
25675                 return node;
25676             },
25677             "PropertyIsLessThanOrEqualTo": function(filter) {
25678                 var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo");
25679                 // no ogc:expression handling for PropertyName for now
25680                 this.writeNode("PropertyName", filter, node);
25681                 // handle Literals or Functions for now
25682                 this.writeOgcExpression(filter.value, node);
25683                 return node;
25684             },
25685             "PropertyIsGreaterThanOrEqualTo": function(filter) {
25686                 var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo");
25687                 // no ogc:expression handling for PropertyName for now
25688                 this.writeNode("PropertyName", filter, node);
25689                 // handle Literals or Functions for now
25690                 this.writeOgcExpression(filter.value, node);
25691                 return node;
25692             },
25693             "PropertyIsBetween": function(filter) {
25694                 var node = this.createElementNSPlus("ogc:PropertyIsBetween");
25695                 // no ogc:expression handling for PropertyName for now
25696                 this.writeNode("PropertyName", filter, node);
25697                 this.writeNode("LowerBoundary", filter, node);
25698                 this.writeNode("UpperBoundary", filter, node);
25699                 return node;
25700             },
25701             "PropertyName": function(filter) {
25702                 // no ogc:expression handling for now
25703                 return this.createElementNSPlus("ogc:PropertyName", {
25704                     value: filter.property
25705                 });
25706             },
25707             "Literal": function(value) {
25708                 var encode = this.encodeLiteral ||
25709                     OpenLayers.Format.Filter.v1.prototype.encodeLiteral;
25710                 return this.createElementNSPlus("ogc:Literal", {
25711                     value: encode(value)
25712                 });
25713             },
25714             "LowerBoundary": function(filter) {
25715                 // handle Literals or Functions for now
25716                 var node = this.createElementNSPlus("ogc:LowerBoundary");
25717                 this.writeOgcExpression(filter.lowerBoundary, node);
25718                 return node;
25719             },
25720             "UpperBoundary": function(filter) {
25721                 // handle Literals or Functions for now
25722                 var node = this.createElementNSPlus("ogc:UpperBoundary");
25723                 this.writeNode("Literal", filter.upperBoundary, node);
25724                 return node;
25725             },
25726             "INTERSECTS": function(filter) {
25727                 return this.writeSpatial(filter, "Intersects");
25728             },
25729             "WITHIN": function(filter) {
25730                 return this.writeSpatial(filter, "Within");
25731             },
25732             "CONTAINS": function(filter) {
25733                 return this.writeSpatial(filter, "Contains");
25734             },
25735             "DWITHIN": function(filter) {
25736                 var node = this.writeSpatial(filter, "DWithin");
25737                 this.writeNode("Distance", filter, node);
25738                 return node;
25739             },
25740             "Distance": function(filter) {
25741                 return this.createElementNSPlus("ogc:Distance", {
25742                     attributes: {
25743                         units: filter.distanceUnits
25744                     },
25745                     value: filter.distance
25746                 });
25747             },
25748             "Function": function(filter) {
25749                 var node = this.createElementNSPlus("ogc:Function", {
25750                     attributes: {
25751                         name: filter.name
25752                     }
25753                 });
25754                 var params = filter.params;
25755                 for(var i=0, len=params.length; i<len; i++){
25756                     this.writeOgcExpression(params[i], node);
25757                 }
25758                 return node;
25759             },
25760             "PropertyIsNull": function(filter) {
25761                 var node = this.createElementNSPlus("ogc:PropertyIsNull");
25762                 this.writeNode("PropertyName", filter, node);
25763                 return node;
25764             }
25765         }
25766     },
25767
25768     /**
25769      * Method: getFilterType
25770      */
25771     getFilterType: function(filter) {
25772         var filterType = this.filterMap[filter.type];
25773         if(!filterType) {
25774             throw "Filter writing not supported for rule type: " + filter.type;
25775         }
25776         return filterType;
25777     },
25778     
25779     /**
25780      * Property: filterMap
25781      * {Object} Contains a member for each filter type.  Values are node names
25782      *     for corresponding OGC Filter child elements.
25783      */
25784     filterMap: {
25785         "&&": "And",
25786         "||": "Or",
25787         "!": "Not",
25788         "==": "PropertyIsEqualTo",
25789         "!=": "PropertyIsNotEqualTo",
25790         "<": "PropertyIsLessThan",
25791         ">": "PropertyIsGreaterThan",
25792         "<=": "PropertyIsLessThanOrEqualTo",
25793         ">=": "PropertyIsGreaterThanOrEqualTo",
25794         "..": "PropertyIsBetween",
25795         "~": "PropertyIsLike",
25796         "NULL": "PropertyIsNull",
25797         "BBOX": "BBOX",
25798         "DWITHIN": "DWITHIN",
25799         "WITHIN": "WITHIN",
25800         "CONTAINS": "CONTAINS",
25801         "INTERSECTS": "INTERSECTS",
25802         "FID": "_featureIds"
25803     },
25804
25805     CLASS_NAME: "OpenLayers.Format.Filter.v1" 
25806
25807 });
25808 /* ======================================================================
25809     OpenLayers/Format/Filter/v1_0_0.js
25810    ====================================================================== */
25811
25812 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
25813  * full list of contributors). Published under the 2-clause BSD license.
25814  * See license.txt in the OpenLayers distribution or repository for the
25815  * full text of the license. */
25816
25817 /**
25818  * @requires OpenLayers/Format/GML/v2.js
25819  * @requires OpenLayers/Format/Filter/v1.js
25820  */
25821
25822 /**
25823  * Class: OpenLayers.Format.Filter.v1_0_0
25824  * Write ogc:Filter version 1.0.0.
25825  * 
25826  * Inherits from:
25827  *  - <OpenLayers.Format.GML.v2>
25828  *  - <OpenLayers.Format.Filter.v1>
25829  */
25830 OpenLayers.Format.Filter.v1_0_0 = OpenLayers.Class(
25831     OpenLayers.Format.GML.v2, OpenLayers.Format.Filter.v1, {
25832     
25833     /**
25834      * Constant: VERSION
25835      * {String} 1.0.0
25836      */
25837     VERSION: "1.0.0",
25838     
25839     /**
25840      * Property: schemaLocation
25841      * {String} http://www.opengis.net/ogc/filter/1.0.0/filter.xsd
25842      */
25843     schemaLocation: "http://www.opengis.net/ogc/filter/1.0.0/filter.xsd",
25844
25845     /**
25846      * Constructor: OpenLayers.Format.Filter.v1_0_0
25847      * Instances of this class are not created directly.  Use the
25848      *     <OpenLayers.Format.Filter> constructor instead.
25849      *
25850      * Parameters:
25851      * options - {Object} An optional object whose properties will be set on
25852      *     this instance.
25853      */
25854     initialize: function(options) {
25855         OpenLayers.Format.GML.v2.prototype.initialize.apply(
25856             this, [options]
25857         );
25858     },
25859
25860     /**
25861      * Property: readers
25862      * Contains public functions, grouped by namespace prefix, that will
25863      *     be applied when a namespaced node is found matching the function
25864      *     name.  The function will be applied in the scope of this parser
25865      *     with two arguments: the node being read and a context object passed
25866      *     from the parent.
25867      */
25868     readers: {
25869         "ogc": OpenLayers.Util.applyDefaults({
25870             "PropertyIsEqualTo": function(node, obj) {
25871                 var filter = new OpenLayers.Filter.Comparison({
25872                     type: OpenLayers.Filter.Comparison.EQUAL_TO
25873                 });
25874                 this.readChildNodes(node, filter);
25875                 obj.filters.push(filter);
25876             },
25877             "PropertyIsNotEqualTo": function(node, obj) {
25878                 var filter = new OpenLayers.Filter.Comparison({
25879                     type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO
25880                 });
25881                 this.readChildNodes(node, filter);
25882                 obj.filters.push(filter);
25883             },
25884             "PropertyIsLike": function(node, obj) {
25885                 var filter = new OpenLayers.Filter.Comparison({
25886                     type: OpenLayers.Filter.Comparison.LIKE
25887                 });
25888                 this.readChildNodes(node, filter);
25889                 var wildCard = node.getAttribute("wildCard");
25890                 var singleChar = node.getAttribute("singleChar");
25891                 var esc = node.getAttribute("escape");
25892                 filter.value2regex(wildCard, singleChar, esc);
25893                 obj.filters.push(filter);
25894             }
25895         }, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]),
25896         "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"],
25897         "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"]        
25898     },
25899
25900     /**
25901      * Property: writers
25902      * As a compliment to the readers property, this structure contains public
25903      *     writing functions grouped by namespace alias and named like the
25904      *     node names they produce.
25905      */
25906     writers: {
25907         "ogc": OpenLayers.Util.applyDefaults({
25908             "PropertyIsEqualTo": function(filter) {
25909                 var node = this.createElementNSPlus("ogc:PropertyIsEqualTo");
25910                 // no ogc:expression handling for PropertyName for now
25911                 this.writeNode("PropertyName", filter, node);
25912                 // handle Literals or Functions for now
25913                 this.writeOgcExpression(filter.value, node);
25914                 return node;
25915             },
25916             "PropertyIsNotEqualTo": function(filter) {
25917                 var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo");
25918                 // no ogc:expression handling for PropertyName for now
25919                 this.writeNode("PropertyName", filter, node);
25920                 // handle Literals or Functions for now
25921                 this.writeOgcExpression(filter.value, node);
25922                 return node;
25923             },
25924             "PropertyIsLike": function(filter) {
25925                 var node = this.createElementNSPlus("ogc:PropertyIsLike", {
25926                     attributes: {
25927                         wildCard: "*", singleChar: ".", escape: "!"
25928                     }
25929                 });
25930                 // no ogc:expression handling for now
25931                 this.writeNode("PropertyName", filter, node);
25932                 // convert regex string to ogc string
25933                 this.writeNode("Literal", filter.regex2value(), node);
25934                 return node;
25935             },
25936             "BBOX": function(filter) {
25937                 var node = this.createElementNSPlus("ogc:BBOX");
25938                 // PropertyName is mandatory in 1.0.0, but e.g. GeoServer also
25939                 // accepts filters without it. When this is used with
25940                 // OpenLayers.Protocol.WFS, OpenLayers.Format.WFST will set a
25941                 // missing filter.property to the geometryName that is
25942                 // configured with the protocol, which defaults to "the_geom".
25943                 // So the only way to omit this mandatory property is to not
25944                 // set the property on the filter and to set the geometryName
25945                 // on the WFS protocol to null. The latter also happens when
25946                 // the protocol is configured without a geometryName and a
25947                 // featureNS.
25948                 filter.property && this.writeNode("PropertyName", filter, node);
25949                 var box = this.writeNode("gml:Box", filter.value, node);
25950                 if(filter.projection) {
25951                     box.setAttribute("srsName", filter.projection);
25952                 }
25953                 return node;
25954             }
25955         }, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]),
25956         "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"],
25957         "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"]
25958     },
25959
25960     /**
25961      * Method: writeSpatial
25962      *
25963      * Read a {<OpenLayers.Filter.Spatial>} filter and converts it into XML.
25964      *
25965      * Parameters:
25966      * filter - {<OpenLayers.Filter.Spatial>} The filter.
25967      * name - {String} Name of the generated XML element.
25968      *
25969      * Returns:
25970      * {DOMElement} The created XML element.
25971      */
25972     writeSpatial: function(filter, name) {
25973         var node = this.createElementNSPlus("ogc:"+name);
25974         this.writeNode("PropertyName", filter, node);
25975         if(filter.value instanceof OpenLayers.Filter.Function) {
25976             this.writeNode("Function", filter.value, node);
25977         } else {
25978         var child;
25979         if(filter.value instanceof OpenLayers.Geometry) {
25980             child = this.writeNode("feature:_geometry", filter.value).firstChild;
25981         } else {
25982             child = this.writeNode("gml:Box", filter.value);
25983         }
25984         if(filter.projection) {
25985             child.setAttribute("srsName", filter.projection);
25986         }
25987         node.appendChild(child);
25988         }
25989         return node;
25990     },
25991
25992
25993     CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0" 
25994
25995 });
25996 /* ======================================================================
25997     OpenLayers/Format/WFST/v1_0_0.js
25998    ====================================================================== */
25999
26000 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
26001  * full list of contributors). Published under the 2-clause BSD license.
26002  * See license.txt in the OpenLayers distribution or repository for the
26003  * full text of the license. */
26004
26005 /**
26006  * @requires OpenLayers/Format/WFST/v1.js
26007  * @requires OpenLayers/Format/Filter/v1_0_0.js
26008  */
26009
26010 /**
26011  * Class: OpenLayers.Format.WFST.v1_0_0
26012  * A format for creating WFS v1.0.0 transactions.  Create a new instance with the
26013  *     <OpenLayers.Format.WFST.v1_0_0> constructor.
26014  *
26015  * Inherits from:
26016  *  - <OpenLayers.Format.Filter.v1_0_0>
26017  *  - <OpenLayers.Format.WFST.v1>
26018  */
26019 OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class(
26020     OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, {
26021     
26022     /**
26023      * Property: version
26024      * {String} WFS version number.
26025      */
26026     version: "1.0.0",
26027
26028     /**
26029      * APIProperty: srsNameInQuery
26030      * {Boolean} If true the reference system is passed in Query requests
26031      *     via the "srsName" attribute to the "wfs:Query" element, this
26032      *     property defaults to false as it isn't WFS 1.0.0 compliant.
26033      */
26034     srsNameInQuery: false,
26035     
26036     /**
26037      * Property: schemaLocations
26038      * {Object} Properties are namespace aliases, values are schema locations.
26039      */
26040     schemaLocations: {
26041         "wfs": "http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd"
26042     },
26043
26044     /**
26045      * Constructor: OpenLayers.Format.WFST.v1_0_0
26046      * A class for parsing and generating WFS v1.0.0 transactions.
26047      *
26048      * Parameters:
26049      * options - {Object} Optional object whose properties will be set on the
26050      *     instance.
26051      *
26052      * Valid options properties:
26053      * featureType - {String} Local (without prefix) feature typeName (required).
26054      * featureNS - {String} Feature namespace (optional).
26055      * featurePrefix - {String} Feature namespace alias (optional - only used
26056      *     if featureNS is provided).  Default is 'feature'.
26057      * geometryName - {String} Name of geometry attribute.  Default is 'the_geom'.
26058      */
26059     initialize: function(options) {
26060         OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]);
26061         OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);
26062     },
26063     
26064     /**
26065      * Method: readNode
26066      * Shorthand for applying one of the named readers given the node
26067      *     namespace and local name.  Readers take two args (node, obj) and
26068      *     generally extend or modify the second.
26069      *
26070      * Parameters:
26071      * node - {DOMElement} The node to be read (required).
26072      * obj - {Object} The object to be modified (optional).
26073      * first - {Boolean} Should be set to true for the first node read. This
26074      *     is usually the readNode call in the read method. Without this being
26075      *     set, auto-configured properties will stick on subsequent reads.
26076      *
26077      * Returns:
26078      * {Object} The input object, modified (or a new one if none was provided).
26079      */
26080     readNode: function(node, obj, first) {
26081         // Not the superclass, only the mixin classes inherit from
26082         // Format.GML.v2. We need this because we don't want to get readNode
26083         // from the superclass's superclass, which is OpenLayers.Format.XML.
26084         return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, arguments);
26085     },
26086     
26087     /**
26088      * Property: readers
26089      * Contains public functions, grouped by namespace prefix, that will
26090      *     be applied when a namespaced node is found matching the function
26091      *     name.  The function will be applied in the scope of this parser
26092      *     with two arguments: the node being read and a context object passed
26093      *     from the parent.
26094      */
26095     readers: {
26096         "wfs": OpenLayers.Util.applyDefaults({
26097             "WFS_TransactionResponse": function(node, obj) {
26098                 obj.insertIds = [];
26099                 obj.success = false;
26100                 this.readChildNodes(node, obj);
26101             },
26102             "InsertResult": function(node, container) {
26103                 var obj = {fids: []};
26104                 this.readChildNodes(node, obj);
26105                 container.insertIds = container.insertIds.concat(obj.fids);
26106             },
26107             "TransactionResult": function(node, obj) {
26108                 this.readChildNodes(node, obj);
26109             },
26110             "Status": function(node, obj) {
26111                 this.readChildNodes(node, obj);
26112             },
26113             "SUCCESS": function(node, obj) {
26114                 obj.success = true;
26115             }
26116         }, OpenLayers.Format.WFST.v1.prototype.readers["wfs"]),
26117         "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"],
26118         "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"],
26119         "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.readers["ogc"]
26120     },
26121
26122     /**
26123      * Property: writers
26124      * As a compliment to the readers property, this structure contains public
26125      *     writing functions grouped by namespace alias and named like the
26126      *     node names they produce.
26127      */
26128     writers: {
26129         "wfs": OpenLayers.Util.applyDefaults({
26130             "Query": function(options) {
26131                 options = OpenLayers.Util.extend({
26132                     featureNS: this.featureNS,
26133                     featurePrefix: this.featurePrefix,
26134                     featureType: this.featureType,
26135                     srsName: this.srsName,
26136                     srsNameInQuery: this.srsNameInQuery
26137                 }, options);
26138                 var prefix = options.featurePrefix;
26139                 var node = this.createElementNSPlus("wfs:Query", {
26140                     attributes: {
26141                         typeName: (prefix ? prefix + ":" : "") +
26142                             options.featureType
26143                     }
26144                 });
26145                 if(options.srsNameInQuery && options.srsName) {
26146                     node.setAttribute("srsName", options.srsName);
26147                 }
26148                 if(options.featureNS) {
26149                     node.setAttribute("xmlns:" + prefix, options.featureNS);
26150                 }
26151                 if(options.propertyNames) {
26152                     for(var i=0,len = options.propertyNames.length; i<len; i++) {
26153                         this.writeNode(
26154                             "ogc:PropertyName", 
26155                             {property: options.propertyNames[i]},
26156                             node
26157                         );
26158                     }
26159                 }
26160                 if(options.filter) {
26161                     this.setFilterProperty(options.filter);
26162                     this.writeNode("ogc:Filter", options.filter, node);
26163                 }
26164                 return node;
26165             }
26166         }, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]),
26167         "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"],
26168         "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"],
26169         "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.writers["ogc"]
26170     },
26171    
26172     CLASS_NAME: "OpenLayers.Format.WFST.v1_0_0" 
26173 });
26174 /* ======================================================================
26175     OpenLayers/Renderer/Elements.js
26176    ====================================================================== */
26177
26178 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
26179  * full list of contributors). Published under the 2-clause BSD license.
26180  * See license.txt in the OpenLayers distribution or repository for the
26181  * full text of the license. */
26182
26183 /**
26184  * @requires OpenLayers/Renderer.js
26185  */
26186
26187 /**
26188  * Class: OpenLayers.ElementsIndexer
26189  * This class takes care of figuring out which order elements should be
26190  *     placed in the DOM based on given indexing methods. 
26191  */
26192 OpenLayers.ElementsIndexer = OpenLayers.Class({
26193    
26194     /**
26195      * Property: maxZIndex
26196      * {Integer} This is the largest-most z-index value for a node
26197      *     contained within the indexer.
26198      */
26199     maxZIndex: null,
26200     
26201     /**
26202      * Property: order
26203      * {Array<String>} This is an array of node id's stored in the
26204      *     order that they should show up on screen. Id's higher up in the
26205      *     array (higher array index) represent nodes with higher z-indeces.
26206      */
26207     order: null, 
26208     
26209     /**
26210      * Property: indices
26211      * {Object} This is a hash that maps node ids to their z-index value
26212      *     stored in the indexer. This is done to make finding a nodes z-index 
26213      *     value O(1).
26214      */
26215     indices: null,
26216     
26217     /**
26218      * Property: compare
26219      * {Function} This is the function used to determine placement of
26220      *     of a new node within the indexer. If null, this defaults to to
26221      *     the Z_ORDER_DRAWING_ORDER comparison method.
26222      */
26223     compare: null,
26224     
26225     /**
26226      * APIMethod: initialize
26227      * Create a new indexer with 
26228      * 
26229      * Parameters:
26230      * yOrdering - {Boolean} Whether to use y-ordering.
26231      */
26232     initialize: function(yOrdering) {
26233
26234         this.compare = yOrdering ? 
26235             OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER :
26236             OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;
26237
26238         this.clear();
26239     },
26240     
26241     /**
26242      * APIMethod: insert
26243      * Insert a new node into the indexer. In order to find the correct 
26244      *     positioning for the node to be inserted, this method uses a binary 
26245      *     search. This makes inserting O(log(n)). 
26246      * 
26247      * Parameters:
26248      * newNode - {DOMElement} The new node to be inserted.
26249      * 
26250      * Returns
26251      * {DOMElement} the node before which we should insert our newNode, or
26252      *     null if newNode can just be appended.
26253      */
26254     insert: function(newNode) {
26255         // If the node is known to the indexer, remove it so we can
26256         // recalculate where it should go.
26257         if (this.exists(newNode)) {
26258             this.remove(newNode);
26259         }
26260         
26261         var nodeId = newNode.id;
26262         
26263         this.determineZIndex(newNode);       
26264
26265         var leftIndex = -1;
26266         var rightIndex = this.order.length;
26267         var middle;
26268
26269         while (rightIndex - leftIndex > 1) {
26270             middle = parseInt((leftIndex + rightIndex) / 2);
26271             
26272             var placement = this.compare(this, newNode,
26273                 OpenLayers.Util.getElement(this.order[middle]));
26274             
26275             if (placement > 0) {
26276                 leftIndex = middle;
26277             } else {
26278                 rightIndex = middle;
26279             } 
26280         }
26281         
26282         this.order.splice(rightIndex, 0, nodeId);
26283         this.indices[nodeId] = this.getZIndex(newNode);
26284         
26285         // If the new node should be before another in the index
26286         // order, return the node before which we have to insert the new one;
26287         // else, return null to indicate that the new node can be appended.
26288         return this.getNextElement(rightIndex);
26289     },
26290     
26291     /**
26292      * APIMethod: remove
26293      * 
26294      * Parameters:
26295      * node - {DOMElement} The node to be removed.
26296      */
26297     remove: function(node) {
26298         var nodeId = node.id;
26299         var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);
26300         if (arrayIndex >= 0) {
26301             // Remove it from the order array, as well as deleting the node
26302             // from the indeces hash.
26303             this.order.splice(arrayIndex, 1);
26304             delete this.indices[nodeId];
26305             
26306             // Reset the maxium z-index based on the last item in the 
26307             // order array.
26308             if (this.order.length > 0) {
26309                 var lastId = this.order[this.order.length - 1];
26310                 this.maxZIndex = this.indices[lastId];
26311             } else {
26312                 this.maxZIndex = 0;
26313             }
26314         }
26315     },
26316     
26317     /**
26318      * APIMethod: clear
26319      */
26320     clear: function() {
26321         this.order = [];
26322         this.indices = {};
26323         this.maxZIndex = 0;
26324     },
26325     
26326     /**
26327      * APIMethod: exists
26328      *
26329      * Parameters:
26330      * node - {DOMElement} The node to test for existence.
26331      *
26332      * Returns:
26333      * {Boolean} Whether or not the node exists in the indexer?
26334      */
26335     exists: function(node) {
26336         return (this.indices[node.id] != null);
26337     },
26338
26339     /**
26340      * APIMethod: getZIndex
26341      * Get the z-index value for the current node from the node data itself.
26342      * 
26343      * Parameters:
26344      * node - {DOMElement} The node whose z-index to get.
26345      * 
26346      * Returns:
26347      * {Integer} The z-index value for the specified node (from the node 
26348      *     data itself).
26349      */
26350     getZIndex: function(node) {
26351         return node._style.graphicZIndex;  
26352     },
26353     
26354     /**
26355      * Method: determineZIndex
26356      * Determine the z-index for the current node if there isn't one, 
26357      *     and set the maximum value if we've found a new maximum.
26358      * 
26359      * Parameters:
26360      * node - {DOMElement} 
26361      */
26362     determineZIndex: function(node) {
26363         var zIndex = node._style.graphicZIndex;
26364         
26365         // Everything must have a zIndex. If none is specified,
26366         // this means the user *must* (hint: assumption) want this
26367         // node to succomb to drawing order. To enforce drawing order
26368         // over all indexing methods, we'll create a new z-index that's
26369         // greater than any currently in the indexer.
26370         if (zIndex == null) {
26371             zIndex = this.maxZIndex;
26372             node._style.graphicZIndex = zIndex; 
26373         } else if (zIndex > this.maxZIndex) {
26374             this.maxZIndex = zIndex;
26375         }
26376     },
26377
26378     /**
26379      * APIMethod: getNextElement
26380      * Get the next element in the order stack.
26381      * 
26382      * Parameters:
26383      * index - {Integer} The index of the current node in this.order.
26384      * 
26385      * Returns:
26386      * {DOMElement} the node following the index passed in, or
26387      *     null.
26388      */
26389     getNextElement: function(index) {
26390         var nextIndex = index + 1;
26391         if (nextIndex < this.order.length) {
26392             var nextElement = OpenLayers.Util.getElement(this.order[nextIndex]);
26393             if (nextElement == undefined) {
26394                 nextElement = this.getNextElement(nextIndex);
26395             }
26396             return nextElement;
26397         } else {
26398             return null;
26399         } 
26400     },
26401     
26402     CLASS_NAME: "OpenLayers.ElementsIndexer"
26403 });
26404
26405 /**
26406  * Namespace: OpenLayers.ElementsIndexer.IndexingMethods
26407  * These are the compare methods for figuring out where a new node should be 
26408  *     placed within the indexer. These methods are very similar to general 
26409  *     sorting methods in that they return -1, 0, and 1 to specify the 
26410  *     direction in which new nodes fall in the ordering.
26411  */
26412 OpenLayers.ElementsIndexer.IndexingMethods = {
26413     
26414     /**
26415      * Method: Z_ORDER
26416      * This compare method is used by other comparison methods.
26417      *     It can be used individually for ordering, but is not recommended,
26418      *     because it doesn't subscribe to drawing order.
26419      * 
26420      * Parameters:
26421      * indexer - {<OpenLayers.ElementsIndexer>}
26422      * newNode - {DOMElement}
26423      * nextNode - {DOMElement}
26424      * 
26425      * Returns:
26426      * {Integer}
26427      */
26428     Z_ORDER: function(indexer, newNode, nextNode) {
26429         var newZIndex = indexer.getZIndex(newNode);
26430
26431         var returnVal = 0;
26432         if (nextNode) {
26433             var nextZIndex = indexer.getZIndex(nextNode);
26434             returnVal = newZIndex - nextZIndex; 
26435         }
26436         
26437         return returnVal;
26438     },
26439
26440     /**
26441      * APIMethod: Z_ORDER_DRAWING_ORDER
26442      * This method orders nodes by their z-index, but does so in a way
26443      *     that, if there are other nodes with the same z-index, the newest 
26444      *     drawn will be the front most within that z-index. This is the 
26445      *     default indexing method.
26446      * 
26447      * Parameters:
26448      * indexer - {<OpenLayers.ElementsIndexer>}
26449      * newNode - {DOMElement}
26450      * nextNode - {DOMElement}
26451      * 
26452      * Returns:
26453      * {Integer}
26454      */
26455     Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) {
26456         var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
26457             indexer, 
26458             newNode, 
26459             nextNode
26460         );
26461         
26462         // Make Z_ORDER subscribe to drawing order by pushing it above
26463         // all of the other nodes with the same z-index.
26464         if (nextNode && returnVal == 0) {
26465             returnVal = 1;
26466         }
26467         
26468         return returnVal;
26469     },
26470
26471     /**
26472      * APIMethod: Z_ORDER_Y_ORDER
26473      * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it
26474      *     best describes which ordering methods have precedence (though, the 
26475      *     name would be too long). This method orders nodes by their z-index, 
26476      *     but does so in a way that, if there are other nodes with the same 
26477      *     z-index, the nodes with the lower y position will be "closer" than 
26478      *     those with a higher y position. If two nodes have the exact same y 
26479      *     position, however, then this method will revert to using drawing  
26480      *     order to decide placement.
26481      * 
26482      * Parameters:
26483      * indexer - {<OpenLayers.ElementsIndexer>}
26484      * newNode - {DOMElement}
26485      * nextNode - {DOMElement}
26486      * 
26487      * Returns:
26488      * {Integer}
26489      */
26490     Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) {
26491         var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(
26492             indexer, 
26493             newNode, 
26494             nextNode
26495         );
26496         
26497         if (nextNode && returnVal === 0) {            
26498             var result = nextNode._boundsBottom - newNode._boundsBottom;
26499             returnVal = (result === 0) ? 1 : result;
26500         }
26501         
26502         return returnVal;       
26503     }
26504 };
26505
26506 /**
26507  * Class: OpenLayers.Renderer.Elements
26508  * This is another virtual class in that it should never be instantiated by 
26509  *  itself as a Renderer. It exists because there is *tons* of shared 
26510  *  functionality between different vector libraries which use nodes/elements
26511  *  as a base for rendering vectors. 
26512  * 
26513  * The highlevel bits of code that are implemented here are the adding and 
26514  *  removing of geometries, which is essentially the same for any 
26515  *  element-based renderer. The details of creating each node and drawing the
26516  *  paths are of course different, but the machinery is the same. 
26517  * 
26518  * Inherits:
26519  *  - <OpenLayers.Renderer>
26520  */
26521 OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
26522
26523     /**
26524      * Property: rendererRoot
26525      * {DOMElement}
26526      */
26527     rendererRoot: null,
26528     
26529     /**
26530      * Property: root
26531      * {DOMElement}
26532      */
26533     root: null,
26534     
26535     /**
26536      * Property: vectorRoot
26537      * {DOMElement}
26538      */
26539     vectorRoot: null,
26540
26541     /**
26542      * Property: textRoot
26543      * {DOMElement}
26544      */
26545     textRoot: null,
26546
26547     /**
26548      * Property: xmlns
26549      * {String}
26550      */    
26551     xmlns: null,
26552     
26553     /**
26554      * Property: xOffset
26555      * {Number} Offset to apply to the renderer viewport translation in x
26556      * direction. If the renderer extent's center is on the right of the
26557      * dateline (i.e. exceeds the world bounds), we shift the viewport to the
26558      * left by one world width. This avoids that features disappear from the
26559      * map viewport. Because our dateline handling logic in other places
26560      * ensures that extents crossing the dateline always have a center
26561      * exceeding the world bounds on the left, we need this offset to make sure
26562      * that the same is true for the renderer extent in pixel space as well.
26563      */
26564     xOffset: 0,
26565     
26566     /**
26567      * Property: rightOfDateLine
26568      * {Boolean} Keeps track of the location of the map extent relative to the
26569      * date line. The <setExtent> method compares this value (which is the one
26570      * from the previous <setExtent> call) with the current position of the map
26571      * extent relative to the date line and updates the xOffset when the extent
26572      * has moved from one side of the date line to the other.
26573      */
26574     
26575     /**
26576      * Property: Indexer
26577      * {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer 
26578      *     created upon initialization if the zIndexing or yOrdering options
26579      *     passed to this renderer's constructor are set to true.
26580      */
26581     indexer: null, 
26582     
26583     /**
26584      * Constant: BACKGROUND_ID_SUFFIX
26585      * {String}
26586      */
26587     BACKGROUND_ID_SUFFIX: "_background",
26588     
26589     /**
26590      * Constant: LABEL_ID_SUFFIX
26591      * {String}
26592      */
26593     LABEL_ID_SUFFIX: "_label",
26594     
26595     /**
26596      * Constant: LABEL_OUTLINE_SUFFIX
26597      * {String}
26598      */
26599     LABEL_OUTLINE_SUFFIX: "_outline",
26600
26601     /**
26602      * Constructor: OpenLayers.Renderer.Elements
26603      * 
26604      * Parameters:
26605      * containerID - {String}
26606      * options - {Object} options for this renderer. 
26607      *
26608      * Supported options are:
26609      *     yOrdering - {Boolean} Whether to use y-ordering
26610      *     zIndexing - {Boolean} Whether to use z-indexing. Will be ignored
26611      *         if yOrdering is set to true.
26612      */
26613     initialize: function(containerID, options) {
26614         OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
26615
26616         this.rendererRoot = this.createRenderRoot();
26617         this.root = this.createRoot("_root");
26618         this.vectorRoot = this.createRoot("_vroot");
26619         this.textRoot = this.createRoot("_troot");
26620         
26621         this.root.appendChild(this.vectorRoot);
26622         this.root.appendChild(this.textRoot);
26623         
26624         this.rendererRoot.appendChild(this.root);
26625         this.container.appendChild(this.rendererRoot);
26626         
26627         if(options && (options.zIndexing || options.yOrdering)) {
26628             this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering);
26629         }
26630     },
26631     
26632     /**
26633      * Method: destroy
26634      */
26635     destroy: function() {
26636
26637         this.clear(); 
26638
26639         this.rendererRoot = null;
26640         this.root = null;
26641         this.xmlns = null;
26642
26643         OpenLayers.Renderer.prototype.destroy.apply(this, arguments);
26644     },
26645     
26646     /**
26647      * Method: clear
26648      * Remove all the elements from the root
26649      */    
26650     clear: function() {
26651         var child;
26652         var root = this.vectorRoot;
26653         if (root) {
26654             while (child = root.firstChild) {
26655                 root.removeChild(child);
26656             }
26657         }
26658         root = this.textRoot;
26659         if (root) {
26660             while (child = root.firstChild) {
26661                 root.removeChild(child);
26662             }
26663         }
26664         if (this.indexer) {
26665             this.indexer.clear();
26666         }
26667     },
26668     
26669     /**
26670      * Method: setExtent
26671      * Set the visible part of the layer.
26672      *
26673      * Parameters:
26674      * extent - {<OpenLayers.Bounds>}
26675      * resolutionChanged - {Boolean}
26676      *
26677      * Returns:
26678      * {Boolean} true to notify the layer that the new extent does not exceed
26679      *     the coordinate range, and the features will not need to be redrawn.
26680      *     False otherwise.
26681      */
26682     setExtent: function(extent, resolutionChanged) {
26683         var coordSysUnchanged = OpenLayers.Renderer.prototype.setExtent.apply(this, arguments);
26684         var resolution = this.getResolution();
26685         if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) {
26686             var rightOfDateLine,
26687                 ratio = extent.getWidth() / this.map.getExtent().getWidth(),
26688                 extent = extent.scale(1 / ratio),
26689                 world = this.map.getMaxExtent();
26690             if (world.right > extent.left && world.right < extent.right) {
26691                 rightOfDateLine = true;
26692             } else if (world.left > extent.left && world.left < extent.right) {
26693                 rightOfDateLine = false;
26694             }
26695             if (rightOfDateLine !== this.rightOfDateLine || resolutionChanged) {
26696                 coordSysUnchanged = false;
26697                 this.xOffset = rightOfDateLine === true ?
26698                     world.getWidth() / resolution : 0;
26699             }
26700             this.rightOfDateLine = rightOfDateLine;
26701         }
26702         return coordSysUnchanged;
26703     },
26704
26705     /** 
26706      * Method: getNodeType
26707      * This function is in charge of asking the specific renderer which type
26708      *     of node to create for the given geometry and style. All geometries
26709      *     in an Elements-based renderer consist of one node and some
26710      *     attributes. We have the nodeFactory() function which creates a node
26711      *     for us, but it takes a 'type' as input, and that is precisely what
26712      *     this function tells us.  
26713      *  
26714      * Parameters:
26715      * geometry - {<OpenLayers.Geometry>}
26716      * style - {Object}
26717      * 
26718      * Returns:
26719      * {String} The corresponding node type for the specified geometry
26720      */
26721     getNodeType: function(geometry, style) { },
26722
26723     /** 
26724      * Method: drawGeometry 
26725      * Draw the geometry, creating new nodes, setting paths, setting style,
26726      *     setting featureId on the node.  This method should only be called
26727      *     by the renderer itself.
26728      *
26729      * Parameters:
26730      * geometry - {<OpenLayers.Geometry>}
26731      * style - {Object}
26732      * featureId - {String}
26733      * 
26734      * Returns:
26735      * {Boolean} true if the geometry has been drawn completely; null if
26736      *     incomplete; false otherwise
26737      */
26738     drawGeometry: function(geometry, style, featureId) {
26739         var className = geometry.CLASS_NAME;
26740         var rendered = true;
26741         if ((className == "OpenLayers.Geometry.Collection") ||
26742             (className == "OpenLayers.Geometry.MultiPoint") ||
26743             (className == "OpenLayers.Geometry.MultiLineString") ||
26744             (className == "OpenLayers.Geometry.MultiPolygon")) {
26745             for (var i = 0, len=geometry.components.length; i<len; i++) {
26746                 rendered = this.drawGeometry(
26747                     geometry.components[i], style, featureId) && rendered;
26748             }
26749             return rendered;
26750         }
26751
26752         rendered = false;
26753         var removeBackground = false;
26754         if (style.display != "none") {
26755             if (style.backgroundGraphic) {
26756                 this.redrawBackgroundNode(geometry.id, geometry, style,
26757                     featureId);
26758             } else {
26759                 removeBackground = true;
26760             }
26761             rendered = this.redrawNode(geometry.id, geometry, style,
26762                 featureId);
26763         }
26764         if (rendered == false) {
26765             var node = document.getElementById(geometry.id);
26766             if (node) {
26767                 if (node._style.backgroundGraphic) {
26768                     removeBackground = true;
26769                 }
26770                 node.parentNode.removeChild(node);
26771             }
26772         }
26773         if (removeBackground) {
26774             var node = document.getElementById(
26775                 geometry.id + this.BACKGROUND_ID_SUFFIX);
26776             if (node) {
26777                 node.parentNode.removeChild(node);
26778             }
26779         }
26780         return rendered;
26781     },
26782     
26783     /**
26784      * Method: redrawNode
26785      * 
26786      * Parameters:
26787      * id - {String}
26788      * geometry - {<OpenLayers.Geometry>}
26789      * style - {Object}
26790      * featureId - {String}
26791      * 
26792      * Returns:
26793      * {Boolean} true if the complete geometry could be drawn, null if parts of
26794      *     the geometry could not be drawn, false otherwise
26795      */
26796     redrawNode: function(id, geometry, style, featureId) {
26797         style = this.applyDefaultSymbolizer(style);
26798         // Get the node if it's already on the map.
26799         var node = this.nodeFactory(id, this.getNodeType(geometry, style));
26800         
26801         // Set the data for the node, then draw it.
26802         node._featureId = featureId;
26803         node._boundsBottom = geometry.getBounds().bottom;
26804         node._geometryClass = geometry.CLASS_NAME;
26805         node._style = style;
26806
26807         var drawResult = this.drawGeometryNode(node, geometry, style);
26808         if(drawResult === false) {
26809             return false;
26810         }
26811          
26812         node = drawResult.node;
26813         
26814         // Insert the node into the indexer so it can show us where to
26815         // place it. Note that this operation is O(log(n)). If there's a
26816         // performance problem (when dragging, for instance) this is
26817         // likely where it would be.
26818         if (this.indexer) {
26819             var insert = this.indexer.insert(node);
26820             if (insert) {
26821                 this.vectorRoot.insertBefore(node, insert);
26822             } else {
26823                 this.vectorRoot.appendChild(node);
26824             }
26825         } else {
26826             // if there's no indexer, simply append the node to root,
26827             // but only if the node is a new one
26828             if (node.parentNode !== this.vectorRoot){ 
26829                 this.vectorRoot.appendChild(node);
26830             }
26831         }
26832         
26833         this.postDraw(node);
26834         
26835         return drawResult.complete;
26836     },
26837     
26838     /**
26839      * Method: redrawBackgroundNode
26840      * Redraws the node using special 'background' style properties. Basically
26841      *     just calls redrawNode(), but instead of directly using the 
26842      *     'externalGraphic', 'graphicXOffset', 'graphicYOffset', and 
26843      *     'graphicZIndex' properties directly from the specified 'style' 
26844      *     parameter, we create a new style object and set those properties 
26845      *     from the corresponding 'background'-prefixed properties from 
26846      *     specified 'style' parameter.
26847      * 
26848      * Parameters:
26849      * id - {String}
26850      * geometry - {<OpenLayers.Geometry>}
26851      * style - {Object}
26852      * featureId - {String}
26853      * 
26854      * Returns:
26855      * {Boolean} true if the complete geometry could be drawn, null if parts of
26856      *     the geometry could not be drawn, false otherwise
26857      */
26858     redrawBackgroundNode: function(id, geometry, style, featureId) {
26859         var backgroundStyle = OpenLayers.Util.extend({}, style);
26860         
26861         // Set regular style attributes to apply to the background styles.
26862         backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;
26863         backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;
26864         backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;
26865         backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;
26866         backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth;
26867         backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight;
26868         
26869         // Erase background styles.
26870         backgroundStyle.backgroundGraphic = null;
26871         backgroundStyle.backgroundXOffset = null;
26872         backgroundStyle.backgroundYOffset = null;
26873         backgroundStyle.backgroundGraphicZIndex = null;
26874         
26875         return this.redrawNode(
26876             id + this.BACKGROUND_ID_SUFFIX, 
26877             geometry, 
26878             backgroundStyle, 
26879             null
26880         );
26881     },
26882
26883     /**
26884      * Method: drawGeometryNode
26885      * Given a node, draw a geometry on the specified layer.
26886      *     node and geometry are required arguments, style is optional.
26887      *     This method is only called by the render itself.
26888      *
26889      * Parameters:
26890      * node - {DOMElement}
26891      * geometry - {<OpenLayers.Geometry>}
26892      * style - {Object}
26893      * 
26894      * Returns:
26895      * {Object} a hash with properties "node" (the drawn node) and "complete"
26896      *     (null if parts of the geometry could not be drawn, false if nothing
26897      *     could be drawn)
26898      */
26899     drawGeometryNode: function(node, geometry, style) {
26900         style = style || node._style;
26901
26902         var options = {
26903             'isFilled': style.fill === undefined ?
26904                 true :
26905                 style.fill,
26906             'isStroked': style.stroke === undefined ?
26907                 !!style.strokeWidth :
26908                 style.stroke
26909         };
26910         var drawn;
26911         switch (geometry.CLASS_NAME) {
26912             case "OpenLayers.Geometry.Point":
26913                 if(style.graphic === false) {
26914                     options.isFilled = false;
26915                     options.isStroked = false;
26916                 }
26917                 drawn = this.drawPoint(node, geometry);
26918                 break;
26919             case "OpenLayers.Geometry.LineString":
26920                 options.isFilled = false;
26921                 drawn = this.drawLineString(node, geometry);
26922                 break;
26923             case "OpenLayers.Geometry.LinearRing":
26924                 drawn = this.drawLinearRing(node, geometry);
26925                 break;
26926             case "OpenLayers.Geometry.Polygon":
26927                 drawn = this.drawPolygon(node, geometry);
26928                 break;
26929             case "OpenLayers.Geometry.Rectangle":
26930                 drawn = this.drawRectangle(node, geometry);
26931                 break;
26932             default:
26933                 break;
26934         }
26935
26936         node._options = options; 
26937
26938         //set style
26939         //TBD simplify this
26940         if (drawn != false) {
26941             return {
26942                 node: this.setStyle(node, style, options, geometry),
26943                 complete: drawn
26944             };
26945         } else {
26946             return false;
26947         }
26948     },
26949     
26950     /**
26951      * Method: postDraw
26952      * Things that have do be done after the geometry node is appended
26953      *     to its parent node. To be overridden by subclasses.
26954      * 
26955      * Parameters:
26956      * node - {DOMElement}
26957      */
26958     postDraw: function(node) {},
26959     
26960     /**
26961      * Method: drawPoint
26962      * Virtual function for drawing Point Geometry. 
26963      *     Should be implemented by subclasses.
26964      *     This method is only called by the renderer itself.
26965      * 
26966      * Parameters: 
26967      * node - {DOMElement}
26968      * geometry - {<OpenLayers.Geometry>}
26969      * 
26970      * Returns:
26971      * {DOMElement} or false if the renderer could not draw the point
26972      */ 
26973     drawPoint: function(node, geometry) {},
26974
26975     /**
26976      * Method: drawLineString
26977      * Virtual function for drawing LineString Geometry. 
26978      *     Should be implemented by subclasses.
26979      *     This method is only called by the renderer itself.
26980      * 
26981      * Parameters: 
26982      * node - {DOMElement}
26983      * geometry - {<OpenLayers.Geometry>}
26984      * 
26985      * Returns:
26986      * {DOMElement} or null if the renderer could not draw all components of
26987      *     the linestring, or false if nothing could be drawn
26988      */ 
26989     drawLineString: function(node, geometry) {},
26990
26991     /**
26992      * Method: drawLinearRing
26993      * Virtual function for drawing LinearRing Geometry. 
26994      *     Should be implemented by subclasses.
26995      *     This method is only called by the renderer itself.
26996      * 
26997      * Parameters: 
26998      * node - {DOMElement}
26999      * geometry - {<OpenLayers.Geometry>}
27000      * 
27001      * Returns:
27002      * {DOMElement} or null if the renderer could not draw all components
27003      *     of the linear ring, or false if nothing could be drawn
27004      */ 
27005     drawLinearRing: function(node, geometry) {},
27006
27007     /**
27008      * Method: drawPolygon
27009      * Virtual function for drawing Polygon Geometry. 
27010      *    Should be implemented by subclasses.
27011      *    This method is only called by the renderer itself.
27012      * 
27013      * Parameters: 
27014      * node - {DOMElement}
27015      * geometry - {<OpenLayers.Geometry>}
27016      * 
27017      * Returns:
27018      * {DOMElement} or null if the renderer could not draw all components
27019      *     of the polygon, or false if nothing could be drawn
27020      */ 
27021     drawPolygon: function(node, geometry) {},
27022
27023     /**
27024      * Method: drawRectangle
27025      * Virtual function for drawing Rectangle Geometry. 
27026      *     Should be implemented by subclasses.
27027      *     This method is only called by the renderer itself.
27028      * 
27029      * Parameters: 
27030      * node - {DOMElement}
27031      * geometry - {<OpenLayers.Geometry>}
27032      * 
27033      * Returns:
27034      * {DOMElement} or false if the renderer could not draw the rectangle
27035      */ 
27036     drawRectangle: function(node, geometry) {},
27037
27038     /**
27039      * Method: drawCircle
27040      * Virtual function for drawing Circle Geometry. 
27041      *     Should be implemented by subclasses.
27042      *     This method is only called by the renderer itself.
27043      * 
27044      * Parameters: 
27045      * node - {DOMElement}
27046      * geometry - {<OpenLayers.Geometry>}
27047      * 
27048      * Returns:
27049      * {DOMElement} or false if the renderer could not draw the circle
27050      */ 
27051     drawCircle: function(node, geometry) {},
27052
27053     /**
27054      * Method: removeText
27055      * Removes a label
27056      * 
27057      * Parameters:
27058      * featureId - {String}
27059      */
27060     removeText: function(featureId) {
27061         var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);
27062         if (label) {
27063             this.textRoot.removeChild(label);
27064         }
27065         var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX);
27066         if (outline) {
27067             this.textRoot.removeChild(outline);
27068         }
27069     },
27070
27071     /**
27072      * Method: getFeatureIdFromEvent
27073      * 
27074      * Parameters:
27075      * evt - {Object} An <OpenLayers.Event> object
27076      *
27077      * Returns:
27078      * {String} A feature id or undefined.
27079      */
27080     getFeatureIdFromEvent: function(evt) {
27081         var target = evt.target;
27082         var useElement = target && target.correspondingUseElement;
27083         var node = useElement ? useElement : (target || evt.srcElement);
27084         return node._featureId;
27085     },
27086
27087     /** 
27088      * Method: eraseGeometry
27089      * Erase a geometry from the renderer. In the case of a multi-geometry, 
27090      *     we cycle through and recurse on ourselves. Otherwise, we look for a 
27091      *     node with the geometry.id, destroy its geometry, and remove it from
27092      *     the DOM.
27093      * 
27094      * Parameters:
27095      * geometry - {<OpenLayers.Geometry>}
27096      * featureId - {String}
27097      */
27098     eraseGeometry: function(geometry, featureId) {
27099         if ((geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") ||
27100             (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString") ||
27101             (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") ||
27102             (geometry.CLASS_NAME == "OpenLayers.Geometry.Collection")) {
27103             for (var i=0, len=geometry.components.length; i<len; i++) {
27104                 this.eraseGeometry(geometry.components[i], featureId);
27105             }
27106         } else {    
27107             var element = OpenLayers.Util.getElement(geometry.id);
27108             if (element && element.parentNode) {
27109                 if (element.geometry) {
27110                     element.geometry.destroy();
27111                     element.geometry = null;
27112                 }
27113                 element.parentNode.removeChild(element);
27114
27115                 if (this.indexer) {
27116                     this.indexer.remove(element);
27117                 }
27118                 
27119                 if (element._style.backgroundGraphic) {
27120                     var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;
27121                     var bElem = OpenLayers.Util.getElement(backgroundId);
27122                     if (bElem && bElem.parentNode) {
27123                         // No need to destroy the geometry since the element and the background
27124                         // node share the same geometry.
27125                         bElem.parentNode.removeChild(bElem);
27126                     }
27127                 }
27128             }
27129         }
27130     },
27131
27132     /** 
27133      * Method: nodeFactory
27134      * Create new node of the specified type, with the (optional) specified id.
27135      * 
27136      * If node already exists with same ID and a different type, we remove it
27137      *     and then call ourselves again to recreate it.
27138      * 
27139      * Parameters:
27140      * id - {String}
27141      * type - {String} type Kind of node to draw.
27142      * 
27143      * Returns:
27144      * {DOMElement} A new node of the given type and id.
27145      */
27146     nodeFactory: function(id, type) {
27147         var node = OpenLayers.Util.getElement(id);
27148         if (node) {
27149             if (!this.nodeTypeCompare(node, type)) {
27150                 node.parentNode.removeChild(node);
27151                 node = this.nodeFactory(id, type);
27152             }
27153         } else {
27154             node = this.createNode(type, id);
27155         }
27156         return node;
27157     },
27158     
27159     /** 
27160      * Method: nodeTypeCompare
27161      * 
27162      * Parameters:
27163      * node - {DOMElement}
27164      * type - {String} Kind of node
27165      * 
27166      * Returns:
27167      * {Boolean} Whether or not the specified node is of the specified type
27168      *     This function must be overridden by subclasses.
27169      */
27170     nodeTypeCompare: function(node, type) {},
27171     
27172     /** 
27173      * Method: createNode
27174      * 
27175      * Parameters:
27176      * type - {String} Kind of node to draw.
27177      * id - {String} Id for node.
27178      * 
27179      * Returns:
27180      * {DOMElement} A new node of the given type and id.
27181      *     This function must be overridden by subclasses.
27182      */
27183     createNode: function(type, id) {},
27184
27185     /**
27186      * Method: moveRoot
27187      * moves this renderer's root to a different renderer.
27188      * 
27189      * Parameters:
27190      * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
27191      */
27192     moveRoot: function(renderer) {
27193         var root = this.root;
27194         if(renderer.root.parentNode == this.rendererRoot) {
27195             root = renderer.root;
27196         }
27197         root.parentNode.removeChild(root);
27198         renderer.rendererRoot.appendChild(root);
27199     },
27200     
27201     /**
27202      * Method: getRenderLayerId
27203      * Gets the layer that this renderer's output appears on. If moveRoot was
27204      * used, this will be different from the id of the layer containing the
27205      * features rendered by this renderer.
27206      * 
27207      * Returns:
27208      * {String} the id of the output layer.
27209      */
27210     getRenderLayerId: function() {
27211         return this.root.parentNode.parentNode.id;
27212     },
27213     
27214     /**
27215      * Method: isComplexSymbol
27216      * Determines if a symbol cannot be rendered using drawCircle
27217      * 
27218      * Parameters:
27219      * graphicName - {String}
27220      * 
27221      * Returns
27222      * {Boolean} true if the symbol is complex, false if not
27223      */
27224     isComplexSymbol: function(graphicName) {
27225         return (graphicName != "circle") && !!graphicName;
27226     },
27227
27228     CLASS_NAME: "OpenLayers.Renderer.Elements"
27229 });
27230
27231 /* ======================================================================
27232     OpenLayers/Control/Zoom.js
27233    ====================================================================== */
27234
27235 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27236  * full list of contributors). Published under the 2-clause BSD license.
27237  * See license.txt in the OpenLayers distribution or repository for the
27238  * full text of the license. */
27239
27240 /**
27241  * @requires OpenLayers/Control.js
27242  * @requires OpenLayers/Events/buttonclick.js
27243  */
27244
27245 /**
27246  * Class: OpenLayers.Control.Zoom
27247  * The Zoom control is a pair of +/- links for zooming in and out.
27248  *
27249  * Inherits from:
27250  *  - <OpenLayers.Control>
27251  */
27252 OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, {
27253     
27254     /**
27255      * APIProperty: zoomInText
27256      * {String}
27257      * Text for zoom-in link.  Default is "+".
27258      */
27259     zoomInText: "+",
27260
27261     /**
27262      * APIProperty: zoomInId
27263      * {String}
27264      * Instead of having the control create a zoom in link, you can provide 
27265      *     the identifier for an anchor element already added to the document.
27266      *     By default, an element with id "olZoomInLink" will be searched for
27267      *     and used if it exists.
27268      */
27269     zoomInId: "olZoomInLink",
27270
27271     /**
27272      * APIProperty: zoomOutText
27273      * {String}
27274      * Text for zoom-out link.  Default is "\u2212".
27275      */
27276     zoomOutText: "\u2212",
27277
27278     /**
27279      * APIProperty: zoomOutId
27280      * {String}
27281      * Instead of having the control create a zoom out link, you can provide 
27282      *     the identifier for an anchor element already added to the document.
27283      *     By default, an element with id "olZoomOutLink" will be searched for
27284      *     and used if it exists.
27285      */
27286     zoomOutId: "olZoomOutLink",
27287
27288     /**
27289      * Method: draw
27290      *
27291      * Returns:
27292      * {DOMElement} A reference to the DOMElement containing the zoom links.
27293      */
27294     draw: function() {
27295         var div = OpenLayers.Control.prototype.draw.apply(this),
27296             links = this.getOrCreateLinks(div),
27297             zoomIn = links.zoomIn,
27298             zoomOut = links.zoomOut,
27299             eventsInstance = this.map.events;
27300         
27301         if (zoomOut.parentNode !== div) {
27302             eventsInstance = this.events;
27303             eventsInstance.attachToElement(zoomOut.parentNode);
27304         }
27305         eventsInstance.register("buttonclick", this, this.onZoomClick);
27306         
27307         this.zoomInLink = zoomIn;
27308         this.zoomOutLink = zoomOut;
27309         return div;
27310     },
27311     
27312     /**
27313      * Method: getOrCreateLinks
27314      * 
27315      * Parameters:
27316      * el - {DOMElement}
27317      *
27318      * Return: 
27319      * {Object} Object with zoomIn and zoomOut properties referencing links.
27320      */
27321     getOrCreateLinks: function(el) {
27322         var zoomIn = document.getElementById(this.zoomInId),
27323             zoomOut = document.getElementById(this.zoomOutId);
27324         if (!zoomIn) {
27325             zoomIn = document.createElement("a");
27326             zoomIn.href = "#zoomIn";
27327             zoomIn.appendChild(document.createTextNode(this.zoomInText));
27328             zoomIn.className = "olControlZoomIn";
27329             el.appendChild(zoomIn);
27330         }
27331         OpenLayers.Element.addClass(zoomIn, "olButton");
27332         if (!zoomOut) {
27333             zoomOut = document.createElement("a");
27334             zoomOut.href = "#zoomOut";
27335             zoomOut.appendChild(document.createTextNode(this.zoomOutText));
27336             zoomOut.className = "olControlZoomOut";
27337             el.appendChild(zoomOut);
27338         }
27339         OpenLayers.Element.addClass(zoomOut, "olButton");
27340         return {
27341             zoomIn: zoomIn, zoomOut: zoomOut
27342         };
27343     },
27344     
27345     /**
27346      * Method: onZoomClick
27347      * Called when zoomin/out link is clicked.
27348      */
27349     onZoomClick: function(evt) {
27350         var button = evt.buttonElement;
27351         if (button === this.zoomInLink) {
27352             this.map.zoomIn();
27353         } else if (button === this.zoomOutLink) {
27354             this.map.zoomOut();
27355         }
27356     },
27357
27358     /** 
27359      * Method: destroy
27360      * Clean up.
27361      */
27362     destroy: function() {
27363         if (this.map) {
27364             this.map.events.unregister("buttonclick", this, this.onZoomClick);
27365         }
27366         delete this.zoomInLink;
27367         delete this.zoomOutLink;
27368         OpenLayers.Control.prototype.destroy.apply(this);
27369     },
27370
27371     CLASS_NAME: "OpenLayers.Control.Zoom"
27372 });
27373 /* ======================================================================
27374     OpenLayers/Protocol.js
27375    ====================================================================== */
27376
27377 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27378  * full list of contributors). Published under the 2-clause BSD license.
27379  * See license.txt in the OpenLayers distribution or repository for the
27380  * full text of the license. */
27381
27382 /**
27383  * @requires OpenLayers/BaseTypes/Class.js
27384  */
27385
27386 /**
27387  * Class: OpenLayers.Protocol
27388  * Abstract vector layer protocol class.  Not to be instantiated directly.  Use
27389  *     one of the protocol subclasses instead.
27390  */
27391 OpenLayers.Protocol = OpenLayers.Class({
27392     
27393     /**
27394      * Property: format
27395      * {<OpenLayers.Format>} The format used by this protocol.
27396      */
27397     format: null,
27398     
27399     /**
27400      * Property: options
27401      * {Object} Any options sent to the constructor.
27402      */
27403     options: null,
27404
27405     /**
27406      * Property: autoDestroy
27407      * {Boolean} The creator of the protocol can set autoDestroy to false
27408      *      to fully control when the protocol is destroyed. Defaults to
27409      *      true.
27410      */
27411     autoDestroy: true,
27412    
27413     /**
27414      * Property: defaultFilter
27415      * {<OpenLayers.Filter>} Optional default filter to read requests
27416      */
27417     defaultFilter: null,
27418     
27419     /**
27420      * Constructor: OpenLayers.Protocol
27421      * Abstract class for vector protocols.  Create instances of a subclass.
27422      *
27423      * Parameters:
27424      * options - {Object} Optional object whose properties will be set on the
27425      *     instance.
27426      */
27427     initialize: function(options) {
27428         options = options || {};
27429         OpenLayers.Util.extend(this, options);
27430         this.options = options;
27431     },
27432
27433     /**
27434      * Method: mergeWithDefaultFilter
27435      * Merge filter passed to the read method with the default one
27436      *
27437      * Parameters:
27438      * filter - {<OpenLayers.Filter>}
27439      */
27440     mergeWithDefaultFilter: function(filter) {
27441         var merged;
27442         if (filter && this.defaultFilter) {
27443             merged = new OpenLayers.Filter.Logical({
27444                 type: OpenLayers.Filter.Logical.AND,
27445                 filters: [this.defaultFilter, filter]
27446             });
27447         } else {
27448             merged = filter || this.defaultFilter || undefined;
27449         }
27450         return merged;
27451     },
27452
27453     /**
27454      * APIMethod: destroy
27455      * Clean up the protocol.
27456      */
27457     destroy: function() {
27458         this.options = null;
27459         this.format = null;
27460     },
27461     
27462     /**
27463      * APIMethod: read
27464      * Construct a request for reading new features.
27465      *
27466      * Parameters:
27467      * options - {Object} Optional object for configuring the request.
27468      *
27469      * Returns:
27470      * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
27471      * object, the same object will be passed to the callback function passed
27472      * if one exists in the options object.
27473      */
27474     read: function(options) {
27475         options = options || {};
27476         options.filter = this.mergeWithDefaultFilter(options.filter);
27477     },
27478     
27479     
27480     /**
27481      * APIMethod: create
27482      * Construct a request for writing newly created features.
27483      *
27484      * Parameters:
27485      * features - {Array({<OpenLayers.Feature.Vector>})} or
27486      *            {<OpenLayers.Feature.Vector>}
27487      * options - {Object} Optional object for configuring the request.
27488      *
27489      * Returns:
27490      * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
27491      * object, the same object will be passed to the callback function passed
27492      * if one exists in the options object.
27493      */
27494     create: function() {
27495     },
27496     
27497     /**
27498      * APIMethod: update
27499      * Construct a request updating modified features.
27500      *
27501      * Parameters:
27502      * features - {Array({<OpenLayers.Feature.Vector>})} or
27503      *            {<OpenLayers.Feature.Vector>}
27504      * options - {Object} Optional object for configuring the request.
27505      *
27506      * Returns:
27507      * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
27508      * object, the same object will be passed to the callback function passed
27509      * if one exists in the options object.
27510      */
27511     update: function() {
27512     },
27513     
27514     /**
27515      * APIMethod: delete
27516      * Construct a request deleting a removed feature.
27517      *
27518      * Parameters:
27519      * feature - {<OpenLayers.Feature.Vector>}
27520      * options - {Object} Optional object for configuring the request.
27521      *
27522      * Returns:
27523      * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
27524      * object, the same object will be passed to the callback function passed
27525      * if one exists in the options object.
27526      */
27527     "delete": function() {
27528     },
27529
27530     /**
27531      * APIMethod: commit
27532      * Go over the features and for each take action
27533      * based on the feature state. Possible actions are create,
27534      * update and delete.
27535      *
27536      * Parameters:
27537      * features - {Array({<OpenLayers.Feature.Vector>})}
27538      * options - {Object} Object whose possible keys are "create", "update",
27539      *      "delete", "callback" and "scope", the values referenced by the
27540      *      first three are objects as passed to the "create", "update", and
27541      *      "delete" methods, the value referenced by the "callback" key is
27542      *      a function which is called when the commit operation is complete
27543      *      using the scope referenced by the "scope" key.
27544      *
27545      * Returns:
27546      * {Array({<OpenLayers.Protocol.Response>})} An array of
27547      * <OpenLayers.Protocol.Response> objects.
27548      */
27549     commit: function() {
27550     },
27551
27552     /**
27553      * Method: abort
27554      * Abort an ongoing request.
27555      *
27556      * Parameters:
27557      * response - {<OpenLayers.Protocol.Response>}
27558      */
27559     abort: function(response) {
27560     },
27561    
27562     /**
27563      * Method: createCallback
27564      * Returns a function that applies the given public method with resp and
27565      *     options arguments.
27566      *
27567      * Parameters:
27568      * method - {Function} The method to be applied by the callback.
27569      * response - {<OpenLayers.Protocol.Response>} The protocol response object.
27570      * options - {Object} Options sent to the protocol method
27571      */
27572     createCallback: function(method, response, options) {
27573         return OpenLayers.Function.bind(function() {
27574             method.apply(this, [response, options]);
27575         }, this);
27576     },
27577    
27578     CLASS_NAME: "OpenLayers.Protocol" 
27579 });
27580
27581 /**
27582  * Class: OpenLayers.Protocol.Response
27583  * Protocols return Response objects to their users.
27584  */
27585 OpenLayers.Protocol.Response = OpenLayers.Class({
27586     /**
27587      * Property: code
27588      * {Number} - OpenLayers.Protocol.Response.SUCCESS or
27589      *            OpenLayers.Protocol.Response.FAILURE
27590      */
27591     code: null,
27592
27593     /**
27594      * Property: requestType
27595      * {String} The type of request this response corresponds to. Either
27596      *      "create", "read", "update" or "delete".
27597      */
27598     requestType: null,
27599
27600     /**
27601      * Property: last
27602      * {Boolean} - true if this is the last response expected in a commit,
27603      * false otherwise, defaults to true.
27604      */
27605     last: true,
27606
27607     /**
27608      * Property: features
27609      * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
27610      * The features returned in the response by the server. Depending on the 
27611      * protocol's read payload, either features or data will be populated.
27612      */
27613     features: null,
27614
27615     /**
27616      * Property: data
27617      * {Object}
27618      * The data returned in the response by the server. Depending on the 
27619      * protocol's read payload, either features or data will be populated.
27620      */
27621     data: null,
27622
27623     /**
27624      * Property: reqFeatures
27625      * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>}
27626      * The features provided by the user and placed in the request by the
27627      *      protocol.
27628      */
27629     reqFeatures: null,
27630
27631     /**
27632      * Property: priv
27633      */
27634     priv: null,
27635
27636     /**
27637      * Property: error
27638      * {Object} The error object in case a service exception was encountered.
27639      */
27640     error: null,
27641
27642     /**
27643      * Constructor: OpenLayers.Protocol.Response
27644      *
27645      * Parameters:
27646      * options - {Object} Optional object whose properties will be set on the
27647      *     instance.
27648      */
27649     initialize: function(options) {
27650         OpenLayers.Util.extend(this, options);
27651     },
27652
27653     /**
27654      * Method: success
27655      *
27656      * Returns:
27657      * {Boolean} - true on success, false otherwise
27658      */
27659     success: function() {
27660         return this.code > 0;
27661     },
27662
27663     CLASS_NAME: "OpenLayers.Protocol.Response"
27664 });
27665
27666 OpenLayers.Protocol.Response.SUCCESS = 1;
27667 OpenLayers.Protocol.Response.FAILURE = 0;
27668 /* ======================================================================
27669     OpenLayers/Protocol/WFS.js
27670    ====================================================================== */
27671
27672 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27673  * full list of contributors). Published under the 2-clause BSD license.
27674  * See license.txt in the OpenLayers distribution or repository for the
27675  * full text of the license. */
27676
27677 /**
27678  * @requires OpenLayers/Protocol.js
27679  */
27680
27681 /**
27682  * Class: OpenLayers.Protocol.WFS
27683  * Used to create a versioned WFS protocol.  Default version is 1.0.0.
27684  *
27685  * Returns:
27686  * {<OpenLayers.Protocol>} A WFS protocol of the given version.
27687  *
27688  * Example:
27689  * (code)
27690  *     var protocol = new OpenLayers.Protocol.WFS({
27691  *         version: "1.1.0",
27692  *         url:  "http://demo.opengeo.org/geoserver/wfs",
27693  *         featureType: "tasmania_roads",
27694  *         featureNS: "http://www.openplans.org/topp",
27695  *         geometryName: "the_geom"
27696  *     });
27697  * (end)
27698  *
27699  * See the protocols for specific WFS versions for more detail.
27700  */
27701 OpenLayers.Protocol.WFS = function(options) {
27702     options = OpenLayers.Util.applyDefaults(
27703         options, OpenLayers.Protocol.WFS.DEFAULTS
27704     );
27705     var cls = OpenLayers.Protocol.WFS["v"+options.version.replace(/\./g, "_")];
27706     if(!cls) {
27707         throw "Unsupported WFS version: " + options.version;
27708     }
27709     return new cls(options);
27710 };
27711
27712 /**
27713  * Function: fromWMSLayer
27714  * Convenience function to create a WFS protocol from a WMS layer.  This makes
27715  *     the assumption that a WFS requests can be issued at the same URL as
27716  *     WMS requests and that a WFS featureType exists with the same name as the
27717  *     WMS layer.
27718  *     
27719  * This function is designed to auto-configure <url>, <featureType>,
27720  *     <featurePrefix> and <srsName> for WFS <version> 1.1.0. Note that
27721  *     srsName matching with the WMS layer will not work with WFS 1.0.0.
27722  * 
27723  * Parameters:
27724  * layer - {<OpenLayers.Layer.WMS>} WMS layer that has a matching WFS
27725  *     FeatureType at the same server url with the same typename.
27726  * options - {Object} Default properties to be set on the protocol.
27727  *
27728  * Returns:
27729  * {<OpenLayers.Protocol.WFS>}
27730  */
27731 OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) {
27732     var typeName, featurePrefix;
27733     var param = layer.params["LAYERS"];
27734     var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(":");
27735     if(parts.length > 1) {
27736         featurePrefix = parts[0];
27737     }
27738     typeName = parts.pop();
27739     var protocolOptions = {
27740         url: layer.url,
27741         featureType: typeName,
27742         featurePrefix: featurePrefix,
27743         srsName: layer.projection && layer.projection.getCode() ||
27744                  layer.map && layer.map.getProjectionObject().getCode(),
27745         version: "1.1.0"
27746     };
27747     return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(
27748         options, protocolOptions
27749     ));
27750 };
27751
27752 /**
27753  * Constant: OpenLayers.Protocol.WFS.DEFAULTS
27754  */
27755 OpenLayers.Protocol.WFS.DEFAULTS = {
27756     "version": "1.0.0"
27757 };
27758 /* ======================================================================
27759     OpenLayers/Layer/Markers.js
27760    ====================================================================== */
27761
27762 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27763  * full list of contributors). Published under the 2-clause BSD license.
27764  * See license.txt in the OpenLayers distribution or repository for the
27765  * full text of the license. */
27766
27767
27768 /**
27769  * @requires OpenLayers/Layer.js
27770  */
27771
27772 /**
27773  * Class: OpenLayers.Layer.Markers
27774  * 
27775  * Inherits from:
27776  *  - <OpenLayers.Layer> 
27777  */
27778 OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, {
27779     
27780     /** 
27781      * APIProperty: isBaseLayer 
27782      * {Boolean} Markers layer is never a base layer.  
27783      */
27784     isBaseLayer: false,
27785     
27786     /** 
27787      * APIProperty: markers 
27788      * {Array(<OpenLayers.Marker>)} internal marker list 
27789      */
27790     markers: null,
27791
27792
27793     /** 
27794      * Property: drawn 
27795      * {Boolean} internal state of drawing. This is a workaround for the fact
27796      * that the map does not call moveTo with a zoomChanged when the map is
27797      * first starting up. This lets us catch the case where we have *never*
27798      * drawn the layer, and draw it even if the zoom hasn't changed.
27799      */
27800     drawn: false,
27801     
27802     /**
27803      * Constructor: OpenLayers.Layer.Markers 
27804      * Create a Markers layer.
27805      *
27806      * Parameters:
27807      * name - {String} 
27808      * options - {Object} Hashtable of extra options to tag onto the layer
27809      */
27810     initialize: function(name, options) {
27811         OpenLayers.Layer.prototype.initialize.apply(this, arguments);
27812         this.markers = [];
27813     },
27814     
27815     /**
27816      * APIMethod: destroy 
27817      */
27818     destroy: function() {
27819         this.clearMarkers();
27820         this.markers = null;
27821         OpenLayers.Layer.prototype.destroy.apply(this, arguments);
27822     },
27823
27824     /**
27825      * APIMethod: setOpacity
27826      * Sets the opacity for all the markers.
27827      * 
27828      * Parameters:
27829      * opacity - {Float}
27830      */
27831     setOpacity: function(opacity) {
27832         if (opacity != this.opacity) {
27833             this.opacity = opacity;
27834             for (var i=0, len=this.markers.length; i<len; i++) {
27835                 this.markers[i].setOpacity(this.opacity);
27836             }
27837         }
27838     },
27839
27840     /** 
27841      * Method: moveTo
27842      *
27843      * Parameters:
27844      * bounds - {<OpenLayers.Bounds>} 
27845      * zoomChanged - {Boolean} 
27846      * dragging - {Boolean} 
27847      */
27848     moveTo:function(bounds, zoomChanged, dragging) {
27849         OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
27850
27851         if (zoomChanged || !this.drawn) {
27852             for(var i=0, len=this.markers.length; i<len; i++) {
27853                 this.drawMarker(this.markers[i]);
27854             }
27855             this.drawn = true;
27856         }
27857     },
27858
27859     /**
27860      * APIMethod: addMarker
27861      *
27862      * Parameters:
27863      * marker - {<OpenLayers.Marker>} 
27864      */
27865     addMarker: function(marker) {
27866         this.markers.push(marker);
27867
27868         if (this.opacity < 1) {
27869             marker.setOpacity(this.opacity);
27870         }
27871
27872         if (this.map && this.map.getExtent()) {
27873             marker.map = this.map;
27874             this.drawMarker(marker);
27875         }
27876     },
27877
27878     /**
27879      * APIMethod: removeMarker
27880      *
27881      * Parameters:
27882      * marker - {<OpenLayers.Marker>} 
27883      */
27884     removeMarker: function(marker) {
27885         if (this.markers && this.markers.length) {
27886             OpenLayers.Util.removeItem(this.markers, marker);
27887             marker.erase();
27888         }
27889     },
27890
27891     /**
27892      * Method: clearMarkers
27893      * This method removes all markers from a layer. The markers are not
27894      * destroyed by this function, but are removed from the list of markers.
27895      */
27896     clearMarkers: function() {
27897         if (this.markers != null) {
27898             while(this.markers.length > 0) {
27899                 this.removeMarker(this.markers[0]);
27900             }
27901         }
27902     },
27903
27904     /** 
27905      * Method: drawMarker
27906      * Calculate the pixel location for the marker, create it, and 
27907      *    add it to the layer's div
27908      *
27909      * Parameters:
27910      * marker - {<OpenLayers.Marker>} 
27911      */
27912     drawMarker: function(marker) {
27913         var px = this.map.getLayerPxFromLonLat(marker.lonlat);
27914         if (px == null) {
27915             marker.display(false);
27916         } else {
27917             if (!marker.isDrawn()) {
27918                 var markerImg = marker.draw(px);
27919                 this.div.appendChild(markerImg);
27920             } else if(marker.icon) {
27921                 marker.icon.moveTo(px);
27922             }
27923         }
27924     },
27925     
27926     /** 
27927      * APIMethod: getDataExtent
27928      * Calculates the max extent which includes all of the markers.
27929      * 
27930      * Returns:
27931      * {<OpenLayers.Bounds>}
27932      */
27933     getDataExtent: function () {
27934         var maxExtent = null;
27935         
27936         if ( this.markers && (this.markers.length > 0)) {
27937             var maxExtent = new OpenLayers.Bounds();
27938             for(var i=0, len=this.markers.length; i<len; i++) {
27939                 var marker = this.markers[i];
27940                 maxExtent.extend(marker.lonlat);
27941             }
27942         }
27943
27944         return maxExtent;
27945     },
27946
27947     CLASS_NAME: "OpenLayers.Layer.Markers"
27948 });
27949 /* ======================================================================
27950     OpenLayers/Strategy/BBOX.js
27951    ====================================================================== */
27952
27953 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
27954  * full list of contributors). Published under the 2-clause BSD license.
27955  * See license.txt in the OpenLayers distribution or repository for the
27956  * full text of the license. */
27957
27958 /**
27959  * @requires OpenLayers/Strategy.js
27960  * @requires OpenLayers/Filter/Spatial.js
27961  */
27962
27963 /**
27964  * Class: OpenLayers.Strategy.BBOX
27965  * A simple strategy that reads new features when the viewport invalidates
27966  *     some bounds.
27967  *
27968  * Inherits from:
27969  *  - <OpenLayers.Strategy>
27970  */
27971 OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {
27972     
27973     /**
27974      * Property: bounds
27975      * {<OpenLayers.Bounds>} The current data bounds (in the same projection
27976      *     as the layer - not always the same projection as the map).
27977      */
27978     bounds: null,
27979     
27980     /** 
27981      * Property: resolution 
27982      * {Float} The current data resolution. 
27983      */ 
27984     resolution: null, 
27985            
27986     /**
27987      * APIProperty: ratio
27988      * {Float} The ratio of the data bounds to the viewport bounds (in each
27989      *     dimension).  Default is 2.
27990      */
27991     ratio: 2,
27992
27993     /** 
27994      * Property: resFactor 
27995      * {Float} Optional factor used to determine when previously requested 
27996      *     features are invalid.  If set, the resFactor will be compared to the
27997      *     resolution of the previous request to the current map resolution.
27998      *     If resFactor > (old / new) and 1/resFactor < (old / new).  If you
27999      *     set a resFactor of 1, data will be requested every time the
28000      *     resolution changes.  If you set a resFactor of 3, data will be
28001      *     requested if the old resolution is 3 times the new, or if the new is
28002      *     3 times the old.  If the old bounds do not contain the new bounds
28003      *     new data will always be requested (with or without considering
28004      *     resFactor). 
28005      */ 
28006     resFactor: null, 
28007     
28008     /**
28009      * Property: response
28010      * {<OpenLayers.Protocol.Response>} The protocol response object returned
28011      *      by the layer protocol.
28012      */
28013     response: null,
28014
28015     /**
28016      * Constructor: OpenLayers.Strategy.BBOX
28017      * Create a new BBOX strategy.
28018      *
28019      * Parameters:
28020      * options - {Object} Optional object whose properties will be set on the
28021      *     instance.
28022      */
28023     
28024     /**
28025      * Method: activate
28026      * Set up strategy with regard to reading new batches of remote data.
28027      * 
28028      * Returns:
28029      * {Boolean} The strategy was successfully activated.
28030      */
28031     activate: function() {
28032         var activated = OpenLayers.Strategy.prototype.activate.call(this);
28033         if(activated) {
28034             this.layer.events.on({
28035                 "moveend": this.update,
28036                 "refresh": this.update,
28037                 "visibilitychanged": this.update,
28038                 scope: this
28039             });
28040             this.update();
28041         }
28042         return activated;
28043     },
28044     
28045     /**
28046      * Method: deactivate
28047      * Tear down strategy with regard to reading new batches of remote data.
28048      * 
28049      * Returns:
28050      * {Boolean} The strategy was successfully deactivated.
28051      */
28052     deactivate: function() {
28053         var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
28054         if(deactivated) {
28055             this.layer.events.un({
28056                 "moveend": this.update,
28057                 "refresh": this.update,
28058                 "visibilitychanged": this.update,
28059                 scope: this
28060             });
28061         }
28062         return deactivated;
28063     },
28064
28065     /**
28066      * Method: update
28067      * Callback function called on "moveend" or "refresh" layer events.
28068      *
28069      * Parameters:
28070      * options - {Object} Optional object whose properties will determine
28071      *     the behaviour of this Strategy
28072      *
28073      * Valid options include:
28074      * force - {Boolean} if true, new data must be unconditionally read.
28075      * noAbort - {Boolean} if true, do not abort previous requests.
28076      */
28077     update: function(options) {
28078         var mapBounds = this.getMapBounds();
28079         if (mapBounds !== null && ((options && options.force) ||
28080           (this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) {
28081             this.calculateBounds(mapBounds);
28082             this.resolution = this.layer.map.getResolution(); 
28083             this.triggerRead(options);
28084         }
28085     },
28086     
28087     /**
28088      * Method: getMapBounds
28089      * Get the map bounds expressed in the same projection as this layer.
28090      *
28091      * Returns:
28092      * {<OpenLayers.Bounds>} Map bounds in the projection of the layer.
28093      */
28094     getMapBounds: function() {
28095         if (this.layer.map === null) {
28096             return null;
28097         }
28098         var bounds = this.layer.map.getExtent();
28099         if(bounds && !this.layer.projection.equals(
28100                 this.layer.map.getProjectionObject())) {
28101             bounds = bounds.clone().transform(
28102                 this.layer.map.getProjectionObject(), this.layer.projection
28103             );
28104         }
28105         return bounds;
28106     },
28107
28108     /**
28109      * Method: invalidBounds
28110      * Determine whether the previously requested set of features is invalid. 
28111      *     This occurs when the new map bounds do not contain the previously 
28112      *     requested bounds.  In addition, if <resFactor> is set, it will be 
28113      *     considered.
28114      *
28115      * Parameters:
28116      * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
28117      *      retrieved from the map object if not provided
28118      *
28119      * Returns:
28120      * {Boolean} 
28121      */
28122     invalidBounds: function(mapBounds) {
28123         if(!mapBounds) {
28124             mapBounds = this.getMapBounds();
28125         }
28126         var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);
28127         if(!invalid && this.resFactor) {
28128             var ratio = this.resolution / this.layer.map.getResolution();
28129             invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor));
28130         }
28131         return invalid;
28132     },
28133  
28134     /**
28135      * Method: calculateBounds
28136      *
28137      * Parameters:
28138      * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be
28139      *      retrieved from the map object if not provided
28140      */
28141     calculateBounds: function(mapBounds) {
28142         if(!mapBounds) {
28143             mapBounds = this.getMapBounds();
28144         }
28145         var center = mapBounds.getCenterLonLat();
28146         var dataWidth = mapBounds.getWidth() * this.ratio;
28147         var dataHeight = mapBounds.getHeight() * this.ratio;
28148         this.bounds = new OpenLayers.Bounds(
28149             center.lon - (dataWidth / 2),
28150             center.lat - (dataHeight / 2),
28151             center.lon + (dataWidth / 2),
28152             center.lat + (dataHeight / 2)
28153         );
28154     },
28155     
28156     /**
28157      * Method: triggerRead
28158      *
28159      * Parameters:
28160      * options - {Object} Additional options for the protocol's read method 
28161      *     (optional)
28162      *
28163      * Returns:
28164      * {<OpenLayers.Protocol.Response>} The protocol response object
28165      *      returned by the layer protocol.
28166      */
28167     triggerRead: function(options) {
28168         if (this.response && !(options && options.noAbort === true)) {
28169             this.layer.protocol.abort(this.response);
28170             this.layer.events.triggerEvent("loadend");
28171         }
28172         var evt = {filter: this.createFilter()};
28173         this.layer.events.triggerEvent("loadstart", evt);
28174         this.response = this.layer.protocol.read(
28175             OpenLayers.Util.applyDefaults({
28176                 filter: evt.filter,
28177                 callback: this.merge,
28178                 scope: this
28179         }, options));
28180     },
28181  
28182     /**
28183      * Method: createFilter
28184      * Creates a spatial BBOX filter. If the layer that this strategy belongs
28185      * to has a filter property, this filter will be combined with the BBOX 
28186      * filter.
28187      * 
28188      * Returns
28189      * {<OpenLayers.Filter>} The filter object.
28190      */
28191     createFilter: function() {
28192         var filter = new OpenLayers.Filter.Spatial({
28193             type: OpenLayers.Filter.Spatial.BBOX,
28194             value: this.bounds,
28195             projection: this.layer.projection
28196         });
28197         if (this.layer.filter) {
28198             filter = new OpenLayers.Filter.Logical({
28199                 type: OpenLayers.Filter.Logical.AND,
28200                 filters: [this.layer.filter, filter]
28201             });
28202         }
28203         return filter;
28204     },
28205    
28206     /**
28207      * Method: merge
28208      * Given a list of features, determine which ones to add to the layer.
28209      *     If the layer projection differs from the map projection, features
28210      *     will be transformed from the layer projection to the map projection.
28211      *
28212      * Parameters:
28213      * resp - {<OpenLayers.Protocol.Response>} The response object passed
28214      *      by the protocol.
28215      */
28216     merge: function(resp) {
28217         this.layer.destroyFeatures();
28218         if (resp.success()) {
28219             var features = resp.features;
28220             if(features && features.length > 0) {
28221                 var remote = this.layer.projection;
28222                 var local = this.layer.map.getProjectionObject();
28223                 if(!local.equals(remote)) {
28224                     var geom;
28225                     for(var i=0, len=features.length; i<len; ++i) {
28226                         geom = features[i].geometry;
28227                         if(geom) {
28228                             geom.transform(remote, local);
28229                         }
28230                     }
28231                 }
28232                 this.layer.addFeatures(features);
28233             }
28234         } else {
28235             this.bounds = null;
28236         }
28237         this.response = null;
28238         this.layer.events.triggerEvent("loadend", {response: resp});
28239     },
28240    
28241     CLASS_NAME: "OpenLayers.Strategy.BBOX" 
28242 });
28243 /* ======================================================================
28244     OpenLayers/Handler/Feature.js
28245    ====================================================================== */
28246
28247 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
28248  * full list of contributors). Published under the 2-clause BSD license.
28249  * See license.txt in the OpenLayers distribution or repository for the
28250  * full text of the license. */
28251
28252
28253 /**
28254  * @requires OpenLayers/Handler.js
28255  */
28256
28257 /**
28258  * Class: OpenLayers.Handler.Feature 
28259  * Handler to respond to mouse events related to a drawn feature.  Callbacks
28260  *     with the following keys will be notified of the following events
28261  *     associated with features: click, clickout, over, out, and dblclick.
28262  *
28263  * This handler stops event propagation for mousedown and mouseup if those
28264  *     browser events target features that can be selected.
28265  *
28266  * Inherits from:
28267  *  - <OpenLayers.Handler>
28268  */
28269 OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {
28270
28271     /**
28272      * Property: EVENTMAP
28273      * {Object} A object mapping the browser events to objects with callback
28274      *     keys for in and out.
28275      */
28276     EVENTMAP: {
28277         'click': {'in': 'click', 'out': 'clickout'},
28278         'mousemove': {'in': 'over', 'out': 'out'},
28279         'dblclick': {'in': 'dblclick', 'out': null},
28280         'mousedown': {'in': null, 'out': null},
28281         'mouseup': {'in': null, 'out': null},
28282         'touchstart': {'in': 'click', 'out': 'clickout'}
28283     },
28284
28285     /**
28286      * Property: feature
28287      * {<OpenLayers.Feature.Vector>} The last feature that was hovered.
28288      */
28289     feature: null,
28290
28291     /**
28292      * Property: lastFeature
28293      * {<OpenLayers.Feature.Vector>} The last feature that was handled.
28294      */
28295     lastFeature: null,
28296
28297     /**
28298      * Property: down
28299      * {<OpenLayers.Pixel>} The location of the last mousedown.
28300      */
28301     down: null,
28302
28303     /**
28304      * Property: up
28305      * {<OpenLayers.Pixel>} The location of the last mouseup.
28306      */
28307     up: null,
28308     
28309     /**
28310      * Property: clickTolerance
28311      * {Number} The number of pixels the mouse can move between mousedown
28312      *     and mouseup for the event to still be considered a click.
28313      *     Dragging the map should not trigger the click and clickout callbacks
28314      *     unless the map is moved by less than this tolerance. Defaults to 4.
28315      */
28316     clickTolerance: 4,
28317
28318     /**
28319      * Property: geometryTypes
28320      * To restrict dragging to a limited set of geometry types, send a list
28321      * of strings corresponding to the geometry class names.
28322      * 
28323      * @type Array(String)
28324      */
28325     geometryTypes: null,
28326
28327     /**
28328      * Property: stopClick
28329      * {Boolean} If stopClick is set to true, handled clicks do not
28330      *      propagate to other click listeners. Otherwise, handled clicks
28331      *      do propagate. Unhandled clicks always propagate, whatever the
28332      *      value of stopClick. Defaults to true.
28333      */
28334     stopClick: true,
28335
28336     /**
28337      * Property: stopDown
28338      * {Boolean} If stopDown is set to true, handled mousedowns do not
28339      *      propagate to other mousedown listeners. Otherwise, handled
28340      *      mousedowns do propagate. Unhandled mousedowns always propagate,
28341      *      whatever the value of stopDown. Defaults to true.
28342      */
28343     stopDown: true,
28344
28345     /**
28346      * Property: stopUp
28347      * {Boolean} If stopUp is set to true, handled mouseups do not
28348      *      propagate to other mouseup listeners. Otherwise, handled mouseups
28349      *      do propagate. Unhandled mouseups always propagate, whatever the
28350      *      value of stopUp. Defaults to false.
28351      */
28352     stopUp: false,
28353     
28354     /**
28355      * Constructor: OpenLayers.Handler.Feature
28356      *
28357      * Parameters:
28358      * control - {<OpenLayers.Control>} 
28359      * layer - {<OpenLayers.Layer.Vector>}
28360      * callbacks - {Object} An object with a 'over' property whos value is
28361      *     a function to be called when the mouse is over a feature. The 
28362      *     callback should expect to recieve a single argument, the feature.
28363      * options - {Object} 
28364      */
28365     initialize: function(control, layer, callbacks, options) {
28366         OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);
28367         this.layer = layer;
28368     },
28369
28370     /**
28371      * Method: touchstart
28372      * Handle touchstart events
28373      *
28374      * Parameters:
28375      * evt - {Event}
28376      *
28377      * Returns:
28378      * {Boolean} Let the event propagate.
28379      */
28380     touchstart: function(evt) {
28381         this.startTouch(); 
28382         return OpenLayers.Event.isMultiTouch(evt) ?
28383                 true : this.mousedown(evt);
28384     },
28385
28386     /**
28387      * Method: touchmove
28388      * Handle touchmove events. We just prevent the browser default behavior,
28389      *    for Android Webkit not to select text when moving the finger after
28390      *    selecting a feature.
28391      *
28392      * Parameters:
28393      * evt - {Event}
28394      */
28395     touchmove: function(evt) {
28396         OpenLayers.Event.preventDefault(evt);
28397     },
28398
28399     /**
28400      * Method: mousedown
28401      * Handle mouse down.  Stop propagation if a feature is targeted by this
28402      *     event (stops map dragging during feature selection).
28403      * 
28404      * Parameters:
28405      * evt - {Event} 
28406      */
28407     mousedown: function(evt) {
28408         // Feature selection is only done with a left click. Other handlers may stop the
28409         // propagation of left-click mousedown events but not right-click mousedown events.
28410         // This mismatch causes problems when comparing the location of the down and up
28411         // events in the click function so it is important ignore right-clicks.
28412         if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) {
28413             this.down = evt.xy;
28414         }
28415         return this.handle(evt) ? !this.stopDown : true;
28416     },
28417     
28418     /**
28419      * Method: mouseup
28420      * Handle mouse up.  Stop propagation if a feature is targeted by this
28421      *     event.
28422      * 
28423      * Parameters:
28424      * evt - {Event} 
28425      */
28426     mouseup: function(evt) {
28427         this.up = evt.xy;
28428         return this.handle(evt) ? !this.stopUp : true;
28429     },
28430
28431     /**
28432      * Method: click
28433      * Handle click.  Call the "click" callback if click on a feature,
28434      *     or the "clickout" callback if click outside any feature.
28435      * 
28436      * Parameters:
28437      * evt - {Event} 
28438      *
28439      * Returns:
28440      * {Boolean}
28441      */
28442     click: function(evt) {
28443         return this.handle(evt) ? !this.stopClick : true;
28444     },
28445         
28446     /**
28447      * Method: mousemove
28448      * Handle mouse moves.  Call the "over" callback if moving in to a feature,
28449      *     or the "out" callback if moving out of a feature.
28450      * 
28451      * Parameters:
28452      * evt - {Event} 
28453      *
28454      * Returns:
28455      * {Boolean}
28456      */
28457     mousemove: function(evt) {
28458         if (!this.callbacks['over'] && !this.callbacks['out']) {
28459             return true;
28460         }     
28461         this.handle(evt);
28462         return true;
28463     },
28464     
28465     /**
28466      * Method: dblclick
28467      * Handle dblclick.  Call the "dblclick" callback if dblclick on a feature.
28468      *
28469      * Parameters:
28470      * evt - {Event} 
28471      *
28472      * Returns:
28473      * {Boolean}
28474      */
28475     dblclick: function(evt) {
28476         return !this.handle(evt);
28477     },
28478
28479     /**
28480      * Method: geometryTypeMatches
28481      * Return true if the geometry type of the passed feature matches
28482      *     one of the geometry types in the geometryTypes array.
28483      *
28484      * Parameters:
28485      * feature - {<OpenLayers.Vector.Feature>}
28486      *
28487      * Returns:
28488      * {Boolean}
28489      */
28490     geometryTypeMatches: function(feature) {
28491         return this.geometryTypes == null ||
28492             OpenLayers.Util.indexOf(this.geometryTypes,
28493                                     feature.geometry.CLASS_NAME) > -1;
28494     },
28495
28496     /**
28497      * Method: handle
28498      *
28499      * Parameters:
28500      * evt - {Event}
28501      *
28502      * Returns:
28503      * {Boolean} The event occurred over a relevant feature.
28504      */
28505     handle: function(evt) {
28506         if(this.feature && !this.feature.layer) {
28507             // feature has been destroyed
28508             this.feature = null;
28509         }
28510         var type = evt.type;
28511         var handled = false;
28512         var previouslyIn = !!(this.feature); // previously in a feature
28513         var click = (type == "click" || type == "dblclick" || type == "touchstart");
28514         this.feature = this.layer.getFeatureFromEvent(evt);
28515         if(this.feature && !this.feature.layer) {
28516             // feature has been destroyed
28517             this.feature = null;
28518         }
28519         if(this.lastFeature && !this.lastFeature.layer) {
28520             // last feature has been destroyed
28521             this.lastFeature = null;
28522         }
28523         if(this.feature) {
28524             if(type === "touchstart") {
28525                 // stop the event to prevent Android Webkit from
28526                 // "flashing" the map div
28527                 OpenLayers.Event.preventDefault(evt);
28528             }
28529             var inNew = (this.feature != this.lastFeature);
28530             if(this.geometryTypeMatches(this.feature)) {
28531                 // in to a feature
28532                 if(previouslyIn && inNew) {
28533                     // out of last feature and in to another
28534                     if(this.lastFeature) {
28535                         this.triggerCallback(type, 'out', [this.lastFeature]);
28536                     }
28537                     this.triggerCallback(type, 'in', [this.feature]);
28538                 } else if(!previouslyIn || click) {
28539                     // in feature for the first time
28540                     this.triggerCallback(type, 'in', [this.feature]);
28541                 }
28542                 this.lastFeature = this.feature;
28543                 handled = true;
28544             } else {
28545                 // not in to a feature
28546                 if(this.lastFeature && (previouslyIn && inNew || click)) {
28547                     // out of last feature for the first time
28548                     this.triggerCallback(type, 'out', [this.lastFeature]);
28549                 }
28550                 // next time the mouse goes in a feature whose geometry type
28551                 // doesn't match we don't want to call the 'out' callback
28552                 // again, so let's set this.feature to null so that
28553                 // previouslyIn will evaluate to false the next time
28554                 // we enter handle. Yes, a bit hackish...
28555                 this.feature = null;
28556             }
28557         } else if(this.lastFeature && (previouslyIn || click)) {
28558             this.triggerCallback(type, 'out', [this.lastFeature]);
28559         }
28560         return handled;
28561     },
28562     
28563     /**
28564      * Method: triggerCallback
28565      * Call the callback keyed in the event map with the supplied arguments.
28566      *     For click and clickout, the <clickTolerance> is checked first.
28567      *
28568      * Parameters:
28569      * type - {String}
28570      */
28571     triggerCallback: function(type, mode, args) {
28572         var key = this.EVENTMAP[type][mode];
28573         if(key) {
28574             if(type == 'click' && this.up && this.down) {
28575                 // for click/clickout, only trigger callback if tolerance is met
28576                 var dpx = Math.sqrt(
28577                     Math.pow(this.up.x - this.down.x, 2) +
28578                     Math.pow(this.up.y - this.down.y, 2)
28579                 );
28580                 if(dpx <= this.clickTolerance) {
28581                     this.callback(key, args);
28582                 }
28583                 // we're done with this set of events now: clear the cached
28584                 // positions so we can't trip over them later (this can occur
28585                 // if one of the up/down events gets eaten before it gets to us
28586                 // but we still get the click)
28587                 this.up = this.down = null;
28588             } else {
28589                 this.callback(key, args);
28590             }
28591         }
28592     },
28593
28594     /**
28595      * Method: activate 
28596      * Turn on the handler.  Returns false if the handler was already active.
28597      *
28598      * Returns:
28599      * {Boolean}
28600      */
28601     activate: function() {
28602         var activated = false;
28603         if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
28604             this.moveLayerToTop();
28605             this.map.events.on({
28606                 "removelayer": this.handleMapEvents,
28607                 "changelayer": this.handleMapEvents,
28608                 scope: this
28609             });
28610             activated = true;
28611         }
28612         return activated;
28613     },
28614     
28615     /**
28616      * Method: deactivate 
28617      * Turn off the handler.  Returns false if the handler was already active.
28618      *
28619      * Returns: 
28620      * {Boolean}
28621      */
28622     deactivate: function() {
28623         var deactivated = false;
28624         if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
28625             this.moveLayerBack();
28626             this.feature = null;
28627             this.lastFeature = null;
28628             this.down = null;
28629             this.up = null;
28630             this.map.events.un({
28631                 "removelayer": this.handleMapEvents,
28632                 "changelayer": this.handleMapEvents,
28633                 scope: this
28634             });
28635             deactivated = true;
28636         }
28637         return deactivated;
28638     },
28639     
28640     /**
28641      * Method: handleMapEvents
28642      * 
28643      * Parameters:
28644      * evt - {Object}
28645      */
28646     handleMapEvents: function(evt) {
28647         if (evt.type == "removelayer" || evt.property == "order") {
28648             this.moveLayerToTop();
28649         }
28650     },
28651     
28652     /**
28653      * Method: moveLayerToTop
28654      * Moves the layer for this handler to the top, so mouse events can reach
28655      * it.
28656      */
28657     moveLayerToTop: function() {
28658         var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,
28659             this.layer.getZIndex()) + 1;
28660         this.layer.setZIndex(index);
28661         
28662     },
28663     
28664     /**
28665      * Method: moveLayerBack
28666      * Moves the layer back to the position determined by the map's layers
28667      * array.
28668      */
28669     moveLayerBack: function() {
28670         var index = this.layer.getZIndex() - 1;
28671         if (index >= this.map.Z_INDEX_BASE['Feature']) {
28672             this.layer.setZIndex(index);
28673         } else {
28674             this.map.setLayerZIndex(this.layer,
28675                 this.map.getLayerIndex(this.layer));
28676         }
28677     },
28678
28679     CLASS_NAME: "OpenLayers.Handler.Feature"
28680 });
28681 /* ======================================================================
28682     OpenLayers/StyleMap.js
28683    ====================================================================== */
28684
28685 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
28686  * full list of contributors). Published under the 2-clause BSD license.
28687  * See license.txt in the OpenLayers distribution or repository for the
28688  * full text of the license. */
28689
28690 /**
28691  * @requires OpenLayers/BaseTypes/Class.js
28692  * @requires OpenLayers/Style.js
28693  * @requires OpenLayers/Feature/Vector.js
28694  */
28695  
28696 /**
28697  * Class: OpenLayers.StyleMap
28698  */
28699 OpenLayers.StyleMap = OpenLayers.Class({
28700     
28701     /**
28702      * Property: styles
28703      * {Object} Hash of {<OpenLayers.Style>}, keyed by names of well known
28704      * rendering intents (e.g. "default", "temporary", "select", "delete").
28705      */
28706     styles: null,
28707     
28708     /**
28709      * Property: extendDefault
28710      * {Boolean} if true, every render intent will extend the symbolizers
28711      * specified for the "default" intent at rendering time. Otherwise, every
28712      * rendering intent will be treated as a completely independent style.
28713      */
28714     extendDefault: true,
28715     
28716     /**
28717      * Constructor: OpenLayers.StyleMap
28718      * 
28719      * Parameters:
28720      * style   - {Object} Optional. Either a style hash, or a style object, or
28721      *           a hash of style objects (style hashes) keyed by rendering
28722      *           intent. If just one style hash or style object is passed,
28723      *           this will be used for all known render intents (default,
28724      *           select, temporary)
28725      * options - {Object} optional hash of additional options for this
28726      *           instance
28727      */
28728     initialize: function (style, options) {
28729         this.styles = {
28730             "default": new OpenLayers.Style(
28731                 OpenLayers.Feature.Vector.style["default"]),
28732             "select": new OpenLayers.Style(
28733                 OpenLayers.Feature.Vector.style["select"]),
28734             "temporary": new OpenLayers.Style(
28735                 OpenLayers.Feature.Vector.style["temporary"]),
28736             "delete": new OpenLayers.Style(
28737                 OpenLayers.Feature.Vector.style["delete"])
28738         };
28739         
28740         // take whatever the user passed as style parameter and convert it
28741         // into parts of stylemap.
28742         if(style instanceof OpenLayers.Style) {
28743             // user passed a style object
28744             this.styles["default"] = style;
28745             this.styles["select"] = style;
28746             this.styles["temporary"] = style;
28747             this.styles["delete"] = style;
28748         } else if(typeof style == "object") {
28749             for(var key in style) {
28750                 if(style[key] instanceof OpenLayers.Style) {
28751                     // user passed a hash of style objects
28752                     this.styles[key] = style[key];
28753                 } else if(typeof style[key] == "object") {
28754                     // user passsed a hash of style hashes
28755                     this.styles[key] = new OpenLayers.Style(style[key]);
28756                 } else {
28757                     // user passed a style hash (i.e. symbolizer)
28758                     this.styles["default"] = new OpenLayers.Style(style);
28759                     this.styles["select"] = new OpenLayers.Style(style);
28760                     this.styles["temporary"] = new OpenLayers.Style(style);
28761                     this.styles["delete"] = new OpenLayers.Style(style);
28762                     break;
28763                 }
28764             }
28765         }
28766         OpenLayers.Util.extend(this, options);
28767     },
28768
28769     /**
28770      * Method: destroy
28771      */
28772     destroy: function() {
28773         for(var key in this.styles) {
28774             this.styles[key].destroy();
28775         }
28776         this.styles = null;
28777     },
28778     
28779     /**
28780      * Method: createSymbolizer
28781      * Creates the symbolizer for a feature for a render intent.
28782      * 
28783      * Parameters:
28784      * feature - {<OpenLayers.Feature>} The feature to evaluate the rules
28785      *           of the intended style against.
28786      * intent  - {String} The intent determines the symbolizer that will be
28787      *           used to draw the feature. Well known intents are "default"
28788      *           (for just drawing the features), "select" (for selected
28789      *           features) and "temporary" (for drawing features).
28790      * 
28791      * Returns:
28792      * {Object} symbolizer hash
28793      */
28794     createSymbolizer: function(feature, intent) {
28795         if(!feature) {
28796             feature = new OpenLayers.Feature.Vector();
28797         }
28798         if(!this.styles[intent]) {
28799             intent = "default";
28800         }
28801         feature.renderIntent = intent;
28802         var defaultSymbolizer = {};
28803         if(this.extendDefault && intent != "default") {
28804             defaultSymbolizer = this.styles["default"].createSymbolizer(feature);
28805         }
28806         return OpenLayers.Util.extend(defaultSymbolizer,
28807             this.styles[intent].createSymbolizer(feature));
28808     },
28809     
28810     /**
28811      * Method: addUniqueValueRules
28812      * Convenience method to create comparison rules for unique values of a
28813      * property. The rules will be added to the style object for a specified
28814      * rendering intent. This method is a shortcut for creating something like
28815      * the "unique value legends" familiar from well known desktop GIS systems
28816      * 
28817      * Parameters:
28818      * renderIntent - {String} rendering intent to add the rules to
28819      * property     - {String} values of feature attributes to create the
28820      *                rules for
28821      * symbolizers  - {Object} Hash of symbolizers, keyed by the desired
28822      *                property values 
28823      * context      - {Object} An optional object with properties that
28824      *                symbolizers' property values should be evaluated
28825      *                against. If no context is specified, feature.attributes
28826      *                will be used
28827      */
28828     addUniqueValueRules: function(renderIntent, property, symbolizers, context) {
28829         var rules = [];
28830         for (var value in symbolizers) {
28831             rules.push(new OpenLayers.Rule({
28832                 symbolizer: symbolizers[value],
28833                 context: context,
28834                 filter: new OpenLayers.Filter.Comparison({
28835                     type: OpenLayers.Filter.Comparison.EQUAL_TO,
28836                     property: property,
28837                     value: value
28838                 })
28839             }));
28840         }
28841         this.styles[renderIntent].addRules(rules);
28842     },
28843
28844     CLASS_NAME: "OpenLayers.StyleMap"
28845 });
28846 /* ======================================================================
28847     OpenLayers/Layer/Vector.js
28848    ====================================================================== */
28849
28850 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
28851  * full list of contributors). Published under the 2-clause BSD license.
28852  * See license.txt in the OpenLayers distribution or repository for the
28853  * full text of the license. */
28854
28855 /**
28856  * @requires OpenLayers/Layer.js
28857  * @requires OpenLayers/Renderer.js
28858  * @requires OpenLayers/StyleMap.js
28859  * @requires OpenLayers/Feature/Vector.js
28860  * @requires OpenLayers/Console.js
28861  * @requires OpenLayers/Lang.js
28862  */
28863
28864 /**
28865  * Class: OpenLayers.Layer.Vector
28866  * Instances of OpenLayers.Layer.Vector are used to render vector data from
28867  *     a variety of sources. Create a new vector layer with the
28868  *     <OpenLayers.Layer.Vector> constructor.
28869  *
28870  * Inherits from:
28871  *  - <OpenLayers.Layer>
28872  */
28873 OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
28874
28875     /**
28876      * APIProperty: events
28877      * {<OpenLayers.Events>}
28878      *
28879      * Register a listener for a particular event with the following syntax:
28880      * (code)
28881      * layer.events.register(type, obj, listener);
28882      * (end)
28883      *
28884      * Listeners will be called with a reference to an event object.  The
28885      *     properties of this event depends on exactly what happened.
28886      *
28887      * All event objects have at least the following properties:
28888      * object - {Object} A reference to layer.events.object.
28889      * element - {DOMElement} A reference to layer.events.element.
28890      *
28891      * Supported map event types (in addition to those from <OpenLayers.Layer.events>):
28892      * beforefeatureadded - Triggered before a feature is added.  Listeners
28893      *      will receive an object with a *feature* property referencing the
28894      *      feature to be added.  To stop the feature from being added, a
28895      *      listener should return false.
28896      * beforefeaturesadded - Triggered before an array of features is added.
28897      *      Listeners will receive an object with a *features* property
28898      *      referencing the feature to be added. To stop the features from
28899      *      being added, a listener should return false.
28900      * featureadded - Triggered after a feature is added.  The event
28901      *      object passed to listeners will have a *feature* property with a
28902      *      reference to the added feature.
28903      * featuresadded - Triggered after features are added.  The event
28904      *      object passed to listeners will have a *features* property with a
28905      *      reference to an array of added features.
28906      * beforefeatureremoved - Triggered before a feature is removed. Listeners
28907      *      will receive an object with a *feature* property referencing the
28908      *      feature to be removed.
28909      * beforefeaturesremoved - Triggered before multiple features are removed. 
28910      *      Listeners will receive an object with a *features* property
28911      *      referencing the features to be removed.
28912      * featureremoved - Triggerd after a feature is removed. The event
28913      *      object passed to listeners will have a *feature* property with a
28914      *      reference to the removed feature.
28915      * featuresremoved - Triggered after features are removed. The event
28916      *      object passed to listeners will have a *features* property with a
28917      *      reference to an array of removed features.
28918      * beforefeatureselected - Triggered before a feature is selected.  Listeners
28919      *      will receive an object with a *feature* property referencing the
28920      *      feature to be selected. To stop the feature from being selectd, a
28921      *      listener should return false.
28922      * featureselected - Triggered after a feature is selected.  Listeners
28923      *      will receive an object with a *feature* property referencing the
28924      *      selected feature.
28925      * featureunselected - Triggered after a feature is unselected.
28926      *      Listeners will receive an object with a *feature* property
28927      *      referencing the unselected feature.
28928      * beforefeaturemodified - Triggered when a feature is selected to 
28929      *      be modified.  Listeners will receive an object with a *feature* 
28930      *      property referencing the selected feature.
28931      * featuremodified - Triggered when a feature has been modified.
28932      *      Listeners will receive an object with a *feature* property referencing 
28933      *      the modified feature.
28934      * afterfeaturemodified - Triggered when a feature is finished being modified.
28935      *      Listeners will receive an object with a *feature* property referencing 
28936      *      the modified feature.
28937      * vertexmodified - Triggered when a vertex within any feature geometry
28938      *      has been modified.  Listeners will receive an object with a
28939      *      *feature* property referencing the modified feature, a *vertex*
28940      *      property referencing the vertex modified (always a point geometry),
28941      *      and a *pixel* property referencing the pixel location of the
28942      *      modification.
28943      * vertexremoved - Triggered when a vertex within any feature geometry
28944      *      has been deleted.  Listeners will receive an object with a
28945      *      *feature* property referencing the modified feature, a *vertex*
28946      *      property referencing the vertex modified (always a point geometry),
28947      *      and a *pixel* property referencing the pixel location of the
28948      *      removal.
28949      * sketchstarted - Triggered when a feature sketch bound for this layer
28950      *      is started.  Listeners will receive an object with a *feature*
28951      *      property referencing the new sketch feature and a *vertex* property
28952      *      referencing the creation point.
28953      * sketchmodified - Triggered when a feature sketch bound for this layer
28954      *      is modified.  Listeners will receive an object with a *vertex*
28955      *      property referencing the modified vertex and a *feature* property
28956      *      referencing the sketch feature.
28957      * sketchcomplete - Triggered when a feature sketch bound for this layer
28958      *      is complete.  Listeners will receive an object with a *feature*
28959      *      property referencing the sketch feature.  By returning false, a
28960      *      listener can stop the sketch feature from being added to the layer.
28961      * refresh - Triggered when something wants a strategy to ask the protocol
28962      *      for a new set of features.
28963      */
28964
28965     /**
28966      * APIProperty: isBaseLayer
28967      * {Boolean} The layer is a base layer.  Default is false.  Set this property
28968      * in the layer options.
28969      */
28970     isBaseLayer: false,
28971
28972     /** 
28973      * APIProperty: isFixed
28974      * {Boolean} Whether the layer remains in one place while dragging the
28975      * map. Note that setting this to true will move the layer to the bottom
28976      * of the layer stack.
28977      */
28978     isFixed: false,
28979
28980     /** 
28981      * APIProperty: features
28982      * {Array(<OpenLayers.Feature.Vector>)} 
28983      */
28984     features: null,
28985     
28986     /** 
28987      * Property: filter
28988      * {<OpenLayers.Filter>} The filter set in this layer,
28989      *     a strategy launching read requests can combined
28990      *     this filter with its own filter.
28991      */
28992     filter: null,
28993     
28994     /** 
28995      * Property: selectedFeatures
28996      * {Array(<OpenLayers.Feature.Vector>)} 
28997      */
28998     selectedFeatures: null,
28999     
29000     /**
29001      * Property: unrenderedFeatures
29002      * {Object} hash of features, keyed by feature.id, that the renderer
29003      *     failed to draw
29004      */
29005     unrenderedFeatures: null,
29006
29007     /**
29008      * APIProperty: reportError
29009      * {Boolean} report friendly error message when loading of renderer
29010      * fails.
29011      */
29012     reportError: true, 
29013
29014     /** 
29015      * APIProperty: style
29016      * {Object} Default style for the layer
29017      */
29018     style: null,
29019     
29020     /**
29021      * Property: styleMap
29022      * {<OpenLayers.StyleMap>}
29023      */
29024     styleMap: null,
29025     
29026     /**
29027      * Property: strategies
29028      * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer.
29029      */
29030     strategies: null,
29031     
29032     /**
29033      * Property: protocol
29034      * {<OpenLayers.Protocol>} Optional protocol for the layer.
29035      */
29036     protocol: null,
29037     
29038     /**
29039      * Property: renderers
29040      * {Array(String)} List of supported Renderer classes. Add to this list to
29041      * add support for additional renderers. This list is ordered:
29042      * the first renderer which returns true for the  'supported()'
29043      * method will be used, if not defined in the 'renderer' option.
29044      */
29045     renderers: ['SVG', 'VML', 'Canvas'],
29046     
29047     /** 
29048      * Property: renderer
29049      * {<OpenLayers.Renderer>}
29050      */
29051     renderer: null,
29052     
29053     /**
29054      * APIProperty: rendererOptions
29055      * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for
29056      *     supported options.
29057      */
29058     rendererOptions: null,
29059     
29060     /** 
29061      * APIProperty: geometryType
29062      * {String} geometryType allows you to limit the types of geometries this
29063      * layer supports. This should be set to something like
29064      * "OpenLayers.Geometry.Point" to limit types.
29065      */
29066     geometryType: null,
29067
29068     /** 
29069      * Property: drawn
29070      * {Boolean} Whether the Vector Layer features have been drawn yet.
29071      */
29072     drawn: false,
29073     
29074     /** 
29075      * APIProperty: ratio
29076      * {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map.
29077      */   
29078     ratio: 1,
29079
29080     /**
29081      * Constructor: OpenLayers.Layer.Vector
29082      * Create a new vector layer
29083      *
29084      * Parameters:
29085      * name - {String} A name for the layer
29086      * options - {Object} Optional object with non-default properties to set on
29087      *           the layer.
29088      *
29089      * Returns:
29090      * {<OpenLayers.Layer.Vector>} A new vector layer
29091      */
29092     initialize: function(name, options) {
29093         OpenLayers.Layer.prototype.initialize.apply(this, arguments);
29094
29095         // allow user-set renderer, otherwise assign one
29096         if (!this.renderer || !this.renderer.supported()) {  
29097             this.assignRenderer();
29098         }
29099
29100         // if no valid renderer found, display error
29101         if (!this.renderer || !this.renderer.supported()) {
29102             this.renderer = null;
29103             this.displayError();
29104         } 
29105
29106         if (!this.styleMap) {
29107             this.styleMap = new OpenLayers.StyleMap();
29108         }
29109
29110         this.features = [];
29111         this.selectedFeatures = [];
29112         this.unrenderedFeatures = {};
29113         
29114         // Allow for custom layer behavior
29115         if(this.strategies){
29116             for(var i=0, len=this.strategies.length; i<len; i++) {
29117                 this.strategies[i].setLayer(this);
29118             }
29119         }
29120
29121     },
29122
29123     /**
29124      * APIMethod: destroy
29125      * Destroy this layer
29126      */
29127     destroy: function() {
29128         if (this.strategies) {
29129             var strategy, i, len;
29130             for(i=0, len=this.strategies.length; i<len; i++) {
29131                 strategy = this.strategies[i];
29132                 if(strategy.autoDestroy) {
29133                     strategy.destroy();
29134                 }
29135             }
29136             this.strategies = null;
29137         }
29138         if (this.protocol) {
29139             if(this.protocol.autoDestroy) {
29140                 this.protocol.destroy();
29141             }
29142             this.protocol = null;
29143         }
29144         this.destroyFeatures();
29145         this.features = null;
29146         this.selectedFeatures = null;
29147         this.unrenderedFeatures = null;
29148         if (this.renderer) {
29149             this.renderer.destroy();
29150         }
29151         this.renderer = null;
29152         this.geometryType = null;
29153         this.drawn = null;
29154         OpenLayers.Layer.prototype.destroy.apply(this, arguments);  
29155     },
29156
29157     /**
29158      * Method: clone
29159      * Create a clone of this layer.
29160      * 
29161      * Note: Features of the layer are also cloned.
29162      *
29163      * Returns:
29164      * {<OpenLayers.Layer.Vector>} An exact clone of this layer
29165      */
29166     clone: function (obj) {
29167         
29168         if (obj == null) {
29169             obj = new OpenLayers.Layer.Vector(this.name, this.getOptions());
29170         }
29171
29172         //get all additions from superclasses
29173         obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
29174
29175         // copy/set any non-init, non-simple values here
29176         var features = this.features;
29177         var len = features.length;
29178         var clonedFeatures = new Array(len);
29179         for(var i=0; i<len; ++i) {
29180             clonedFeatures[i] = features[i].clone();
29181         }
29182         obj.features = clonedFeatures;
29183
29184         return obj;
29185     },    
29186     
29187     /**
29188      * Method: refresh
29189      * Ask the layer to request features again and redraw them.  Triggers
29190      *     the refresh event if the layer is in range and visible.
29191      *
29192      * Parameters:
29193      * obj - {Object} Optional object with properties for any listener of
29194      *     the refresh event.
29195      */
29196     refresh: function(obj) {
29197         if(this.calculateInRange() && this.visibility) {
29198             this.events.triggerEvent("refresh", obj);
29199         }
29200     },
29201
29202     /** 
29203      * Method: assignRenderer
29204      * Iterates through the available renderer implementations and selects 
29205      * and assigns the first one whose "supported()" function returns true.
29206      */    
29207     assignRenderer: function()  {
29208         for (var i=0, len=this.renderers.length; i<len; i++) {
29209             var rendererClass = this.renderers[i];
29210             var renderer = (typeof rendererClass == "function") ?
29211                 rendererClass :
29212                 OpenLayers.Renderer[rendererClass];
29213             if (renderer && renderer.prototype.supported()) {
29214                 this.renderer = new renderer(this.div, this.rendererOptions);
29215                 break;
29216             }  
29217         }  
29218     },
29219
29220     /** 
29221      * Method: displayError 
29222      * Let the user know their browser isn't supported.
29223      */
29224     displayError: function() {
29225         if (this.reportError) {
29226             OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported", 
29227                                      {renderers: this. renderers.join('\n')}));
29228         }    
29229     },
29230
29231     /** 
29232      * Method: setMap
29233      * The layer has been added to the map. 
29234      * 
29235      * If there is no renderer set, the layer can't be used. Remove it.
29236      * Otherwise, give the renderer a reference to the map and set its size.
29237      * 
29238      * Parameters:
29239      * map - {<OpenLayers.Map>} 
29240      */
29241     setMap: function(map) {        
29242         OpenLayers.Layer.prototype.setMap.apply(this, arguments);
29243
29244         if (!this.renderer) {
29245             this.map.removeLayer(this);
29246         } else {
29247             this.renderer.map = this.map;
29248
29249             var newSize = this.map.getSize();
29250             newSize.w = newSize.w * this.ratio;
29251             newSize.h = newSize.h * this.ratio;
29252             this.renderer.setSize(newSize);
29253         }
29254     },
29255
29256     /**
29257      * Method: afterAdd
29258      * Called at the end of the map.addLayer sequence.  At this point, the map
29259      *     will have a base layer.  Any autoActivate strategies will be
29260      *     activated here.
29261      */
29262     afterAdd: function() {
29263         if(this.strategies) {
29264             var strategy, i, len;
29265             for(i=0, len=this.strategies.length; i<len; i++) {
29266                 strategy = this.strategies[i];
29267                 if(strategy.autoActivate) {
29268                     strategy.activate();
29269                 }
29270             }
29271         }
29272     },
29273
29274     /**
29275      * Method: removeMap
29276      * The layer has been removed from the map.
29277      *
29278      * Parameters:
29279      * map - {<OpenLayers.Map>}
29280      */
29281     removeMap: function(map) {
29282         this.drawn = false;
29283         if(this.strategies) {
29284             var strategy, i, len;
29285             for(i=0, len=this.strategies.length; i<len; i++) {
29286                 strategy = this.strategies[i];
29287                 if(strategy.autoActivate) {
29288                     strategy.deactivate();
29289                 }
29290             }
29291         }
29292     },
29293     
29294     /**
29295      * Method: onMapResize
29296      * Notify the renderer of the change in size. 
29297      * 
29298      */
29299     onMapResize: function() {
29300         OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);
29301         
29302         var newSize = this.map.getSize();
29303         newSize.w = newSize.w * this.ratio;
29304         newSize.h = newSize.h * this.ratio;
29305         this.renderer.setSize(newSize);
29306     },
29307
29308     /**
29309      * Method: moveTo
29310      *  Reset the vector layer's div so that it once again is lined up with 
29311      *   the map. Notify the renderer of the change of extent, and in the
29312      *   case of a change of zoom level (resolution), have the 
29313      *   renderer redraw features.
29314      * 
29315      *  If the layer has not yet been drawn, cycle through the layer's 
29316      *   features and draw each one.
29317      * 
29318      * Parameters:
29319      * bounds - {<OpenLayers.Bounds>} 
29320      * zoomChanged - {Boolean} 
29321      * dragging - {Boolean} 
29322      */
29323     moveTo: function(bounds, zoomChanged, dragging) {
29324         OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
29325         
29326         var coordSysUnchanged = true;
29327         if (!dragging) {
29328             this.renderer.root.style.visibility = 'hidden';
29329
29330             var viewSize = this.map.getSize(),
29331                 viewWidth = viewSize.w,
29332                 viewHeight = viewSize.h,
29333                 offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2,
29334                 offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2;
29335             offsetLeft += this.map.layerContainerOriginPx.x;
29336             offsetLeft = -Math.round(offsetLeft);
29337             offsetTop += this.map.layerContainerOriginPx.y;
29338             offsetTop = -Math.round(offsetTop);
29339
29340             this.div.style.left = offsetLeft + 'px';
29341             this.div.style.top = offsetTop + 'px';
29342
29343             var extent = this.map.getExtent().scale(this.ratio);
29344             coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);
29345
29346             this.renderer.root.style.visibility = 'visible';
29347
29348             // Force a reflow on gecko based browsers to prevent jump/flicker.
29349             // This seems to happen on only certain configurations; it was originally
29350             // noticed in FF 2.0 and Linux.
29351             if (OpenLayers.IS_GECKO === true) {
29352                 this.div.scrollLeft = this.div.scrollLeft;
29353             }
29354             
29355             if (!zoomChanged && coordSysUnchanged) {
29356                 for (var i in this.unrenderedFeatures) {
29357                     var feature = this.unrenderedFeatures[i];
29358                     this.drawFeature(feature);
29359                 }
29360             }
29361         }
29362         if (!this.drawn || zoomChanged || !coordSysUnchanged) {
29363             this.drawn = true;
29364             var feature;
29365             for(var i=0, len=this.features.length; i<len; i++) {
29366                 this.renderer.locked = (i !== (len - 1));
29367                 feature = this.features[i];
29368                 this.drawFeature(feature);
29369             }
29370         }    
29371     },
29372     
29373     /** 
29374      * APIMethod: display
29375      * Hide or show the Layer
29376      * 
29377      * Parameters:
29378      * display - {Boolean}
29379      */
29380     display: function(display) {
29381         OpenLayers.Layer.prototype.display.apply(this, arguments);
29382         // we need to set the display style of the root in case it is attached
29383         // to a foreign layer
29384         var currentDisplay = this.div.style.display;
29385         if(currentDisplay != this.renderer.root.style.display) {
29386             this.renderer.root.style.display = currentDisplay;
29387         }
29388     },
29389
29390     /**
29391      * APIMethod: addFeatures
29392      * Add Features to the layer.
29393      *
29394      * Parameters:
29395      * features - {Array(<OpenLayers.Feature.Vector>)} 
29396      * options - {Object}
29397      */
29398     addFeatures: function(features, options) {
29399         if (!(OpenLayers.Util.isArray(features))) {
29400             features = [features];
29401         }
29402         
29403         var notify = !options || !options.silent;
29404         if(notify) {
29405             var event = {features: features};
29406             var ret = this.events.triggerEvent("beforefeaturesadded", event);
29407             if(ret === false) {
29408                 return;
29409             }
29410             features = event.features;
29411         }
29412         
29413         // Track successfully added features for featuresadded event, since
29414         // beforefeatureadded can veto single features.
29415         var featuresAdded = [];
29416         for (var i=0, len=features.length; i<len; i++) {
29417             if (i != (features.length - 1)) {
29418                 this.renderer.locked = true;
29419             } else {
29420                 this.renderer.locked = false;
29421             }    
29422             var feature = features[i];
29423             
29424             if (this.geometryType &&
29425               !(feature.geometry instanceof this.geometryType)) {
29426                 throw new TypeError('addFeatures: component should be an ' +
29427                                     this.geometryType.prototype.CLASS_NAME);
29428               }
29429
29430             //give feature reference to its layer
29431             feature.layer = this;
29432
29433             if (!feature.style && this.style) {
29434                 feature.style = OpenLayers.Util.extend({}, this.style);
29435             }
29436
29437             if (notify) {
29438                 if(this.events.triggerEvent("beforefeatureadded",
29439                                             {feature: feature}) === false) {
29440                     continue;
29441                 }
29442                 this.preFeatureInsert(feature);
29443             }
29444
29445             featuresAdded.push(feature);
29446             this.features.push(feature);
29447             this.drawFeature(feature);
29448             
29449             if (notify) {
29450                 this.events.triggerEvent("featureadded", {
29451                     feature: feature
29452                 });
29453                 this.onFeatureInsert(feature);
29454             }
29455         }
29456         
29457         if(notify) {
29458             this.events.triggerEvent("featuresadded", {features: featuresAdded});
29459         }
29460     },
29461
29462
29463     /**
29464      * APIMethod: removeFeatures
29465      * Remove features from the layer.  This erases any drawn features and
29466      *     removes them from the layer's control.  The beforefeatureremoved
29467      *     and featureremoved events will be triggered for each feature.  The
29468      *     featuresremoved event will be triggered after all features have
29469      *     been removed.  To supress event triggering, use the silent option.
29470      * 
29471      * Parameters:
29472      * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be
29473      *     removed.
29474      * options - {Object} Optional properties for changing behavior of the
29475      *     removal.
29476      *
29477      * Valid options:
29478      * silent - {Boolean} Supress event triggering.  Default is false.
29479      */
29480     removeFeatures: function(features, options) {
29481         if(!features || features.length === 0) {
29482             return;
29483         }
29484         if (features === this.features) {
29485             return this.removeAllFeatures(options);
29486         }
29487         if (!(OpenLayers.Util.isArray(features))) {
29488             features = [features];
29489         }
29490         if (features === this.selectedFeatures) {
29491             features = features.slice();
29492         }
29493
29494         var notify = !options || !options.silent;
29495         
29496         if (notify) {
29497             this.events.triggerEvent(
29498                 "beforefeaturesremoved", {features: features}
29499             );
29500         }
29501
29502         for (var i = features.length - 1; i >= 0; i--) {
29503             // We remain locked so long as we're not at 0
29504             // and the 'next' feature has a geometry. We do the geometry check
29505             // because if all the features after the current one are 'null', we
29506             // won't call eraseGeometry, so we break the 'renderer functions
29507             // will always be called with locked=false *last*' rule. The end result
29508             // is a possible gratiutious unlocking to save a loop through the rest 
29509             // of the list checking the remaining features every time. So long as
29510             // null geoms are rare, this is probably okay.    
29511             if (i != 0 && features[i-1].geometry) {
29512                 this.renderer.locked = true;
29513             } else {
29514                 this.renderer.locked = false;
29515             }
29516     
29517             var feature = features[i];
29518             delete this.unrenderedFeatures[feature.id];
29519
29520             if (notify) {
29521                 this.events.triggerEvent("beforefeatureremoved", {
29522                     feature: feature
29523                 });
29524             }
29525
29526             this.features = OpenLayers.Util.removeItem(this.features, feature);
29527             // feature has no layer at this point
29528             feature.layer = null;
29529
29530             if (feature.geometry) {
29531                 this.renderer.eraseFeatures(feature);
29532             }
29533                     
29534             //in the case that this feature is one of the selected features, 
29535             // remove it from that array as well.
29536             if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1){
29537                 OpenLayers.Util.removeItem(this.selectedFeatures, feature);
29538             }
29539
29540             if (notify) {
29541                 this.events.triggerEvent("featureremoved", {
29542                     feature: feature
29543                 });
29544             }
29545         }
29546
29547         if (notify) {
29548             this.events.triggerEvent("featuresremoved", {features: features});
29549         }
29550     },
29551     
29552     /** 
29553      * APIMethod: removeAllFeatures
29554      * Remove all features from the layer.
29555      *
29556      * Parameters:
29557      * options - {Object} Optional properties for changing behavior of the
29558      *     removal.
29559      *
29560      * Valid options:
29561      * silent - {Boolean} Supress event triggering.  Default is false.
29562      */
29563     removeAllFeatures: function(options) {
29564         var notify = !options || !options.silent;
29565         var features = this.features;
29566         if (notify) {
29567             this.events.triggerEvent(
29568                 "beforefeaturesremoved", {features: features}
29569             );
29570         }
29571         var feature;
29572         for (var i = features.length-1; i >= 0; i--) {
29573             feature = features[i];
29574             if (notify) {
29575                 this.events.triggerEvent("beforefeatureremoved", {
29576                     feature: feature
29577                 });
29578             }
29579             feature.layer = null;
29580             if (notify) {
29581                 this.events.triggerEvent("featureremoved", {
29582                     feature: feature
29583                 });
29584             }
29585         }
29586         this.renderer.clear();
29587         this.features = [];
29588         this.unrenderedFeatures = {};
29589         this.selectedFeatures = [];
29590         if (notify) {
29591             this.events.triggerEvent("featuresremoved", {features: features});
29592         }
29593     },
29594
29595     /**
29596      * APIMethod: destroyFeatures
29597      * Erase and destroy features on the layer.
29598      *
29599      * Parameters:
29600      * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of
29601      *     features to destroy.  If not supplied, all features on the layer
29602      *     will be destroyed.
29603      * options - {Object}
29604      */
29605     destroyFeatures: function(features, options) {
29606         var all = (features == undefined); // evaluates to true if
29607                                            // features is null
29608         if(all) {
29609             features = this.features;
29610         }
29611         if(features) {
29612             this.removeFeatures(features, options);
29613             for(var i=features.length-1; i>=0; i--) {
29614                 features[i].destroy();
29615             }
29616         }
29617     },
29618
29619     /**
29620      * APIMethod: drawFeature
29621      * Draw (or redraw) a feature on the layer.  If the optional style argument
29622      * is included, this style will be used.  If no style is included, the
29623      * feature's style will be used.  If the feature doesn't have a style,
29624      * the layer's style will be used.
29625      * 
29626      * This function is not designed to be used when adding features to 
29627      * the layer (use addFeatures instead). It is meant to be used when
29628      * the style of a feature has changed, or in some other way needs to 
29629      * visually updated *after* it has already been added to a layer. You
29630      * must add the feature to the layer for most layer-related events to 
29631      * happen.
29632      *
29633      * Parameters: 
29634      * feature - {<OpenLayers.Feature.Vector>} 
29635      * style - {String | Object} Named render intent or full symbolizer object.
29636      */
29637     drawFeature: function(feature, style) {
29638         // don't try to draw the feature with the renderer if the layer is not 
29639         // drawn itself
29640         if (!this.drawn) {
29641             return;
29642         }
29643         if (typeof style != "object") {
29644             if(!style && feature.state === OpenLayers.State.DELETE) {
29645                 style = "delete";
29646             }
29647             var renderIntent = style || feature.renderIntent;
29648             style = feature.style || this.style;
29649             if (!style) {
29650                 style = this.styleMap.createSymbolizer(feature, renderIntent);
29651             }
29652         }
29653         
29654         var drawn = this.renderer.drawFeature(feature, style);
29655         //TODO remove the check for null when we get rid of Renderer.SVG
29656         if (drawn === false || drawn === null) {
29657             this.unrenderedFeatures[feature.id] = feature;
29658         } else {
29659             delete this.unrenderedFeatures[feature.id];
29660         }
29661     },
29662     
29663     /**
29664      * Method: eraseFeatures
29665      * Erase features from the layer.
29666      *
29667      * Parameters:
29668      * features - {Array(<OpenLayers.Feature.Vector>)} 
29669      */
29670     eraseFeatures: function(features) {
29671         this.renderer.eraseFeatures(features);
29672     },
29673
29674     /**
29675      * Method: getFeatureFromEvent
29676      * Given an event, return a feature if the event occurred over one.
29677      * Otherwise, return null.
29678      *
29679      * Parameters:
29680      * evt - {Event} 
29681      *
29682      * Returns:
29683      * {<OpenLayers.Feature.Vector>} A feature if one was under the event.
29684      */
29685     getFeatureFromEvent: function(evt) {
29686         if (!this.renderer) {
29687             throw new Error('getFeatureFromEvent called on layer with no ' +
29688                             'renderer. This usually means you destroyed a ' +
29689                             'layer, but not some handler which is associated ' +
29690                             'with it.');
29691         }
29692         var feature = null;
29693         var featureId = this.renderer.getFeatureIdFromEvent(evt);
29694         if (featureId) {
29695             if (typeof featureId === "string") {
29696                 feature = this.getFeatureById(featureId);
29697             } else {
29698                 feature = featureId;
29699             }
29700         }
29701         return feature;
29702     },
29703
29704     /**
29705      * APIMethod: getFeatureBy
29706      * Given a property value, return the feature if it exists in the features array
29707      *
29708      * Parameters:
29709      * property - {String}
29710      * value - {String}
29711      *
29712      * Returns:
29713      * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
29714      * property value or null if there is no such feature.
29715      */
29716     getFeatureBy: function(property, value) {
29717         //TBD - would it be more efficient to use a hash for this.features?
29718         var feature = null;
29719         for(var i=0, len=this.features.length; i<len; ++i) {
29720             if(this.features[i][property] == value) {
29721                 feature = this.features[i];
29722                 break;
29723             }
29724         }
29725         return feature;
29726     },
29727
29728     /**
29729      * APIMethod: getFeatureById
29730      * Given a feature id, return the feature if it exists in the features array
29731      *
29732      * Parameters:
29733      * featureId - {String}
29734      *
29735      * Returns:
29736      * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
29737      * featureId or null if there is no such feature.
29738      */
29739     getFeatureById: function(featureId) {
29740         return this.getFeatureBy('id', featureId);
29741     },
29742
29743     /**
29744      * APIMethod: getFeatureByFid
29745      * Given a feature fid, return the feature if it exists in the features array
29746      *
29747      * Parameters:
29748      * featureFid - {String}
29749      *
29750      * Returns:
29751      * {<OpenLayers.Feature.Vector>} A feature corresponding to the given
29752      * featureFid or null if there is no such feature.
29753      */
29754     getFeatureByFid: function(featureFid) {
29755         return this.getFeatureBy('fid', featureFid);
29756     },
29757     
29758     /**
29759      * APIMethod: getFeaturesByAttribute
29760      * Returns an array of features that have the given attribute key set to the
29761      * given value. Comparison of attribute values takes care of datatypes, e.g.
29762      * the string '1234' is not equal to the number 1234.
29763      *
29764      * Parameters:
29765      * attrName - {String}
29766      * attrValue - {Mixed}
29767      *
29768      * Returns:
29769      * Array({<OpenLayers.Feature.Vector>}) An array of features that have the 
29770      * passed named attribute set to the given value.
29771      */
29772     getFeaturesByAttribute: function(attrName, attrValue) {
29773         var i,
29774             feature,    
29775             len = this.features.length,
29776             foundFeatures = [];
29777         for(i = 0; i < len; i++) {            
29778             feature = this.features[i];
29779             if(feature && feature.attributes) {
29780                 if (feature.attributes[attrName] === attrValue) {
29781                     foundFeatures.push(feature);
29782                 }
29783             }
29784         }
29785         return foundFeatures;
29786     },
29787
29788     /**
29789      * Unselect the selected features
29790      * i.e. clears the featureSelection array
29791      * change the style back
29792     clearSelection: function() {
29793
29794        var vectorLayer = this.map.vectorLayer;
29795         for (var i = 0; i < this.map.featureSelection.length; i++) {
29796             var featureSelection = this.map.featureSelection[i];
29797             vectorLayer.drawFeature(featureSelection, vectorLayer.style);
29798         }
29799         this.map.featureSelection = [];
29800     },
29801      */
29802
29803
29804     /**
29805      * APIMethod: onFeatureInsert
29806      * method called after a feature is inserted.
29807      * Does nothing by default. Override this if you
29808      * need to do something on feature updates.
29809      *
29810      * Parameters: 
29811      * feature - {<OpenLayers.Feature.Vector>} 
29812      */
29813     onFeatureInsert: function(feature) {
29814     },
29815     
29816     /**
29817      * APIMethod: preFeatureInsert
29818      * method called before a feature is inserted.
29819      * Does nothing by default. Override this if you
29820      * need to do something when features are first added to the
29821      * layer, but before they are drawn, such as adjust the style.
29822      *
29823      * Parameters:
29824      * feature - {<OpenLayers.Feature.Vector>} 
29825      */
29826     preFeatureInsert: function(feature) {
29827     },
29828
29829     /** 
29830      * APIMethod: getDataExtent
29831      * Calculates the max extent which includes all of the features.
29832      * 
29833      * Returns:
29834      * {<OpenLayers.Bounds>} or null if the layer has no features with
29835      * geometries.
29836      */
29837     getDataExtent: function () {
29838         var maxExtent = null;
29839         var features = this.features;
29840         if(features && (features.length > 0)) {
29841             var geometry = null;
29842             for(var i=0, len=features.length; i<len; i++) {
29843                 geometry = features[i].geometry;
29844                 if (geometry) {
29845                     if (maxExtent === null) {
29846                         maxExtent = new OpenLayers.Bounds();
29847                     }
29848                     maxExtent.extend(geometry.getBounds());
29849                 }
29850             }
29851         }
29852         return maxExtent;
29853     },
29854
29855     CLASS_NAME: "OpenLayers.Layer.Vector"
29856 });
29857 /* ======================================================================
29858     OpenLayers/Layer/Vector/RootContainer.js
29859    ====================================================================== */
29860
29861 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
29862  * full list of contributors). Published under the 2-clause BSD license.
29863  * See license.txt in the OpenLayers distribution or repository for the
29864  * full text of the license. */
29865
29866 /**
29867  * @requires OpenLayers/Layer/Vector.js
29868  */
29869
29870 /**
29871  * Class: OpenLayers.Layer.Vector.RootContainer
29872  * A special layer type to combine multiple vector layers inside a single
29873  *     renderer root container. This class is not supposed to be instantiated
29874  *     from user space, it is a helper class for controls that require event
29875  *     processing for multiple vector layers.
29876  *
29877  * Inherits from:
29878  *  - <OpenLayers.Layer.Vector>
29879  */
29880 OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {
29881     
29882     /**
29883      * Property: displayInLayerSwitcher
29884      * Set to false for this layer type
29885      */
29886     displayInLayerSwitcher: false,
29887     
29888     /**
29889      * APIProperty: layers
29890      * Layers that are attached to this container. Required config option.
29891      */
29892     layers: null,
29893     
29894     /**
29895      * Constructor: OpenLayers.Layer.Vector.RootContainer
29896      * Create a new root container for multiple vector layer. This constructor
29897      * is not supposed to be used from user space, it is only to be used by
29898      * controls that need feature selection across multiple vector layers.
29899      *
29900      * Parameters:
29901      * name - {String} A name for the layer
29902      * options - {Object} Optional object with non-default properties to set on
29903      *           the layer.
29904      * 
29905      * Required options properties:
29906      * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this
29907      *     container
29908      *
29909      * Returns:
29910      * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root
29911      *     container
29912      */
29913     
29914     /**
29915      * Method: display
29916      */
29917     display: function() {},
29918     
29919     /**
29920      * Method: getFeatureFromEvent
29921      * walk through the layers to find the feature returned by the event
29922      * 
29923      * Parameters:
29924      * evt - {Object} event object with a feature property
29925      * 
29926      * Returns:
29927      * {<OpenLayers.Feature.Vector>}
29928      */
29929     getFeatureFromEvent: function(evt) {
29930         var layers = this.layers;
29931         var feature;
29932         for(var i=0; i<layers.length; i++) {
29933             feature = layers[i].getFeatureFromEvent(evt);
29934             if(feature) {
29935                 return feature;
29936             }
29937         }
29938     },
29939     
29940     /**
29941      * Method: setMap
29942      * 
29943      * Parameters:
29944      * map - {<OpenLayers.Map>}
29945      */
29946     setMap: function(map) {
29947         OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
29948         this.collectRoots();
29949         map.events.register("changelayer", this, this.handleChangeLayer);
29950     },
29951     
29952     /**
29953      * Method: removeMap
29954      * 
29955      * Parameters:
29956      * map - {<OpenLayers.Map>}
29957      */
29958     removeMap: function(map) {
29959         map.events.unregister("changelayer", this, this.handleChangeLayer);
29960         this.resetRoots();
29961         OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);
29962     },
29963     
29964     /**
29965      * Method: collectRoots
29966      * Collects the root nodes of all layers this control is configured with
29967      * and moveswien the nodes to this control's layer
29968      */
29969     collectRoots: function() {
29970         var layer;
29971         // walk through all map layers, because we want to keep the order
29972         for(var i=0; i<this.map.layers.length; ++i) {
29973             layer = this.map.layers[i];
29974             if(OpenLayers.Util.indexOf(this.layers, layer) != -1) {
29975                 layer.renderer.moveRoot(this.renderer);
29976             }
29977         }
29978     },
29979     
29980     /**
29981      * Method: resetRoots
29982      * Resets the root nodes back into the layers they belong to.
29983      */
29984     resetRoots: function() {
29985         var layer;
29986         for(var i=0; i<this.layers.length; ++i) {
29987             layer = this.layers[i];
29988             if(this.renderer && layer.renderer.getRenderLayerId() == this.id) {
29989                 this.renderer.moveRoot(layer.renderer);
29990             }
29991         }
29992     },
29993     
29994     /**
29995      * Method: handleChangeLayer
29996      * Event handler for the map's changelayer event. We need to rebuild
29997      * this container's layer dom if order of one of its layers changes.
29998      * This handler is added with the setMap method, and removed with the
29999      * removeMap method.
30000      * 
30001      * Parameters:
30002      * evt - {Object}
30003      */
30004     handleChangeLayer: function(evt) {
30005         var layer = evt.layer;
30006         if(evt.property == "order" &&
30007                         OpenLayers.Util.indexOf(this.layers, layer) != -1) {
30008             this.resetRoots();
30009             this.collectRoots();
30010         }
30011     },
30012
30013     CLASS_NAME: "OpenLayers.Layer.Vector.RootContainer"
30014 });
30015 /* ======================================================================
30016     OpenLayers/Control/SelectFeature.js
30017    ====================================================================== */
30018
30019 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
30020  * full list of contributors). Published under the 2-clause BSD license.
30021  * See license.txt in the OpenLayers distribution or repository for the
30022  * full text of the license. */
30023
30024
30025 /**
30026  * @requires OpenLayers/Control.js
30027  * @requires OpenLayers/Feature/Vector.js
30028  * @requires OpenLayers/Handler/Feature.js
30029  * @requires OpenLayers/Layer/Vector/RootContainer.js
30030  */
30031
30032 /**
30033  * Class: OpenLayers.Control.SelectFeature
30034  * The SelectFeature control selects vector features from a given layer on 
30035  * click or hover. 
30036  *
30037  * Inherits from:
30038  *  - <OpenLayers.Control>
30039  */
30040 OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
30041
30042     /** 
30043      * APIProperty: events
30044      * {<OpenLayers.Events>} Events instance for listeners and triggering
30045      *     control specific events.
30046      *
30047      * Register a listener for a particular event with the following syntax:
30048      * (code)
30049      * control.events.register(type, obj, listener);
30050      * (end)
30051      *
30052      * Supported event types (in addition to those from <OpenLayers.Control.events>):
30053      * beforefeaturehighlighted - Triggered before a feature is highlighted
30054      * featurehighlighted - Triggered when a feature is highlighted
30055      * featureunhighlighted - Triggered when a feature is unhighlighted
30056      * boxselectionstart - Triggered before box selection starts
30057      * boxselectionend - Triggered after box selection ends
30058      */
30059     
30060     /**
30061      * Property: multipleKey
30062      * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
30063      *     the <multiple> property to true.  Default is null.
30064      */
30065     multipleKey: null,
30066     
30067     /**
30068      * Property: toggleKey
30069      * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets
30070      *     the <toggle> property to true.  Default is null.
30071      */
30072     toggleKey: null,
30073     
30074     /**
30075      * APIProperty: multiple
30076      * {Boolean} Allow selection of multiple geometries.  Default is false.
30077      */
30078     multiple: false, 
30079
30080     /**
30081      * APIProperty: clickout
30082      * {Boolean} Unselect features when clicking outside any feature.
30083      *     Default is true.
30084      */
30085     clickout: true,
30086
30087     /**
30088      * APIProperty: toggle
30089      * {Boolean} Unselect a selected feature on click.  Default is false.  Only
30090      *     has meaning if hover is false.
30091      */
30092     toggle: false,
30093
30094     /**
30095      * APIProperty: hover
30096      * {Boolean} Select on mouse over and deselect on mouse out.  If true, this
30097      * ignores clicks and only listens to mouse moves.
30098      */
30099     hover: false,
30100
30101     /**
30102      * APIProperty: highlightOnly
30103      * {Boolean} If true do not actually select features (that is place them in 
30104      * the layer's selected features array), just highlight them. This property
30105      * has no effect if hover is false. Defaults to false.
30106      */
30107     highlightOnly: false,
30108     
30109     /**
30110      * APIProperty: box
30111      * {Boolean} Allow feature selection by drawing a box.
30112      */
30113     box: false,
30114     
30115     /**
30116      * Property: onBeforeSelect 
30117      * {Function} Optional function to be called before a feature is selected.
30118      *     The function should expect to be called with a feature.
30119      */
30120     onBeforeSelect: function() {},
30121     
30122     /**
30123      * APIProperty: onSelect 
30124      * {Function} Optional function to be called when a feature is selected.
30125      *     The function should expect to be called with a feature.
30126      */
30127     onSelect: function() {},
30128
30129     /**
30130      * APIProperty: onUnselect
30131      * {Function} Optional function to be called when a feature is unselected.
30132      *     The function should expect to be called with a feature.
30133      */
30134     onUnselect: function() {},
30135     
30136     /**
30137      * Property: scope
30138      * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect
30139      *     callbacks. If null the scope will be this control.
30140      */
30141     scope: null,
30142
30143     /**
30144      * APIProperty: geometryTypes
30145      * {Array(String)} To restrict selecting to a limited set of geometry types,
30146      *     send a list of strings corresponding to the geometry class names.
30147      */
30148     geometryTypes: null,
30149
30150     /**
30151      * Property: layer
30152      * {<OpenLayers.Layer.Vector>} The vector layer with a common renderer
30153      * root for all layers this control is configured with (if an array of
30154      * layers was passed to the constructor), or the vector layer the control
30155      * was configured with (if a single layer was passed to the constructor).
30156      */
30157     layer: null,
30158     
30159     /**
30160      * Property: layers
30161      * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on,
30162      * or null if the control was configured with a single layer
30163      */
30164     layers: null,
30165     
30166     /**
30167      * APIProperty: callbacks
30168      * {Object} The functions that are sent to the handlers.feature for callback
30169      */
30170     callbacks: null,
30171     
30172     /**
30173      * APIProperty: selectStyle 
30174      * {Object} Hash of styles
30175      */
30176     selectStyle: null,
30177     
30178     /**
30179      * Property: renderIntent
30180      * {String} key used to retrieve the select style from the layer's
30181      * style map.
30182      */
30183     renderIntent: "select",
30184
30185     /**
30186      * Property: handlers
30187      * {Object} Object with references to multiple <OpenLayers.Handler>
30188      *     instances.
30189      */
30190     handlers: null,
30191
30192     /**
30193      * Constructor: OpenLayers.Control.SelectFeature
30194      * Create a new control for selecting features.
30195      *
30196      * Parameters:
30197      * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The
30198      *     layer(s) this control will select features from.
30199      * options - {Object} 
30200      */
30201     initialize: function(layers, options) {
30202         OpenLayers.Control.prototype.initialize.apply(this, [options]);
30203         
30204         if(this.scope === null) {
30205             this.scope = this;
30206         }
30207         this.initLayer(layers);
30208         var callbacks = {
30209             click: this.clickFeature,
30210             clickout: this.clickoutFeature
30211         };
30212         if (this.hover) {
30213             callbacks.over = this.overFeature;
30214             callbacks.out = this.outFeature;
30215         }
30216              
30217         this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
30218         this.handlers = {
30219             feature: new OpenLayers.Handler.Feature(
30220                 this, this.layer, this.callbacks,
30221                 {geometryTypes: this.geometryTypes}
30222             )
30223         };
30224
30225         if (this.box) {
30226             this.handlers.box = new OpenLayers.Handler.Box(
30227                 this, {done: this.selectBox},
30228                 {boxDivClassName: "olHandlerBoxSelectFeature"}
30229             ); 
30230         }
30231     },
30232
30233     /**
30234      * Method: initLayer
30235      * Assign the layer property. If layers is an array, we need to use
30236      *     a RootContainer.
30237      *
30238      * Parameters:
30239      * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers.
30240      */
30241     initLayer: function(layers) {
30242         if(OpenLayers.Util.isArray(layers)) {
30243             this.layers = layers;
30244             this.layer = new OpenLayers.Layer.Vector.RootContainer(
30245                 this.id + "_container", {
30246                     layers: layers
30247                 }
30248             );
30249         } else {
30250             this.layer = layers;
30251         }
30252     },
30253     
30254     /**
30255      * Method: destroy
30256      */
30257     destroy: function() {
30258         if(this.active && this.layers) {
30259             this.map.removeLayer(this.layer);
30260         }
30261         OpenLayers.Control.prototype.destroy.apply(this, arguments);
30262         if(this.layers) {
30263             this.layer.destroy();
30264         }
30265     },
30266
30267     /**
30268      * Method: activate
30269      * Activates the control.
30270      * 
30271      * Returns:
30272      * {Boolean} The control was effectively activated.
30273      */
30274     activate: function () {
30275         if (!this.active) {
30276             if(this.layers) {
30277                 this.map.addLayer(this.layer);
30278             }
30279             this.handlers.feature.activate();
30280             if(this.box && this.handlers.box) {
30281                 this.handlers.box.activate();
30282             }
30283         }
30284         return OpenLayers.Control.prototype.activate.apply(
30285             this, arguments
30286         );
30287     },
30288
30289     /**
30290      * Method: deactivate
30291      * Deactivates the control.
30292      * 
30293      * Returns:
30294      * {Boolean} The control was effectively deactivated.
30295      */
30296     deactivate: function () {
30297         if (this.active) {
30298             this.handlers.feature.deactivate();
30299             if(this.handlers.box) {
30300                 this.handlers.box.deactivate();
30301             }
30302             if(this.layers) {
30303                 this.map.removeLayer(this.layer);
30304             }
30305         }
30306         return OpenLayers.Control.prototype.deactivate.apply(
30307             this, arguments
30308         );
30309     },
30310
30311     /**
30312      * Method: unselectAll
30313      * Unselect all selected features.  To unselect all except for a single
30314      *     feature, set the options.except property to the feature.
30315      *
30316      * Parameters:
30317      * options - {Object} Optional configuration object.
30318      */
30319     unselectAll: function(options) {
30320         // we'll want an option to supress notification here
30321         var layers = this.layers || [this.layer],
30322             layer, feature, l, numExcept;
30323         for(l=0; l<layers.length; ++l) {
30324             layer = layers[l];
30325             numExcept = 0;
30326             //layer.selectedFeatures is null when layer is destroyed and 
30327             //one of it's preremovelayer listener calls setLayer 
30328             //with another layer on this control
30329             if(layer.selectedFeatures != null) {
30330                 while(layer.selectedFeatures.length > numExcept) {
30331                     feature = layer.selectedFeatures[numExcept];
30332                     if(!options || options.except != feature) {
30333                         this.unselect(feature);
30334                     } else {
30335                         ++numExcept;
30336                     }
30337                 }
30338             }
30339         }
30340     },
30341
30342     /**
30343      * Method: clickFeature
30344      * Called on click in a feature
30345      * Only responds if this.hover is false.
30346      *
30347      * Parameters:
30348      * feature - {<OpenLayers.Feature.Vector>} 
30349      */
30350     clickFeature: function(feature) {
30351         if(!this.hover) {
30352             var selected = (OpenLayers.Util.indexOf(
30353                 feature.layer.selectedFeatures, feature) > -1);
30354             if(selected) {
30355                 if(this.toggleSelect()) {
30356                     this.unselect(feature);
30357                 } else if(!this.multipleSelect()) {
30358                     this.unselectAll({except: feature});
30359                 }
30360             } else {
30361                 if(!this.multipleSelect()) {
30362                     this.unselectAll({except: feature});
30363                 }
30364                 this.select(feature);
30365             }
30366         }
30367     },
30368
30369     /**
30370      * Method: multipleSelect
30371      * Allow for multiple selected features based on <multiple> property and
30372      *     <multipleKey> event modifier.
30373      *
30374      * Returns:
30375      * {Boolean} Allow for multiple selected features.
30376      */
30377     multipleSelect: function() {
30378         return this.multiple || (this.handlers.feature.evt &&
30379                                  this.handlers.feature.evt[this.multipleKey]);
30380     },
30381     
30382     /**
30383      * Method: toggleSelect
30384      * Event should toggle the selected state of a feature based on <toggle>
30385      *     property and <toggleKey> event modifier.
30386      *
30387      * Returns:
30388      * {Boolean} Toggle the selected state of a feature.
30389      */
30390     toggleSelect: function() {
30391         return this.toggle || (this.handlers.feature.evt &&
30392                                this.handlers.feature.evt[this.toggleKey]);
30393     },
30394
30395     /**
30396      * Method: clickoutFeature
30397      * Called on click outside a previously clicked (selected) feature.
30398      * Only responds if this.hover is false.
30399      *
30400      * Parameters:
30401      * feature - {<OpenLayers.Vector.Feature>} 
30402      */
30403     clickoutFeature: function(feature) {
30404         if(!this.hover && this.clickout) {
30405             this.unselectAll();
30406         }
30407     },
30408
30409     /**
30410      * Method: overFeature
30411      * Called on over a feature.
30412      * Only responds if this.hover is true.
30413      *
30414      * Parameters:
30415      * feature - {<OpenLayers.Feature.Vector>} 
30416      */
30417     overFeature: function(feature) {
30418         var layer = feature.layer;
30419         if(this.hover) {
30420             if(this.highlightOnly) {
30421                 this.highlight(feature);
30422             } else if(OpenLayers.Util.indexOf(
30423                 layer.selectedFeatures, feature) == -1) {
30424                 this.select(feature);
30425             }
30426         }
30427     },
30428
30429     /**
30430      * Method: outFeature
30431      * Called on out of a selected feature.
30432      * Only responds if this.hover is true.
30433      *
30434      * Parameters:
30435      * feature - {<OpenLayers.Feature.Vector>} 
30436      */
30437     outFeature: function(feature) {
30438         if(this.hover) {
30439             if(this.highlightOnly) {
30440                 // we do nothing if we're not the last highlighter of the
30441                 // feature
30442                 if(feature._lastHighlighter == this.id) {
30443                     // if another select control had highlighted the feature before
30444                     // we did it ourself then we use that control to highlight the
30445                     // feature as it was before we highlighted it, else we just
30446                     // unhighlight it
30447                     if(feature._prevHighlighter &&
30448                        feature._prevHighlighter != this.id) {
30449                         delete feature._lastHighlighter;
30450                         var control = this.map.getControl(
30451                             feature._prevHighlighter);
30452                         if(control) {
30453                             control.highlight(feature);
30454                         }
30455                     } else {
30456                         this.unhighlight(feature);
30457                     }
30458                 }
30459             } else {
30460                 this.unselect(feature);
30461             }
30462         }
30463     },
30464
30465     /**
30466      * Method: highlight
30467      * Redraw feature with the select style.
30468      *
30469      * Parameters:
30470      * feature - {<OpenLayers.Feature.Vector>} 
30471      */
30472     highlight: function(feature) {
30473         var layer = feature.layer;
30474         var cont = this.events.triggerEvent("beforefeaturehighlighted", {
30475             feature : feature
30476         });
30477         if(cont !== false) {
30478             feature._prevHighlighter = feature._lastHighlighter;
30479             feature._lastHighlighter = this.id;
30480             var style = this.selectStyle || this.renderIntent;
30481             layer.drawFeature(feature, style);
30482             this.events.triggerEvent("featurehighlighted", {feature : feature});
30483         }
30484     },
30485
30486     /**
30487      * Method: unhighlight
30488      * Redraw feature with the "default" style
30489      *
30490      * Parameters:
30491      * feature - {<OpenLayers.Feature.Vector>} 
30492      */
30493     unhighlight: function(feature) {
30494         var layer = feature.layer;
30495         // three cases:
30496         // 1. there's no other highlighter, in that case _prev is undefined,
30497         //    and we just need to undef _last
30498         // 2. another control highlighted the feature after we did it, in
30499         //    that case _last references this other control, and we just
30500         //    need to undef _prev
30501         // 3. another control highlighted the feature before we did it, in
30502         //    that case _prev references this other control, and we need to
30503         //    set _last to _prev and undef _prev
30504         if(feature._prevHighlighter == undefined) {
30505             delete feature._lastHighlighter;
30506         } else if(feature._prevHighlighter == this.id) {
30507             delete feature._prevHighlighter;
30508         } else {
30509             feature._lastHighlighter = feature._prevHighlighter;
30510             delete feature._prevHighlighter;
30511         }
30512         layer.drawFeature(feature, feature.style || feature.layer.style ||
30513             "default");
30514         this.events.triggerEvent("featureunhighlighted", {feature : feature});
30515     },
30516     
30517     /**
30518      * Method: select
30519      * Add feature to the layer's selectedFeature array, render the feature as
30520      * selected, and call the onSelect function.
30521      * 
30522      * Parameters:
30523      * feature - {<OpenLayers.Feature.Vector>} 
30524      */
30525     select: function(feature) {
30526         var cont = this.onBeforeSelect.call(this.scope, feature);
30527         var layer = feature.layer;
30528         if(cont !== false) {
30529             cont = layer.events.triggerEvent("beforefeatureselected", {
30530                 feature: feature
30531             });
30532             if(cont !== false) {
30533                 layer.selectedFeatures.push(feature);
30534                 this.highlight(feature);
30535                 // if the feature handler isn't involved in the feature
30536                 // selection (because the box handler is used or the
30537                 // feature is selected programatically) we fake the
30538                 // feature handler to allow unselecting on click
30539                 if(!this.handlers.feature.lastFeature) {
30540                     this.handlers.feature.lastFeature = layer.selectedFeatures[0];
30541                 }
30542                 layer.events.triggerEvent("featureselected", {feature: feature});
30543                 this.onSelect.call(this.scope, feature);
30544             }
30545         }
30546     },
30547
30548     /**
30549      * Method: unselect
30550      * Remove feature from the layer's selectedFeature array, render the feature as
30551      * normal, and call the onUnselect function.
30552      *
30553      * Parameters:
30554      * feature - {<OpenLayers.Feature.Vector>}
30555      */
30556     unselect: function(feature) {
30557         var layer = feature.layer;
30558         // Store feature style for restoration later
30559         this.unhighlight(feature);
30560         OpenLayers.Util.removeItem(layer.selectedFeatures, feature);
30561         layer.events.triggerEvent("featureunselected", {feature: feature});
30562         this.onUnselect.call(this.scope, feature);
30563     },
30564     
30565     /**
30566      * Method: selectBox
30567      * Callback from the handlers.box set up when <box> selection is true
30568      *     on.
30569      *
30570      * Parameters:
30571      * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> }  
30572      */
30573     selectBox: function(position) {
30574         if (position instanceof OpenLayers.Bounds) {
30575             var minXY = this.map.getLonLatFromPixel({
30576                 x: position.left,
30577                 y: position.bottom
30578             });
30579             var maxXY = this.map.getLonLatFromPixel({
30580                 x: position.right,
30581                 y: position.top
30582             });
30583             var bounds = new OpenLayers.Bounds(
30584                 minXY.lon, minXY.lat, maxXY.lon, maxXY.lat
30585             );
30586             
30587             // if multiple is false, first deselect currently selected features
30588             if (!this.multipleSelect()) {
30589                 this.unselectAll();
30590             }
30591             
30592             // because we're using a box, we consider we want multiple selection
30593             var prevMultiple = this.multiple;
30594             this.multiple = true;
30595             var layers = this.layers || [this.layer];
30596             this.events.triggerEvent("boxselectionstart", {layers: layers}); 
30597             var layer;
30598             for(var l=0; l<layers.length; ++l) {
30599                 layer = layers[l];
30600                 for(var i=0, len = layer.features.length; i<len; ++i) {
30601                     var feature = layer.features[i];
30602                     // check if the feature is displayed
30603                     if (!feature.getVisibility()) {
30604                         continue;
30605                     }
30606
30607                     if (this.geometryTypes == null || OpenLayers.Util.indexOf(
30608                             this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {
30609                         if (bounds.toGeometry().intersects(feature.geometry)) {
30610                             if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {
30611                                 this.select(feature);
30612                             }
30613                         }
30614                     }
30615                 }
30616             }
30617             this.multiple = prevMultiple;
30618             this.events.triggerEvent("boxselectionend", {layers: layers}); 
30619         }
30620     },
30621
30622     /** 
30623      * Method: setMap
30624      * Set the map property for the control. 
30625      * 
30626      * Parameters:
30627      * map - {<OpenLayers.Map>} 
30628      */
30629     setMap: function(map) {
30630         this.handlers.feature.setMap(map);
30631         if (this.box) {
30632             this.handlers.box.setMap(map);
30633         }
30634         OpenLayers.Control.prototype.setMap.apply(this, arguments);
30635     },
30636     
30637     /**
30638      * APIMethod: setLayer
30639      * Attach a new layer to the control, overriding any existing layers.
30640      *
30641      * Parameters:
30642      * layers - Array of {<OpenLayers.Layer.Vector>} or a single
30643      *     {<OpenLayers.Layer.Vector>}
30644      */
30645     setLayer: function(layers) {
30646         var isActive = this.active;
30647         this.unselectAll();
30648         this.deactivate();
30649         if(this.layers) {
30650             this.layer.destroy();
30651             this.layers = null;
30652         }
30653         this.initLayer(layers);
30654         this.handlers.feature.layer = this.layer;
30655         if (isActive) {
30656             this.activate();
30657         }
30658     },
30659     
30660     CLASS_NAME: "OpenLayers.Control.SelectFeature"
30661 });
30662 /* ======================================================================
30663     OpenLayers/Control/Attribution.js
30664    ====================================================================== */
30665
30666 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
30667  * full list of contributors). Published under the 2-clause BSD license.
30668  * See license.txt in the OpenLayers distribution or repository for the
30669  * full text of the license. */
30670
30671 /**
30672  * @requires OpenLayers/Control.js
30673  */
30674
30675 /**
30676  * Class: OpenLayers.Control.Attribution
30677  * The attribution control adds attribution from layers to the map display. 
30678  * It uses 'attribution' property of each layer.
30679  *
30680  * Inherits from:
30681  *  - <OpenLayers.Control>
30682  */
30683 OpenLayers.Control.Attribution = 
30684   OpenLayers.Class(OpenLayers.Control, {
30685     
30686     /**
30687      * APIProperty: separator
30688      * {String} String used to separate layers.
30689      */
30690     separator: ", ",
30691     
30692     /**
30693      * APIProperty: template
30694      * {String} Template for the attribution. This has to include the substring
30695      *     "${layers}", which will be replaced by the layer specific
30696      *     attributions, separated by <separator>. The default is "${layers}".
30697      */
30698     template: "${layers}",
30699     
30700     /**
30701      * Constructor: OpenLayers.Control.Attribution 
30702      * 
30703      * Parameters:
30704      * options - {Object} Options for control.
30705      */
30706
30707     /** 
30708      * Method: destroy
30709      * Destroy control.
30710      */
30711     destroy: function() {
30712         this.map.events.un({
30713             "removelayer": this.updateAttribution,
30714             "addlayer": this.updateAttribution,
30715             "changelayer": this.updateAttribution,
30716             "changebaselayer": this.updateAttribution,
30717             scope: this
30718         });
30719         
30720         OpenLayers.Control.prototype.destroy.apply(this, arguments);
30721     },    
30722     
30723     /**
30724      * Method: draw
30725      * Initialize control.
30726      * 
30727      * Returns: 
30728      * {DOMElement} A reference to the DIV DOMElement containing the control
30729      */    
30730     draw: function() {
30731         OpenLayers.Control.prototype.draw.apply(this, arguments);
30732         
30733         this.map.events.on({
30734             'changebaselayer': this.updateAttribution,
30735             'changelayer': this.updateAttribution,
30736             'addlayer': this.updateAttribution,
30737             'removelayer': this.updateAttribution,
30738             scope: this
30739         });
30740         this.updateAttribution();
30741         
30742         return this.div;    
30743     },
30744
30745     /**
30746      * Method: updateAttribution
30747      * Update attribution string.
30748      */
30749     updateAttribution: function() {
30750         var attributions = [];
30751         if (this.map && this.map.layers) {
30752             for(var i=0, len=this.map.layers.length; i<len; i++) {
30753                 var layer = this.map.layers[i];
30754                 if (layer.attribution && layer.getVisibility()) {
30755                     // add attribution only if attribution text is unique
30756                     if (OpenLayers.Util.indexOf(
30757                                     attributions, layer.attribution) === -1) {
30758                         attributions.push( layer.attribution );
30759                     }
30760                 }
30761             } 
30762             this.div.innerHTML = OpenLayers.String.format(this.template, {
30763                 layers: attributions.join(this.separator)
30764             });
30765         }
30766     },
30767
30768     CLASS_NAME: "OpenLayers.Control.Attribution"
30769 });
30770 /* ======================================================================
30771     OpenLayers/Handler/Drag.js
30772    ====================================================================== */
30773
30774 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
30775  * full list of contributors). Published under the 2-clause BSD license.
30776  * See license.txt in the OpenLayers distribution or repository for the
30777  * full text of the license. */
30778
30779 /**
30780  * @requires OpenLayers/Handler.js
30781  */
30782
30783 /**
30784  * Class: OpenLayers.Handler.Drag
30785  * The drag handler is used to deal with sequences of browser events related
30786  *     to dragging.  The handler is used by controls that want to know when
30787  *     a drag sequence begins, when a drag is happening, and when it has
30788  *     finished.
30789  *
30790  * Controls that use the drag handler typically construct it with callbacks
30791  *     for 'down', 'move', and 'done'.  Callbacks for these keys are called
30792  *     when the drag begins, with each move, and when the drag is done.  In
30793  *     addition, controls can have callbacks keyed to 'up' and 'out' if they
30794  *     care to differentiate between the types of events that correspond with
30795  *     the end of a drag sequence.  If no drag actually occurs (no mouse move)
30796  *     the 'down' and 'up' callbacks will be called, but not the 'done'
30797  *     callback.
30798  *
30799  * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor.
30800  *
30801  * Inherits from:
30802  *  - <OpenLayers.Handler>
30803  */
30804 OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {
30805   
30806     /** 
30807      * Property: started
30808      * {Boolean} When a mousedown or touchstart event is received, we want to
30809      * record it, but not set 'dragging' until the mouse moves after starting.
30810      */
30811     started: false,
30812
30813     /**
30814      * Property: stopDown
30815      * {Boolean} Stop propagation of mousedown events from getting to listeners
30816      *     on the same element.  Default is true.
30817      */
30818     stopDown: true,
30819
30820     /** 
30821      * Property: dragging 
30822      * {Boolean} 
30823      */
30824     dragging: false,
30825
30826     /** 
30827      * Property: last
30828      * {<OpenLayers.Pixel>} The last pixel location of the drag.
30829      */
30830     last: null,
30831
30832     /** 
30833      * Property: start
30834      * {<OpenLayers.Pixel>} The first pixel location of the drag.
30835      */
30836     start: null,
30837
30838     /**
30839      * Property: lastMoveEvt
30840      * {Object} The last mousemove event that occurred. Used to
30841      *     position the map correctly when our "delay drag"
30842      *     timeout expired.
30843      */
30844     lastMoveEvt: null,
30845
30846     /**
30847      * Property: oldOnselectstart
30848      * {Function}
30849      */
30850     oldOnselectstart: null,
30851     
30852     /**
30853      * Property: interval
30854      * {Integer} In order to increase performance, an interval (in 
30855      *     milliseconds) can be set to reduce the number of drag events 
30856      *     called. If set, a new drag event will not be set until the 
30857      *     interval has passed. 
30858      *     Defaults to 0, meaning no interval. 
30859      */
30860     interval: 0,
30861     
30862     /**
30863      * Property: timeoutId
30864      * {String} The id of the timeout used for the mousedown interval.
30865      *     This is "private", and should be left alone.
30866      */
30867     timeoutId: null,
30868     
30869     /**
30870      * APIProperty: documentDrag
30871      * {Boolean} If set to true, the handler will also handle mouse moves when
30872      *     the cursor has moved out of the map viewport. Default is false.
30873      */
30874     documentDrag: false,
30875     
30876     /**
30877      * Property: documentEvents
30878      * {Boolean} Are we currently observing document events?
30879      */
30880     documentEvents: null,
30881
30882     /**
30883      * Constructor: OpenLayers.Handler.Drag
30884      * Returns OpenLayers.Handler.Drag
30885      * 
30886      * Parameters:
30887      * control - {<OpenLayers.Control>} The control that is making use of
30888      *     this handler.  If a handler is being used without a control, the
30889      *     handlers setMap method must be overridden to deal properly with
30890      *     the map.
30891      * callbacks - {Object} An object containing a single function to be
30892      *     called when the drag operation is finished. The callback should
30893      *     expect to recieve a single argument, the pixel location of the event.
30894      *     Callbacks for 'move' and 'done' are supported. You can also speficy
30895      *     callbacks for 'down', 'up', and 'out' to respond to those events.
30896      * options - {Object} 
30897      */
30898     initialize: function(control, callbacks, options) {
30899         OpenLayers.Handler.prototype.initialize.apply(this, arguments);
30900         
30901         if (this.documentDrag === true) {
30902             var me = this;
30903             this._docMove = function(evt) {
30904                 me.mousemove({
30905                     xy: {x: evt.clientX, y: evt.clientY},
30906                     element: document
30907                 });
30908             };
30909             this._docUp = function(evt) {
30910                 me.mouseup({xy: {x: evt.clientX, y: evt.clientY}});
30911             };
30912         }
30913     },
30914
30915     
30916     /**
30917      * Method: dragstart
30918      * This private method is factorized from mousedown and touchstart methods
30919      *
30920      * Parameters:
30921      * evt - {Event} The event
30922      *
30923      * Returns:
30924      * {Boolean} Let the event propagate.
30925      */
30926     dragstart: function (evt) {
30927         var propagate = true;
30928         this.dragging = false;
30929         if (this.checkModifiers(evt) &&
30930                (OpenLayers.Event.isLeftClick(evt) ||
30931                 OpenLayers.Event.isSingleTouch(evt))) {
30932             this.started = true;
30933             this.start = evt.xy;
30934             this.last = evt.xy;
30935             OpenLayers.Element.addClass(
30936                 this.map.viewPortDiv, "olDragDown"
30937             );
30938             this.down(evt);
30939             this.callback("down", [evt.xy]);
30940
30941             // prevent document dragging
30942             OpenLayers.Event.preventDefault(evt);
30943
30944             if(!this.oldOnselectstart) {
30945                 this.oldOnselectstart = document.onselectstart ?
30946                     document.onselectstart : OpenLayers.Function.True;
30947             }
30948             document.onselectstart = OpenLayers.Function.False;
30949
30950             propagate = !this.stopDown;
30951         } else {
30952             this.started = false;
30953             this.start = null;
30954             this.last = null;
30955         }
30956         return propagate;
30957     },
30958
30959     /**
30960      * Method: dragmove
30961      * This private method is factorized from mousemove and touchmove methods
30962      *
30963      * Parameters:
30964      * evt - {Event} The event
30965      *
30966      * Returns:
30967      * {Boolean} Let the event propagate.
30968      */
30969     dragmove: function (evt) {
30970         this.lastMoveEvt = evt;
30971         if (this.started && !this.timeoutId && (evt.xy.x != this.last.x ||
30972                                                 evt.xy.y != this.last.y)) {
30973             if(this.documentDrag === true && this.documentEvents) {
30974                 if(evt.element === document) {
30975                     this.adjustXY(evt);
30976                     // do setEvent manually because the documentEvents are not
30977                     // registered with the map
30978                     this.setEvent(evt);
30979                 } else {
30980                     this.removeDocumentEvents();
30981                 }
30982             }
30983             if (this.interval > 0) {
30984                 this.timeoutId = setTimeout(
30985                     OpenLayers.Function.bind(this.removeTimeout, this),
30986                     this.interval);
30987             }
30988             this.dragging = true;
30989
30990             this.move(evt);
30991             this.callback("move", [evt.xy]);
30992             if(!this.oldOnselectstart) {
30993                 this.oldOnselectstart = document.onselectstart;
30994                 document.onselectstart = OpenLayers.Function.False;
30995             }
30996             this.last = evt.xy;
30997         }
30998         return true;
30999     },
31000
31001     /**
31002      * Method: dragend
31003      * This private method is factorized from mouseup and touchend methods
31004      *
31005      * Parameters:
31006      * evt - {Event} The event
31007      *
31008      * Returns:
31009      * {Boolean} Let the event propagate.
31010      */
31011     dragend: function (evt) {
31012         if (this.started) {
31013             if(this.documentDrag === true && this.documentEvents) {
31014                 this.adjustXY(evt);
31015                 this.removeDocumentEvents();
31016             }
31017             var dragged = (this.start != this.last);
31018             this.started = false;
31019             this.dragging = false;
31020             OpenLayers.Element.removeClass(
31021                 this.map.viewPortDiv, "olDragDown"
31022             );
31023             this.up(evt);
31024             this.callback("up", [evt.xy]);
31025             if(dragged) {
31026                 this.callback("done", [evt.xy]);
31027             }
31028             document.onselectstart = this.oldOnselectstart;
31029         }
31030         return true;
31031     },
31032
31033     /**
31034      * The four methods below (down, move, up, and out) are used by subclasses
31035      *     to do their own processing related to these mouse events.
31036      */
31037
31038     /**
31039      * Method: down
31040      * This method is called during the handling of the mouse down event.
31041      *     Subclasses can do their own processing here.
31042      *
31043      * Parameters:
31044      * evt - {Event} The mouse down event
31045      */
31046     down: function(evt) {
31047     },
31048
31049     /**
31050      * Method: move
31051      * This method is called during the handling of the mouse move event.
31052      *     Subclasses can do their own processing here.
31053      *
31054      * Parameters:
31055      * evt - {Event} The mouse move event
31056      *
31057      */
31058     move: function(evt) {
31059     },
31060
31061     /**
31062      * Method: up
31063      * This method is called during the handling of the mouse up event.
31064      *     Subclasses can do their own processing here.
31065      *
31066      * Parameters:
31067      * evt - {Event} The mouse up event
31068      */
31069     up: function(evt) {
31070     },
31071
31072     /**
31073      * Method: out
31074      * This method is called during the handling of the mouse out event.
31075      *     Subclasses can do their own processing here.
31076      *
31077      * Parameters:
31078      * evt - {Event} The mouse out event
31079      */
31080     out: function(evt) {
31081     },
31082
31083     /**
31084      * The methods below are part of the magic of event handling.  Because
31085      *     they are named like browser events, they are registered as listeners
31086      *     for the events they represent.
31087      */
31088
31089     /**
31090      * Method: mousedown
31091      * Handle mousedown events
31092      *
31093      * Parameters:
31094      * evt - {Event}
31095      *
31096      * Returns:
31097      * {Boolean} Let the event propagate.
31098      */
31099     mousedown: function(evt) {
31100         return this.dragstart(evt);
31101     },
31102
31103     /**
31104      * Method: touchstart
31105      * Handle touchstart events
31106      *
31107      * Parameters:
31108      * evt - {Event}
31109      *
31110      * Returns:
31111      * {Boolean} Let the event propagate.
31112      */
31113     touchstart: function(evt) {
31114         this.startTouch();
31115         return this.dragstart(evt);
31116     },
31117
31118     /**
31119      * Method: mousemove
31120      * Handle mousemove events
31121      *
31122      * Parameters:
31123      * evt - {Event}
31124      *
31125      * Returns:
31126      * {Boolean} Let the event propagate.
31127      */
31128     mousemove: function(evt) {
31129         return this.dragmove(evt);
31130     },
31131
31132     /**
31133      * Method: touchmove
31134      * Handle touchmove events
31135      *
31136      * Parameters:
31137      * evt - {Event}
31138      *
31139      * Returns:
31140      * {Boolean} Let the event propagate.
31141      */
31142     touchmove: function(evt) {
31143         return this.dragmove(evt);
31144     },
31145
31146     /**
31147      * Method: removeTimeout
31148      * Private. Called by mousemove() to remove the drag timeout.
31149      */
31150     removeTimeout: function() {
31151         this.timeoutId = null;
31152         // if timeout expires while we're still dragging (mouseup
31153         // hasn't occurred) then call mousemove to move to the
31154         // correct position
31155         if(this.dragging) {
31156             this.mousemove(this.lastMoveEvt);
31157         }
31158     },
31159
31160     /**
31161      * Method: mouseup
31162      * Handle mouseup events
31163      *
31164      * Parameters:
31165      * evt - {Event}
31166      *
31167      * Returns:
31168      * {Boolean} Let the event propagate.
31169      */
31170     mouseup: function(evt) {
31171         return this.dragend(evt);
31172     },
31173
31174     /**
31175      * Method: touchend
31176      * Handle touchend events
31177      *
31178      * Parameters:
31179      * evt - {Event}
31180      *
31181      * Returns:
31182      * {Boolean} Let the event propagate.
31183      */
31184     touchend: function(evt) {
31185         // override evt.xy with last position since touchend does not have
31186         // any touch position
31187         evt.xy = this.last;
31188         return this.dragend(evt);
31189     },
31190
31191     /**
31192      * Method: mouseout
31193      * Handle mouseout events
31194      *
31195      * Parameters:
31196      * evt - {Event}
31197      *
31198      * Returns:
31199      * {Boolean} Let the event propagate.
31200      */
31201     mouseout: function (evt) {
31202         if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {
31203             if(this.documentDrag === true) {
31204                 this.addDocumentEvents();
31205             } else {
31206                 var dragged = (this.start != this.last);
31207                 this.started = false; 
31208                 this.dragging = false;
31209                 OpenLayers.Element.removeClass(
31210                     this.map.viewPortDiv, "olDragDown"
31211                 );
31212                 this.out(evt);
31213                 this.callback("out", []);
31214                 if(dragged) {
31215                     this.callback("done", [evt.xy]);
31216                 }
31217                 if(document.onselectstart) {
31218                     document.onselectstart = this.oldOnselectstart;
31219                 }
31220             }
31221         }
31222         return true;
31223     },
31224
31225     /**
31226      * Method: click
31227      * The drag handler captures the click event.  If something else registers
31228      *     for clicks on the same element, its listener will not be called 
31229      *     after a drag.
31230      * 
31231      * Parameters: 
31232      * evt - {Event} 
31233      * 
31234      * Returns:
31235      * {Boolean} Let the event propagate.
31236      */
31237     click: function (evt) {
31238         // let the click event propagate only if the mouse moved
31239         return (this.start == this.last);
31240     },
31241
31242     /**
31243      * Method: activate
31244      * Activate the handler.
31245      * 
31246      * Returns:
31247      * {Boolean} The handler was successfully activated.
31248      */
31249     activate: function() {
31250         var activated = false;
31251         if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
31252             this.dragging = false;
31253             activated = true;
31254         }
31255         return activated;
31256     },
31257
31258     /**
31259      * Method: deactivate 
31260      * Deactivate the handler.
31261      * 
31262      * Returns:
31263      * {Boolean} The handler was successfully deactivated.
31264      */
31265     deactivate: function() {
31266         var deactivated = false;
31267         if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
31268             this.started = false;
31269             this.dragging = false;
31270             this.start = null;
31271             this.last = null;
31272             deactivated = true;
31273             OpenLayers.Element.removeClass(
31274                 this.map.viewPortDiv, "olDragDown"
31275             );
31276         }
31277         return deactivated;
31278     },
31279     
31280     /**
31281      * Method: adjustXY
31282      * Converts event coordinates that are relative to the document body to
31283      * ones that are relative to the map viewport. The latter is the default in
31284      * OpenLayers.
31285      * 
31286      * Parameters:
31287      * evt - {Object}
31288      */
31289     adjustXY: function(evt) {
31290         var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv);
31291         evt.xy.x -= pos[0];
31292         evt.xy.y -= pos[1];
31293     },
31294     
31295     /**
31296      * Method: addDocumentEvents
31297      * Start observing document events when documentDrag is true and the mouse
31298      * cursor leaves the map viewport while dragging.
31299      */
31300     addDocumentEvents: function() {
31301         OpenLayers.Element.addClass(document.body, "olDragDown");
31302         this.documentEvents = true;
31303         OpenLayers.Event.observe(document, "mousemove", this._docMove);
31304         OpenLayers.Event.observe(document, "mouseup", this._docUp);
31305     },
31306     
31307     /**
31308      * Method: removeDocumentEvents
31309      * Stops observing document events when documentDrag is true and the mouse
31310      * cursor re-enters the map viewport while dragging.
31311      */
31312     removeDocumentEvents: function() {
31313         OpenLayers.Element.removeClass(document.body, "olDragDown");
31314         this.documentEvents = false;
31315         OpenLayers.Event.stopObserving(document, "mousemove", this._docMove);
31316         OpenLayers.Event.stopObserving(document, "mouseup", this._docUp);
31317     },
31318
31319     CLASS_NAME: "OpenLayers.Handler.Drag"
31320 });
31321 /* ======================================================================
31322     OpenLayers/Handler/Box.js
31323    ====================================================================== */
31324
31325 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
31326  * full list of contributors). Published under the 2-clause BSD license.
31327  * See license.txt in the OpenLayers distribution or repository for the
31328  * full text of the license. */
31329
31330 /**
31331  * @requires OpenLayers/Handler.js
31332  * @requires OpenLayers/Handler/Drag.js
31333  */
31334
31335 /**
31336  * Class: OpenLayers.Handler.Box
31337  * Handler for dragging a rectangle across the map.  Box is displayed 
31338  * on mouse down, moves on mouse move, and is finished on mouse up.
31339  *
31340  * Inherits from:
31341  *  - <OpenLayers.Handler> 
31342  */
31343 OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {
31344
31345     /** 
31346      * Property: dragHandler 
31347      * {<OpenLayers.Handler.Drag>} 
31348      */
31349     dragHandler: null,
31350
31351     /**
31352      * APIProperty: boxDivClassName
31353      * {String} The CSS class to use for drawing the box. Default is
31354      *     olHandlerBoxZoomBox
31355      */
31356     boxDivClassName: 'olHandlerBoxZoomBox',
31357     
31358     /**
31359      * Property: boxOffsets
31360      * {Object} Caches box offsets from css. This is used by the getBoxOffsets
31361      * method.
31362      */
31363     boxOffsets: null,
31364
31365     /**
31366      * Constructor: OpenLayers.Handler.Box
31367      *
31368      * Parameters:
31369      * control - {<OpenLayers.Control>} 
31370      * callbacks - {Object} An object with a properties whose values are
31371      *     functions.  Various callbacks described below.
31372      * options - {Object} 
31373      *
31374      * Named callbacks:
31375      * start - Called when the box drag operation starts.
31376      * done - Called when the box drag operation is finished.
31377      *     The callback should expect to receive a single argument, the box 
31378      *     bounds or a pixel. If the box dragging didn't span more than a 5 
31379      *     pixel distance, a pixel will be returned instead of a bounds object.
31380      */
31381     initialize: function(control, callbacks, options) {
31382         OpenLayers.Handler.prototype.initialize.apply(this, arguments);
31383         this.dragHandler = new OpenLayers.Handler.Drag(
31384             this, 
31385             {
31386                 down: this.startBox, 
31387                 move: this.moveBox, 
31388                 out: this.removeBox,
31389                 up: this.endBox
31390             }, 
31391             {keyMask: this.keyMask}
31392         );
31393     },
31394
31395     /**
31396      * Method: destroy
31397      */
31398     destroy: function() {
31399         OpenLayers.Handler.prototype.destroy.apply(this, arguments);
31400         if (this.dragHandler) {
31401             this.dragHandler.destroy();
31402             this.dragHandler = null;
31403         }            
31404     },
31405
31406     /**
31407      * Method: setMap
31408      */
31409     setMap: function (map) {
31410         OpenLayers.Handler.prototype.setMap.apply(this, arguments);
31411         if (this.dragHandler) {
31412             this.dragHandler.setMap(map);
31413         }
31414     },
31415
31416     /**
31417     * Method: startBox
31418     *
31419     * Parameters:
31420     * xy - {<OpenLayers.Pixel>}
31421     */
31422     startBox: function (xy) {
31423         this.callback("start", []);
31424         this.zoomBox = OpenLayers.Util.createDiv('zoomBox', {
31425             x: -9999, y: -9999
31426         });
31427         this.zoomBox.className = this.boxDivClassName;                                         
31428         this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
31429         
31430         this.map.viewPortDiv.appendChild(this.zoomBox);
31431         
31432         OpenLayers.Element.addClass(
31433             this.map.viewPortDiv, "olDrawBox"
31434         );
31435     },
31436
31437     /**
31438     * Method: moveBox
31439     */
31440     moveBox: function (xy) {
31441         var startX = this.dragHandler.start.x;
31442         var startY = this.dragHandler.start.y;
31443         var deltaX = Math.abs(startX - xy.x);
31444         var deltaY = Math.abs(startY - xy.y);
31445
31446         var offset = this.getBoxOffsets();
31447         this.zoomBox.style.width = (deltaX + offset.width + 1) + "px";
31448         this.zoomBox.style.height = (deltaY + offset.height + 1) + "px";
31449         this.zoomBox.style.left = (xy.x < startX ?
31450             startX - deltaX - offset.left : startX - offset.left) + "px";
31451         this.zoomBox.style.top = (xy.y < startY ?
31452             startY - deltaY - offset.top : startY - offset.top) + "px";
31453     },
31454
31455     /**
31456     * Method: endBox
31457     */
31458     endBox: function(end) {
31459         var result;
31460         if (Math.abs(this.dragHandler.start.x - end.x) > 5 ||    
31461             Math.abs(this.dragHandler.start.y - end.y) > 5) {   
31462             var start = this.dragHandler.start;
31463             var top = Math.min(start.y, end.y);
31464             var bottom = Math.max(start.y, end.y);
31465             var left = Math.min(start.x, end.x);
31466             var right = Math.max(start.x, end.x);
31467             result = new OpenLayers.Bounds(left, bottom, right, top);
31468         } else {
31469             result = this.dragHandler.start.clone(); // i.e. OL.Pixel
31470         } 
31471         this.removeBox();
31472
31473         this.callback("done", [result]);
31474     },
31475
31476     /**
31477      * Method: removeBox
31478      * Remove the zoombox from the screen and nullify our reference to it.
31479      */
31480     removeBox: function() {
31481         this.map.viewPortDiv.removeChild(this.zoomBox);
31482         this.zoomBox = null;
31483         this.boxOffsets = null;
31484         OpenLayers.Element.removeClass(
31485             this.map.viewPortDiv, "olDrawBox"
31486         );
31487
31488     },
31489
31490     /**
31491      * Method: activate
31492      */
31493     activate: function () {
31494         if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
31495             this.dragHandler.activate();
31496             return true;
31497         } else {
31498             return false;
31499         }
31500     },
31501
31502     /**
31503      * Method: deactivate
31504      */
31505     deactivate: function () {
31506         if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
31507             if (this.dragHandler.deactivate()) {
31508                 if (this.zoomBox) {
31509                     this.removeBox();
31510                 }
31511             }
31512             return true;
31513         } else {
31514             return false;
31515         }
31516     },
31517     
31518     /**
31519      * Method: getBoxOffsets
31520      * Determines border offsets for a box, according to the box model.
31521      * 
31522      * Returns:
31523      * {Object} an object with the following offsets:
31524      *     - left
31525      *     - right
31526      *     - top
31527      *     - bottom
31528      *     - width
31529      *     - height
31530      */
31531     getBoxOffsets: function() {
31532         if (!this.boxOffsets) {
31533             // Determine the box model. If the testDiv's clientWidth is 3, then
31534             // the borders are outside and we are dealing with the w3c box
31535             // model. Otherwise, the browser uses the traditional box model and
31536             // the borders are inside the box bounds, leaving us with a
31537             // clientWidth of 1.
31538             var testDiv = document.createElement("div");
31539             //testDiv.style.visibility = "hidden";
31540             testDiv.style.position = "absolute";
31541             testDiv.style.border = "1px solid black";
31542             testDiv.style.width = "3px";
31543             document.body.appendChild(testDiv);
31544             var w3cBoxModel = testDiv.clientWidth == 3;
31545             document.body.removeChild(testDiv);
31546             
31547             var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
31548                 "border-left-width"));
31549             var right = parseInt(OpenLayers.Element.getStyle(
31550                 this.zoomBox, "border-right-width"));
31551             var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
31552                 "border-top-width"));
31553             var bottom = parseInt(OpenLayers.Element.getStyle(
31554                 this.zoomBox, "border-bottom-width"));
31555             this.boxOffsets = {
31556                 left: left,
31557                 right: right,
31558                 top: top,
31559                 bottom: bottom,
31560                 width: w3cBoxModel === false ? left + right : 0,
31561                 height: w3cBoxModel === false ? top + bottom : 0
31562             };
31563         }
31564         return this.boxOffsets;
31565     },
31566   
31567     CLASS_NAME: "OpenLayers.Handler.Box"
31568 });
31569 /* ======================================================================
31570     OpenLayers/Control/ZoomBox.js
31571    ====================================================================== */
31572
31573 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
31574  * full list of contributors). Published under the 2-clause BSD license.
31575  * See license.txt in the OpenLayers distribution or repository for the
31576  * full text of the license. */
31577
31578 /**
31579  * @requires OpenLayers/Control.js
31580  * @requires OpenLayers/Handler/Box.js
31581  */
31582
31583 /**
31584  * Class: OpenLayers.Control.ZoomBox
31585  * The ZoomBox control enables zooming directly to a given extent, by drawing 
31586  * a box on the map. The box is drawn by holding down shift, whilst dragging 
31587  * the mouse.
31588  *
31589  * Inherits from:
31590  *  - <OpenLayers.Control>
31591  */
31592 OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {
31593     /**
31594      * Property: type
31595      * {OpenLayers.Control.TYPE}
31596      */
31597     type: OpenLayers.Control.TYPE_TOOL,
31598
31599     /**
31600      * Property: out
31601      * {Boolean} Should the control be used for zooming out?
31602      */
31603     out: false,
31604
31605     /**
31606      * APIProperty: keyMask
31607      * {Integer} Zoom only occurs if the keyMask matches the combination of 
31608      *     keys down. Use bitwise operators and one or more of the
31609      *     <OpenLayers.Handler> constants to construct a keyMask. Leave null if 
31610      *     not used mask. Default is null.
31611      */
31612     keyMask: null,
31613
31614     /**
31615      * APIProperty: alwaysZoom
31616      * {Boolean} Always zoom in/out when box drawn, even if the zoom level does
31617      * not change.
31618      */
31619     alwaysZoom: false,
31620     
31621     /**
31622      * APIProperty: zoomOnClick
31623      * {Boolean} Should we zoom when no box was dragged, i.e. the user only
31624      * clicked? Default is true.
31625      */
31626     zoomOnClick: true,
31627
31628     /**
31629      * Method: draw
31630      */    
31631     draw: function() {
31632         this.handler = new OpenLayers.Handler.Box( this,
31633                             {done: this.zoomBox}, {keyMask: this.keyMask} );
31634     },
31635
31636     /**
31637      * Method: zoomBox
31638      *
31639      * Parameters:
31640      * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>}
31641      */
31642     zoomBox: function (position) {
31643         if (position instanceof OpenLayers.Bounds) {
31644             var bounds,
31645                 targetCenterPx = position.getCenterPixel();
31646             if (!this.out) {
31647                 var minXY = this.map.getLonLatFromPixel({
31648                     x: position.left,
31649                     y: position.bottom
31650                 });
31651                 var maxXY = this.map.getLonLatFromPixel({
31652                     x: position.right,
31653                     y: position.top
31654                 });
31655                 bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat,
31656                                                maxXY.lon, maxXY.lat);
31657             } else {
31658                 var pixWidth = position.right - position.left;
31659                 var pixHeight = position.bottom - position.top;
31660                 var zoomFactor = Math.min((this.map.size.h / pixHeight),
31661                     (this.map.size.w / pixWidth));
31662                 var extent = this.map.getExtent();
31663                 var center = this.map.getLonLatFromPixel(targetCenterPx);
31664                 var xmin = center.lon - (extent.getWidth()/2)*zoomFactor;
31665                 var xmax = center.lon + (extent.getWidth()/2)*zoomFactor;
31666                 var ymin = center.lat - (extent.getHeight()/2)*zoomFactor;
31667                 var ymax = center.lat + (extent.getHeight()/2)*zoomFactor;
31668                 bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax);
31669             }
31670             // always zoom in/out 
31671             var lastZoom = this.map.getZoom(),
31672                 size = this.map.getSize(),
31673                 centerPx = {x: size.w / 2, y: size.h / 2},
31674                 zoom = this.map.getZoomForExtent(bounds),
31675                 oldRes = this.map.getResolution(),
31676                 newRes = this.map.getResolutionForZoom(zoom);
31677             if (oldRes == newRes) {
31678                 this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx));
31679             } else {
31680               var zoomOriginPx = {
31681                     x: (oldRes * targetCenterPx.x - newRes * centerPx.x) /
31682                         (oldRes - newRes),
31683                     y: (oldRes * targetCenterPx.y - newRes * centerPx.y) /
31684                         (oldRes - newRes)
31685                 };
31686                 this.map.zoomTo(zoom, zoomOriginPx);
31687             }
31688             if (lastZoom == this.map.getZoom() && this.alwaysZoom == true){ 
31689                 this.map.zoomTo(lastZoom + (this.out ? -1 : 1)); 
31690             }
31691         } else if (this.zoomOnClick) { // it's a pixel
31692             if (!this.out) {
31693                 this.map.zoomTo(this.map.getZoom() + 1, position);
31694             } else {
31695                 this.map.zoomTo(this.map.getZoom() - 1, position);
31696             }
31697         }
31698     },
31699
31700     CLASS_NAME: "OpenLayers.Control.ZoomBox"
31701 });
31702 /* ======================================================================
31703     OpenLayers/Control/DragPan.js
31704    ====================================================================== */
31705
31706 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
31707  * full list of contributors). Published under the 2-clause BSD license.
31708  * See license.txt in the OpenLayers distribution or repository for the
31709  * full text of the license. */
31710
31711 /**
31712  * @requires OpenLayers/Control.js
31713  * @requires OpenLayers/Handler/Drag.js
31714  */
31715
31716 /**
31717  * Class: OpenLayers.Control.DragPan
31718  * The DragPan control pans the map with a drag of the mouse.
31719  *
31720  * Inherits from:
31721  *  - <OpenLayers.Control>
31722  */
31723 OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {
31724
31725     /** 
31726      * Property: type
31727      * {OpenLayers.Control.TYPES}
31728      */
31729     type: OpenLayers.Control.TYPE_TOOL,
31730     
31731     /**
31732      * Property: panned
31733      * {Boolean} The map moved.
31734      */
31735     panned: false,
31736     
31737     /**
31738      * Property: interval
31739      * {Integer} The number of milliseconds that should ellapse before
31740      *     panning the map again. Defaults to 0 milliseconds, which means that
31741      *     no separate cycle is used for panning. In most cases you won't want
31742      *     to change this value. For slow machines/devices larger values can be
31743      *     tried out.
31744      */
31745     interval: 0,
31746     
31747     /**
31748      * APIProperty: documentDrag
31749      * {Boolean} If set to true, mouse dragging will continue even if the
31750      *     mouse cursor leaves the map viewport. Default is false.
31751      */
31752     documentDrag: false,
31753
31754     /**
31755      * Property: kinetic
31756      * {<OpenLayers.Kinetic>} The OpenLayers.Kinetic object.
31757      */
31758     kinetic: null,
31759
31760     /**
31761      * APIProperty: enableKinetic
31762      * {Boolean} Set this option to enable "kinetic dragging". Can be
31763      *     set to true or to an object. If set to an object this
31764      *     object will be passed to the {<OpenLayers.Kinetic>}
31765      *     constructor. Defaults to true.
31766      *     To get kinetic dragging, ensure that OpenLayers/Kinetic.js is
31767      *     included in your build config.
31768      */
31769     enableKinetic: true,
31770
31771     /**
31772      * APIProperty: kineticInterval
31773      * {Integer} Interval in milliseconds between 2 steps in the "kinetic
31774      *     scrolling". Applies only if enableKinetic is set. Defaults
31775      *     to 10 milliseconds.
31776      */
31777     kineticInterval: 10,
31778
31779
31780     /**
31781      * Method: draw
31782      * Creates a Drag handler, using <panMap> and
31783      * <panMapDone> as callbacks.
31784      */    
31785     draw: function() {
31786         if (this.enableKinetic && OpenLayers.Kinetic) {
31787             var config = {interval: this.kineticInterval};
31788             if(typeof this.enableKinetic === "object") {
31789                 config = OpenLayers.Util.extend(config, this.enableKinetic);
31790             }
31791             this.kinetic = new OpenLayers.Kinetic(config);
31792         }
31793         this.handler = new OpenLayers.Handler.Drag(this, {
31794                 "move": this.panMap,
31795                 "done": this.panMapDone,
31796                 "down": this.panMapStart
31797             }, {
31798                 interval: this.interval,
31799                 documentDrag: this.documentDrag
31800             }
31801         );
31802     },
31803
31804     /**
31805      * Method: panMapStart
31806      */
31807     panMapStart: function() {
31808         if(this.kinetic) {
31809             this.kinetic.begin();
31810         }
31811     },
31812
31813     /**
31814     * Method: panMap
31815     *
31816     * Parameters:
31817     * xy - {<OpenLayers.Pixel>} Pixel of the mouse position
31818     */
31819     panMap: function(xy) {
31820         if(this.kinetic) {
31821             this.kinetic.update(xy);
31822         }
31823         this.panned = true;
31824         this.map.pan(
31825             this.handler.last.x - xy.x,
31826             this.handler.last.y - xy.y,
31827             {dragging: true, animate: false}
31828         );
31829     },
31830     
31831     /**
31832      * Method: panMapDone
31833      * Finish the panning operation.  Only call setCenter (through <panMap>)
31834      *     if the map has actually been moved.
31835      *
31836      * Parameters:
31837      * xy - {<OpenLayers.Pixel>} Pixel of the mouse position
31838      */
31839     panMapDone: function(xy) {
31840         if(this.panned) {
31841             var res = null;
31842             if (this.kinetic) {
31843                 res = this.kinetic.end(xy);
31844             }
31845             this.map.pan(
31846                 this.handler.last.x - xy.x,
31847                 this.handler.last.y - xy.y,
31848                 {dragging: !!res, animate: false}
31849             );
31850             if (res) {
31851                 var self = this;
31852                 this.kinetic.move(res, function(x, y, end) {
31853                     self.map.pan(x, y, {dragging: !end, animate: false});
31854                 });
31855             }
31856             this.panned = false;
31857         }
31858     },
31859
31860     CLASS_NAME: "OpenLayers.Control.DragPan"
31861 });
31862 /* ======================================================================
31863     OpenLayers/Handler/Click.js
31864    ====================================================================== */
31865
31866 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
31867  * full list of contributors). Published under the 2-clause BSD license.
31868  * See license.txt in the OpenLayers distribution or repository for the
31869  * full text of the license. */
31870
31871 /**
31872  * @requires OpenLayers/Handler.js
31873  */
31874
31875 /**
31876  * Class: OpenLayers.Handler.Click
31877  * A handler for mouse clicks.  The intention of this handler is to give
31878  *     controls more flexibility with handling clicks.  Browsers trigger
31879  *     click events twice for a double-click.  In addition, the mousedown,
31880  *     mousemove, mouseup sequence fires a click event.  With this handler,
31881  *     controls can decide whether to ignore clicks associated with a double
31882  *     click.  By setting a <pixelTolerance>, controls can also ignore clicks
31883  *     that include a drag.  Create a new instance with the
31884  *     <OpenLayers.Handler.Click> constructor.
31885  * 
31886  * Inherits from:
31887  *  - <OpenLayers.Handler> 
31888  */
31889 OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {
31890     /**
31891      * APIProperty: delay
31892      * {Number} Number of milliseconds between clicks before the event is
31893      *     considered a double-click.
31894      */
31895     delay: 300,
31896     
31897     /**
31898      * APIProperty: single
31899      * {Boolean} Handle single clicks.  Default is true.  If false, clicks
31900      * will not be reported.  If true, single-clicks will be reported.
31901      */
31902     single: true,
31903     
31904     /**
31905      * APIProperty: double
31906      * {Boolean} Handle double-clicks.  Default is false.
31907      */
31908     'double': false,
31909     
31910     /**
31911      * APIProperty: pixelTolerance
31912      * {Number} Maximum number of pixels between mouseup and mousedown for an
31913      *     event to be considered a click.  Default is 0.  If set to an
31914      *     integer value, clicks with a drag greater than the value will be
31915      *     ignored.  This property can only be set when the handler is
31916      *     constructed.
31917      */
31918     pixelTolerance: 0,
31919         
31920     /**
31921      * APIProperty: dblclickTolerance
31922      * {Number} Maximum distance in pixels between clicks for a sequence of 
31923      *     events to be considered a double click.  Default is 13.  If the
31924      *     distance between two clicks is greater than this value, a double-
31925      *     click will not be fired.
31926      */
31927     dblclickTolerance: 13,
31928         
31929     /**
31930      * APIProperty: stopSingle
31931      * {Boolean} Stop other listeners from being notified of clicks.  Default
31932      *     is false.  If true, any listeners registered before this one for 
31933      *     click or rightclick events will not be notified.
31934      */
31935     stopSingle: false,
31936     
31937     /**
31938      * APIProperty: stopDouble
31939      * {Boolean} Stop other listeners from being notified of double-clicks.
31940      *     Default is false.  If true, any click listeners registered before
31941      *     this one will not be notified of *any* double-click events.
31942      * 
31943      * The one caveat with stopDouble is that given a map with two click
31944      *     handlers, one with stopDouble true and the other with stopSingle
31945      *     true, the stopSingle handler should be activated last to get
31946      *     uniform cross-browser performance.  Since IE triggers one click
31947      *     with a dblclick and FF triggers two, if a stopSingle handler is
31948      *     activated first, all it gets in IE is a single click when the
31949      *     second handler stops propagation on the dblclick.
31950      */
31951     stopDouble: false,
31952
31953     /**
31954      * Property: timerId
31955      * {Number} The id of the timeout waiting to clear the <delayedCall>.
31956      */
31957     timerId: null,
31958     
31959     /**
31960      * Property: down
31961      * {Object} Object that store relevant information about the last
31962      *     mousedown or touchstart. Its 'xy' OpenLayers.Pixel property gives
31963      *     the average location of the mouse/touch event. Its 'touches'
31964      *     property records clientX/clientY of each touches.
31965      */
31966     down: null,
31967
31968     /**
31969      * Property: last
31970      * {Object} Object that store relevant information about the last
31971      *     mousemove or touchmove. Its 'xy' OpenLayers.Pixel property gives
31972      *     the average location of the mouse/touch event. Its 'touches'
31973      *     property records clientX/clientY of each touches.
31974      */
31975     last: null,
31976
31977     /** 
31978      * Property: first
31979      * {Object} When waiting for double clicks, this object will store 
31980      *     information about the first click in a two click sequence.
31981      */
31982     first: null,
31983
31984     /**
31985      * Property: rightclickTimerId
31986      * {Number} The id of the right mouse timeout waiting to clear the 
31987      *     <delayedEvent>.
31988      */
31989     rightclickTimerId: null,
31990     
31991     /**
31992      * Constructor: OpenLayers.Handler.Click
31993      * Create a new click handler.
31994      * 
31995      * Parameters:
31996      * control - {<OpenLayers.Control>} The control that is making use of
31997      *     this handler.  If a handler is being used without a control, the
31998      *     handler's setMap method must be overridden to deal properly with
31999      *     the map.
32000      * callbacks - {Object} An object with keys corresponding to callbacks
32001      *     that will be called by the handler. The callbacks should
32002      *     expect to recieve a single argument, the click event.
32003      *     Callbacks for 'click' and 'dblclick' are supported.
32004      * options - {Object} Optional object whose properties will be set on the
32005      *     handler.
32006      */
32007     
32008     /**
32009      * Method: touchstart
32010      * Handle touchstart.
32011      *
32012      * Returns:
32013      * {Boolean} Continue propagating this event.
32014      */
32015     touchstart: function(evt) {
32016         this.startTouch();
32017         this.down = this.getEventInfo(evt);
32018         this.last = this.getEventInfo(evt);
32019         return true;
32020     },
32021     
32022     /**
32023      * Method: touchmove
32024      *    Store position of last move, because touchend event can have
32025      *    an empty "touches" property.
32026      *
32027      * Returns:
32028      * {Boolean} Continue propagating this event.
32029      */
32030     touchmove: function(evt) {
32031         this.last = this.getEventInfo(evt);
32032         return true;
32033     },
32034
32035     /**
32036      * Method: touchend
32037      *   Correctly set event xy property, and add lastTouches to have
32038      *   touches property from last touchstart or touchmove
32039      *
32040      * Returns:
32041      * {Boolean} Continue propagating this event.
32042      */
32043     touchend: function(evt) {
32044         // touchstart may not have been allowed to propagate
32045         if (this.down) {
32046             evt.xy = this.last.xy;
32047             evt.lastTouches = this.last.touches;
32048             this.handleSingle(evt);
32049             this.down = null;
32050         }
32051         return true;
32052     },
32053
32054     /**
32055      * Method: mousedown
32056      * Handle mousedown.
32057      *
32058      * Returns:
32059      * {Boolean} Continue propagating this event.
32060      */
32061     mousedown: function(evt) {
32062         this.down = this.getEventInfo(evt);
32063         this.last = this.getEventInfo(evt);
32064         return true;
32065     },
32066
32067     /**
32068      * Method: mouseup
32069      * Handle mouseup.  Installed to support collection of right mouse events.
32070      * 
32071      * Returns:
32072      * {Boolean} Continue propagating this event.
32073      */
32074     mouseup: function (evt) {
32075         var propagate = true;
32076
32077         // Collect right mouse clicks from the mouseup
32078         //  IE - ignores the second right click in mousedown so using
32079         //  mouseup instead
32080         if (this.checkModifiers(evt) && this.control.handleRightClicks &&
32081            OpenLayers.Event.isRightClick(evt)) {
32082             propagate = this.rightclick(evt);
32083         }
32084
32085         return propagate;
32086     },
32087     
32088     /**
32089      * Method: rightclick
32090      * Handle rightclick.  For a dblrightclick, we get two clicks so we need 
32091      *     to always register for dblrightclick to properly handle single 
32092      *     clicks.
32093      *     
32094      * Returns:
32095      * {Boolean} Continue propagating this event.
32096      */
32097     rightclick: function(evt) {
32098         if(this.passesTolerance(evt)) {
32099            if(this.rightclickTimerId != null) {
32100                 //Second click received before timeout this must be 
32101                 // a double click
32102                 this.clearTimer();
32103                 this.callback('dblrightclick', [evt]);
32104                 return !this.stopDouble;
32105             } else { 
32106                 //Set the rightclickTimerId, send evt only if double is 
32107                 // true else trigger single
32108                 var clickEvent = this['double'] ?
32109                     OpenLayers.Util.extend({}, evt) : 
32110                     this.callback('rightclick', [evt]);
32111
32112                 var delayedRightCall = OpenLayers.Function.bind(
32113                     this.delayedRightCall, 
32114                     this, 
32115                     clickEvent
32116                 );
32117                 this.rightclickTimerId = window.setTimeout(
32118                     delayedRightCall, this.delay
32119                 );
32120             } 
32121         }
32122         return !this.stopSingle;
32123     },
32124     
32125     /**
32126      * Method: delayedRightCall
32127      * Sets <rightclickTimerId> to null.  And optionally triggers the 
32128      *     rightclick callback if evt is set.
32129      */
32130     delayedRightCall: function(evt) {
32131         this.rightclickTimerId = null;
32132         if (evt) {
32133            this.callback('rightclick', [evt]);
32134         }
32135     },
32136     
32137     /**
32138      * Method: click
32139      * Handle click events from the browser.  This is registered as a listener
32140      *     for click events and should not be called from other events in this
32141      *     handler.
32142      *
32143      * Returns:
32144      * {Boolean} Continue propagating this event.
32145      */
32146     click: function(evt) {
32147         if (!this.last) {
32148             this.last = this.getEventInfo(evt);
32149         }
32150         this.handleSingle(evt);
32151         return !this.stopSingle;
32152     },
32153
32154     /**
32155      * Method: dblclick
32156      * Handle dblclick.  For a dblclick, we get two clicks in some browsers
32157      *     (FF) and one in others (IE).  So we need to always register for
32158      *     dblclick to properly handle single clicks.  This method is registered
32159      *     as a listener for the dblclick browser event.  It should *not* be
32160      *     called by other methods in this handler.
32161      *     
32162      * Returns:
32163      * {Boolean} Continue propagating this event.
32164      */
32165     dblclick: function(evt) {
32166         this.handleDouble(evt);
32167         return !this.stopDouble;
32168     },
32169     
32170     /** 
32171      * Method: handleDouble
32172      * Handle double-click sequence.
32173      */
32174     handleDouble: function(evt) {
32175         if (this.passesDblclickTolerance(evt)) {
32176             if (this["double"]) {
32177                 this.callback("dblclick", [evt]);
32178             }
32179             // to prevent a dblclick from firing the click callback in IE
32180             this.clearTimer();
32181         }
32182     },
32183     
32184     /** 
32185      * Method: handleSingle
32186      * Handle single click sequence.
32187      */
32188     handleSingle: function(evt) {
32189         if (this.passesTolerance(evt)) {
32190             if (this.timerId != null) {
32191                 // already received a click
32192                 if (this.last.touches && this.last.touches.length === 1) {
32193                     // touch device, no dblclick event - this may be a double
32194                     if (this["double"]) {
32195                         // on Android don't let the browser zoom on the page
32196                         OpenLayers.Event.preventDefault(evt);
32197                     }
32198                     this.handleDouble(evt);
32199                 }
32200                 // if we're not in a touch environment we clear the click timer
32201                 // if we've got a second touch, we'll get two touchend events
32202                 if (!this.last.touches || this.last.touches.length !== 2) {
32203                     this.clearTimer();
32204                 }
32205             } else {
32206                 // remember the first click info so we can compare to the second
32207                 this.first = this.getEventInfo(evt);
32208                 // set the timer, send evt only if single is true
32209                 //use a clone of the event object because it will no longer 
32210                 //be a valid event object in IE in the timer callback
32211                 var clickEvent = this.single ?
32212                     OpenLayers.Util.extend({}, evt) : null;
32213                 this.queuePotentialClick(clickEvent);
32214             }
32215         }
32216     },
32217     
32218     /** 
32219      * Method: queuePotentialClick
32220      * This method is separated out largely to make testing easier (so we
32221      *     don't have to override window.setTimeout)
32222      */
32223     queuePotentialClick: function(evt) {
32224         this.timerId = window.setTimeout(
32225             OpenLayers.Function.bind(this.delayedCall, this, evt),
32226             this.delay
32227         );
32228     },
32229
32230     /**
32231      * Method: passesTolerance
32232      * Determine whether the event is within the optional pixel tolerance.  Note
32233      *     that the pixel tolerance check only works if mousedown events get to
32234      *     the listeners registered here.  If they are stopped by other elements,
32235      *     the <pixelTolerance> will have no effect here (this method will always
32236      *     return true).
32237      *
32238      * Returns:
32239      * {Boolean} The click is within the pixel tolerance (if specified).
32240      */
32241     passesTolerance: function(evt) {
32242         var passes = true;
32243         if (this.pixelTolerance != null && this.down && this.down.xy) {
32244             passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy);
32245             // for touch environments, we also enforce that all touches
32246             // start and end within the given tolerance to be considered a click
32247             if (passes && this.touch && 
32248                 this.down.touches.length === this.last.touches.length) {
32249                 // the touchend event doesn't come with touches, so we check
32250                 // down and last
32251                 for (var i=0, ii=this.down.touches.length; i<ii; ++i) {
32252                     if (this.getTouchDistance(
32253                             this.down.touches[i], 
32254                             this.last.touches[i]
32255                         ) > this.pixelTolerance) {
32256                         passes = false;
32257                         break;
32258                     }
32259                 }
32260             }
32261         }
32262         return passes;
32263     },
32264     
32265     /** 
32266      * Method: getTouchDistance
32267      *
32268      * Returns:
32269      * {Boolean} The pixel displacement between two touches.
32270      */
32271     getTouchDistance: function(from, to) {
32272         return Math.sqrt(
32273             Math.pow(from.clientX - to.clientX, 2) +
32274             Math.pow(from.clientY - to.clientY, 2)
32275         );
32276     },
32277     
32278     /**
32279      * Method: passesDblclickTolerance
32280      * Determine whether the event is within the optional double-cick pixel 
32281      *     tolerance.
32282      *
32283      * Returns:
32284      * {Boolean} The click is within the double-click pixel tolerance.
32285      */
32286     passesDblclickTolerance: function(evt) {
32287         var passes = true;
32288         if (this.down && this.first) {
32289             passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance;
32290         }
32291         return passes;
32292     },
32293
32294     /**
32295      * Method: clearTimer
32296      * Clear the timer and set <timerId> to null.
32297      */
32298     clearTimer: function() {
32299         if (this.timerId != null) {
32300             window.clearTimeout(this.timerId);
32301             this.timerId = null;
32302         }
32303         if (this.rightclickTimerId != null) {
32304             window.clearTimeout(this.rightclickTimerId);
32305             this.rightclickTimerId = null;
32306         }
32307     },
32308     
32309     /**
32310      * Method: delayedCall
32311      * Sets <timerId> to null.  And optionally triggers the click callback if
32312      *     evt is set.
32313      */
32314     delayedCall: function(evt) {
32315         this.timerId = null;
32316         if (evt) {
32317             this.callback("click", [evt]);
32318         }
32319     },
32320
32321     /**
32322      * Method: getEventInfo
32323      * This method allows us to store event information without storing the
32324      *     actual event.  In touch devices (at least), the same event is 
32325      *     modified between touchstart, touchmove, and touchend.
32326      *
32327      * Returns:
32328      * {Object} An object with event related info.
32329      */
32330     getEventInfo: function(evt) {
32331         var touches;
32332         if (evt.touches) {
32333             var len = evt.touches.length;
32334             touches = new Array(len);
32335             var touch;
32336             for (var i=0; i<len; i++) {
32337                 touch = evt.touches[i];
32338                 touches[i] = {
32339                     clientX: touch.olClientX,
32340                     clientY: touch.olClientY
32341                 };
32342             }
32343         }
32344         return {
32345             xy: evt.xy,
32346             touches: touches
32347         };
32348     },
32349
32350     /**
32351      * APIMethod: deactivate
32352      * Deactivate the handler.
32353      *
32354      * Returns:
32355      * {Boolean} The handler was successfully deactivated.
32356      */
32357     deactivate: function() {
32358         var deactivated = false;
32359         if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
32360             this.clearTimer();
32361             this.down = null;
32362             this.first = null;
32363             this.last = null;
32364             deactivated = true;
32365         }
32366         return deactivated;
32367     },
32368
32369     CLASS_NAME: "OpenLayers.Handler.Click"
32370 });
32371 /* ======================================================================
32372     OpenLayers/Control/Navigation.js
32373    ====================================================================== */
32374
32375 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
32376  * full list of contributors). Published under the 2-clause BSD license.
32377  * See license.txt in the OpenLayers distribution or repository for the
32378  * full text of the license. */
32379
32380 /**
32381  * @requires OpenLayers/Control/ZoomBox.js
32382  * @requires OpenLayers/Control/DragPan.js
32383  * @requires OpenLayers/Handler/MouseWheel.js
32384  * @requires OpenLayers/Handler/Click.js
32385  */
32386
32387 /**
32388  * Class: OpenLayers.Control.Navigation
32389  * The navigation control handles map browsing with mouse events (dragging,
32390  *     double-clicking, and scrolling the wheel).  Create a new navigation 
32391  *     control with the <OpenLayers.Control.Navigation> control.  
32392  * 
32393  *     Note that this control is added to the map by default (if no controls 
32394  *     array is sent in the options object to the <OpenLayers.Map> 
32395  *     constructor).
32396  * 
32397  * Inherits:
32398  *  - <OpenLayers.Control>
32399  */
32400 OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {
32401
32402     /** 
32403      * Property: dragPan
32404      * {<OpenLayers.Control.DragPan>} 
32405      */
32406     dragPan: null,
32407
32408     /**
32409      * APIProperty: dragPanOptions
32410      * {Object} Options passed to the DragPan control.
32411      */
32412     dragPanOptions: null,
32413
32414     /**
32415      * Property: pinchZoom
32416      * {<OpenLayers.Control.PinchZoom>}
32417      */
32418     pinchZoom: null,
32419
32420     /**
32421      * APIProperty: pinchZoomOptions
32422      * {Object} Options passed to the PinchZoom control.
32423      */
32424     pinchZoomOptions: null,
32425
32426     /**
32427      * APIProperty: documentDrag
32428      * {Boolean} Allow panning of the map by dragging outside map viewport.
32429      *     Default is false.
32430      */
32431     documentDrag: false,
32432
32433     /** 
32434      * Property: zoomBox
32435      * {<OpenLayers.Control.ZoomBox>}
32436      */
32437     zoomBox: null,
32438
32439     /**
32440      * APIProperty: zoomBoxEnabled
32441      * {Boolean} Whether the user can draw a box to zoom
32442      */
32443     zoomBoxEnabled: true, 
32444
32445     /**
32446      * APIProperty: zoomWheelEnabled
32447      * {Boolean} Whether the mousewheel should zoom the map
32448      */
32449     zoomWheelEnabled: true,
32450     
32451     /**
32452      * Property: mouseWheelOptions
32453      * {Object} Options passed to the MouseWheel control (only useful if
32454      *     <zoomWheelEnabled> is set to true). Default is no options for maps
32455      *     with fractionalZoom set to true, otherwise
32456      *     {cumulative: false, interval: 50, maxDelta: 6} 
32457      */
32458     mouseWheelOptions: null,
32459
32460     /**
32461      * APIProperty: handleRightClicks
32462      * {Boolean} Whether or not to handle right clicks. Default is false.
32463      */
32464     handleRightClicks: false,
32465
32466     /**
32467      * APIProperty: zoomBoxKeyMask
32468      * {Integer} <OpenLayers.Handler> key code of the key, which has to be
32469      *    pressed, while drawing the zoom box with the mouse on the screen. 
32470      *    You should probably set handleRightClicks to true if you use this
32471      *    with MOD_CTRL, to disable the context menu for machines which use
32472      *    CTRL-Click as a right click.
32473      * Default: <OpenLayers.Handler.MOD_SHIFT>
32474      */
32475     zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,
32476     
32477     /**
32478      * APIProperty: autoActivate
32479      * {Boolean} Activate the control when it is added to a map.  Default is
32480      *     true.
32481      */
32482     autoActivate: true,
32483
32484     /**
32485      * Constructor: OpenLayers.Control.Navigation
32486      * Create a new navigation control
32487      * 
32488      * Parameters:
32489      * options - {Object} An optional object whose properties will be set on
32490      *                    the control
32491      */
32492     initialize: function(options) {
32493         this.handlers = {};
32494         OpenLayers.Control.prototype.initialize.apply(this, arguments);
32495     },
32496
32497     /**
32498      * Method: destroy
32499      * The destroy method is used to perform any clean up before the control
32500      * is dereferenced.  Typically this is where event listeners are removed
32501      * to prevent memory leaks.
32502      */
32503     destroy: function() {
32504         this.deactivate();
32505
32506         if (this.dragPan) {
32507             this.dragPan.destroy();
32508         }
32509         this.dragPan = null;
32510
32511         if (this.zoomBox) {
32512             this.zoomBox.destroy();
32513         }
32514         this.zoomBox = null;
32515
32516         if (this.pinchZoom) {
32517             this.pinchZoom.destroy();
32518         }
32519         this.pinchZoom = null;
32520
32521         OpenLayers.Control.prototype.destroy.apply(this,arguments);
32522     },
32523     
32524     /**
32525      * Method: activate
32526      */
32527     activate: function() {
32528         this.dragPan.activate();
32529         if (this.zoomWheelEnabled) {
32530             this.handlers.wheel.activate();
32531         }    
32532         this.handlers.click.activate();
32533         if (this.zoomBoxEnabled) {
32534             this.zoomBox.activate();
32535         }
32536         if (this.pinchZoom) {
32537             this.pinchZoom.activate();
32538         }
32539         return OpenLayers.Control.prototype.activate.apply(this,arguments);
32540     },
32541
32542     /**
32543      * Method: deactivate
32544      */
32545     deactivate: function() {
32546         if (this.pinchZoom) {
32547             this.pinchZoom.deactivate();
32548         }
32549         this.zoomBox.deactivate();
32550         this.dragPan.deactivate();
32551         this.handlers.click.deactivate();
32552         this.handlers.wheel.deactivate();
32553         return OpenLayers.Control.prototype.deactivate.apply(this,arguments);
32554     },
32555     
32556     /**
32557      * Method: draw
32558      */
32559     draw: function() {
32560         // disable right mouse context menu for support of right click events
32561         if (this.handleRightClicks) {
32562             this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False;
32563         }
32564
32565         var clickCallbacks = { 
32566             'click': this.defaultClick,
32567             'dblclick': this.defaultDblClick, 
32568             'dblrightclick': this.defaultDblRightClick 
32569         };
32570         var clickOptions = {
32571             'double': true, 
32572             'stopDouble': true
32573         };
32574         this.handlers.click = new OpenLayers.Handler.Click(
32575             this, clickCallbacks, clickOptions
32576         );
32577         this.dragPan = new OpenLayers.Control.DragPan(
32578             OpenLayers.Util.extend({
32579                 map: this.map,
32580                 documentDrag: this.documentDrag
32581             }, this.dragPanOptions)
32582         );
32583         this.zoomBox = new OpenLayers.Control.ZoomBox(
32584                     {map: this.map, keyMask: this.zoomBoxKeyMask});
32585         this.dragPan.draw();
32586         this.zoomBox.draw();
32587         var wheelOptions = this.map.fractionalZoom ? {} : {
32588             cumulative: false,
32589             interval: 50,
32590             maxDelta: 6
32591         };
32592         this.handlers.wheel = new OpenLayers.Handler.MouseWheel(
32593             this, {up : this.wheelUp, down: this.wheelDown},
32594             OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions)
32595         );
32596         if (OpenLayers.Control.PinchZoom) {
32597             this.pinchZoom = new OpenLayers.Control.PinchZoom(
32598                 OpenLayers.Util.extend(
32599                     {map: this.map}, this.pinchZoomOptions));
32600         }
32601     },
32602
32603     /**
32604      * Method: defaultClick
32605      *
32606      * Parameters:
32607      * evt - {Event}
32608      */
32609     defaultClick: function (evt) {
32610         if (evt.lastTouches && evt.lastTouches.length == 2) {
32611             this.map.zoomOut();
32612         }
32613     },
32614
32615     /**
32616      * Method: defaultDblClick 
32617      * 
32618      * Parameters:
32619      * evt - {Event} 
32620      */
32621     defaultDblClick: function (evt) {
32622         this.map.zoomTo(this.map.zoom + 1, evt.xy);
32623     },
32624
32625     /**
32626      * Method: defaultDblRightClick 
32627      * 
32628      * Parameters:
32629      * evt - {Event} 
32630      */
32631     defaultDblRightClick: function (evt) {
32632         this.map.zoomTo(this.map.zoom - 1, evt.xy);
32633     },
32634     
32635     /**
32636      * Method: wheelChange  
32637      *
32638      * Parameters:
32639      * evt - {Event}
32640      * deltaZ - {Integer}
32641      */
32642     wheelChange: function(evt, deltaZ) {
32643         if (!this.map.fractionalZoom) {
32644             deltaZ =  Math.round(deltaZ);
32645         }
32646         var currentZoom = this.map.getZoom(),
32647             newZoom = currentZoom + deltaZ;
32648         newZoom = Math.max(newZoom, 0);
32649         newZoom = Math.min(newZoom, this.map.getNumZoomLevels());
32650         if (newZoom === currentZoom) {
32651             return;
32652         }
32653         this.map.zoomTo(newZoom, evt.xy);
32654     },
32655
32656     /** 
32657      * Method: wheelUp
32658      * User spun scroll wheel up
32659      * 
32660      * Parameters:
32661      * evt - {Event}
32662      * delta - {Integer}
32663      */
32664     wheelUp: function(evt, delta) {
32665         this.wheelChange(evt, delta || 1);
32666     },
32667
32668     /** 
32669      * Method: wheelDown
32670      * User spun scroll wheel down
32671      * 
32672      * Parameters:
32673      * evt - {Event}
32674      * delta - {Integer}
32675      */
32676     wheelDown: function(evt, delta) {
32677         this.wheelChange(evt, delta || -1);
32678     },
32679     
32680     /**
32681      * Method: disableZoomBox
32682      */
32683     disableZoomBox : function() {
32684         this.zoomBoxEnabled = false;
32685         this.zoomBox.deactivate();       
32686     },
32687     
32688     /**
32689      * Method: enableZoomBox
32690      */
32691     enableZoomBox : function() {
32692         this.zoomBoxEnabled = true;
32693         if (this.active) {
32694             this.zoomBox.activate();
32695         }    
32696     },
32697     
32698     /**
32699      * Method: disableZoomWheel
32700      */
32701     
32702     disableZoomWheel : function() {
32703         this.zoomWheelEnabled = false;
32704         this.handlers.wheel.deactivate();       
32705     },
32706     
32707     /**
32708      * Method: enableZoomWheel
32709      */
32710     
32711     enableZoomWheel : function() {
32712         this.zoomWheelEnabled = true;
32713         if (this.active) {
32714             this.handlers.wheel.activate();
32715         }    
32716     },
32717
32718     CLASS_NAME: "OpenLayers.Control.Navigation"
32719 });
32720 /* ======================================================================
32721     OpenLayers/Renderer/SVG.js
32722    ====================================================================== */
32723
32724 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
32725  * full list of contributors). Published under the 2-clause BSD license.
32726  * See license.txt in the OpenLayers distribution or repository for the
32727  * full text of the license. */
32728
32729 /**
32730  * @requires OpenLayers/Renderer/Elements.js
32731  */
32732
32733 /**
32734  * Class: OpenLayers.Renderer.SVG
32735  * 
32736  * Inherits:
32737  *  - <OpenLayers.Renderer.Elements>
32738  */
32739 OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
32740
32741     /** 
32742      * Property: xmlns
32743      * {String}
32744      */
32745     xmlns: "http://www.w3.org/2000/svg",
32746     
32747     /**
32748      * Property: xlinkns
32749      * {String}
32750      */
32751     xlinkns: "http://www.w3.org/1999/xlink",
32752
32753     /**
32754      * Constant: MAX_PIXEL
32755      * {Integer} Firefox has a limitation where values larger or smaller than  
32756      *           about 15000 in an SVG document lock the browser up. This 
32757      *           works around it.
32758      */
32759     MAX_PIXEL: 15000,
32760
32761     /**
32762      * Property: translationParameters
32763      * {Object} Hash with "x" and "y" properties
32764      */
32765     translationParameters: null,
32766     
32767     /**
32768      * Property: symbolMetrics
32769      * {Object} Cache for symbol metrics according to their svg coordinate
32770      *     space. This is an object keyed by the symbol's id, and values are
32771      *     an array of [width, centerX, centerY].
32772      */
32773     symbolMetrics: null,
32774     
32775     /**
32776      * Constructor: OpenLayers.Renderer.SVG
32777      * 
32778      * Parameters:
32779      * containerID - {String}
32780      */
32781     initialize: function(containerID) {
32782         if (!this.supported()) { 
32783             return; 
32784         }
32785         OpenLayers.Renderer.Elements.prototype.initialize.apply(this, 
32786                                                                 arguments);
32787         this.translationParameters = {x: 0, y: 0};
32788         
32789         this.symbolMetrics = {};
32790     },
32791
32792     /**
32793      * APIMethod: supported
32794      * 
32795      * Returns:
32796      * {Boolean} Whether or not the browser supports the SVG renderer
32797      */
32798     supported: function() {
32799         var svgFeature = "http://www.w3.org/TR/SVG11/feature#";
32800         return (document.implementation && 
32801            (document.implementation.hasFeature("org.w3c.svg", "1.0") || 
32802             document.implementation.hasFeature(svgFeature + "SVG", "1.1") || 
32803             document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1") ));
32804     },    
32805
32806     /**
32807      * Method: inValidRange
32808      * See #669 for more information
32809      *
32810      * Parameters:
32811      * x      - {Integer}
32812      * y      - {Integer}
32813      * xyOnly - {Boolean} whether or not to just check for x and y, which means
32814      *     to not take the current translation parameters into account if true.
32815      * 
32816      * Returns:
32817      * {Boolean} Whether or not the 'x' and 'y' coordinates are in the  
32818      *           valid range.
32819      */ 
32820     inValidRange: function(x, y, xyOnly) {
32821         var left = x + (xyOnly ? 0 : this.translationParameters.x);
32822         var top = y + (xyOnly ? 0 : this.translationParameters.y);
32823         return (left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL &&
32824                 top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL);
32825     },
32826
32827     /**
32828      * Method: setExtent
32829      * 
32830      * Parameters:
32831      * extent - {<OpenLayers.Bounds>}
32832      * resolutionChanged - {Boolean}
32833      * 
32834      * Returns:
32835      * {Boolean} true to notify the layer that the new extent does not exceed
32836      *     the coordinate range, and the features will not need to be redrawn.
32837      *     False otherwise.
32838      */
32839     setExtent: function(extent, resolutionChanged) {
32840         var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);
32841         
32842         var resolution = this.getResolution(),
32843             left = -extent.left / resolution,
32844             top = extent.top / resolution;
32845
32846         // If the resolution has changed, start over changing the corner, because
32847         // the features will redraw.
32848         if (resolutionChanged) {
32849             this.left = left;
32850             this.top = top;
32851             // Set the viewbox
32852             var extentString = "0 0 " + this.size.w + " " + this.size.h;
32853
32854             this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
32855             this.translate(this.xOffset, 0);
32856             return true;
32857         } else {
32858             var inRange = this.translate(left - this.left + this.xOffset, top - this.top);
32859             if (!inRange) {
32860                 // recenter the coordinate system
32861                 this.setExtent(extent, true);
32862             }
32863             return coordSysUnchanged && inRange;
32864         }
32865     },
32866     
32867     /**
32868      * Method: translate
32869      * Transforms the SVG coordinate system
32870      * 
32871      * Parameters:
32872      * x - {Float}
32873      * y - {Float}
32874      * 
32875      * Returns:
32876      * {Boolean} true if the translation parameters are in the valid coordinates
32877      *     range, false otherwise.
32878      */
32879     translate: function(x, y) {
32880         if (!this.inValidRange(x, y, true)) {
32881             return false;
32882         } else {
32883             var transformString = "";
32884             if (x || y) {
32885                 transformString = "translate(" + x + "," + y + ")";
32886             }
32887             this.root.setAttributeNS(null, "transform", transformString);
32888             this.translationParameters = {x: x, y: y};
32889             return true;
32890         }
32891     },
32892
32893     /**
32894      * Method: setSize
32895      * Sets the size of the drawing surface.
32896      * 
32897      * Parameters:
32898      * size - {<OpenLayers.Size>} The size of the drawing surface
32899      */
32900     setSize: function(size) {
32901         OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
32902         
32903         this.rendererRoot.setAttributeNS(null, "width", this.size.w);
32904         this.rendererRoot.setAttributeNS(null, "height", this.size.h);
32905     },
32906
32907     /** 
32908      * Method: getNodeType 
32909      * 
32910      * Parameters:
32911      * geometry - {<OpenLayers.Geometry>}
32912      * style - {Object}
32913      * 
32914      * Returns:
32915      * {String} The corresponding node type for the specified geometry
32916      */
32917     getNodeType: function(geometry, style) {
32918         var nodeType = null;
32919         switch (geometry.CLASS_NAME) {
32920             case "OpenLayers.Geometry.Point":
32921                 if (style.externalGraphic) {
32922                     nodeType = "image";
32923                 } else if (this.isComplexSymbol(style.graphicName)) {
32924                     nodeType = "svg";
32925                 } else {
32926                     nodeType = "circle";
32927                 }
32928                 break;
32929             case "OpenLayers.Geometry.Rectangle":
32930                 nodeType = "rect";
32931                 break;
32932             case "OpenLayers.Geometry.LineString":
32933                 nodeType = "polyline";
32934                 break;
32935             case "OpenLayers.Geometry.LinearRing":
32936                 nodeType = "polygon";
32937                 break;
32938             case "OpenLayers.Geometry.Polygon":
32939             case "OpenLayers.Geometry.Curve":
32940                 nodeType = "path";
32941                 break;
32942             default:
32943                 break;
32944         }
32945         return nodeType;
32946     },
32947
32948     /** 
32949      * Method: setStyle
32950      * Use to set all the style attributes to a SVG node.
32951      * 
32952      * Takes care to adjust stroke width and point radius to be
32953      * resolution-relative
32954      *
32955      * Parameters:
32956      * node - {SVGDomElement} An SVG element to decorate
32957      * style - {Object}
32958      * options - {Object} Currently supported options include 
32959      *                              'isFilled' {Boolean} and
32960      *                              'isStroked' {Boolean}
32961      */
32962     setStyle: function(node, style, options) {
32963         style = style  || node._style;
32964         options = options || node._options;
32965
32966         var title = style.title || style.graphicTitle;
32967         if (title) {
32968             node.setAttributeNS(null, "title", title);
32969             //Standards-conformant SVG
32970             // Prevent duplicate nodes. See issue https://github.com/openlayers/openlayers/issues/92 
32971             var titleNode = node.getElementsByTagName("title");
32972             if (titleNode.length > 0) {
32973                 titleNode[0].firstChild.textContent = title;
32974             } else {
32975                 var label = this.nodeFactory(null, "title");
32976                 label.textContent = title;
32977                 node.appendChild(label);
32978             }
32979         }
32980
32981         var r = parseFloat(node.getAttributeNS(null, "r"));
32982         var widthFactor = 1;
32983         var pos;
32984         if (node._geometryClass == "OpenLayers.Geometry.Point" && r) {
32985             node.style.visibility = "";
32986             if (style.graphic === false) {
32987                 node.style.visibility = "hidden";
32988             } else if (style.externalGraphic) {
32989                 pos = this.getPosition(node);
32990                 if (style.graphicWidth && style.graphicHeight) {
32991                   node.setAttributeNS(null, "preserveAspectRatio", "none");
32992                 }
32993                 var width = style.graphicWidth || style.graphicHeight;
32994                 var height = style.graphicHeight || style.graphicWidth;
32995                 width = width ? width : style.pointRadius*2;
32996                 height = height ? height : style.pointRadius*2;
32997                 var xOffset = (style.graphicXOffset != undefined) ?
32998                     style.graphicXOffset : -(0.5 * width);
32999                 var yOffset = (style.graphicYOffset != undefined) ?
33000                     style.graphicYOffset : -(0.5 * height);
33001
33002                 var opacity = style.graphicOpacity || style.fillOpacity;
33003                 
33004                 node.setAttributeNS(null, "x", (pos.x + xOffset).toFixed());
33005                 node.setAttributeNS(null, "y", (pos.y + yOffset).toFixed());
33006                 node.setAttributeNS(null, "width", width);
33007                 node.setAttributeNS(null, "height", height);
33008                 node.setAttributeNS(this.xlinkns, "xlink:href", style.externalGraphic);
33009                 node.setAttributeNS(null, "style", "opacity: "+opacity);
33010                 node.onclick = OpenLayers.Event.preventDefault;
33011             } else if (this.isComplexSymbol(style.graphicName)) {
33012                 // the symbol viewBox is three times as large as the symbol
33013                 var offset = style.pointRadius * 3;
33014                 var size = offset * 2;
33015                 var src = this.importSymbol(style.graphicName);
33016                 pos = this.getPosition(node);
33017                 widthFactor = this.symbolMetrics[src.id][0] * 3 / size;
33018                 
33019                 // remove the node from the dom before we modify it. This
33020                 // prevents various rendering issues in Safari and FF
33021                 var parent = node.parentNode;
33022                 var nextSibling = node.nextSibling;
33023                 if(parent) {
33024                     parent.removeChild(node);
33025                 }
33026                 
33027                 // The more appropriate way to implement this would be use/defs,
33028                 // but due to various issues in several browsers, it is safer to
33029                 // copy the symbols instead of referencing them. 
33030                 // See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985 
33031                 // and this email thread
33032                 // http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html
33033                 node.firstChild && node.removeChild(node.firstChild);
33034                 node.appendChild(src.firstChild.cloneNode(true));
33035                 node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox"));
33036                 
33037                 node.setAttributeNS(null, "width", size);
33038                 node.setAttributeNS(null, "height", size);
33039                 node.setAttributeNS(null, "x", pos.x - offset);
33040                 node.setAttributeNS(null, "y", pos.y - offset);
33041                 
33042                 // now that the node has all its new properties, insert it
33043                 // back into the dom where it was
33044                 if(nextSibling) {
33045                     parent.insertBefore(node, nextSibling);
33046                 } else if(parent) {
33047                     parent.appendChild(node);
33048                 }
33049             } else {
33050                 node.setAttributeNS(null, "r", style.pointRadius);
33051             }
33052
33053             var rotation = style.rotation;
33054             
33055             if ((rotation !== undefined || node._rotation !== undefined) && pos) {
33056                 node._rotation = rotation;
33057                 rotation |= 0;
33058                 if (node.nodeName !== "svg") { 
33059                     node.setAttributeNS(null, "transform", 
33060                         "rotate(" + rotation + " " + pos.x + " " + 
33061                         pos.y + ")"); 
33062                 } else {
33063                     var metrics = this.symbolMetrics[src.id];
33064                     node.firstChild.setAttributeNS(null, "transform", "rotate(" 
33065                         + rotation + " " 
33066                         + metrics[1] + " "
33067                         + metrics[2] + ")");
33068                 }
33069             }
33070         }
33071         
33072         if (options.isFilled) {
33073             node.setAttributeNS(null, "fill", style.fillColor);
33074             node.setAttributeNS(null, "fill-opacity", style.fillOpacity);
33075         } else {
33076             node.setAttributeNS(null, "fill", "none");
33077         }
33078
33079         if (options.isStroked) {
33080             node.setAttributeNS(null, "stroke", style.strokeColor);
33081             node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity);
33082             node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor);
33083             node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap || "round");
33084             // Hard-coded linejoin for now, to make it look the same as in VML.
33085             // There is no strokeLinejoin property yet for symbolizers.
33086             node.setAttributeNS(null, "stroke-linejoin", "round");
33087             style.strokeDashstyle && node.setAttributeNS(null,
33088                 "stroke-dasharray", this.dashStyle(style, widthFactor));
33089         } else {
33090             node.setAttributeNS(null, "stroke", "none");
33091         }
33092         
33093         if (style.pointerEvents) {
33094             node.setAttributeNS(null, "pointer-events", style.pointerEvents);
33095         }
33096                 
33097         if (style.cursor != null) {
33098             node.setAttributeNS(null, "cursor", style.cursor);
33099         }
33100         
33101         return node;
33102     },
33103
33104     /** 
33105      * Method: dashStyle
33106      * 
33107      * Parameters:
33108      * style - {Object}
33109      * widthFactor - {Number}
33110      * 
33111      * Returns:
33112      * {String} A SVG compliant 'stroke-dasharray' value
33113      */
33114     dashStyle: function(style, widthFactor) {
33115         var w = style.strokeWidth * widthFactor;
33116         var str = style.strokeDashstyle;
33117         switch (str) {
33118             case 'solid':
33119                 return 'none';
33120             case 'dot':
33121                 return [1, 4 * w].join();
33122             case 'dash':
33123                 return [4 * w, 4 * w].join();
33124             case 'dashdot':
33125                 return [4 * w, 4 * w, 1, 4 * w].join();
33126             case 'longdash':
33127                 return [8 * w, 4 * w].join();
33128             case 'longdashdot':
33129                 return [8 * w, 4 * w, 1, 4 * w].join();
33130             default:
33131                 return OpenLayers.String.trim(str).replace(/\s+/g, ",");
33132         }
33133     },
33134     
33135     /** 
33136      * Method: createNode
33137      * 
33138      * Parameters:
33139      * type - {String} Kind of node to draw
33140      * id - {String} Id for node
33141      * 
33142      * Returns:
33143      * {DOMElement} A new node of the given type and id
33144      */
33145     createNode: function(type, id) {
33146         var node = document.createElementNS(this.xmlns, type);
33147         if (id) {
33148             node.setAttributeNS(null, "id", id);
33149         }
33150         return node;    
33151     },
33152     
33153     /** 
33154      * Method: nodeTypeCompare
33155      * 
33156      * Parameters:
33157      * node - {SVGDomElement} An SVG element
33158      * type - {String} Kind of node
33159      * 
33160      * Returns:
33161      * {Boolean} Whether or not the specified node is of the specified type
33162      */
33163     nodeTypeCompare: function(node, type) {
33164         return (type == node.nodeName);
33165     },
33166    
33167     /**
33168      * Method: createRenderRoot
33169      * 
33170      * Returns:
33171      * {DOMElement} The specific render engine's root element
33172      */
33173     createRenderRoot: function() {
33174         var svg = this.nodeFactory(this.container.id + "_svgRoot", "svg");
33175         svg.style.display = "block";
33176         return svg;
33177     },
33178
33179     /**
33180      * Method: createRoot
33181      * 
33182      * Parameters:
33183      * suffix - {String} suffix to append to the id
33184      * 
33185      * Returns:
33186      * {DOMElement}
33187      */
33188     createRoot: function(suffix) {
33189         return this.nodeFactory(this.container.id + suffix, "g");
33190     },
33191
33192     /**
33193      * Method: createDefs
33194      *
33195      * Returns:
33196      * {DOMElement} The element to which we'll add the symbol definitions
33197      */
33198     createDefs: function() {
33199         var defs = this.nodeFactory(this.container.id + "_defs", "defs");
33200         this.rendererRoot.appendChild(defs);
33201         return defs;
33202     },
33203
33204     /**************************************
33205      *                                    *
33206      *     GEOMETRY DRAWING FUNCTIONS     *
33207      *                                    *
33208      **************************************/
33209
33210     /**
33211      * Method: drawPoint
33212      * This method is only called by the renderer itself.
33213      * 
33214      * Parameters: 
33215      * node - {DOMElement}
33216      * geometry - {<OpenLayers.Geometry>}
33217      * 
33218      * Returns:
33219      * {DOMElement} or false if the renderer could not draw the point
33220      */ 
33221     drawPoint: function(node, geometry) {
33222         return this.drawCircle(node, geometry, 1);
33223     },
33224
33225     /**
33226      * Method: drawCircle
33227      * This method is only called by the renderer itself.
33228      * 
33229      * Parameters: 
33230      * node - {DOMElement}
33231      * geometry - {<OpenLayers.Geometry>}
33232      * radius - {Float}
33233      * 
33234      * Returns:
33235      * {DOMElement} or false if the renderer could not draw the circle
33236      */
33237     drawCircle: function(node, geometry, radius) {
33238         var resolution = this.getResolution();
33239         var x = ((geometry.x - this.featureDx) / resolution + this.left);
33240         var y = (this.top - geometry.y / resolution);
33241
33242         if (this.inValidRange(x, y)) { 
33243             node.setAttributeNS(null, "cx", x);
33244             node.setAttributeNS(null, "cy", y);
33245             node.setAttributeNS(null, "r", radius);
33246             return node;
33247         } else {
33248             return false;
33249         }    
33250             
33251     },
33252     
33253     /**
33254      * Method: drawLineString
33255      * This method is only called by the renderer itself.
33256      * 
33257      * Parameters: 
33258      * node - {DOMElement}
33259      * geometry - {<OpenLayers.Geometry>}
33260      * 
33261      * Returns:
33262      * {DOMElement} or null if the renderer could not draw all components of
33263      *     the linestring, or false if nothing could be drawn
33264      */ 
33265     drawLineString: function(node, geometry) {
33266         var componentsResult = this.getComponentsString(geometry.components);
33267         if (componentsResult.path) {
33268             node.setAttributeNS(null, "points", componentsResult.path);
33269             return (componentsResult.complete ? node : null);  
33270         } else {
33271             return false;
33272         }
33273     },
33274     
33275     /**
33276      * Method: drawLinearRing
33277      * This method is only called by the renderer itself.
33278      * 
33279      * Parameters: 
33280      * node - {DOMElement}
33281      * geometry - {<OpenLayers.Geometry>}
33282      * 
33283      * Returns:
33284      * {DOMElement} or null if the renderer could not draw all components
33285      *     of the linear ring, or false if nothing could be drawn
33286      */ 
33287     drawLinearRing: function(node, geometry) {
33288         var componentsResult = this.getComponentsString(geometry.components);
33289         if (componentsResult.path) {
33290             node.setAttributeNS(null, "points", componentsResult.path);
33291             return (componentsResult.complete ? node : null);  
33292         } else {
33293             return false;
33294         }
33295     },
33296     
33297     /**
33298      * Method: drawPolygon
33299      * This method is only called by the renderer itself.
33300      * 
33301      * Parameters: 
33302      * node - {DOMElement}
33303      * geometry - {<OpenLayers.Geometry>}
33304      * 
33305      * Returns:
33306      * {DOMElement} or null if the renderer could not draw all components
33307      *     of the polygon, or false if nothing could be drawn
33308      */ 
33309     drawPolygon: function(node, geometry) {
33310         var d = "";
33311         var draw = true;
33312         var complete = true;
33313         var linearRingResult, path;
33314         for (var j=0, len=geometry.components.length; j<len; j++) {
33315             d += " M";
33316             linearRingResult = this.getComponentsString(
33317                 geometry.components[j].components, " ");
33318             path = linearRingResult.path;
33319             if (path) {
33320                 d += " " + path;
33321                 complete = linearRingResult.complete && complete;
33322             } else {
33323                 draw = false;
33324             }
33325         }
33326         d += " z";
33327         if (draw) {
33328             node.setAttributeNS(null, "d", d);
33329             node.setAttributeNS(null, "fill-rule", "evenodd");
33330             return complete ? node : null;
33331         } else {
33332             return false;
33333         }    
33334     },
33335     
33336     /**
33337      * Method: drawRectangle
33338      * This method is only called by the renderer itself.
33339      * 
33340      * Parameters: 
33341      * node - {DOMElement}
33342      * geometry - {<OpenLayers.Geometry>}
33343      * 
33344      * Returns:
33345      * {DOMElement} or false if the renderer could not draw the rectangle
33346      */ 
33347     drawRectangle: function(node, geometry) {
33348         var resolution = this.getResolution();
33349         var x = ((geometry.x - this.featureDx) / resolution + this.left);
33350         var y = (this.top - geometry.y / resolution);
33351
33352         if (this.inValidRange(x, y)) { 
33353             node.setAttributeNS(null, "x", x);
33354             node.setAttributeNS(null, "y", y);
33355             node.setAttributeNS(null, "width", geometry.width / resolution);
33356             node.setAttributeNS(null, "height", geometry.height / resolution);
33357             return node;
33358         } else {
33359             return false;
33360         }
33361     },
33362     
33363     /**
33364      * Method: drawText
33365      * This method is only called by the renderer itself.
33366      *
33367      * Parameters:
33368      * featureId - {String}
33369      * style -
33370      * location - {<OpenLayers.Geometry.Point>}
33371      */
33372     drawText: function(featureId, style, location) {
33373         var drawOutline = (!!style.labelOutlineWidth);
33374         // First draw text in halo color and size and overlay the
33375         // normal text afterwards
33376         if (drawOutline) {
33377             var outlineStyle = OpenLayers.Util.extend({}, style);
33378             outlineStyle.fontColor = outlineStyle.labelOutlineColor;
33379             outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor;
33380             outlineStyle.fontStrokeWidth = style.labelOutlineWidth;
33381             if (style.labelOutlineOpacity) {
33382                 outlineStyle.fontOpacity = style.labelOutlineOpacity;
33383             }
33384             delete outlineStyle.labelOutlineWidth;
33385             this.drawText(featureId, outlineStyle, location);
33386         }
33387
33388         var resolution = this.getResolution();
33389
33390         var x = ((location.x - this.featureDx) / resolution + this.left);
33391         var y = (location.y / resolution - this.top);
33392
33393         var suffix = (drawOutline)?this.LABEL_OUTLINE_SUFFIX:this.LABEL_ID_SUFFIX;
33394         var label = this.nodeFactory(featureId + suffix, "text");
33395
33396         label.setAttributeNS(null, "x", x);
33397         label.setAttributeNS(null, "y", -y);
33398
33399         if (style.fontColor) {
33400             label.setAttributeNS(null, "fill", style.fontColor);
33401         }
33402         if (style.fontStrokeColor) {
33403             label.setAttributeNS(null, "stroke", style.fontStrokeColor);
33404         }
33405         if (style.fontStrokeWidth) {
33406             label.setAttributeNS(null, "stroke-width", style.fontStrokeWidth);
33407         }
33408         if (style.fontOpacity) {
33409             label.setAttributeNS(null, "opacity", style.fontOpacity);
33410         }
33411         if (style.fontFamily) {
33412             label.setAttributeNS(null, "font-family", style.fontFamily);
33413         }
33414         if (style.fontSize) {
33415             label.setAttributeNS(null, "font-size", style.fontSize);
33416         }
33417         if (style.fontWeight) {
33418             label.setAttributeNS(null, "font-weight", style.fontWeight);
33419         }
33420         if (style.fontStyle) {
33421             label.setAttributeNS(null, "font-style", style.fontStyle);
33422         }
33423         if (style.labelSelect === true) {
33424             label.setAttributeNS(null, "pointer-events", "visible");
33425             label._featureId = featureId;
33426         } else {
33427             label.setAttributeNS(null, "pointer-events", "none");
33428         }
33429         var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign;
33430         label.setAttributeNS(null, "text-anchor",
33431             OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle");
33432
33433         if (OpenLayers.IS_GECKO === true) {
33434             label.setAttributeNS(null, "dominant-baseline",
33435                 OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central");
33436         }
33437
33438         var labelRows = style.label.split('\n');
33439         var numRows = labelRows.length;
33440         while (label.childNodes.length > numRows) {
33441             label.removeChild(label.lastChild);
33442         }
33443         for (var i = 0; i < numRows; i++) {
33444             var tspan = this.nodeFactory(featureId + suffix + "_tspan_" + i, "tspan");
33445             if (style.labelSelect === true) {
33446                 tspan._featureId = featureId;
33447                 tspan._geometry = location;
33448                 tspan._geometryClass = location.CLASS_NAME;
33449             }
33450             if (OpenLayers.IS_GECKO === false) {
33451                 tspan.setAttributeNS(null, "baseline-shift",
33452                     OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%");
33453             }
33454             tspan.setAttribute("x", x);
33455             if (i == 0) {
33456                 var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]];
33457                 if (vfactor == null) {
33458                      vfactor = -.5;
33459                 }
33460                 tspan.setAttribute("dy", (vfactor*(numRows-1)) + "em");
33461             } else {
33462                 tspan.setAttribute("dy", "1em");
33463             }
33464             tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i];
33465             if (!tspan.parentNode) {
33466                 label.appendChild(tspan);
33467             }
33468         }
33469
33470         if (!label.parentNode) {
33471             this.textRoot.appendChild(label);
33472         }
33473     },
33474     
33475     /** 
33476      * Method: getComponentString
33477      * 
33478      * Parameters:
33479      * components - {Array(<OpenLayers.Geometry.Point>)} Array of points
33480      * separator - {String} character between coordinate pairs. Defaults to ","
33481      * 
33482      * Returns:
33483      * {Object} hash with properties "path" (the string created from the
33484      *     components and "complete" (false if the renderer was unable to
33485      *     draw all components)
33486      */
33487     getComponentsString: function(components, separator) {
33488         var renderCmp = [];
33489         var complete = true;
33490         var len = components.length;
33491         var strings = [];
33492         var str, component;
33493         for(var i=0; i<len; i++) {
33494             component = components[i];
33495             renderCmp.push(component);
33496             str = this.getShortString(component);
33497             if (str) {
33498                 strings.push(str);
33499             } else {
33500                 // The current component is outside the valid range. Let's
33501                 // see if the previous or next component is inside the range.
33502                 // If so, add the coordinate of the intersection with the
33503                 // valid range bounds.
33504                 if (i > 0) {
33505                     if (this.getShortString(components[i - 1])) {
33506                         strings.push(this.clipLine(components[i],
33507                             components[i-1]));
33508                     }
33509                 }
33510                 if (i < len - 1) {
33511                     if (this.getShortString(components[i + 1])) {
33512                         strings.push(this.clipLine(components[i],
33513                             components[i+1]));
33514                     }
33515                 }
33516                 complete = false;
33517             }
33518         }
33519
33520         return {
33521             path: strings.join(separator || ","),
33522             complete: complete
33523         };
33524     },
33525     
33526     /**
33527      * Method: clipLine
33528      * Given two points (one inside the valid range, and one outside),
33529      * clips the line betweeen the two points so that the new points are both
33530      * inside the valid range.
33531      * 
33532      * Parameters:
33533      * badComponent - {<OpenLayers.Geometry.Point>} original geometry of the
33534      *     invalid point
33535      * goodComponent - {<OpenLayers.Geometry.Point>} original geometry of the
33536      *     valid point
33537      * Returns
33538      * {String} the SVG coordinate pair of the clipped point (like
33539      *     getShortString), or an empty string if both passed componets are at
33540      *     the same point.
33541      */
33542     clipLine: function(badComponent, goodComponent) {
33543         if (goodComponent.equals(badComponent)) {
33544             return "";
33545         }
33546         var resolution = this.getResolution();
33547         var maxX = this.MAX_PIXEL - this.translationParameters.x;
33548         var maxY = this.MAX_PIXEL - this.translationParameters.y;
33549         var x1 = (goodComponent.x - this.featureDx) / resolution + this.left;
33550         var y1 = this.top - goodComponent.y / resolution;
33551         var x2 = (badComponent.x - this.featureDx) / resolution + this.left;
33552         var y2 = this.top - badComponent.y / resolution;
33553         var k;
33554         if (x2 < -maxX || x2 > maxX) {
33555             k = (y2 - y1) / (x2 - x1);
33556             x2 = x2 < 0 ? -maxX : maxX;
33557             y2 = y1 + (x2 - x1) * k;
33558         }
33559         if (y2 < -maxY || y2 > maxY) {
33560             k = (x2 - x1) / (y2 - y1);
33561             y2 = y2 < 0 ? -maxY : maxY;
33562             x2 = x1 + (y2 - y1) * k;
33563         }
33564         return x2 + "," + y2;
33565     },
33566
33567     /** 
33568      * Method: getShortString
33569      * 
33570      * Parameters:
33571      * point - {<OpenLayers.Geometry.Point>}
33572      * 
33573      * Returns:
33574      * {String} or false if point is outside the valid range
33575      */
33576     getShortString: function(point) {
33577         var resolution = this.getResolution();
33578         var x = ((point.x - this.featureDx) / resolution + this.left);
33579         var y = (this.top - point.y / resolution);
33580
33581         if (this.inValidRange(x, y)) { 
33582             return x + "," + y;
33583         } else {
33584             return false;
33585         }
33586     },
33587     
33588     /**
33589      * Method: getPosition
33590      * Finds the position of an svg node.
33591      * 
33592      * Parameters:
33593      * node - {DOMElement}
33594      * 
33595      * Returns:
33596      * {Object} hash with x and y properties, representing the coordinates
33597      *     within the svg coordinate system
33598      */
33599     getPosition: function(node) {
33600         return({
33601             x: parseFloat(node.getAttributeNS(null, "cx")),
33602             y: parseFloat(node.getAttributeNS(null, "cy"))
33603         });
33604     },
33605
33606     /**
33607      * Method: importSymbol
33608      * add a new symbol definition from the rendererer's symbol hash
33609      * 
33610      * Parameters:
33611      * graphicName - {String} name of the symbol to import
33612      * 
33613      * Returns:
33614      * {DOMElement} - the imported symbol
33615      */      
33616     importSymbol: function (graphicName)  {
33617         if (!this.defs) {
33618             // create svg defs tag
33619             this.defs = this.createDefs();
33620         }
33621         var id = this.container.id + "-" + graphicName;
33622         
33623         // check if symbol already exists in the defs
33624         var existing = document.getElementById(id);
33625         if (existing != null) {
33626             return existing;
33627         }
33628         
33629         var symbol = OpenLayers.Renderer.symbol[graphicName];
33630         if (!symbol) {
33631             throw new Error(graphicName + ' is not a valid symbol name');
33632         }
33633
33634         var symbolNode = this.nodeFactory(id, "symbol");
33635         var node = this.nodeFactory(null, "polygon");
33636         symbolNode.appendChild(node);
33637         var symbolExtent = new OpenLayers.Bounds(
33638                                     Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
33639
33640         var points = [];
33641         var x,y;
33642         for (var i=0; i<symbol.length; i=i+2) {
33643             x = symbol[i];
33644             y = symbol[i+1];
33645             symbolExtent.left = Math.min(symbolExtent.left, x);
33646             symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
33647             symbolExtent.right = Math.max(symbolExtent.right, x);
33648             symbolExtent.top = Math.max(symbolExtent.top, y);
33649             points.push(x, ",", y);
33650         }
33651         
33652         node.setAttributeNS(null, "points", points.join(" "));
33653         
33654         var width = symbolExtent.getWidth();
33655         var height = symbolExtent.getHeight();
33656         // create a viewBox three times as large as the symbol itself,
33657         // to allow for strokeWidth being displayed correctly at the corners.
33658         var viewBox = [symbolExtent.left - width,
33659                         symbolExtent.bottom - height, width * 3, height * 3];
33660         symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
33661         this.symbolMetrics[id] = [
33662             Math.max(width, height),
33663             symbolExtent.getCenterLonLat().lon,
33664             symbolExtent.getCenterLonLat().lat
33665         ];
33666         
33667         this.defs.appendChild(symbolNode);
33668         return symbolNode;
33669     },
33670     
33671     /**
33672      * Method: getFeatureIdFromEvent
33673      * 
33674      * Parameters:
33675      * evt - {Object} An <OpenLayers.Event> object
33676      *
33677      * Returns:
33678      * {String} A feature id or undefined.
33679      */
33680     getFeatureIdFromEvent: function(evt) {
33681         var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);
33682         if(!featureId) {
33683             var target = evt.target;
33684             featureId = target.parentNode && target != this.rendererRoot ?
33685                 target.parentNode._featureId : undefined;
33686         }
33687         return featureId;
33688     },
33689
33690     CLASS_NAME: "OpenLayers.Renderer.SVG"
33691 });
33692
33693 /**
33694  * Constant: OpenLayers.Renderer.SVG.LABEL_ALIGN
33695  * {Object}
33696  */
33697 OpenLayers.Renderer.SVG.LABEL_ALIGN = {
33698     "l": "start",
33699     "r": "end",
33700     "b": "bottom",
33701     "t": "hanging"
33702 };
33703
33704 /**
33705  * Constant: OpenLayers.Renderer.SVG.LABEL_VSHIFT
33706  * {Object}
33707  */
33708 OpenLayers.Renderer.SVG.LABEL_VSHIFT = {
33709     // according to
33710     // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html
33711     // a baseline-shift of -70% shifts the text exactly from the
33712     // bottom to the top of the baseline, so -35% moves the text to
33713     // the center of the baseline.
33714     "t": "-70%",
33715     "b": "0"    
33716 };
33717
33718 /**
33719  * Constant: OpenLayers.Renderer.SVG.LABEL_VFACTOR
33720  * {Object}
33721  */
33722 OpenLayers.Renderer.SVG.LABEL_VFACTOR = {
33723     "t": 0,
33724     "b": -1
33725 };
33726
33727 /**
33728  * Function: OpenLayers.Renderer.SVG.preventDefault
33729  * *Deprecated*.  Use <OpenLayers.Event.preventDefault> method instead.
33730  * Used to prevent default events (especially opening images in a new tab on
33731  * ctrl-click) from being executed for externalGraphic symbols
33732  */
33733 OpenLayers.Renderer.SVG.preventDefault = function(e) {
33734     OpenLayers.Event.preventDefault(e);
33735 };
33736 /* ======================================================================
33737     OpenLayers/Control/PanZoom.js
33738    ====================================================================== */
33739
33740 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
33741  * full list of contributors). Published under the 2-clause BSD license.
33742  * See license.txt in the OpenLayers distribution or repository for the
33743  * full text of the license. */
33744
33745
33746 /**
33747  * @requires OpenLayers/Control.js
33748  * @requires OpenLayers/Events/buttonclick.js
33749  */
33750
33751 /**
33752  * Class: OpenLayers.Control.PanZoom
33753  * The PanZoom is a visible control, composed of a
33754  * <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomPanel>. By
33755  * default it is drawn in the upper left corner of the map.
33756  *
33757  * Inherits from:
33758  *  - <OpenLayers.Control>
33759  */
33760 OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, {
33761
33762     /** 
33763      * APIProperty: slideFactor
33764      * {Integer} Number of pixels by which we'll pan the map in any direction 
33765      *     on clicking the arrow buttons.  If you want to pan by some ratio
33766      *     of the map dimensions, use <slideRatio> instead.
33767      */
33768     slideFactor: 50,
33769
33770     /** 
33771      * APIProperty: slideRatio
33772      * {Number} The fraction of map width/height by which we'll pan the map            
33773      *     on clicking the arrow buttons.  Default is null.  If set, will
33774      *     override <slideFactor>. E.g. if slideRatio is .5, then the Pan Up
33775      *     button will pan up half the map height. 
33776      */
33777     slideRatio: null,
33778
33779     /** 
33780      * Property: buttons
33781      * {Array(DOMElement)} Array of Button Divs 
33782      */
33783     buttons: null,
33784
33785     /** 
33786      * Property: position
33787      * {<OpenLayers.Pixel>} 
33788      */
33789     position: null,
33790
33791     /**
33792      * Constructor: OpenLayers.Control.PanZoom
33793      * 
33794      * Parameters:
33795      * options - {Object}
33796      */
33797     initialize: function(options) {
33798         this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X,
33799                                              OpenLayers.Control.PanZoom.Y);
33800         OpenLayers.Control.prototype.initialize.apply(this, arguments);
33801     },
33802
33803     /**
33804      * APIMethod: destroy
33805      */
33806     destroy: function() {
33807         if (this.map) {
33808             this.map.events.unregister("buttonclick", this, this.onButtonClick);
33809         }
33810         this.removeButtons();
33811         this.buttons = null;
33812         this.position = null;
33813         OpenLayers.Control.prototype.destroy.apply(this, arguments);
33814     },
33815
33816     /** 
33817      * Method: setMap
33818      *
33819      * Properties:
33820      * map - {<OpenLayers.Map>} 
33821      */
33822     setMap: function(map) {
33823         OpenLayers.Control.prototype.setMap.apply(this, arguments);
33824         this.map.events.register("buttonclick", this, this.onButtonClick);
33825     },
33826
33827     /**
33828      * Method: draw
33829      *
33830      * Parameters:
33831      * px - {<OpenLayers.Pixel>} 
33832      * 
33833      * Returns:
33834      * {DOMElement} A reference to the container div for the PanZoom control.
33835      */
33836     draw: function(px) {
33837         // initialize our internal div
33838         OpenLayers.Control.prototype.draw.apply(this, arguments);
33839         px = this.position;
33840
33841         // place the controls
33842         this.buttons = [];
33843
33844         var sz = {w: 18, h: 18};
33845         var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
33846
33847         this._addButton("panup", "north-mini.png", centered, sz);
33848         px.y = centered.y+sz.h;
33849         this._addButton("panleft", "west-mini.png", px, sz);
33850         this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz);
33851         this._addButton("pandown", "south-mini.png", 
33852                         centered.add(0, sz.h*2), sz);
33853         this._addButton("zoomin", "zoom-plus-mini.png", 
33854                         centered.add(0, sz.h*3+5), sz);
33855         this._addButton("zoomworld", "zoom-world-mini.png", 
33856                         centered.add(0, sz.h*4+5), sz);
33857         this._addButton("zoomout", "zoom-minus-mini.png", 
33858                         centered.add(0, sz.h*5+5), sz);
33859         return this.div;
33860     },
33861     
33862     /**
33863      * Method: _addButton
33864      * 
33865      * Parameters:
33866      * id - {String} 
33867      * img - {String} 
33868      * xy - {<OpenLayers.Pixel>} 
33869      * sz - {<OpenLayers.Size>} 
33870      * 
33871      * Returns:
33872      * {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the
33873      *     image of the button, and has all the proper event handlers set.
33874      */
33875     _addButton:function(id, img, xy, sz) {
33876         var imgLocation = OpenLayers.Util.getImageLocation(img);
33877         var btn = OpenLayers.Util.createAlphaImageDiv(
33878                                     this.id + "_" + id, 
33879                                     xy, sz, imgLocation, "absolute");
33880         btn.style.cursor = "pointer";
33881         //we want to add the outer div
33882         this.div.appendChild(btn);
33883         btn.action = id;
33884         btn.className = "olButton";
33885     
33886         //we want to remember/reference the outer div
33887         this.buttons.push(btn);
33888         return btn;
33889     },
33890     
33891     /**
33892      * Method: _removeButton
33893      * 
33894      * Parameters:
33895      * btn - {Object}
33896      */
33897     _removeButton: function(btn) {
33898         this.div.removeChild(btn);
33899         OpenLayers.Util.removeItem(this.buttons, btn);
33900     },
33901     
33902     /**
33903      * Method: removeButtons
33904      */
33905     removeButtons: function() {
33906         for(var i=this.buttons.length-1; i>=0; --i) {
33907             this._removeButton(this.buttons[i]);
33908         }
33909     },
33910     
33911     /**
33912      * Method: onButtonClick
33913      *
33914      * Parameters:
33915      * evt - {Event}
33916      */
33917     onButtonClick: function(evt) {
33918         var btn = evt.buttonElement;
33919         switch (btn.action) {
33920             case "panup": 
33921                 this.map.pan(0, -this.getSlideFactor("h"));
33922                 break;
33923             case "pandown": 
33924                 this.map.pan(0, this.getSlideFactor("h"));
33925                 break;
33926             case "panleft": 
33927                 this.map.pan(-this.getSlideFactor("w"), 0);
33928                 break;
33929             case "panright": 
33930                 this.map.pan(this.getSlideFactor("w"), 0);
33931                 break;
33932             case "zoomin": 
33933                 this.map.zoomIn(); 
33934                 break;
33935             case "zoomout": 
33936                 this.map.zoomOut(); 
33937                 break;
33938             case "zoomworld": 
33939                 this.map.zoomToMaxExtent(); 
33940                 break;
33941         }
33942     },
33943     
33944     /**
33945      * Method: getSlideFactor
33946      *
33947      * Parameters:
33948      * dim - {String} "w" or "h" (for width or height).
33949      *
33950      * Returns:
33951      * {Number} The slide factor for panning in the requested direction.
33952      */
33953     getSlideFactor: function(dim) {
33954         return this.slideRatio ?
33955             this.map.getSize()[dim] * this.slideRatio :
33956             this.slideFactor;
33957     },
33958
33959     CLASS_NAME: "OpenLayers.Control.PanZoom"
33960 });
33961
33962 /**
33963  * Constant: X
33964  * {Integer}
33965  */
33966 OpenLayers.Control.PanZoom.X = 4;
33967
33968 /**
33969  * Constant: Y
33970  * {Integer}
33971  */
33972 OpenLayers.Control.PanZoom.Y = 4;
33973 /* ======================================================================
33974     OpenLayers/Icon.js
33975    ====================================================================== */
33976
33977 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
33978  * full list of contributors). Published under the 2-clause BSD license.
33979  * See license.txt in the OpenLayers distribution or repository for the
33980  * full text of the license. */
33981
33982 /**
33983  * @requires OpenLayers/BaseTypes/Class.js
33984  */
33985
33986 /**
33987  * Class: OpenLayers.Icon
33988  * 
33989  * The icon represents a graphical icon on the screen.  Typically used in
33990  * conjunction with a <OpenLayers.Marker> to represent markers on a screen.
33991  *
33992  * An icon has a url, size and position.  It also contains an offset which 
33993  * allows the center point to be represented correctly.  This can be
33994  * provided either as a fixed offset or a function provided to calculate
33995  * the desired offset. 
33996  * 
33997  */
33998 OpenLayers.Icon = OpenLayers.Class({
33999     
34000     /** 
34001      * Property: url 
34002      * {String}  image url
34003      */
34004     url: null,
34005     
34006     /** 
34007      * Property: size 
34008      * {<OpenLayers.Size>|Object} An OpenLayers.Size or
34009      * an object with a 'w' and 'h' properties.
34010      */
34011     size: null,
34012
34013     /** 
34014      * Property: offset 
34015      * {<OpenLayers.Pixel>|Object} distance in pixels to offset the
34016      * image when being rendered. An OpenLayers.Pixel or an object
34017      * with a 'x' and 'y' properties.
34018      */
34019     offset: null,    
34020     
34021     /** 
34022      * Property: calculateOffset 
34023      * {Function} Function to calculate the offset (based on the size)
34024      */
34025     calculateOffset: null,    
34026     
34027     /** 
34028      * Property: imageDiv 
34029      * {DOMElement} 
34030      */
34031     imageDiv: null,
34032
34033     /** 
34034      * Property: px 
34035      * {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an object
34036      * with a 'x' and 'y' properties.
34037      */
34038     px: null,
34039     
34040     /** 
34041      * Constructor: OpenLayers.Icon
34042      * Creates an icon, which is an image tag in a div.  
34043      *
34044      * url - {String} 
34045      * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or an
34046      *                                   object with a 'w' and 'h'
34047      *                                   properties.
34048      * offset - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an
34049      *                                      object with a 'x' and 'y'
34050      *                                      properties.
34051      * calculateOffset - {Function} 
34052      */
34053     initialize: function(url, size, offset, calculateOffset) {
34054         this.url = url;
34055         this.size = size || {w: 20, h: 20};
34056         this.offset = offset || {x: -(this.size.w/2), y: -(this.size.h/2)};
34057         this.calculateOffset = calculateOffset;
34058
34059         var id = OpenLayers.Util.createUniqueID("OL_Icon_");
34060         this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id);
34061     },
34062     
34063     /** 
34064      * Method: destroy
34065      * Nullify references and remove event listeners to prevent circular 
34066      * references and memory leaks
34067      */
34068     destroy: function() {
34069         // erase any drawn elements
34070         this.erase();
34071
34072         OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild); 
34073         this.imageDiv.innerHTML = "";
34074         this.imageDiv = null;
34075     },
34076
34077     /** 
34078      * Method: clone
34079      * 
34080      * Returns:
34081      * {<OpenLayers.Icon>} A fresh copy of the icon.
34082      */
34083     clone: function() {
34084         return new OpenLayers.Icon(this.url, 
34085                                    this.size, 
34086                                    this.offset, 
34087                                    this.calculateOffset);
34088     },
34089     
34090     /**
34091      * Method: setSize
34092      * 
34093      * Parameters:
34094      * size - {<OpenLayers.Size>|Object} An OpenLayers.Size or
34095      * an object with a 'w' and 'h' properties.
34096      */
34097     setSize: function(size) {
34098         if (size != null) {
34099             this.size = size;
34100         }
34101         this.draw();
34102     },
34103     
34104     /**
34105      * Method: setUrl
34106      * 
34107      * Parameters:
34108      * url - {String} 
34109      */
34110     setUrl: function(url) {
34111         if (url != null) {
34112             this.url = url;
34113         }
34114         this.draw();
34115     },
34116
34117     /** 
34118      * Method: draw
34119      * Move the div to the given pixel.
34120      * 
34121      * Parameters:
34122      * px - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or an
34123      *                                  object with a 'x' and 'y' properties.
34124      * 
34125      * Returns:
34126      * {DOMElement} A new DOM Image of this icon set at the location passed-in
34127      */
34128     draw: function(px) {
34129         OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, 
34130                                             null, 
34131                                             null, 
34132                                             this.size, 
34133                                             this.url, 
34134                                             "absolute");
34135         this.moveTo(px);
34136         return this.imageDiv;
34137     }, 
34138
34139     /** 
34140      * Method: erase
34141      * Erase the underlying image element.
34142      */
34143     erase: function() {
34144         if (this.imageDiv != null && this.imageDiv.parentNode != null) {
34145             OpenLayers.Element.remove(this.imageDiv);
34146         }
34147     }, 
34148     
34149     /** 
34150      * Method: setOpacity
34151      * Change the icon's opacity
34152      *
34153      * Parameters:
34154      * opacity - {float} 
34155      */
34156     setOpacity: function(opacity) {
34157         OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null, 
34158                                             null, null, null, null, opacity);
34159
34160     },
34161     
34162     /**
34163      * Method: moveTo
34164      * move icon to passed in px.
34165      *
34166      * Parameters:
34167      * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.
34168      * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.
34169      */
34170     moveTo: function (px) {
34171         //if no px passed in, use stored location
34172         if (px != null) {
34173             this.px = px;
34174         }
34175
34176         if (this.imageDiv != null) {
34177             if (this.px == null) {
34178                 this.display(false);
34179             } else {
34180                 if (this.calculateOffset) {
34181                     this.offset = this.calculateOffset(this.size);  
34182                 }
34183                 OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, {
34184                     x: this.px.x + this.offset.x,
34185                     y: this.px.y + this.offset.y
34186                 });
34187             }
34188         }
34189     },
34190     
34191     /** 
34192      * Method: display
34193      * Hide or show the icon
34194      *
34195      * Parameters:
34196      * display - {Boolean} 
34197      */
34198     display: function(display) {
34199         this.imageDiv.style.display = (display) ? "" : "none"; 
34200     },
34201     
34202
34203     /**
34204      * APIMethod: isDrawn
34205      * 
34206      * Returns:
34207      * {Boolean} Whether or not the icon is drawn.
34208      */
34209     isDrawn: function() {
34210         // nodeType 11 for ie, whose nodes *always* have a parentNode
34211         // (of type document fragment)
34212         var isDrawn = (this.imageDiv && this.imageDiv.parentNode && 
34213                        (this.imageDiv.parentNode.nodeType != 11));    
34214
34215         return isDrawn;   
34216     },
34217
34218     CLASS_NAME: "OpenLayers.Icon"
34219 });
34220 /* ======================================================================
34221     OpenLayers/Marker.js
34222    ====================================================================== */
34223
34224 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
34225  * full list of contributors). Published under the 2-clause BSD license.
34226  * See license.txt in the OpenLayers distribution or repository for the
34227  * full text of the license. */
34228
34229
34230 /**
34231  * @requires OpenLayers/BaseTypes/Class.js
34232  * @requires OpenLayers/Events.js
34233  * @requires OpenLayers/Icon.js
34234  */
34235
34236 /**
34237  * Class: OpenLayers.Marker
34238  * Instances of OpenLayers.Marker are a combination of a 
34239  * <OpenLayers.LonLat> and an <OpenLayers.Icon>.  
34240  *
34241  * Markers are generally added to a special layer called
34242  * <OpenLayers.Layer.Markers>.
34243  *
34244  * Example:
34245  * (code)
34246  * var markers = new OpenLayers.Layer.Markers( "Markers" );
34247  * map.addLayer(markers);
34248  *
34249  * var size = new OpenLayers.Size(21,25);
34250  * var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
34251  * var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset);
34252  * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon));
34253  * markers.addMarker(new OpenLayers.Marker(new OpenLayers.LonLat(0,0),icon.clone()));
34254  *
34255  * (end)
34256  *
34257  * Note that if you pass an icon into the Marker constructor, it will take
34258  * that icon and use it. This means that you should not share icons between
34259  * markers -- you use them once, but you should clone() for any additional
34260  * markers using that same icon.
34261  */
34262 OpenLayers.Marker = OpenLayers.Class({
34263     
34264     /** 
34265      * Property: icon 
34266      * {<OpenLayers.Icon>} The icon used by this marker.
34267      */
34268     icon: null,
34269
34270     /** 
34271      * Property: lonlat 
34272      * {<OpenLayers.LonLat>} location of object
34273      */
34274     lonlat: null,
34275     
34276     /** 
34277      * Property: events 
34278      * {<OpenLayers.Events>} the event handler.
34279      */
34280     events: null,
34281     
34282     /** 
34283      * Property: map 
34284      * {<OpenLayers.Map>} the map this marker is attached to
34285      */
34286     map: null,
34287     
34288     /** 
34289      * Constructor: OpenLayers.Marker
34290      *
34291      * Parameters:
34292      * lonlat - {<OpenLayers.LonLat>} the position of this marker
34293      * icon - {<OpenLayers.Icon>}  the icon for this marker
34294      */
34295     initialize: function(lonlat, icon) {
34296         this.lonlat = lonlat;
34297         
34298         var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon();
34299         if (this.icon == null) {
34300             this.icon = newIcon;
34301         } else {
34302             this.icon.url = newIcon.url;
34303             this.icon.size = newIcon.size;
34304             this.icon.offset = newIcon.offset;
34305             this.icon.calculateOffset = newIcon.calculateOffset;
34306         }
34307         this.events = new OpenLayers.Events(this, this.icon.imageDiv);
34308     },
34309     
34310     /**
34311      * APIMethod: destroy
34312      * Destroy the marker. You must first remove the marker from any 
34313      * layer which it has been added to, or you will get buggy behavior.
34314      * (This can not be done within the marker since the marker does not
34315      * know which layer it is attached to.)
34316      */
34317     destroy: function() {
34318         // erase any drawn features
34319         this.erase();
34320
34321         this.map = null;
34322
34323         this.events.destroy();
34324         this.events = null;
34325
34326         if (this.icon != null) {
34327             this.icon.destroy();
34328             this.icon = null;
34329         }
34330     },
34331     
34332     /** 
34333     * Method: draw
34334     * Calls draw on the icon, and returns that output.
34335     * 
34336     * Parameters:
34337     * px - {<OpenLayers.Pixel>}
34338     * 
34339     * Returns:
34340     * {DOMElement} A new DOM Image with this marker's icon set at the 
34341     * location passed-in
34342     */
34343     draw: function(px) {
34344         return this.icon.draw(px);
34345     }, 
34346
34347     /** 
34348     * Method: erase
34349     * Erases any drawn elements for this marker.
34350     */
34351     erase: function() {
34352         if (this.icon != null) {
34353             this.icon.erase();
34354         }
34355     }, 
34356
34357     /**
34358     * Method: moveTo
34359     * Move the marker to the new location.
34360     *
34361     * Parameters:
34362     * px - {<OpenLayers.Pixel>|Object} the pixel position to move to.
34363     * An OpenLayers.Pixel or an object with a 'x' and 'y' properties.
34364     */
34365     moveTo: function (px) {
34366         if ((px != null) && (this.icon != null)) {
34367             this.icon.moveTo(px);
34368         }           
34369         this.lonlat = this.map.getLonLatFromLayerPx(px);
34370     },
34371
34372     /**
34373      * APIMethod: isDrawn
34374      * 
34375      * Returns:
34376      * {Boolean} Whether or not the marker is drawn.
34377      */
34378     isDrawn: function() {
34379         var isDrawn = (this.icon && this.icon.isDrawn());
34380         return isDrawn;   
34381     },
34382
34383     /**
34384      * Method: onScreen
34385      *
34386      * Returns:
34387      * {Boolean} Whether or not the marker is currently visible on screen.
34388      */
34389     onScreen:function() {
34390         
34391         var onScreen = false;
34392         if (this.map) {
34393             var screenBounds = this.map.getExtent();
34394             onScreen = screenBounds.containsLonLat(this.lonlat);
34395         }    
34396         return onScreen;
34397     },
34398     
34399     /**
34400      * Method: inflate
34401      * Englarges the markers icon by the specified ratio.
34402      *
34403      * Parameters:
34404      * inflate - {float} the ratio to enlarge the marker by (passing 2
34405      *                   will double the size).
34406      */
34407     inflate: function(inflate) {
34408         if (this.icon) {
34409             this.icon.setSize({
34410                 w: this.icon.size.w * inflate,
34411                 h: this.icon.size.h * inflate
34412             });
34413         }        
34414     },
34415     
34416     /** 
34417      * Method: setOpacity
34418      * Change the opacity of the marker by changin the opacity of 
34419      *   its icon
34420      * 
34421      * Parameters:
34422      * opacity - {float}  Specified as fraction (0.4, etc)
34423      */
34424     setOpacity: function(opacity) {
34425         this.icon.setOpacity(opacity);
34426     },
34427
34428     /**
34429      * Method: setUrl
34430      * Change URL of the Icon Image.
34431      * 
34432      * url - {String} 
34433      */
34434     setUrl: function(url) {
34435         this.icon.setUrl(url);
34436     },    
34437
34438     /** 
34439      * Method: display
34440      * Hide or show the icon
34441      * 
34442      * display - {Boolean} 
34443      */
34444     display: function(display) {
34445         this.icon.display(display);
34446     },
34447
34448     CLASS_NAME: "OpenLayers.Marker"
34449 });
34450
34451
34452 /**
34453  * Function: defaultIcon
34454  * Creates a default <OpenLayers.Icon>.
34455  * 
34456  * Returns:
34457  * {<OpenLayers.Icon>} A default OpenLayers.Icon to use for a marker
34458  */
34459 OpenLayers.Marker.defaultIcon = function() {
34460     return new OpenLayers.Icon(OpenLayers.Util.getImageLocation("marker.png"),
34461                                {w: 21, h: 25}, {x: -10.5, y: -25});
34462 };
34463     
34464
34465 /* ======================================================================
34466     OpenLayers/Popup.js
34467    ====================================================================== */
34468
34469 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
34470  * full list of contributors). Published under the 2-clause BSD license.
34471  * See license.txt in the OpenLayers distribution or repository for the
34472  * full text of the license. */
34473
34474 /**
34475  * @requires OpenLayers/BaseTypes/Class.js
34476  */
34477
34478
34479 /**
34480  * Class: OpenLayers.Popup
34481  * A popup is a small div that can opened and closed on the map.
34482  * Typically opened in response to clicking on a marker.  
34483  * See <OpenLayers.Marker>.  Popup's don't require their own
34484  * layer and are added the the map using the <OpenLayers.Map.addPopup>
34485  * method.
34486  *
34487  * Example:
34488  * (code)
34489  * popup = new OpenLayers.Popup("chicken", 
34490  *                    new OpenLayers.LonLat(5,40),
34491  *                    new OpenLayers.Size(200,200),
34492  *                    "example popup",
34493  *                    true);
34494  *       
34495  * map.addPopup(popup);
34496  * (end)
34497  */
34498 OpenLayers.Popup = OpenLayers.Class({
34499
34500     /** 
34501      * Property: events  
34502      * {<OpenLayers.Events>} custom event manager 
34503      */
34504     events: null,
34505     
34506     /** Property: id
34507      * {String} the unique identifier assigned to this popup.
34508      */
34509     id: "",
34510
34511     /** 
34512      * Property: lonlat 
34513      * {<OpenLayers.LonLat>} the position of this popup on the map
34514      */
34515     lonlat: null,
34516
34517     /** 
34518      * Property: div 
34519      * {DOMElement} the div that contains this popup.
34520      */
34521     div: null,
34522
34523     /** 
34524      * Property: contentSize 
34525      * {<OpenLayers.Size>} the width and height of the content.
34526      */
34527     contentSize: null,    
34528
34529     /** 
34530      * Property: size 
34531      * {<OpenLayers.Size>} the width and height of the popup.
34532      */
34533     size: null,    
34534
34535     /** 
34536      * Property: contentHTML 
34537      * {String} An HTML string for this popup to display.
34538      */
34539     contentHTML: null,
34540     
34541     /** 
34542      * Property: backgroundColor 
34543      * {String} the background color used by the popup.
34544      */
34545     backgroundColor: "",
34546     
34547     /** 
34548      * Property: opacity 
34549      * {float} the opacity of this popup (between 0.0 and 1.0)
34550      */
34551     opacity: "",
34552
34553     /** 
34554      * Property: border 
34555      * {String} the border size of the popup.  (eg 2px)
34556      */
34557     border: "",
34558     
34559     /** 
34560      * Property: contentDiv 
34561      * {DOMElement} a reference to the element that holds the content of
34562      *              the div.
34563      */
34564     contentDiv: null,
34565     
34566     /** 
34567      * Property: groupDiv 
34568      * {DOMElement} First and only child of 'div'. The group Div contains the
34569      *     'contentDiv' and the 'closeDiv'.
34570      */
34571     groupDiv: null,
34572
34573     /** 
34574      * Property: closeDiv
34575      * {DOMElement} the optional closer image
34576      */
34577     closeDiv: null,
34578
34579     /** 
34580      * APIProperty: autoSize
34581      * {Boolean} Resize the popup to auto-fit the contents.
34582      *     Default is false.
34583      */
34584     autoSize: false,
34585
34586     /**
34587      * APIProperty: minSize
34588      * {<OpenLayers.Size>} Minimum size allowed for the popup's contents.
34589      */
34590     minSize: null,
34591
34592     /**
34593      * APIProperty: maxSize
34594      * {<OpenLayers.Size>} Maximum size allowed for the popup's contents.
34595      */
34596     maxSize: null,
34597
34598     /** 
34599      * Property: displayClass
34600      * {String} The CSS class of the popup.
34601      */
34602     displayClass: "olPopup",
34603
34604     /** 
34605      * Property: contentDisplayClass
34606      * {String} The CSS class of the popup content div.
34607      */
34608     contentDisplayClass: "olPopupContent",
34609
34610     /** 
34611      * Property: padding 
34612      * {int or <OpenLayers.Bounds>} An extra opportunity to specify internal 
34613      *     padding of the content div inside the popup. This was originally
34614      *     confused with the css padding as specified in style.css's 
34615      *     'olPopupContent' class. We would like to get rid of this altogether,
34616      *     except that it does come in handy for the framed and anchoredbubble
34617      *     popups, who need to maintain yet another barrier between their 
34618      *     content and the outer border of the popup itself. 
34619      * 
34620      *     Note that in order to not break API, we must continue to support 
34621      *     this property being set as an integer. Really, though, we'd like to 
34622      *     have this specified as a Bounds object so that user can specify
34623      *     distinct left, top, right, bottom paddings. With the 3.0 release
34624      *     we can make this only a bounds.
34625      */
34626     padding: 0,
34627
34628     /** 
34629      * Property: disableFirefoxOverflowHack
34630      * {Boolean} The hack for overflow in Firefox causes all elements 
34631      *     to be re-drawn, which causes Flash elements to be 
34632      *     re-initialized, which is troublesome.
34633      *     With this property the hack can be disabled.
34634      */
34635     disableFirefoxOverflowHack: false,
34636
34637     /**
34638      * Method: fixPadding
34639      * To be removed in 3.0, this function merely helps us to deal with the 
34640      *     case where the user may have set an integer value for padding, 
34641      *     instead of an <OpenLayers.Bounds> object.
34642      */
34643     fixPadding: function() {
34644         if (typeof this.padding == "number") {
34645             this.padding = new OpenLayers.Bounds(
34646                 this.padding, this.padding, this.padding, this.padding
34647             );
34648         }
34649     },
34650
34651     /**
34652      * APIProperty: panMapIfOutOfView
34653      * {Boolean} When drawn, pan map such that the entire popup is visible in
34654      *     the current viewport (if necessary).
34655      *     Default is false.
34656      */
34657     panMapIfOutOfView: false,
34658     
34659     /**
34660      * APIProperty: keepInMap 
34661      * {Boolean} If panMapIfOutOfView is false, and this property is true, 
34662      *     contrain the popup such that it always fits in the available map
34663      *     space. By default, this is not set on the base class. If you are
34664      *     creating popups that are near map edges and not allowing pannning,
34665      *     and especially if you have a popup which has a
34666      *     fixedRelativePosition, setting this to false may be a smart thing to
34667      *     do. Subclasses may want to override this setting.
34668      *   
34669      *     Default is false.
34670      */
34671     keepInMap: false,
34672
34673     /**
34674      * APIProperty: closeOnMove
34675      * {Boolean} When map pans, close the popup.
34676      *     Default is false.
34677      */
34678     closeOnMove: false,
34679     
34680     /** 
34681      * Property: map 
34682      * {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map
34683      */
34684     map: null,
34685
34686     /** 
34687     * Constructor: OpenLayers.Popup
34688     * Create a popup.
34689     * 
34690     * Parameters: 
34691     * id - {String} a unqiue identifier for this popup.  If null is passed
34692     *               an identifier will be automatically generated. 
34693     * lonlat - {<OpenLayers.LonLat>}  The position on the map the popup will
34694     *                                 be shown.
34695     * contentSize - {<OpenLayers.Size>} The size of the content.
34696     * contentHTML - {String}          An HTML string to display inside the   
34697     *                                 popup.
34698     * closeBox - {Boolean}            Whether to display a close box inside
34699     *                                 the popup.
34700     * closeBoxCallback - {Function}   Function to be called on closeBox click.
34701     */
34702     initialize:function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {
34703         if (id == null) {
34704             id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
34705         }
34706
34707         this.id = id;
34708         this.lonlat = lonlat;
34709
34710         this.contentSize = (contentSize != null) ? contentSize 
34711                                   : new OpenLayers.Size(
34712                                                    OpenLayers.Popup.WIDTH,
34713                                                    OpenLayers.Popup.HEIGHT);
34714         if (contentHTML != null) { 
34715              this.contentHTML = contentHTML;
34716         }
34717         this.backgroundColor = OpenLayers.Popup.COLOR;
34718         this.opacity = OpenLayers.Popup.OPACITY;
34719         this.border = OpenLayers.Popup.BORDER;
34720
34721         this.div = OpenLayers.Util.createDiv(this.id, null, null, 
34722                                              null, null, null, "hidden");
34723         this.div.className = this.displayClass;
34724         
34725         var groupDivId = this.id + "_GroupDiv";
34726         this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null, 
34727                                                     null, "relative", null,
34728                                                     "hidden");
34729
34730         var id = this.div.id + "_contentDiv";
34731         this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(), 
34732                                                     null, "relative");
34733         this.contentDiv.className = this.contentDisplayClass;
34734         this.groupDiv.appendChild(this.contentDiv);
34735         this.div.appendChild(this.groupDiv);
34736
34737         if (closeBox) {
34738             this.addCloseBox(closeBoxCallback);
34739         } 
34740
34741         this.registerEvents();
34742     },
34743
34744     /** 
34745      * Method: destroy
34746      * nullify references to prevent circular references and memory leaks
34747      */
34748     destroy: function() {
34749
34750         this.id = null;
34751         this.lonlat = null;
34752         this.size = null;
34753         this.contentHTML = null;
34754         
34755         this.backgroundColor = null;
34756         this.opacity = null;
34757         this.border = null;
34758         
34759         if (this.closeOnMove && this.map) {
34760             this.map.events.unregister("movestart", this, this.hide);
34761         }
34762
34763         this.events.destroy();
34764         this.events = null;
34765         
34766         if (this.closeDiv) {
34767             OpenLayers.Event.stopObservingElement(this.closeDiv); 
34768             this.groupDiv.removeChild(this.closeDiv);
34769         }
34770         this.closeDiv = null;
34771         
34772         this.div.removeChild(this.groupDiv);
34773         this.groupDiv = null;
34774
34775         if (this.map != null) {
34776             this.map.removePopup(this);
34777         }
34778         this.map = null;
34779         this.div = null;
34780         
34781         this.autoSize = null;
34782         this.minSize = null;
34783         this.maxSize = null;
34784         this.padding = null;
34785         this.panMapIfOutOfView = null;
34786     },
34787
34788     /** 
34789     * Method: draw
34790     * Constructs the elements that make up the popup.
34791     *
34792     * Parameters:
34793     * px - {<OpenLayers.Pixel>} the position the popup in pixels.
34794     * 
34795     * Returns:
34796     * {DOMElement} Reference to a div that contains the drawn popup
34797     */
34798     draw: function(px) {
34799         if (px == null) {
34800             if ((this.lonlat != null) && (this.map != null)) {
34801                 px = this.map.getLayerPxFromLonLat(this.lonlat);
34802             }
34803         }
34804
34805         // this assumes that this.map already exists, which is okay because 
34806         // this.draw is only called once the popup has been added to the map.
34807         if (this.closeOnMove) {
34808             this.map.events.register("movestart", this, this.hide);
34809         }
34810         
34811         //listen to movestart, moveend to disable overflow (FF bug)
34812         if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') {
34813             this.map.events.register("movestart", this, function() {
34814                 var style = document.defaultView.getComputedStyle(
34815                     this.contentDiv, null
34816                 );
34817                 var currentOverflow = style.getPropertyValue("overflow");
34818                 if (currentOverflow != "hidden") {
34819                     this.contentDiv._oldOverflow = currentOverflow;
34820                     this.contentDiv.style.overflow = "hidden";
34821                 }
34822             });
34823             this.map.events.register("moveend", this, function() {
34824                 var oldOverflow = this.contentDiv._oldOverflow;
34825                 if (oldOverflow) {
34826                     this.contentDiv.style.overflow = oldOverflow;
34827                     this.contentDiv._oldOverflow = null;
34828                 }
34829             });
34830         }
34831
34832         this.moveTo(px);
34833         if (!this.autoSize && !this.size) {
34834             this.setSize(this.contentSize);
34835         }
34836         this.setBackgroundColor();
34837         this.setOpacity();
34838         this.setBorder();
34839         this.setContentHTML();
34840         
34841         if (this.panMapIfOutOfView) {
34842             this.panIntoView();
34843         }    
34844
34845         return this.div;
34846     },
34847
34848     /** 
34849      * Method: updatePosition
34850      * if the popup has a lonlat and its map members set, 
34851      * then have it move itself to its proper position
34852      */
34853     updatePosition: function() {
34854         if ((this.lonlat) && (this.map)) {
34855             var px = this.map.getLayerPxFromLonLat(this.lonlat);
34856             if (px) {
34857                 this.moveTo(px);           
34858             }    
34859         }
34860     },
34861
34862     /**
34863      * Method: moveTo
34864      * 
34865      * Parameters:
34866      * px - {<OpenLayers.Pixel>} the top and left position of the popup div. 
34867      */
34868     moveTo: function(px) {
34869         if ((px != null) && (this.div != null)) {
34870             this.div.style.left = px.x + "px";
34871             this.div.style.top = px.y + "px";
34872         }
34873     },
34874
34875     /**
34876      * Method: visible
34877      *
34878      * Returns:      
34879      * {Boolean} Boolean indicating whether or not the popup is visible
34880      */
34881     visible: function() {
34882         return OpenLayers.Element.visible(this.div);
34883     },
34884
34885     /**
34886      * Method: toggle
34887      * Toggles visibility of the popup.
34888      */
34889     toggle: function() {
34890         if (this.visible()) {
34891             this.hide();
34892         } else {
34893             this.show();
34894         }
34895     },
34896
34897     /**
34898      * Method: show
34899      * Makes the popup visible.
34900      */
34901     show: function() {
34902         this.div.style.display = '';
34903
34904         if (this.panMapIfOutOfView) {
34905             this.panIntoView();
34906         }    
34907     },
34908
34909     /**
34910      * Method: hide
34911      * Makes the popup invisible.
34912      */
34913     hide: function() {
34914         this.div.style.display = 'none';
34915     },
34916
34917     /**
34918      * Method: setSize
34919      * Used to adjust the size of the popup. 
34920      *
34921      * Parameters:
34922      * contentSize - {<OpenLayers.Size>} the new size for the popup's 
34923      *     contents div (in pixels).
34924      */
34925     setSize:function(contentSize) { 
34926         this.size = contentSize.clone(); 
34927         
34928         // if our contentDiv has a css 'padding' set on it by a stylesheet, we 
34929         //  must add that to the desired "size". 
34930         var contentDivPadding = this.getContentDivPadding();
34931         var wPadding = contentDivPadding.left + contentDivPadding.right;
34932         var hPadding = contentDivPadding.top + contentDivPadding.bottom;
34933
34934         // take into account the popup's 'padding' property
34935         this.fixPadding();
34936         wPadding += this.padding.left + this.padding.right;
34937         hPadding += this.padding.top + this.padding.bottom;
34938
34939         // make extra space for the close div
34940         if (this.closeDiv) {
34941             var closeDivWidth = parseInt(this.closeDiv.style.width);
34942             wPadding += closeDivWidth + contentDivPadding.right;
34943         }
34944
34945         //increase size of the main popup div to take into account the 
34946         // users's desired padding and close div.        
34947         this.size.w += wPadding;
34948         this.size.h += hPadding;
34949
34950         //now if our browser is IE, we need to actually make the contents 
34951         // div itself bigger to take its own padding into effect. this makes 
34952         // me want to shoot someone, but so it goes.
34953         if (OpenLayers.BROWSER_NAME == "msie") {
34954             this.contentSize.w += 
34955                 contentDivPadding.left + contentDivPadding.right;
34956             this.contentSize.h += 
34957                 contentDivPadding.bottom + contentDivPadding.top;
34958         }
34959
34960         if (this.div != null) {
34961             this.div.style.width = this.size.w + "px";
34962             this.div.style.height = this.size.h + "px";
34963         }
34964         if (this.contentDiv != null){
34965             this.contentDiv.style.width = contentSize.w + "px";
34966             this.contentDiv.style.height = contentSize.h + "px";
34967         }
34968     },  
34969
34970     /**
34971      * APIMethod: updateSize
34972      * Auto size the popup so that it precisely fits its contents (as 
34973      *     determined by this.contentDiv.innerHTML). Popup size will, of
34974      *     course, be limited by the available space on the current map
34975      */
34976     updateSize: function() {
34977         
34978         // determine actual render dimensions of the contents by putting its
34979         // contents into a fake contentDiv (for the CSS) and then measuring it
34980         var preparedHTML = "<div class='" + this.contentDisplayClass+ "'>" + 
34981             this.contentDiv.innerHTML + 
34982             "</div>";
34983  
34984         var containerElement = (this.map) ? this.map.div : document.body;
34985         var realSize = OpenLayers.Util.getRenderedDimensions(
34986             preparedHTML, null, {
34987                 displayClass: this.displayClass,
34988                 containerElement: containerElement
34989             }
34990         );
34991
34992         // is the "real" size of the div is safe to display in our map?
34993         var safeSize = this.getSafeContentSize(realSize);
34994
34995         var newSize = null;
34996         if (safeSize.equals(realSize)) {
34997             //real size of content is small enough to fit on the map, 
34998             // so we use real size.
34999             newSize = realSize;
35000
35001         } else {
35002
35003             // make a new 'size' object with the clipped dimensions 
35004             // set or null if not clipped.
35005             var fixedSize = {
35006                 w: (safeSize.w < realSize.w) ? safeSize.w : null,
35007                 h: (safeSize.h < realSize.h) ? safeSize.h : null
35008             };
35009         
35010             if (fixedSize.w && fixedSize.h) {
35011                 //content is too big in both directions, so we will use 
35012                 // max popup size (safeSize), knowing well that it will 
35013                 // overflow both ways.                
35014                 newSize = safeSize;
35015             } else {
35016                 //content is clipped in only one direction, so we need to 
35017                 // run getRenderedDimensions() again with a fixed dimension
35018                 var clippedSize = OpenLayers.Util.getRenderedDimensions(
35019                     preparedHTML, fixedSize, {
35020                         displayClass: this.contentDisplayClass,
35021                         containerElement: containerElement
35022                     }
35023                 );
35024                 
35025                 //if the clipped size is still the same as the safeSize, 
35026                 // that means that our content must be fixed in the 
35027                 // offending direction. If overflow is 'auto', this means 
35028                 // we are going to have a scrollbar for sure, so we must 
35029                 // adjust for that.
35030                 //
35031                 var currentOverflow = OpenLayers.Element.getStyle(
35032                     this.contentDiv, "overflow"
35033                 );
35034                 if ( (currentOverflow != "hidden") && 
35035                      (clippedSize.equals(safeSize)) ) {
35036                     var scrollBar = OpenLayers.Util.getScrollbarWidth();
35037                     if (fixedSize.w) {
35038                         clippedSize.h += scrollBar;
35039                     } else {
35040                         clippedSize.w += scrollBar;
35041                     }
35042                 }
35043                 
35044                 newSize = this.getSafeContentSize(clippedSize);
35045             }
35046         }                        
35047         this.setSize(newSize);     
35048     },    
35049
35050     /**
35051      * Method: setBackgroundColor
35052      * Sets the background color of the popup.
35053      *
35054      * Parameters:
35055      * color - {String} the background color.  eg "#FFBBBB"
35056      */
35057     setBackgroundColor:function(color) { 
35058         if (color != undefined) {
35059             this.backgroundColor = color; 
35060         }
35061         
35062         if (this.div != null) {
35063             this.div.style.backgroundColor = this.backgroundColor;
35064         }
35065     },  
35066     
35067     /**
35068      * Method: setOpacity
35069      * Sets the opacity of the popup.
35070      * 
35071      * Parameters:
35072      * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).   
35073      */
35074     setOpacity:function(opacity) { 
35075         if (opacity != undefined) {
35076             this.opacity = opacity; 
35077         }
35078         
35079         if (this.div != null) {
35080             // for Mozilla and Safari
35081             this.div.style.opacity = this.opacity;
35082
35083             // for IE
35084             this.div.style.filter = 'alpha(opacity=' + this.opacity*100 + ')';
35085         }
35086     },  
35087     
35088     /**
35089      * Method: setBorder
35090      * Sets the border style of the popup.
35091      *
35092      * Parameters:
35093      * border - {String} The border style value. eg 2px 
35094      */
35095     setBorder:function(border) { 
35096         if (border != undefined) {
35097             this.border = border;
35098         }
35099         
35100         if (this.div != null) {
35101             this.div.style.border = this.border;
35102         }
35103     },      
35104     
35105     /**
35106      * Method: setContentHTML
35107      * Allows the user to set the HTML content of the popup.
35108      *
35109      * Parameters:
35110      * contentHTML - {String} HTML for the div.
35111      */
35112     setContentHTML:function(contentHTML) {
35113
35114         if (contentHTML != null) {
35115             this.contentHTML = contentHTML;
35116         }
35117        
35118         if ((this.contentDiv != null) && 
35119             (this.contentHTML != null) &&
35120             (this.contentHTML != this.contentDiv.innerHTML)) {
35121        
35122             this.contentDiv.innerHTML = this.contentHTML;
35123        
35124             if (this.autoSize) {
35125                 
35126                 //if popup has images, listen for when they finish
35127                 // loading and resize accordingly
35128                 this.registerImageListeners();
35129
35130                 //auto size the popup to its current contents
35131                 this.updateSize();
35132             }
35133         }    
35134
35135     },
35136     
35137     /**
35138      * Method: registerImageListeners
35139      * Called when an image contained by the popup loaded. this function
35140      *     updates the popup size, then unregisters the image load listener.
35141      */   
35142     registerImageListeners: function() { 
35143
35144         // As the images load, this function will call updateSize() to 
35145         // resize the popup to fit the content div (which presumably is now
35146         // bigger than when the image was not loaded).
35147         // 
35148         // If the 'panMapIfOutOfView' property is set, we will pan the newly
35149         // resized popup back into view.
35150         // 
35151         // Note that this function, when called, will have 'popup' and 
35152         // 'img' properties in the context.
35153         //
35154         var onImgLoad = function() {
35155             if (this.popup.id === null) { // this.popup has been destroyed!
35156                 return;
35157             }
35158             this.popup.updateSize();
35159      
35160             if ( this.popup.visible() && this.popup.panMapIfOutOfView ) {
35161                 this.popup.panIntoView();
35162             }
35163
35164             OpenLayers.Event.stopObserving(
35165                 this.img, "load", this.img._onImgLoad
35166             );
35167     
35168         };
35169
35170         //cycle through the images and if their size is 0x0, that means that 
35171         // they haven't been loaded yet, so we attach the listener, which 
35172         // will fire when the images finish loading and will resize the 
35173         // popup accordingly to its new size.
35174         var images = this.contentDiv.getElementsByTagName("img");
35175         for (var i = 0, len = images.length; i < len; i++) {
35176             var img = images[i];
35177             if (img.width == 0 || img.height == 0) {
35178
35179                 var context = {
35180                     'popup': this,
35181                     'img': img
35182                 };
35183
35184                 //expando this function to the image itself before registering
35185                 // it. This way we can easily and properly unregister it.
35186                 img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);
35187
35188                 OpenLayers.Event.observe(img, 'load', img._onImgLoad);
35189             }    
35190         } 
35191     },
35192
35193     /**
35194      * APIMethod: getSafeContentSize
35195      * 
35196      * Parameters:
35197      * size - {<OpenLayers.Size>} Desired size to make the popup.
35198      * 
35199      * Returns:
35200      * {<OpenLayers.Size>} A size to make the popup which is neither smaller
35201      *     than the specified minimum size, nor bigger than the maximum 
35202      *     size (which is calculated relative to the size of the viewport).
35203      */
35204     getSafeContentSize: function(size) {
35205
35206         var safeContentSize = size.clone();
35207
35208         // if our contentDiv has a css 'padding' set on it by a stylesheet, we 
35209         //  must add that to the desired "size". 
35210         var contentDivPadding = this.getContentDivPadding();
35211         var wPadding = contentDivPadding.left + contentDivPadding.right;
35212         var hPadding = contentDivPadding.top + contentDivPadding.bottom;
35213
35214         // take into account the popup's 'padding' property
35215         this.fixPadding();
35216         wPadding += this.padding.left + this.padding.right;
35217         hPadding += this.padding.top + this.padding.bottom;
35218
35219         if (this.closeDiv) {
35220             var closeDivWidth = parseInt(this.closeDiv.style.width);
35221             wPadding += closeDivWidth + contentDivPadding.right;
35222         }
35223
35224         // prevent the popup from being smaller than a specified minimal size
35225         if (this.minSize) {
35226             safeContentSize.w = Math.max(safeContentSize.w, 
35227                 (this.minSize.w - wPadding));
35228             safeContentSize.h = Math.max(safeContentSize.h, 
35229                 (this.minSize.h - hPadding));
35230         }
35231
35232         // prevent the popup from being bigger than a specified maximum size
35233         if (this.maxSize) {
35234             safeContentSize.w = Math.min(safeContentSize.w, 
35235                 (this.maxSize.w - wPadding));
35236             safeContentSize.h = Math.min(safeContentSize.h, 
35237                 (this.maxSize.h - hPadding));
35238         }
35239         
35240         //make sure the desired size to set doesn't result in a popup that 
35241         // is bigger than the map's viewport.
35242         //
35243         if (this.map && this.map.size) {
35244             
35245             var extraX = 0, extraY = 0;
35246             if (this.keepInMap && !this.panMapIfOutOfView) {
35247                 var px = this.map.getPixelFromLonLat(this.lonlat);
35248                 switch (this.relativePosition) {
35249                     case "tr":
35250                         extraX = px.x;
35251                         extraY = this.map.size.h - px.y;
35252                         break;
35253                     case "tl":
35254                         extraX = this.map.size.w - px.x;
35255                         extraY = this.map.size.h - px.y;
35256                         break;
35257                     case "bl":
35258                         extraX = this.map.size.w - px.x;
35259                         extraY = px.y;
35260                         break;
35261                     case "br":
35262                         extraX = px.x;
35263                         extraY = px.y;
35264                         break;
35265                     default:    
35266                         extraX = px.x;
35267                         extraY = this.map.size.h - px.y;
35268                         break;
35269                 }
35270             }    
35271           
35272             var maxY = this.map.size.h - 
35273                 this.map.paddingForPopups.top - 
35274                 this.map.paddingForPopups.bottom - 
35275                 hPadding - extraY;
35276             
35277             var maxX = this.map.size.w - 
35278                 this.map.paddingForPopups.left - 
35279                 this.map.paddingForPopups.right - 
35280                 wPadding - extraX;
35281             
35282             safeContentSize.w = Math.min(safeContentSize.w, maxX);
35283             safeContentSize.h = Math.min(safeContentSize.h, maxY);
35284         }
35285         
35286         return safeContentSize;
35287     },
35288     
35289     /**
35290      * Method: getContentDivPadding
35291      * Glorious, oh glorious hack in order to determine the css 'padding' of 
35292      *     the contentDiv. IE/Opera return null here unless we actually add the 
35293      *     popup's main 'div' element (which contains contentDiv) to the DOM. 
35294      *     So we make it invisible and then add it to the document temporarily. 
35295      *
35296      *     Once we've taken the padding readings we need, we then remove it 
35297      *     from the DOM (it will actually get added to the DOM in 
35298      *     Map.js's addPopup)
35299      *
35300      * Returns:
35301      * {<OpenLayers.Bounds>}
35302      */
35303     getContentDivPadding: function() {
35304
35305         //use cached value if we have it
35306         var contentDivPadding = this._contentDivPadding;
35307         if (!contentDivPadding) {
35308
35309             if (this.div.parentNode == null) {
35310                 //make the div invisible and add it to the page        
35311                 this.div.style.display = "none";
35312                 document.body.appendChild(this.div);
35313             }
35314                     
35315             //read the padding settings from css, put them in an OL.Bounds        
35316             contentDivPadding = new OpenLayers.Bounds(
35317                 OpenLayers.Element.getStyle(this.contentDiv, "padding-left"),
35318                 OpenLayers.Element.getStyle(this.contentDiv, "padding-bottom"),
35319                 OpenLayers.Element.getStyle(this.contentDiv, "padding-right"),
35320                 OpenLayers.Element.getStyle(this.contentDiv, "padding-top")
35321             );
35322     
35323             //cache the value
35324             this._contentDivPadding = contentDivPadding;
35325
35326             if (this.div.parentNode == document.body) {
35327                 //remove the div from the page and make it visible again
35328                 document.body.removeChild(this.div);
35329                 this.div.style.display = "";
35330             }
35331         }
35332         return contentDivPadding;
35333     },
35334
35335     /**
35336      * Method: addCloseBox
35337      * 
35338      * Parameters:
35339      * callback - {Function} The callback to be called when the close button
35340      *     is clicked.
35341      */
35342     addCloseBox: function(callback) {
35343
35344         this.closeDiv = OpenLayers.Util.createDiv(
35345             this.id + "_close", null, {w: 17, h: 17}
35346         );
35347         this.closeDiv.className = "olPopupCloseBox"; 
35348         
35349         // use the content div's css padding to determine if we should
35350         //  padd the close div
35351         var contentDivPadding = this.getContentDivPadding();
35352          
35353         this.closeDiv.style.right = contentDivPadding.right + "px";
35354         this.closeDiv.style.top = contentDivPadding.top + "px";
35355         this.groupDiv.appendChild(this.closeDiv);
35356
35357         var closePopup = callback || function(e) {
35358             this.hide();
35359             OpenLayers.Event.stop(e);
35360         };
35361         OpenLayers.Event.observe(this.closeDiv, "touchend", 
35362                 OpenLayers.Function.bindAsEventListener(closePopup, this));
35363         OpenLayers.Event.observe(this.closeDiv, "click", 
35364                 OpenLayers.Function.bindAsEventListener(closePopup, this));
35365     },
35366
35367     /**
35368      * Method: panIntoView
35369      * Pans the map such that the popup is totaly viewable (if necessary)
35370      */
35371     panIntoView: function() {
35372         
35373         var mapSize = this.map.getSize();
35374     
35375         //start with the top left corner of the popup, in px, 
35376         // relative to the viewport
35377         var origTL = this.map.getViewPortPxFromLayerPx( new OpenLayers.Pixel(
35378             parseInt(this.div.style.left),
35379             parseInt(this.div.style.top)
35380         ));
35381         var newTL = origTL.clone();
35382     
35383         //new left (compare to margins, using this.size to calculate right)
35384         if (origTL.x < this.map.paddingForPopups.left) {
35385             newTL.x = this.map.paddingForPopups.left;
35386         } else 
35387         if ( (origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) {
35388             newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w;
35389         }
35390         
35391         //new top (compare to margins, using this.size to calculate bottom)
35392         if (origTL.y < this.map.paddingForPopups.top) {
35393             newTL.y = this.map.paddingForPopups.top;
35394         } else 
35395         if ( (origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) {
35396             newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h;
35397         }
35398         
35399         var dx = origTL.x - newTL.x;
35400         var dy = origTL.y - newTL.y;
35401         
35402         this.map.pan(dx, dy);
35403     },
35404
35405     /** 
35406      * Method: registerEvents
35407      * Registers events on the popup.
35408      *
35409      * Do this in a separate function so that subclasses can 
35410      *   choose to override it if they wish to deal differently
35411      *   with mouse events
35412      * 
35413      *   Note in the following handler functions that some special
35414      *    care is needed to deal correctly with mousing and popups. 
35415      *   
35416      *   Because the user might select the zoom-rectangle option and
35417      *    then drag it over a popup, we need a safe way to allow the
35418      *    mousemove and mouseup events to pass through the popup when
35419      *    they are initiated from outside. The same procedure is needed for
35420      *    touchmove and touchend events.
35421      * 
35422      *   Otherwise, we want to essentially kill the event propagation
35423      *    for all other events, though we have to do so carefully, 
35424      *    without disabling basic html functionality, like clicking on 
35425      *    hyperlinks or drag-selecting text.
35426      */
35427      registerEvents:function() {
35428         this.events = new OpenLayers.Events(this, this.div, null, true);
35429
35430         function onTouchstart(evt) {
35431             OpenLayers.Event.stop(evt, true);
35432         }
35433         this.events.on({
35434             "mousedown": this.onmousedown,
35435             "mousemove": this.onmousemove,
35436             "mouseup": this.onmouseup,
35437             "click": this.onclick,
35438             "mouseout": this.onmouseout,
35439             "dblclick": this.ondblclick,
35440             "touchstart": onTouchstart,
35441             scope: this
35442         });
35443         
35444      },
35445
35446     /** 
35447      * Method: onmousedown 
35448      * When mouse goes down within the popup, make a note of
35449      *   it locally, and then do not propagate the mousedown 
35450      *   (but do so safely so that user can select text inside)
35451      * 
35452      * Parameters:
35453      * evt - {Event} 
35454      */
35455     onmousedown: function (evt) {
35456         this.mousedown = true;
35457         OpenLayers.Event.stop(evt, true);
35458     },
35459
35460     /** 
35461      * Method: onmousemove
35462      * If the drag was started within the popup, then 
35463      *   do not propagate the mousemove (but do so safely
35464      *   so that user can select text inside)
35465      * 
35466      * Parameters:
35467      * evt - {Event} 
35468      */
35469     onmousemove: function (evt) {
35470         if (this.mousedown) {
35471             OpenLayers.Event.stop(evt, true);
35472         }
35473     },
35474
35475     /** 
35476      * Method: onmouseup
35477      * When mouse comes up within the popup, after going down 
35478      *   in it, reset the flag, and then (once again) do not 
35479      *   propagate the event, but do so safely so that user can 
35480      *   select text inside
35481      * 
35482      * Parameters:
35483      * evt - {Event} 
35484      */
35485     onmouseup: function (evt) {
35486         if (this.mousedown) {
35487             this.mousedown = false;
35488             OpenLayers.Event.stop(evt, true);
35489         }
35490     },
35491
35492     /**
35493      * Method: onclick
35494      * Ignore clicks, but allowing default browser handling
35495      * 
35496      * Parameters:
35497      * evt - {Event} 
35498      */
35499     onclick: function (evt) {
35500         OpenLayers.Event.stop(evt, true);
35501     },
35502
35503     /** 
35504      * Method: onmouseout
35505      * When mouse goes out of the popup set the flag to false so that
35506      *   if they let go and then drag back in, we won't be confused.
35507      * 
35508      * Parameters:
35509      * evt - {Event} 
35510      */
35511     onmouseout: function (evt) {
35512         this.mousedown = false;
35513     },
35514     
35515     /** 
35516      * Method: ondblclick
35517      * Ignore double-clicks, but allowing default browser handling
35518      * 
35519      * Parameters:
35520      * evt - {Event} 
35521      */
35522     ondblclick: function (evt) {
35523         OpenLayers.Event.stop(evt, true);
35524     },
35525
35526     CLASS_NAME: "OpenLayers.Popup"
35527 });
35528
35529 OpenLayers.Popup.WIDTH = 200;
35530 OpenLayers.Popup.HEIGHT = 200;
35531 OpenLayers.Popup.COLOR = "white";
35532 OpenLayers.Popup.OPACITY = 1;
35533 OpenLayers.Popup.BORDER = "0px";
35534 /* ======================================================================
35535     OpenLayers/Popup/Anchored.js
35536    ====================================================================== */
35537
35538 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
35539  * full list of contributors). Published under the 2-clause BSD license.
35540  * See license.txt in the OpenLayers distribution or repository for the
35541  * full text of the license. */
35542
35543
35544 /**
35545  * @requires OpenLayers/Popup.js
35546  */
35547
35548 /**
35549  * Class: OpenLayers.Popup.Anchored
35550  * 
35551  * Inherits from:
35552  *  - <OpenLayers.Popup>
35553  */
35554 OpenLayers.Popup.Anchored = 
35555   OpenLayers.Class(OpenLayers.Popup, {
35556
35557     /** 
35558      * Property: relativePosition
35559      * {String} Relative position of the popup ("br", "tr", "tl" or "bl").
35560      */
35561     relativePosition: null,
35562     
35563     /**
35564      * APIProperty: keepInMap 
35565      * {Boolean} If panMapIfOutOfView is false, and this property is true, 
35566      *     contrain the popup such that it always fits in the available map
35567      *     space. By default, this is set. If you are creating popups that are
35568      *     near map edges and not allowing pannning, and especially if you have
35569      *     a popup which has a fixedRelativePosition, setting this to false may
35570      *     be a smart thing to do.
35571      *   
35572      *     For anchored popups, default is true, since subclasses will
35573      *     usually want this functionality.
35574      */
35575     keepInMap: true,
35576
35577     /**
35578      * Property: anchor
35579      * {Object} Object to which we'll anchor the popup. Must expose a 
35580      *     'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>).
35581      */
35582     anchor: null,
35583
35584     /** 
35585     * Constructor: OpenLayers.Popup.Anchored
35586     * 
35587     * Parameters:
35588     * id - {String}
35589     * lonlat - {<OpenLayers.LonLat>}
35590     * contentSize - {<OpenLayers.Size>}
35591     * contentHTML - {String}
35592     * anchor - {Object} Object which must expose a 'size' <OpenLayers.Size> 
35593     *     and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>).
35594     * closeBox - {Boolean}
35595     * closeBoxCallback - {Function} Function to be called on closeBox click.
35596     */
35597     initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
35598                         closeBoxCallback) {
35599         var newArguments = [
35600             id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback
35601         ];
35602         OpenLayers.Popup.prototype.initialize.apply(this, newArguments);
35603
35604         this.anchor = (anchor != null) ? anchor 
35605                                        : { size: new OpenLayers.Size(0,0),
35606                                            offset: new OpenLayers.Pixel(0,0)};
35607     },
35608
35609     /**
35610      * APIMethod: destroy
35611      */
35612     destroy: function() {
35613         this.anchor = null;
35614         this.relativePosition = null;
35615         
35616         OpenLayers.Popup.prototype.destroy.apply(this, arguments);        
35617     },
35618
35619     /**
35620      * APIMethod: show
35621      * Overridden from Popup since user might hide popup and then show() it 
35622      *     in a new location (meaning we might want to update the relative
35623      *     position on the show)
35624      */
35625     show: function() {
35626         this.updatePosition();
35627         OpenLayers.Popup.prototype.show.apply(this, arguments);
35628     },
35629
35630     /**
35631      * Method: moveTo
35632      * Since the popup is moving to a new px, it might need also to be moved
35633      *     relative to where the marker is. We first calculate the new 
35634      *     relativePosition, and then we calculate the new px where we will 
35635      *     put the popup, based on the new relative position. 
35636      * 
35637      *     If the relativePosition has changed, we must also call 
35638      *     updateRelativePosition() to make any visual changes to the popup 
35639      *     which are associated with putting it in a new relativePosition.
35640      * 
35641      * Parameters:
35642      * px - {<OpenLayers.Pixel>}
35643      */
35644     moveTo: function(px) {
35645         var oldRelativePosition = this.relativePosition;
35646         this.relativePosition = this.calculateRelativePosition(px);
35647
35648         OpenLayers.Popup.prototype.moveTo.call(this, this.calculateNewPx(px));
35649         
35650         //if this move has caused the popup to change its relative position, 
35651         // we need to make the appropriate cosmetic changes.
35652         if (this.relativePosition != oldRelativePosition) {
35653             this.updateRelativePosition();
35654         }
35655     },
35656
35657     /**
35658      * APIMethod: setSize
35659      * 
35660      * Parameters:
35661      * contentSize - {<OpenLayers.Size>} the new size for the popup's 
35662      *     contents div (in pixels).
35663      */
35664     setSize:function(contentSize) { 
35665         OpenLayers.Popup.prototype.setSize.apply(this, arguments);
35666
35667         if ((this.lonlat) && (this.map)) {
35668             var px = this.map.getLayerPxFromLonLat(this.lonlat);
35669             this.moveTo(px);
35670         }
35671     },  
35672     
35673     /** 
35674      * Method: calculateRelativePosition
35675      * 
35676      * Parameters:
35677      * px - {<OpenLayers.Pixel>}
35678      * 
35679      * Returns:
35680      * {String} The relative position ("br" "tr" "tl" "bl") at which the popup
35681      *     should be placed.
35682      */
35683     calculateRelativePosition:function(px) {
35684         var lonlat = this.map.getLonLatFromLayerPx(px);        
35685         
35686         var extent = this.map.getExtent();
35687         var quadrant = extent.determineQuadrant(lonlat);
35688         
35689         return OpenLayers.Bounds.oppositeQuadrant(quadrant);
35690     }, 
35691
35692     /**
35693      * Method: updateRelativePosition
35694      * The popup has been moved to a new relative location, so we may want to 
35695      *     make some cosmetic adjustments to it. 
35696      * 
35697      *     Note that in the classic Anchored popup, there is nothing to do 
35698      *     here, since the popup looks exactly the same in all four positions.
35699      *     Subclasses such as Framed, however, will want to do something
35700      *     special here.
35701      */
35702     updateRelativePosition: function() {
35703         //to be overridden by subclasses
35704     },
35705
35706     /** 
35707      * Method: calculateNewPx
35708      * 
35709      * Parameters:
35710      * px - {<OpenLayers.Pixel>}
35711      * 
35712      * Returns:
35713      * {<OpenLayers.Pixel>} The the new px position of the popup on the screen
35714      *     relative to the passed-in px.
35715      */
35716     calculateNewPx:function(px) {
35717         var newPx = px.offset(this.anchor.offset);
35718         
35719         //use contentSize if size is not already set
35720         var size = this.size || this.contentSize;
35721
35722         var top = (this.relativePosition.charAt(0) == 't');
35723         newPx.y += (top) ? -size.h : this.anchor.size.h;
35724         
35725         var left = (this.relativePosition.charAt(1) == 'l');
35726         newPx.x += (left) ? -size.w : this.anchor.size.w;
35727
35728         return newPx;   
35729     },
35730
35731     CLASS_NAME: "OpenLayers.Popup.Anchored"
35732 });
35733 /* ======================================================================
35734     OpenLayers/Popup/Framed.js
35735    ====================================================================== */
35736
35737 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
35738  * full list of contributors). Published under the 2-clause BSD license.
35739  * See license.txt in the OpenLayers distribution or repository for the
35740  * full text of the license. */
35741
35742 /**
35743  * @requires OpenLayers/Popup/Anchored.js
35744  */
35745
35746 /**
35747  * Class: OpenLayers.Popup.Framed
35748  * 
35749  * Inherits from:
35750  *  - <OpenLayers.Popup.Anchored>
35751  */
35752 OpenLayers.Popup.Framed =
35753   OpenLayers.Class(OpenLayers.Popup.Anchored, {
35754
35755     /**
35756      * Property: imageSrc
35757      * {String} location of the image to be used as the popup frame
35758      */
35759     imageSrc: null,
35760
35761     /**
35762      * Property: imageSize
35763      * {<OpenLayers.Size>} Size (measured in pixels) of the image located
35764      *     by the 'imageSrc' property.
35765      */
35766     imageSize: null,
35767
35768     /**
35769      * APIProperty: isAlphaImage
35770      * {Boolean} The image has some alpha and thus needs to use the alpha 
35771      *     image hack. Note that setting this to true will have no noticeable
35772      *     effect in FF or IE7 browsers, but will all but crush the ie6 
35773      *     browser. 
35774      *     Default is false.
35775      */
35776     isAlphaImage: false,
35777
35778     /**
35779      * Property: positionBlocks
35780      * {Object} Hash of different position blocks (Object/Hashs). Each block 
35781      *     will be keyed by a two-character 'relativePosition' 
35782      *     code string (ie "tl", "tr", "bl", "br"). Block properties are 
35783      *     'offset', 'padding' (self-explanatory), and finally the 'blocks'
35784      *     parameter, which is an array of the block objects. 
35785      * 
35786      *     Each block object must have 'size', 'anchor', and 'position' 
35787      *     properties.
35788      * 
35789      *     Note that positionBlocks should never be modified at runtime.
35790      */
35791     positionBlocks: null,
35792
35793     /**
35794      * Property: blocks
35795      * {Array[Object]} Array of objects, each of which is one "block" of the 
35796      *     popup. Each block has a 'div' and an 'image' property, both of 
35797      *     which are DOMElements, and the latter of which is appended to the 
35798      *     former. These are reused as the popup goes changing positions for
35799      *     great economy and elegance.
35800      */
35801     blocks: null,
35802
35803     /** 
35804      * APIProperty: fixedRelativePosition
35805      * {Boolean} We want the framed popup to work dynamically placed relative
35806      *     to its anchor but also in just one fixed position. A well designed
35807      *     framed popup will have the pixels and logic to display itself in 
35808      *     any of the four relative positions, but (understandably), this will
35809      *     not be the case for all of them. By setting this property to 'true', 
35810      *     framed popup will not recalculate for the best placement each time
35811      *     it's open, but will always open the same way. 
35812      *     Note that if this is set to true, it is generally advisable to also
35813      *     set the 'panIntoView' property to true so that the popup can be 
35814      *     scrolled into view (since it will often be offscreen on open)
35815      *     Default is false.
35816      */
35817     fixedRelativePosition: false,
35818
35819     /** 
35820      * Constructor: OpenLayers.Popup.Framed
35821      * 
35822      * Parameters:
35823      * id - {String}
35824      * lonlat - {<OpenLayers.LonLat>}
35825      * contentSize - {<OpenLayers.Size>}
35826      * contentHTML - {String}
35827      * anchor - {Object} Object to which we'll anchor the popup. Must expose 
35828      *     a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) 
35829      *     (Note that this is generally an <OpenLayers.Icon>).
35830      * closeBox - {Boolean}
35831      * closeBoxCallback - {Function} Function to be called on closeBox click.
35832      */
35833     initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox, 
35834                         closeBoxCallback) {
35835
35836         OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
35837
35838         if (this.fixedRelativePosition) {
35839             //based on our decided relativePostion, set the current padding
35840             // this keeps us from getting into trouble 
35841             this.updateRelativePosition();
35842             
35843             //make calculateRelativePosition always return the specified
35844             // fixed position.
35845             this.calculateRelativePosition = function(px) {
35846                 return this.relativePosition;
35847             };
35848         }
35849
35850         this.contentDiv.style.position = "absolute";
35851         this.contentDiv.style.zIndex = 1;
35852
35853         if (closeBox) {
35854             this.closeDiv.style.zIndex = 1;
35855         }
35856
35857         this.groupDiv.style.position = "absolute";
35858         this.groupDiv.style.top = "0px";
35859         this.groupDiv.style.left = "0px";
35860         this.groupDiv.style.height = "100%";
35861         this.groupDiv.style.width = "100%";
35862     },
35863
35864     /** 
35865      * APIMethod: destroy
35866      */
35867     destroy: function() {
35868         this.imageSrc = null;
35869         this.imageSize = null;
35870         this.isAlphaImage = null;
35871
35872         this.fixedRelativePosition = false;
35873         this.positionBlocks = null;
35874
35875         //remove our blocks
35876         for(var i = 0; i < this.blocks.length; i++) {
35877             var block = this.blocks[i];
35878
35879             if (block.image) {
35880                 block.div.removeChild(block.image);
35881             }
35882             block.image = null;
35883
35884             if (block.div) {
35885                 this.groupDiv.removeChild(block.div);
35886             }
35887             block.div = null;
35888         }
35889         this.blocks = null;
35890
35891         OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments);
35892     },
35893
35894     /**
35895      * APIMethod: setBackgroundColor
35896      */
35897     setBackgroundColor:function(color) {
35898         //does nothing since the framed popup's entire scheme is based on a 
35899         // an image -- changing the background color makes no sense. 
35900     },
35901
35902     /**
35903      * APIMethod: setBorder
35904      */
35905     setBorder:function() {
35906         //does nothing since the framed popup's entire scheme is based on a 
35907         // an image -- changing the popup's border makes no sense. 
35908     },
35909
35910     /**
35911      * Method: setOpacity
35912      * Sets the opacity of the popup.
35913      * 
35914      * Parameters:
35915      * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid).   
35916      */
35917     setOpacity:function(opacity) {
35918         //does nothing since we suppose that we'll never apply an opacity
35919         // to a framed popup
35920     },
35921
35922     /**
35923      * APIMethod: setSize
35924      * Overridden here, because we need to update the blocks whenever the size
35925      *     of the popup has changed.
35926      * 
35927      * Parameters:
35928      * contentSize - {<OpenLayers.Size>} the new size for the popup's 
35929      *     contents div (in pixels).
35930      */
35931     setSize:function(contentSize) { 
35932         OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
35933
35934         this.updateBlocks();
35935     },
35936
35937     /**
35938      * Method: updateRelativePosition
35939      * When the relative position changes, we need to set the new padding 
35940      *     BBOX on the popup, reposition the close div, and update the blocks.
35941      */
35942     updateRelativePosition: function() {
35943
35944         //update the padding
35945         this.padding = this.positionBlocks[this.relativePosition].padding;
35946
35947         //update the position of our close box to new padding
35948         if (this.closeDiv) {
35949             // use the content div's css padding to determine if we should
35950             //  padd the close div
35951             var contentDivPadding = this.getContentDivPadding();
35952
35953             this.closeDiv.style.right = contentDivPadding.right + 
35954                                         this.padding.right + "px";
35955             this.closeDiv.style.top = contentDivPadding.top + 
35956                                       this.padding.top + "px";
35957         }
35958
35959         this.updateBlocks();
35960     },
35961
35962     /** 
35963      * Method: calculateNewPx
35964      * Besides the standard offset as determined by the Anchored class, our 
35965      *     Framed popups have a special 'offset' property for each of their 
35966      *     positions, which is used to offset the popup relative to its anchor.
35967      * 
35968      * Parameters:
35969      * px - {<OpenLayers.Pixel>}
35970      * 
35971      * Returns:
35972      * {<OpenLayers.Pixel>} The the new px position of the popup on the screen
35973      *     relative to the passed-in px.
35974      */
35975     calculateNewPx:function(px) {
35976         var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply(
35977             this, arguments
35978         );
35979
35980         newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset);
35981
35982         return newPx;
35983     },
35984
35985     /**
35986      * Method: createBlocks
35987      */
35988     createBlocks: function() {
35989         this.blocks = [];
35990
35991         //since all positions contain the same number of blocks, we can 
35992         // just pick the first position and use its blocks array to create
35993         // our blocks array
35994         var firstPosition = null;
35995         for(var key in this.positionBlocks) {
35996             firstPosition = key;
35997             break;
35998         }
35999         
36000         var position = this.positionBlocks[firstPosition];
36001         for (var i = 0; i < position.blocks.length; i++) {
36002
36003             var block = {};
36004             this.blocks.push(block);
36005
36006             var divId = this.id + '_FrameDecorationDiv_' + i;
36007             block.div = OpenLayers.Util.createDiv(divId, 
36008                 null, null, null, "absolute", null, "hidden", null
36009             );
36010
36011             var imgId = this.id + '_FrameDecorationImg_' + i;
36012             var imageCreator = 
36013                 (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv
36014                                     : OpenLayers.Util.createImage;
36015
36016             block.image = imageCreator(imgId, 
36017                 null, this.imageSize, this.imageSrc, 
36018                 "absolute", null, null, null
36019             );
36020
36021             block.div.appendChild(block.image);
36022             this.groupDiv.appendChild(block.div);
36023         }
36024     },
36025
36026     /**
36027      * Method: updateBlocks
36028      * Internal method, called on initialize and when the popup's relative
36029      *     position has changed. This function takes care of re-positioning
36030      *     the popup's blocks in their appropropriate places.
36031      */
36032     updateBlocks: function() {
36033         if (!this.blocks) {
36034             this.createBlocks();
36035         }
36036         
36037         if (this.size && this.relativePosition) {
36038             var position = this.positionBlocks[this.relativePosition];
36039             for (var i = 0; i < position.blocks.length; i++) {
36040     
36041                 var positionBlock = position.blocks[i];
36042                 var block = this.blocks[i];
36043     
36044                 // adjust sizes
36045                 var l = positionBlock.anchor.left;
36046                 var b = positionBlock.anchor.bottom;
36047                 var r = positionBlock.anchor.right;
36048                 var t = positionBlock.anchor.top;
36049     
36050                 //note that we use the isNaN() test here because if the 
36051                 // size object is initialized with a "auto" parameter, the 
36052                 // size constructor calls parseFloat() on the string, 
36053                 // which will turn it into NaN
36054                 //
36055                 var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l) 
36056                                                       : positionBlock.size.w;
36057     
36058                 var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t) 
36059                                                       : positionBlock.size.h;
36060     
36061                 block.div.style.width = (w < 0 ? 0 : w) + 'px';
36062                 block.div.style.height = (h < 0 ? 0 : h) + 'px';
36063     
36064                 block.div.style.left = (l != null) ? l + 'px' : '';
36065                 block.div.style.bottom = (b != null) ? b + 'px' : '';
36066                 block.div.style.right = (r != null) ? r + 'px' : '';            
36067                 block.div.style.top = (t != null) ? t + 'px' : '';
36068     
36069                 block.image.style.left = positionBlock.position.x + 'px';
36070                 block.image.style.top = positionBlock.position.y + 'px';
36071             }
36072     
36073             this.contentDiv.style.left = this.padding.left + "px";
36074             this.contentDiv.style.top = this.padding.top + "px";
36075         }
36076     },
36077
36078     CLASS_NAME: "OpenLayers.Popup.Framed"
36079 });
36080 /* ======================================================================
36081     OpenLayers/Format/JSON.js
36082    ====================================================================== */
36083
36084 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
36085  * full list of contributors). Published under the 2-clause BSD license.
36086  * See license.txt in the OpenLayers distribution or repository for the
36087  * full text of the license. */
36088
36089 /**
36090  * Note:
36091  * This work draws heavily from the public domain JSON serializer/deserializer
36092  *     at http://www.json.org/json.js. Rewritten so that it doesn't modify
36093  *     basic data prototypes.
36094  */
36095
36096 /**
36097  * @requires OpenLayers/Format.js
36098  */
36099
36100 /**
36101  * Class: OpenLayers.Format.JSON
36102  * A parser to read/write JSON safely.  Create a new instance with the
36103  *     <OpenLayers.Format.JSON> constructor.
36104  *
36105  * Inherits from:
36106  *  - <OpenLayers.Format>
36107  */
36108 OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {
36109     
36110     /**
36111      * APIProperty: indent
36112      * {String} For "pretty" printing, the indent string will be used once for
36113      *     each indentation level.
36114      */
36115     indent: "    ",
36116     
36117     /**
36118      * APIProperty: space
36119      * {String} For "pretty" printing, the space string will be used after
36120      *     the ":" separating a name/value pair.
36121      */
36122     space: " ",
36123     
36124     /**
36125      * APIProperty: newline
36126      * {String} For "pretty" printing, the newline string will be used at the
36127      *     end of each name/value pair or array item.
36128      */
36129     newline: "\n",
36130     
36131     /**
36132      * Property: level
36133      * {Integer} For "pretty" printing, this is incremented/decremented during
36134      *     serialization.
36135      */
36136     level: 0,
36137
36138     /**
36139      * Property: pretty
36140      * {Boolean} Serialize with extra whitespace for structure.  This is set
36141      *     by the <write> method.
36142      */
36143     pretty: false,
36144
36145     /**
36146      * Property: nativeJSON
36147      * {Boolean} Does the browser support native json?
36148      */
36149     nativeJSON: (function() {
36150         return !!(window.JSON && typeof JSON.parse == "function" && typeof JSON.stringify == "function");
36151     })(),
36152
36153     /**
36154      * Constructor: OpenLayers.Format.JSON
36155      * Create a new parser for JSON.
36156      *
36157      * Parameters:
36158      * options - {Object} An optional object whose properties will be set on
36159      *     this instance.
36160      */
36161
36162     /**
36163      * APIMethod: read
36164      * Deserialize a json string.
36165      *
36166      * Parameters:
36167      * json - {String} A JSON string
36168      * filter - {Function} A function which will be called for every key and
36169      *     value at every level of the final result. Each value will be
36170      *     replaced by the result of the filter function. This can be used to
36171      *     reform generic objects into instances of classes, or to transform
36172      *     date strings into Date objects.
36173      *     
36174      * Returns:
36175      * {Object} An object, array, string, or number .
36176      */
36177     read: function(json, filter) {
36178         var object;
36179         if (this.nativeJSON) {
36180             object = JSON.parse(json, filter);
36181         } else try {
36182             /**
36183              * Parsing happens in three stages. In the first stage, we run the
36184              *     text against a regular expression which looks for non-JSON
36185              *     characters. We are especially concerned with '()' and 'new'
36186              *     because they can cause invocation, and '=' because it can
36187              *     cause mutation. But just to be safe, we will reject all
36188              *     unexpected characters.
36189              */
36190             if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@').
36191                                 replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
36192                                 replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
36193
36194                 /**
36195                  * In the second stage we use the eval function to compile the
36196                  *     text into a JavaScript structure. The '{' operator is
36197                  *     subject to a syntactic ambiguity in JavaScript - it can
36198                  *     begin a block or an object literal. We wrap the text in
36199                  *     parens to eliminate the ambiguity.
36200                  */
36201                 object = eval('(' + json + ')');
36202
36203                 /**
36204                  * In the optional third stage, we recursively walk the new
36205                  *     structure, passing each name/value pair to a filter
36206                  *     function for possible transformation.
36207                  */
36208                 if(typeof filter === 'function') {
36209                     function walk(k, v) {
36210                         if(v && typeof v === 'object') {
36211                             for(var i in v) {
36212                                 if(v.hasOwnProperty(i)) {
36213                                     v[i] = walk(i, v[i]);
36214                                 }
36215                             }
36216                         }
36217                         return filter(k, v);
36218                     }
36219                     object = walk('', object);
36220                 }
36221             }
36222         } catch(e) {
36223             // Fall through if the regexp test fails.
36224         }
36225
36226         if(this.keepData) {
36227             this.data = object;
36228         }
36229
36230         return object;
36231     },
36232
36233     /**
36234      * APIMethod: write
36235      * Serialize an object into a JSON string.
36236      *
36237      * Parameters:
36238      * value - {String} The object, array, string, number, boolean or date
36239      *     to be serialized.
36240      * pretty - {Boolean} Structure the output with newlines and indentation.
36241      *     Default is false.
36242      *
36243      * Returns:
36244      * {String} The JSON string representation of the input value.
36245      */
36246     write: function(value, pretty) {
36247         this.pretty = !!pretty;
36248         var json = null;
36249         var type = typeof value;
36250         if(this.serialize[type]) {
36251             try {
36252                 json = (!this.pretty && this.nativeJSON) ?
36253                     JSON.stringify(value) :
36254                     this.serialize[type].apply(this, [value]);
36255             } catch(err) {
36256                 OpenLayers.Console.error("Trouble serializing: " + err);
36257             }
36258         }
36259         return json;
36260     },
36261     
36262     /**
36263      * Method: writeIndent
36264      * Output an indentation string depending on the indentation level.
36265      *
36266      * Returns:
36267      * {String} An appropriate indentation string.
36268      */
36269     writeIndent: function() {
36270         var pieces = [];
36271         if(this.pretty) {
36272             for(var i=0; i<this.level; ++i) {
36273                 pieces.push(this.indent);
36274             }
36275         }
36276         return pieces.join('');
36277     },
36278     
36279     /**
36280      * Method: writeNewline
36281      * Output a string representing a newline if in pretty printing mode.
36282      *
36283      * Returns:
36284      * {String} A string representing a new line.
36285      */
36286     writeNewline: function() {
36287         return (this.pretty) ? this.newline : '';
36288     },
36289     
36290     /**
36291      * Method: writeSpace
36292      * Output a string representing a space if in pretty printing mode.
36293      *
36294      * Returns:
36295      * {String} A space.
36296      */
36297     writeSpace: function() {
36298         return (this.pretty) ? this.space : '';
36299     },
36300
36301     /**
36302      * Property: serialize
36303      * Object with properties corresponding to the serializable data types.
36304      *     Property values are functions that do the actual serializing.
36305      */
36306     serialize: {
36307         /**
36308          * Method: serialize.object
36309          * Transform an object into a JSON string.
36310          *
36311          * Parameters:
36312          * object - {Object} The object to be serialized.
36313          * 
36314          * Returns:
36315          * {String} A JSON string representing the object.
36316          */
36317         'object': function(object) {
36318             // three special objects that we want to treat differently
36319             if(object == null) {
36320                 return "null";
36321             }
36322             if(object.constructor == Date) {
36323                 return this.serialize.date.apply(this, [object]);
36324             }
36325             if(object.constructor == Array) {
36326                 return this.serialize.array.apply(this, [object]);
36327             }
36328             var pieces = ['{'];
36329             this.level += 1;
36330             var key, keyJSON, valueJSON;
36331             
36332             var addComma = false;
36333             for(key in object) {
36334                 if(object.hasOwnProperty(key)) {
36335                     // recursive calls need to allow for sub-classing
36336                     keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
36337                                                     [key, this.pretty]);
36338                     valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this,
36339                                                     [object[key], this.pretty]);
36340                     if(keyJSON != null && valueJSON != null) {
36341                         if(addComma) {
36342                             pieces.push(',');
36343                         }
36344                         pieces.push(this.writeNewline(), this.writeIndent(),
36345                                     keyJSON, ':', this.writeSpace(), valueJSON);
36346                         addComma = true;
36347                     }
36348                 }
36349             }
36350             
36351             this.level -= 1;
36352             pieces.push(this.writeNewline(), this.writeIndent(), '}');
36353             return pieces.join('');
36354         },
36355         
36356         /**
36357          * Method: serialize.array
36358          * Transform an array into a JSON string.
36359          *
36360          * Parameters:
36361          * array - {Array} The array to be serialized
36362          * 
36363          * Returns:
36364          * {String} A JSON string representing the array.
36365          */
36366         'array': function(array) {
36367             var json;
36368             var pieces = ['['];
36369             this.level += 1;
36370     
36371             for(var i=0, len=array.length; i<len; ++i) {
36372                 // recursive calls need to allow for sub-classing
36373                 json = OpenLayers.Format.JSON.prototype.write.apply(this,
36374                                                     [array[i], this.pretty]);
36375                 if(json != null) {
36376                     if(i > 0) {
36377                         pieces.push(',');
36378                     }
36379                     pieces.push(this.writeNewline(), this.writeIndent(), json);
36380                 }
36381             }
36382
36383             this.level -= 1;    
36384             pieces.push(this.writeNewline(), this.writeIndent(), ']');
36385             return pieces.join('');
36386         },
36387         
36388         /**
36389          * Method: serialize.string
36390          * Transform a string into a JSON string.
36391          *
36392          * Parameters:
36393          * string - {String} The string to be serialized
36394          * 
36395          * Returns:
36396          * {String} A JSON string representing the string.
36397          */
36398         'string': function(string) {
36399             // If the string contains no control characters, no quote characters, and no
36400             // backslash characters, then we can simply slap some quotes around it.
36401             // Otherwise we must also replace the offending characters with safe
36402             // sequences.    
36403             var m = {
36404                 '\b': '\\b',
36405                 '\t': '\\t',
36406                 '\n': '\\n',
36407                 '\f': '\\f',
36408                 '\r': '\\r',
36409                 '"' : '\\"',
36410                 '\\': '\\\\'
36411             };
36412             if(/["\\\x00-\x1f]/.test(string)) {
36413                 return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) {
36414                     var c = m[b];
36415                     if(c) {
36416                         return c;
36417                     }
36418                     c = b.charCodeAt();
36419                     return '\\u00' +
36420                         Math.floor(c / 16).toString(16) +
36421                         (c % 16).toString(16);
36422                 }) + '"';
36423             }
36424             return '"' + string + '"';
36425         },
36426
36427         /**
36428          * Method: serialize.number
36429          * Transform a number into a JSON string.
36430          *
36431          * Parameters:
36432          * number - {Number} The number to be serialized.
36433          *
36434          * Returns:
36435          * {String} A JSON string representing the number.
36436          */
36437         'number': function(number) {
36438             return isFinite(number) ? String(number) : "null";
36439         },
36440         
36441         /**
36442          * Method: serialize.boolean
36443          * Transform a boolean into a JSON string.
36444          *
36445          * Parameters:
36446          * bool - {Boolean} The boolean to be serialized.
36447          * 
36448          * Returns:
36449          * {String} A JSON string representing the boolean.
36450          */
36451         'boolean': function(bool) {
36452             return String(bool);
36453         },
36454         
36455         /**
36456          * Method: serialize.object
36457          * Transform a date into a JSON string.
36458          *
36459          * Parameters:
36460          * date - {Date} The date to be serialized.
36461          * 
36462          * Returns:
36463          * {String} A JSON string representing the date.
36464          */
36465         'date': function(date) {    
36466             function format(number) {
36467                 // Format integers to have at least two digits.
36468                 return (number < 10) ? '0' + number : number;
36469             }
36470             return '"' + date.getFullYear() + '-' +
36471                     format(date.getMonth() + 1) + '-' +
36472                     format(date.getDate()) + 'T' +
36473                     format(date.getHours()) + ':' +
36474                     format(date.getMinutes()) + ':' +
36475                     format(date.getSeconds()) + '"';
36476         }
36477     },
36478
36479     CLASS_NAME: "OpenLayers.Format.JSON" 
36480
36481 });     
36482 /* ======================================================================
36483     OpenLayers/Format/GeoJSON.js
36484    ====================================================================== */
36485
36486 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
36487  * full list of contributors). Published under the 2-clause BSD license.
36488  * See license.txt in the OpenLayers distribution or repository for the
36489  * full text of the license. */
36490
36491 /**
36492  * @requires OpenLayers/Format/JSON.js
36493  * @requires OpenLayers/Feature/Vector.js
36494  * @requires OpenLayers/Geometry/Point.js
36495  * @requires OpenLayers/Geometry/MultiPoint.js
36496  * @requires OpenLayers/Geometry/LineString.js
36497  * @requires OpenLayers/Geometry/MultiLineString.js
36498  * @requires OpenLayers/Geometry/Polygon.js
36499  * @requires OpenLayers/Geometry/MultiPolygon.js
36500  * @requires OpenLayers/Console.js
36501  */
36502
36503 /**
36504  * Class: OpenLayers.Format.GeoJSON
36505  * Read and write GeoJSON. Create a new parser with the
36506  *     <OpenLayers.Format.GeoJSON> constructor.
36507  *
36508  * Inherits from:
36509  *  - <OpenLayers.Format.JSON>
36510  */
36511 OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {
36512
36513     /**
36514      * APIProperty: ignoreExtraDims
36515      * {Boolean} Ignore dimensions higher than 2 when reading geometry
36516      * coordinates.
36517      */ 
36518     ignoreExtraDims: false,
36519     
36520     /**
36521      * Constructor: OpenLayers.Format.GeoJSON
36522      * Create a new parser for GeoJSON.
36523      *
36524      * Parameters:
36525      * options - {Object} An optional object whose properties will be set on
36526      *     this instance.
36527      */
36528
36529     /**
36530      * APIMethod: read
36531      * Deserialize a GeoJSON string.
36532      *
36533      * Parameters:
36534      * json - {String} A GeoJSON string
36535      * type - {String} Optional string that determines the structure of
36536      *     the output.  Supported values are "Geometry", "Feature", and
36537      *     "FeatureCollection".  If absent or null, a default of
36538      *     "FeatureCollection" is assumed.
36539      * filter - {Function} A function which will be called for every key and
36540      *     value at every level of the final result. Each value will be
36541      *     replaced by the result of the filter function. This can be used to
36542      *     reform generic objects into instances of classes, or to transform
36543      *     date strings into Date objects.
36544      *
36545      * Returns: 
36546      * {Object} The return depends on the value of the type argument. If type
36547      *     is "FeatureCollection" (the default), the return will be an array
36548      *     of <OpenLayers.Feature.Vector>. If type is "Geometry", the input json
36549      *     must represent a single geometry, and the return will be an
36550      *     <OpenLayers.Geometry>.  If type is "Feature", the input json must
36551      *     represent a single feature, and the return will be an
36552      *     <OpenLayers.Feature.Vector>.
36553      */
36554     read: function(json, type, filter) {
36555         type = (type) ? type : "FeatureCollection";
36556         var results = null;
36557         var obj = null;
36558         if (typeof json == "string") {
36559             obj = OpenLayers.Format.JSON.prototype.read.apply(this,
36560                                                               [json, filter]);
36561         } else { 
36562             obj = json;
36563         }    
36564         if(!obj) {
36565             OpenLayers.Console.error("Bad JSON: " + json);
36566         } else if(typeof(obj.type) != "string") {
36567             OpenLayers.Console.error("Bad GeoJSON - no type: " + json);
36568         } else if(this.isValidType(obj, type)) {
36569             switch(type) {
36570                 case "Geometry":
36571                     try {
36572                         results = this.parseGeometry(obj);
36573                     } catch(err) {
36574                         OpenLayers.Console.error(err);
36575                     }
36576                     break;
36577                 case "Feature":
36578                     try {
36579                         results = this.parseFeature(obj);
36580                         results.type = "Feature";
36581                     } catch(err) {
36582                         OpenLayers.Console.error(err);
36583                     }
36584                     break;
36585                 case "FeatureCollection":
36586                     // for type FeatureCollection, we allow input to be any type
36587                     results = [];
36588                     switch(obj.type) {
36589                         case "Feature":
36590                             try {
36591                                 results.push(this.parseFeature(obj));
36592                             } catch(err) {
36593                                 results = null;
36594                                 OpenLayers.Console.error(err);
36595                             }
36596                             break;
36597                         case "FeatureCollection":
36598                             for(var i=0, len=obj.features.length; i<len; ++i) {
36599                                 try {
36600                                     results.push(this.parseFeature(obj.features[i]));
36601                                 } catch(err) {
36602                                     results = null;
36603                                     OpenLayers.Console.error(err);
36604                                 }
36605                             }
36606                             break;
36607                         default:
36608                             try {
36609                                 var geom = this.parseGeometry(obj);
36610                                 results.push(new OpenLayers.Feature.Vector(geom));
36611                             } catch(err) {
36612                                 results = null;
36613                                 OpenLayers.Console.error(err);
36614                             }
36615                     }
36616                 break;
36617             }
36618         }
36619         return results;
36620     },
36621     
36622     /**
36623      * Method: isValidType
36624      * Check if a GeoJSON object is a valid representative of the given type.
36625      *
36626      * Returns:
36627      * {Boolean} The object is valid GeoJSON object of the given type.
36628      */
36629     isValidType: function(obj, type) {
36630         var valid = false;
36631         switch(type) {
36632             case "Geometry":
36633                 if(OpenLayers.Util.indexOf(
36634                     ["Point", "MultiPoint", "LineString", "MultiLineString",
36635                      "Polygon", "MultiPolygon", "Box", "GeometryCollection"],
36636                     obj.type) == -1) {
36637                     // unsupported geometry type
36638                     OpenLayers.Console.error("Unsupported geometry type: " +
36639                                               obj.type);
36640                 } else {
36641                     valid = true;
36642                 }
36643                 break;
36644             case "FeatureCollection":
36645                 // allow for any type to be converted to a feature collection
36646                 valid = true;
36647                 break;
36648             default:
36649                 // for Feature types must match
36650                 if(obj.type == type) {
36651                     valid = true;
36652                 } else {
36653                     OpenLayers.Console.error("Cannot convert types from " +
36654                                               obj.type + " to " + type);
36655                 }
36656         }
36657         return valid;
36658     },
36659     
36660     /**
36661      * Method: parseFeature
36662      * Convert a feature object from GeoJSON into an
36663      *     <OpenLayers.Feature.Vector>.
36664      *
36665      * Parameters:
36666      * obj - {Object} An object created from a GeoJSON object
36667      *
36668      * Returns:
36669      * {<OpenLayers.Feature.Vector>} A feature.
36670      */
36671     parseFeature: function(obj) {
36672         var feature, geometry, attributes, bbox;
36673         attributes = (obj.properties) ? obj.properties : {};
36674         bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox;
36675         try {
36676             geometry = this.parseGeometry(obj.geometry);
36677         } catch(err) {
36678             // deal with bad geometries
36679             throw err;
36680         }
36681         feature = new OpenLayers.Feature.Vector(geometry, attributes);
36682         if(bbox) {
36683             feature.bounds = OpenLayers.Bounds.fromArray(bbox);
36684         }
36685         if(obj.id) {
36686             feature.fid = obj.id;
36687         }
36688         return feature;
36689     },
36690     
36691     /**
36692      * Method: parseGeometry
36693      * Convert a geometry object from GeoJSON into an <OpenLayers.Geometry>.
36694      *
36695      * Parameters:
36696      * obj - {Object} An object created from a GeoJSON object
36697      *
36698      * Returns: 
36699      * {<OpenLayers.Geometry>} A geometry.
36700      */
36701     parseGeometry: function(obj) {
36702         if (obj == null) {
36703             return null;
36704         }
36705         var geometry, collection = false;
36706         if(obj.type == "GeometryCollection") {
36707             if(!(OpenLayers.Util.isArray(obj.geometries))) {
36708                 throw "GeometryCollection must have geometries array: " + obj;
36709             }
36710             var numGeom = obj.geometries.length;
36711             var components = new Array(numGeom);
36712             for(var i=0; i<numGeom; ++i) {
36713                 components[i] = this.parseGeometry.apply(
36714                     this, [obj.geometries[i]]
36715                 );
36716             }
36717             geometry = new OpenLayers.Geometry.Collection(components);
36718             collection = true;
36719         } else {
36720             if(!(OpenLayers.Util.isArray(obj.coordinates))) {
36721                 throw "Geometry must have coordinates array: " + obj;
36722             }
36723             if(!this.parseCoords[obj.type.toLowerCase()]) {
36724                 throw "Unsupported geometry type: " + obj.type;
36725             }
36726             try {
36727                 geometry = this.parseCoords[obj.type.toLowerCase()].apply(
36728                     this, [obj.coordinates]
36729                 );
36730             } catch(err) {
36731                 // deal with bad coordinates
36732                 throw err;
36733             }
36734         }
36735         // We don't reproject collections because the children are reprojected
36736         // for us when they are created.
36737         if (this.internalProjection && this.externalProjection && !collection) {
36738             geometry.transform(this.externalProjection, 
36739                                this.internalProjection); 
36740         }                       
36741         return geometry;
36742     },
36743     
36744     /**
36745      * Property: parseCoords
36746      * Object with properties corresponding to the GeoJSON geometry types.
36747      *     Property values are functions that do the actual parsing.
36748      */
36749     parseCoords: {
36750         /**
36751          * Method: parseCoords.point
36752          * Convert a coordinate array from GeoJSON into an
36753          *     <OpenLayers.Geometry>.
36754          *
36755          * Parameters:
36756          * array - {Object} The coordinates array from the GeoJSON fragment.
36757          *
36758          * Returns:
36759          * {<OpenLayers.Geometry>} A geometry.
36760          */
36761         "point": function(array) {
36762             if (this.ignoreExtraDims == false && 
36763                   array.length != 2) {
36764                     throw "Only 2D points are supported: " + array;
36765             }
36766             return new OpenLayers.Geometry.Point(array[0], array[1]);
36767         },
36768         
36769         /**
36770          * Method: parseCoords.multipoint
36771          * Convert a coordinate array from GeoJSON into an
36772          *     <OpenLayers.Geometry>.
36773          *
36774          * Parameters:
36775          * array - {Object} The coordinates array from the GeoJSON fragment.
36776          *
36777          * Returns:
36778          * {<OpenLayers.Geometry>} A geometry.
36779          */
36780         "multipoint": function(array) {
36781             var points = [];
36782             var p = null;
36783             for(var i=0, len=array.length; i<len; ++i) {
36784                 try {
36785                     p = this.parseCoords["point"].apply(this, [array[i]]);
36786                 } catch(err) {
36787                     throw err;
36788                 }
36789                 points.push(p);
36790             }
36791             return new OpenLayers.Geometry.MultiPoint(points);
36792         },
36793
36794         /**
36795          * Method: parseCoords.linestring
36796          * Convert a coordinate array from GeoJSON into an
36797          *     <OpenLayers.Geometry>.
36798          *
36799          * Parameters:
36800          * array - {Object} The coordinates array from the GeoJSON fragment.
36801          *
36802          * Returns:
36803          * {<OpenLayers.Geometry>} A geometry.
36804          */
36805         "linestring": function(array) {
36806             var points = [];
36807             var p = null;
36808             for(var i=0, len=array.length; i<len; ++i) {
36809                 try {
36810                     p = this.parseCoords["point"].apply(this, [array[i]]);
36811                 } catch(err) {
36812                     throw err;
36813                 }
36814                 points.push(p);
36815             }
36816             return new OpenLayers.Geometry.LineString(points);
36817         },
36818         
36819         /**
36820          * Method: parseCoords.multilinestring
36821          * Convert a coordinate array from GeoJSON into an
36822          *     <OpenLayers.Geometry>.
36823          *
36824          * Parameters:
36825          * array - {Object} The coordinates array from the GeoJSON fragment.
36826          *
36827          * Returns:
36828          * {<OpenLayers.Geometry>} A geometry.
36829          */
36830         "multilinestring": function(array) {
36831             var lines = [];
36832             var l = null;
36833             for(var i=0, len=array.length; i<len; ++i) {
36834                 try {
36835                     l = this.parseCoords["linestring"].apply(this, [array[i]]);
36836                 } catch(err) {
36837                     throw err;
36838                 }
36839                 lines.push(l);
36840             }
36841             return new OpenLayers.Geometry.MultiLineString(lines);
36842         },
36843         
36844         /**
36845          * Method: parseCoords.polygon
36846          * Convert a coordinate array from GeoJSON into an
36847          *     <OpenLayers.Geometry>.
36848          *
36849          * Returns:
36850          * {<OpenLayers.Geometry>} A geometry.
36851          */
36852         "polygon": function(array) {
36853             var rings = [];
36854             var r, l;
36855             for(var i=0, len=array.length; i<len; ++i) {
36856                 try {
36857                     l = this.parseCoords["linestring"].apply(this, [array[i]]);
36858                 } catch(err) {
36859                     throw err;
36860                 }
36861                 r = new OpenLayers.Geometry.LinearRing(l.components);
36862                 rings.push(r);
36863             }
36864             return new OpenLayers.Geometry.Polygon(rings);
36865         },
36866
36867         /**
36868          * Method: parseCoords.multipolygon
36869          * Convert a coordinate array from GeoJSON into an
36870          *     <OpenLayers.Geometry>.
36871          *
36872          * Parameters:
36873          * array - {Object} The coordinates array from the GeoJSON fragment.
36874          *
36875          * Returns:
36876          * {<OpenLayers.Geometry>} A geometry.
36877          */
36878         "multipolygon": function(array) {
36879             var polys = [];
36880             var p = null;
36881             for(var i=0, len=array.length; i<len; ++i) {
36882                 try {
36883                     p = this.parseCoords["polygon"].apply(this, [array[i]]);
36884                 } catch(err) {
36885                     throw err;
36886                 }
36887                 polys.push(p);
36888             }
36889             return new OpenLayers.Geometry.MultiPolygon(polys);
36890         },
36891
36892         /**
36893          * Method: parseCoords.box
36894          * Convert a coordinate array from GeoJSON into an
36895          *     <OpenLayers.Geometry>.
36896          *
36897          * Parameters:
36898          * array - {Object} The coordinates array from the GeoJSON fragment.
36899          *
36900          * Returns:
36901          * {<OpenLayers.Geometry>} A geometry.
36902          */
36903         "box": function(array) {
36904             if(array.length != 2) {
36905                 throw "GeoJSON box coordinates must have 2 elements";
36906             }
36907             return new OpenLayers.Geometry.Polygon([
36908                 new OpenLayers.Geometry.LinearRing([
36909                     new OpenLayers.Geometry.Point(array[0][0], array[0][1]),
36910                     new OpenLayers.Geometry.Point(array[1][0], array[0][1]),
36911                     new OpenLayers.Geometry.Point(array[1][0], array[1][1]),
36912                     new OpenLayers.Geometry.Point(array[0][0], array[1][1]),
36913                     new OpenLayers.Geometry.Point(array[0][0], array[0][1])
36914                 ])
36915             ]);
36916         }
36917
36918     },
36919
36920     /**
36921      * APIMethod: write
36922      * Serialize a feature, geometry, array of features into a GeoJSON string.
36923      *
36924      * Parameters:
36925      * obj - {Object} An <OpenLayers.Feature.Vector>, <OpenLayers.Geometry>,
36926      *     or an array of features.
36927      * pretty - {Boolean} Structure the output with newlines and indentation.
36928      *     Default is false.
36929      *
36930      * Returns:
36931      * {String} The GeoJSON string representation of the input geometry,
36932      *     features, or array of features.
36933      */
36934     write: function(obj, pretty) {
36935         var geojson = {
36936             "type": null
36937         };
36938         if(OpenLayers.Util.isArray(obj)) {
36939             geojson.type = "FeatureCollection";
36940             var numFeatures = obj.length;
36941             geojson.features = new Array(numFeatures);
36942             for(var i=0; i<numFeatures; ++i) {
36943                 var element = obj[i];
36944                 if(!element instanceof OpenLayers.Feature.Vector) {
36945                     var msg = "FeatureCollection only supports collections " +
36946                               "of features: " + element;
36947                     throw msg;
36948                 }
36949                 geojson.features[i] = this.extract.feature.apply(
36950                     this, [element]
36951                 );
36952             }
36953         } else if (obj.CLASS_NAME.indexOf("OpenLayers.Geometry") == 0) {
36954             geojson = this.extract.geometry.apply(this, [obj]);
36955         } else if (obj instanceof OpenLayers.Feature.Vector) {
36956             geojson = this.extract.feature.apply(this, [obj]);
36957             if(obj.layer && obj.layer.projection) {
36958                 geojson.crs = this.createCRSObject(obj);
36959             }
36960         }
36961         return OpenLayers.Format.JSON.prototype.write.apply(this,
36962                                                             [geojson, pretty]);
36963     },
36964
36965     /**
36966      * Method: createCRSObject
36967      * Create the CRS object for an object.
36968      *
36969      * Parameters:
36970      * object - {<OpenLayers.Feature.Vector>} 
36971      *
36972      * Returns:
36973      * {Object} An object which can be assigned to the crs property
36974      * of a GeoJSON object.
36975      */
36976     createCRSObject: function(object) {
36977        var proj = object.layer.projection.toString();
36978        var crs = {};
36979        if (proj.match(/epsg:/i)) {
36980            var code = parseInt(proj.substring(proj.indexOf(":") + 1));
36981            if (code == 4326) {
36982                crs = {
36983                    "type": "name",
36984                    "properties": {
36985                        "name": "urn:ogc:def:crs:OGC:1.3:CRS84"
36986                    }
36987                };
36988            } else {    
36989                crs = {
36990                    "type": "name",
36991                    "properties": {
36992                        "name": "EPSG:" + code
36993                    }
36994                };
36995            }    
36996        }
36997        return crs;
36998     },
36999     
37000     /**
37001      * Property: extract
37002      * Object with properties corresponding to the GeoJSON types.
37003      *     Property values are functions that do the actual value extraction.
37004      */
37005     extract: {
37006         /**
37007          * Method: extract.feature
37008          * Return a partial GeoJSON object representing a single feature.
37009          *
37010          * Parameters:
37011          * feature - {<OpenLayers.Feature.Vector>}
37012          *
37013          * Returns:
37014          * {Object} An object representing the point.
37015          */
37016         'feature': function(feature) {
37017             var geom = this.extract.geometry.apply(this, [feature.geometry]);
37018             var json = {
37019                 "type": "Feature",
37020                 "properties": feature.attributes,
37021                 "geometry": geom
37022             };
37023             if (feature.fid != null) {
37024                 json.id = feature.fid;
37025             }
37026             return json;
37027         },
37028         
37029         /**
37030          * Method: extract.geometry
37031          * Return a GeoJSON object representing a single geometry.
37032          *
37033          * Parameters:
37034          * geometry - {<OpenLayers.Geometry>}
37035          *
37036          * Returns:
37037          * {Object} An object representing the geometry.
37038          */
37039         'geometry': function(geometry) {
37040             if (geometry == null) {
37041                 return null;
37042             }
37043             if (this.internalProjection && this.externalProjection) {
37044                 geometry = geometry.clone();
37045                 geometry.transform(this.internalProjection, 
37046                                    this.externalProjection);
37047             }                       
37048             var geometryType = geometry.CLASS_NAME.split('.')[2];
37049             var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);
37050             var json;
37051             if(geometryType == "Collection") {
37052                 json = {
37053                     "type": "GeometryCollection",
37054                     "geometries": data
37055                 };
37056             } else {
37057                 json = {
37058                     "type": geometryType,
37059                     "coordinates": data
37060                 };
37061             }
37062             
37063             return json;
37064         },
37065
37066         /**
37067          * Method: extract.point
37068          * Return an array of coordinates from a point.
37069          *
37070          * Parameters:
37071          * point - {<OpenLayers.Geometry.Point>}
37072          *
37073          * Returns: 
37074          * {Array} An array of coordinates representing the point.
37075          */
37076         'point': function(point) {
37077             return [point.x, point.y];
37078         },
37079
37080         /**
37081          * Method: extract.multipoint
37082          * Return an array of point coordinates from a multipoint.
37083          *
37084          * Parameters:
37085          * multipoint - {<OpenLayers.Geometry.MultiPoint>}
37086          *
37087          * Returns:
37088          * {Array} An array of point coordinate arrays representing
37089          *     the multipoint.
37090          */
37091         'multipoint': function(multipoint) {
37092             var array = [];
37093             for(var i=0, len=multipoint.components.length; i<len; ++i) {
37094                 array.push(this.extract.point.apply(this, [multipoint.components[i]]));
37095             }
37096             return array;
37097         },
37098         
37099         /**
37100          * Method: extract.linestring
37101          * Return an array of coordinate arrays from a linestring.
37102          *
37103          * Parameters:
37104          * linestring - {<OpenLayers.Geometry.LineString>}
37105          *
37106          * Returns:
37107          * {Array} An array of coordinate arrays representing
37108          *     the linestring.
37109          */
37110         'linestring': function(linestring) {
37111             var array = [];
37112             for(var i=0, len=linestring.components.length; i<len; ++i) {
37113                 array.push(this.extract.point.apply(this, [linestring.components[i]]));
37114             }
37115             return array;
37116         },
37117
37118         /**
37119          * Method: extract.multilinestring
37120          * Return an array of linestring arrays from a linestring.
37121          * 
37122          * Parameters:
37123          * multilinestring - {<OpenLayers.Geometry.MultiLineString>}
37124          * 
37125          * Returns:
37126          * {Array} An array of linestring arrays representing
37127          *     the multilinestring.
37128          */
37129         'multilinestring': function(multilinestring) {
37130             var array = [];
37131             for(var i=0, len=multilinestring.components.length; i<len; ++i) {
37132                 array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]));
37133             }
37134             return array;
37135         },
37136         
37137         /**
37138          * Method: extract.polygon
37139          * Return an array of linear ring arrays from a polygon.
37140          *
37141          * Parameters:
37142          * polygon - {<OpenLayers.Geometry.Polygon>}
37143          * 
37144          * Returns:
37145          * {Array} An array of linear ring arrays representing the polygon.
37146          */
37147         'polygon': function(polygon) {
37148             var array = [];
37149             for(var i=0, len=polygon.components.length; i<len; ++i) {
37150                 array.push(this.extract.linestring.apply(this, [polygon.components[i]]));
37151             }
37152             return array;
37153         },
37154
37155         /**
37156          * Method: extract.multipolygon
37157          * Return an array of polygon arrays from a multipolygon.
37158          * 
37159          * Parameters:
37160          * multipolygon - {<OpenLayers.Geometry.MultiPolygon>}
37161          * 
37162          * Returns:
37163          * {Array} An array of polygon arrays representing
37164          *     the multipolygon
37165          */
37166         'multipolygon': function(multipolygon) {
37167             var array = [];
37168             for(var i=0, len=multipolygon.components.length; i<len; ++i) {
37169                 array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]));
37170             }
37171             return array;
37172         },
37173         
37174         /**
37175          * Method: extract.collection
37176          * Return an array of geometries from a geometry collection.
37177          * 
37178          * Parameters:
37179          * collection - {<OpenLayers.Geometry.Collection>}
37180          * 
37181          * Returns:
37182          * {Array} An array of geometry objects representing the geometry
37183          *     collection.
37184          */
37185         'collection': function(collection) {
37186             var len = collection.components.length;
37187             var array = new Array(len);
37188             for(var i=0; i<len; ++i) {
37189                 array[i] = this.extract.geometry.apply(
37190                     this, [collection.components[i]]
37191                 );
37192             }
37193             return array;
37194         }
37195         
37196
37197     },
37198
37199     CLASS_NAME: "OpenLayers.Format.GeoJSON" 
37200
37201 });     
37202 /* ======================================================================
37203     OpenLayers/Protocol/WFS/v1.js
37204    ====================================================================== */
37205
37206 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
37207  * full list of contributors). Published under the 2-clause BSD license.
37208  * See license.txt in the OpenLayers distribution or repository for the
37209  * full text of the license. */
37210
37211 /**
37212  * @requires OpenLayers/Protocol/WFS.js
37213  */
37214
37215 /**
37216  * Class: OpenLayers.Protocol.WFS.v1
37217  * Abstract class for for v1.0.0 and v1.1.0 protocol.
37218  *
37219  * Inherits from:
37220  *  - <OpenLayers.Protocol>
37221  */
37222 OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, {
37223     
37224     /**
37225      * Property: version
37226      * {String} WFS version number.
37227      */
37228     version: null,
37229     
37230     /**
37231      * Property: srsName
37232      * {String} Name of spatial reference system.  Default is "EPSG:4326".
37233      */
37234     srsName: "EPSG:4326",
37235     
37236     /**
37237      * Property: featureType
37238      * {String} Local feature typeName.
37239      */
37240     featureType: null,
37241     
37242     /**
37243      * Property: featureNS
37244      * {String} Feature namespace.
37245      */
37246     featureNS: null,
37247     
37248     /**
37249      * Property: geometryName
37250      * {String} Name of the geometry attribute for features.  Default is
37251      *     "the_geom" for WFS <version> 1.0, and null for higher versions.
37252      */
37253     geometryName: "the_geom",
37254
37255     /**
37256      * Property: maxFeatures
37257      * {Integer} Optional maximum number of features to retrieve.
37258      */
37259     
37260     /**
37261      * Property: schema
37262      * {String} Optional schema location that will be included in the
37263      *     schemaLocation attribute value.  Note that the feature type schema
37264      *     is required for a strict XML validator (on transactions with an
37265      *     insert for example), but is *not* required by the WFS specification
37266      *     (since the server is supposed to know about feature type schemas).
37267      */
37268     schema: null,
37269
37270     /**
37271      * Property: featurePrefix
37272      * {String} Namespace alias for feature type.  Default is "feature".
37273      */
37274     featurePrefix: "feature",
37275     
37276     /**
37277      * Property: formatOptions
37278      * {Object} Optional options for the format.  If a format is not provided,
37279      *     this property can be used to extend the default format options.
37280      */
37281     formatOptions: null,
37282
37283     /** 
37284      * Property: readFormat 
37285      * {<OpenLayers.Format>} For WFS requests it is possible to get a  
37286      *     different output format than GML. In that case, we cannot parse  
37287      *     the response with the default format (WFST) and we need a different 
37288      *     format for reading. 
37289      */ 
37290     readFormat: null,
37291     
37292     /**
37293      * Property: readOptions
37294      * {Object} Optional object to pass to format's read.
37295      */
37296     readOptions: null,
37297     
37298     /**
37299      * Constructor: OpenLayers.Protocol.WFS
37300      * A class for giving layers WFS protocol.
37301      *
37302      * Parameters:
37303      * options - {Object} Optional object whose properties will be set on the
37304      *     instance.
37305      *
37306      * Valid options properties:
37307      * url - {String} URL to send requests to (required).
37308      * featureType - {String} Local (without prefix) feature typeName (required).
37309      * featureNS - {String} Feature namespace (required, but can be autodetected
37310      *     during the first query if GML is used as readFormat and
37311      *     featurePrefix is provided and matches the prefix used by the server
37312      *     for this featureType).
37313      * featurePrefix - {String} Feature namespace alias (optional - only used
37314      *     for writing if featureNS is provided).  Default is 'feature'.
37315      * geometryName - {String} Name of geometry attribute.  The default is
37316      *     'the_geom' for WFS <version> 1.0, and null for higher versions. If
37317      *     null, it will be set to the name of the first geometry found in the
37318      *     first read operation.
37319      * multi - {Boolean} If set to true, geometries will be casted to Multi
37320      *     geometries before they are written in a transaction. No casting will
37321      *     be done when reading features.
37322      */
37323     initialize: function(options) {
37324         OpenLayers.Protocol.prototype.initialize.apply(this, [options]);
37325         if(!options.format) {
37326             this.format = OpenLayers.Format.WFST(OpenLayers.Util.extend({
37327                 version: this.version,
37328                 featureType: this.featureType,
37329                 featureNS: this.featureNS,
37330                 featurePrefix: this.featurePrefix,
37331                 geometryName: this.geometryName,
37332                 srsName: this.srsName,
37333                 schema: this.schema
37334             }, this.formatOptions));
37335         }
37336         if (!options.geometryName && parseFloat(this.format.version) > 1.0) {
37337             this.setGeometryName(null);
37338         }
37339     },
37340     
37341     /**
37342      * APIMethod: destroy
37343      * Clean up the protocol.
37344      */
37345     destroy: function() {
37346         if(this.options && !this.options.format) {
37347             this.format.destroy();
37348         }
37349         this.format = null;
37350         OpenLayers.Protocol.prototype.destroy.apply(this);
37351     },
37352
37353     /**
37354      * APIMethod: read
37355      * Construct a request for reading new features.  Since WFS splits the
37356      *     basic CRUD operations into GetFeature requests (for read) and
37357      *     Transactions (for all others), this method does not make use of the
37358      *     format's read method (that is only about reading transaction
37359      *     responses).
37360      *
37361      * Parameters:
37362      * options - {Object} Options for the read operation, in addition to the
37363      *     options set on the instance (options set here will take precedence).
37364      *
37365      * To use a configured protocol to get e.g. a WFS hit count, applications
37366      * could do the following:
37367      *
37368      * (code)
37369      * protocol.read({
37370      *     readOptions: {output: "object"},
37371      *     resultType: "hits",
37372      *     maxFeatures: null,
37373      *     callback: function(resp) {
37374      *         // process resp.numberOfFeatures here
37375      *     }
37376      * });
37377      * (end)
37378      *
37379      * To use a configured protocol to use WFS paging (if supported by the
37380      * server), applications could do the following:
37381      *
37382      * (code)
37383      * protocol.read({
37384      *     startIndex: 0,
37385      *     count: 50
37386      * });
37387      * (end)
37388      *
37389      * To limit the attributes returned by the GetFeature request, applications
37390      * can use the propertyNames option to specify the properties to include in
37391      * the response:
37392      *
37393      * (code)
37394      * protocol.read({
37395      *     propertyNames: ["DURATION", "INTENSITY"]
37396      * });
37397      * (end)
37398      */
37399     read: function(options) {
37400         OpenLayers.Protocol.prototype.read.apply(this, arguments);
37401         options = OpenLayers.Util.extend({}, options);
37402         OpenLayers.Util.applyDefaults(options, this.options || {});
37403         var response = new OpenLayers.Protocol.Response({requestType: "read"});
37404         
37405         var data = OpenLayers.Format.XML.prototype.write.apply(
37406             this.format, [this.format.writeNode("wfs:GetFeature", options)]
37407         );
37408
37409         response.priv = OpenLayers.Request.POST({
37410             url: options.url,
37411             callback: this.createCallback(this.handleRead, response, options),
37412             params: options.params,
37413             headers: options.headers,
37414             data: data
37415         });
37416
37417         return response;
37418     },
37419
37420     /**
37421      * APIMethod: setFeatureType
37422      * Change the feature type on the fly.
37423      *
37424      * Parameters:
37425      * featureType - {String} Local (without prefix) feature typeName.
37426      */
37427     setFeatureType: function(featureType) {
37428         this.featureType = featureType;
37429         this.format.featureType = featureType;
37430     },
37431  
37432     /**
37433      * APIMethod: setGeometryName
37434      * Sets the geometryName option after instantiation.
37435      *
37436      * Parameters:
37437      * geometryName - {String} Name of geometry attribute.
37438      */
37439     setGeometryName: function(geometryName) {
37440         this.geometryName = geometryName;
37441         this.format.geometryName = geometryName;
37442     },
37443     
37444     /**
37445      * Method: handleRead
37446      * Deal with response from the read request.
37447      *
37448      * Parameters:
37449      * response - {<OpenLayers.Protocol.Response>} The response object to pass
37450      *     to the user callback.
37451      * options - {Object} The user options passed to the read call.
37452      */
37453     handleRead: function(response, options) {
37454         options = OpenLayers.Util.extend({}, options);
37455         OpenLayers.Util.applyDefaults(options, this.options);
37456
37457         if(options.callback) {
37458             var request = response.priv;
37459             if(request.status >= 200 && request.status < 300) {
37460                 // success
37461                 var result = this.parseResponse(request, options.readOptions);
37462                 if (result && result.success !== false) { 
37463                     if (options.readOptions && options.readOptions.output == "object") {
37464                         OpenLayers.Util.extend(response, result);
37465                     } else {
37466                         response.features = result;
37467                     }
37468                     response.code = OpenLayers.Protocol.Response.SUCCESS;
37469                 } else {
37470                     // failure (service exception)
37471                     response.code = OpenLayers.Protocol.Response.FAILURE;
37472                     response.error = result;
37473                 }
37474             } else {
37475                 // failure
37476                 response.code = OpenLayers.Protocol.Response.FAILURE;
37477             }
37478             options.callback.call(options.scope, response);
37479         }
37480     },
37481
37482     /**
37483      * Method: parseResponse
37484      * Read HTTP response body and return features
37485      *
37486      * Parameters:
37487      * request - {XMLHttpRequest} The request object
37488      * options - {Object} Optional object to pass to format's read
37489      *
37490      * Returns:
37491      * {Object} or {Array({<OpenLayers.Feature.Vector>})} or
37492      *     {<OpenLayers.Feature.Vector>} 
37493      * An object with a features property, an array of features or a single 
37494      * feature.
37495      */
37496     parseResponse: function(request, options) {
37497         var doc = request.responseXML;
37498         if(!doc || !doc.documentElement) {
37499             doc = request.responseText;
37500         }
37501         if(!doc || doc.length <= 0) {
37502             return null;
37503         }
37504         var result = (this.readFormat !== null) ? this.readFormat.read(doc) : 
37505             this.format.read(doc, options);
37506         if (!this.featureNS) {
37507             var format = this.readFormat || this.format;
37508             this.featureNS = format.featureNS;
37509             // no need to auto-configure again on subsequent reads
37510             format.autoConfig = false;
37511             if (!this.geometryName) {
37512                 this.setGeometryName(format.geometryName);
37513             }
37514         }
37515         return result;
37516     },
37517
37518     /**
37519      * Method: commit
37520      * Given a list of feature, assemble a batch request for update, create,
37521      *     and delete transactions.  A commit call on the prototype amounts
37522      *     to writing a WFS transaction - so the write method on the format
37523      *     is used.
37524      *
37525      * Parameters:
37526      * features - {Array(<OpenLayers.Feature.Vector>)}
37527      * options - {Object}
37528      *
37529      * Valid options properties:
37530      * nativeElements - {Array({Object})} Array of objects with information for writing
37531      * out <Native> elements, these objects have vendorId, safeToIgnore and
37532      * value properties. The <Native> element is intended to allow access to 
37533      * vendor specific capabilities of any particular web feature server or 
37534      * datastore.
37535      *
37536      * Returns:
37537      * {<OpenLayers.Protocol.Response>} A response object with a features
37538      *     property containing any insertIds and a priv property referencing
37539      *     the XMLHttpRequest object.
37540      */
37541     commit: function(features, options) {
37542
37543         options = OpenLayers.Util.extend({}, options);
37544         OpenLayers.Util.applyDefaults(options, this.options);
37545         
37546         var response = new OpenLayers.Protocol.Response({
37547             requestType: "commit",
37548             reqFeatures: features
37549         });
37550         response.priv = OpenLayers.Request.POST({
37551             url: options.url,
37552             headers: options.headers,
37553             data: this.format.write(features, options),
37554             callback: this.createCallback(this.handleCommit, response, options)
37555         });
37556         
37557         return response;
37558     },
37559     
37560     /**
37561      * Method: handleCommit
37562      * Called when the commit request returns.
37563      * 
37564      * Parameters:
37565      * response - {<OpenLayers.Protocol.Response>} The response object to pass
37566      *     to the user callback.
37567      * options - {Object} The user options passed to the commit call.
37568      */
37569     handleCommit: function(response, options) {
37570         if(options.callback) {
37571             var request = response.priv;
37572
37573             // ensure that we have an xml doc
37574             var data = request.responseXML;
37575             if(!data || !data.documentElement) {
37576                 data = request.responseText;
37577             }
37578             
37579             var obj = this.format.read(data) || {};
37580             
37581             response.insertIds = obj.insertIds || [];
37582             if (obj.success) {
37583                 response.code = OpenLayers.Protocol.Response.SUCCESS;
37584             } else {
37585                 response.code = OpenLayers.Protocol.Response.FAILURE;
37586                 response.error = obj;
37587             }
37588             options.callback.call(options.scope, response);
37589         }
37590     },
37591     
37592     /**
37593      * Method: filterDelete
37594      * Send a request that deletes all features by their filter.
37595      * 
37596      * Parameters:
37597      * filter - {<OpenLayers.Filter>} filter
37598      */
37599     filterDelete: function(filter, options) {
37600         options = OpenLayers.Util.extend({}, options);
37601         OpenLayers.Util.applyDefaults(options, this.options);    
37602         
37603         var response = new OpenLayers.Protocol.Response({
37604             requestType: "commit"
37605         });    
37606         
37607         var root = this.format.createElementNSPlus("wfs:Transaction", {
37608             attributes: {
37609                 service: "WFS",
37610                 version: this.version
37611             }
37612         });
37613         
37614         var deleteNode = this.format.createElementNSPlus("wfs:Delete", {
37615             attributes: {
37616                 typeName: (options.featureNS ? this.featurePrefix + ":" : "") +
37617                     options.featureType
37618             }
37619         });       
37620         
37621         if(options.featureNS) {
37622             deleteNode.setAttribute("xmlns:" + this.featurePrefix, options.featureNS);
37623         }
37624         var filterNode = this.format.writeNode("ogc:Filter", filter);
37625         
37626         deleteNode.appendChild(filterNode);
37627         
37628         root.appendChild(deleteNode);
37629         
37630         var data = OpenLayers.Format.XML.prototype.write.apply(
37631             this.format, [root]
37632         );
37633         
37634         return OpenLayers.Request.POST({
37635             url: this.url,
37636             callback : options.callback || function(){},
37637             data: data
37638         });   
37639         
37640     },
37641
37642     /**
37643      * Method: abort
37644      * Abort an ongoing request, the response object passed to
37645      * this method must come from this protocol (as a result
37646      * of a read, or commit operation).
37647      *
37648      * Parameters:
37649      * response - {<OpenLayers.Protocol.Response>}
37650      */
37651     abort: function(response) {
37652         if (response) {
37653             response.priv.abort();
37654         }
37655     },
37656   
37657     CLASS_NAME: "OpenLayers.Protocol.WFS.v1" 
37658 });
37659 /* ======================================================================
37660     OpenLayers/Layer/Google/v3.js
37661    ====================================================================== */
37662
37663 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
37664  * full list of contributors). Published under the 2-clause BSD license.
37665  * See license.txt in the OpenLayers distribution or repository for the
37666  * full text of the license. */
37667
37668
37669 /**
37670  * @requires OpenLayers/Layer/Google.js
37671  */
37672
37673 /**
37674  * Constant: OpenLayers.Layer.Google.v3
37675  * 
37676  * Mixin providing functionality specific to the Google Maps API v3.
37677  * 
37678  * To use this layer, you must include the GMaps v3 API in your html.
37679  * 
37680  * Note that this layer configures the google.maps.map object with the
37681  * "disableDefaultUI" option set to true. Using UI controls that the Google
37682  * Maps API provides is not supported by the OpenLayers API.
37683  */
37684 OpenLayers.Layer.Google.v3 = {
37685     
37686     /**
37687      * Constant: DEFAULTS
37688      * {Object} It is not recommended to change the properties set here. Note
37689      * that Google.v3 layers only work when sphericalMercator is set to true.
37690      * 
37691      * (code)
37692      * {
37693      *     sphericalMercator: true,
37694      *     projection: "EPSG:900913"
37695      * }
37696      * (end)
37697      */
37698     DEFAULTS: {
37699         sphericalMercator: true,
37700         projection: "EPSG:900913"
37701     },
37702
37703     /**
37704      * APIProperty: animationEnabled
37705      * {Boolean} If set to true, the transition between zoom levels will be
37706      *     animated (if supported by the GMaps API for the device used). Set to
37707      *     false to match the zooming experience of other layer types. Default
37708      *     is true. Note that the GMaps API does not give us control over zoom
37709      *     animation, so if set to false, when zooming, this will make the
37710      *     layer temporarily invisible, wait until GMaps reports the map being
37711      *     idle, and make it visible again. The result will be a blank layer
37712      *     for a few moments while zooming.
37713      */
37714     animationEnabled: true, 
37715
37716     /** 
37717      * Method: loadMapObject
37718      * Load the GMap and register appropriate event listeners.
37719      */
37720     loadMapObject: function() {
37721         if (!this.type) {
37722             this.type = google.maps.MapTypeId.ROADMAP;
37723         }
37724         var mapObject;
37725         var cache = OpenLayers.Layer.Google.cache[this.map.id];
37726         if (cache) {
37727             // there are already Google layers added to this map
37728             mapObject = cache.mapObject;
37729             // increment the layer count
37730             ++cache.count;
37731         } else {
37732             // this is the first Google layer for this map
37733             // create GMap
37734             var center = this.map.getCenter();
37735             var container = document.createElement('div');
37736             container.className = "olForeignContainer";
37737             container.style.width = '100%';
37738             container.style.height = '100%';
37739             mapObject = new google.maps.Map(container, {
37740                 center: center ?
37741                     new google.maps.LatLng(center.lat, center.lon) :
37742                     new google.maps.LatLng(0, 0),
37743                 zoom: this.map.getZoom() || 0,
37744                 mapTypeId: this.type,
37745                 disableDefaultUI: true,
37746                 keyboardShortcuts: false,
37747                 draggable: false,
37748                 disableDoubleClickZoom: true,
37749                 scrollwheel: false,
37750                 streetViewControl: false
37751             });
37752             var googleControl = document.createElement('div');
37753             googleControl.style.width = '100%';
37754             googleControl.style.height = '100%';
37755             mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl);
37756             
37757             // cache elements for use by any other google layers added to
37758             // this same map
37759             cache = {
37760                 googleControl: googleControl,
37761                 mapObject: mapObject,
37762                 count: 1
37763             };
37764             OpenLayers.Layer.Google.cache[this.map.id] = cache;
37765         }
37766         this.mapObject = mapObject;
37767         this.setGMapVisibility(this.visibility);
37768     },
37769     
37770     /**
37771      * APIMethod: onMapResize
37772      */
37773     onMapResize: function() {
37774         if (this.visibility) {
37775             google.maps.event.trigger(this.mapObject, "resize");
37776         }
37777     },
37778
37779     /**
37780      * Method: setGMapVisibility
37781      * Display the GMap container and associated elements.
37782      * 
37783      * Parameters:
37784      * visible - {Boolean} Display the GMap elements.
37785      */
37786     setGMapVisibility: function(visible) {
37787         var cache = OpenLayers.Layer.Google.cache[this.map.id];
37788         var map = this.map;
37789         if (cache) {
37790             var type = this.type;
37791             var layers = map.layers;
37792             var layer;
37793             for (var i=layers.length-1; i>=0; --i) {
37794                 layer = layers[i];
37795                 if (layer instanceof OpenLayers.Layer.Google &&
37796                             layer.visibility === true && layer.inRange === true) {
37797                     type = layer.type;
37798                     visible = true;
37799                     break;
37800                 }
37801             }
37802             var container = this.mapObject.getDiv();
37803             if (visible === true) {
37804                 if (container.parentNode !== map.div) {
37805                     if (!cache.rendered) {
37806                         var me = this;
37807                         google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() {
37808                             cache.rendered = true;
37809                             me.setGMapVisibility(me.getVisibility());
37810                             me.moveTo(me.map.getCenter());
37811                         });
37812                     } else {
37813                         map.div.appendChild(container);
37814                         cache.googleControl.appendChild(map.viewPortDiv);
37815                         google.maps.event.trigger(this.mapObject, 'resize');
37816                     }
37817                 }
37818                 this.mapObject.setMapTypeId(type);                
37819             } else if (cache.googleControl.hasChildNodes()) {
37820                 map.div.appendChild(map.viewPortDiv);
37821                 map.div.removeChild(container);
37822             }
37823         }
37824     },
37825     
37826     /**
37827      * Method: getMapContainer
37828      * 
37829      * Returns:
37830      * {DOMElement} the GMap container's div
37831      */
37832     getMapContainer: function() {
37833         return this.mapObject.getDiv();
37834     },
37835     
37836   //
37837   // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds
37838   //
37839
37840     /**
37841      * APIMethod: getMapObjectBoundsFromOLBounds
37842      * 
37843      * Parameters:
37844      * olBounds - {<OpenLayers.Bounds>}
37845      * 
37846      * Returns:
37847      * {Object} A MapObject Bounds, translated from olBounds
37848      *          Returns null if null value is passed in
37849      */
37850     getMapObjectBoundsFromOLBounds: function(olBounds) {
37851         var moBounds = null;
37852         if (olBounds != null) {
37853             var sw = this.sphericalMercator ? 
37854               this.inverseMercator(olBounds.bottom, olBounds.left) : 
37855               new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
37856             var ne = this.sphericalMercator ? 
37857               this.inverseMercator(olBounds.top, olBounds.right) : 
37858               new OpenLayers.LonLat(olBounds.top, olBounds.right);
37859             moBounds = new google.maps.LatLngBounds(
37860                 new google.maps.LatLng(sw.lat, sw.lon),
37861                 new google.maps.LatLng(ne.lat, ne.lon)
37862             );
37863         }
37864         return moBounds;
37865     },
37866
37867
37868     /************************************
37869      *                                  *
37870      *   MapObject Interface Controls   *
37871      *                                  *
37872      ************************************/
37873
37874
37875   // LonLat - Pixel Translation
37876   
37877     /**
37878      * APIMethod: getMapObjectLonLatFromMapObjectPixel
37879      * 
37880      * Parameters:
37881      * moPixel - {Object} MapObject Pixel format
37882      * 
37883      * Returns:
37884      * {Object} MapObject LonLat translated from MapObject Pixel
37885      */
37886     getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
37887         var size = this.map.getSize();
37888         var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);
37889         var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);
37890         var res = this.map.getResolution();
37891
37892         var delta_x = moPixel.x - (size.w / 2);
37893         var delta_y = moPixel.y - (size.h / 2);
37894     
37895         var lonlat = new OpenLayers.LonLat(
37896             lon + delta_x * res,
37897             lat - delta_y * res
37898         ); 
37899
37900         if (this.wrapDateLine) {
37901             lonlat = lonlat.wrapDateLine(this.maxExtent);
37902         }
37903         return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);
37904     },
37905
37906     /**
37907      * APIMethod: getMapObjectPixelFromMapObjectLonLat
37908      * 
37909      * Parameters:
37910      * moLonLat - {Object} MapObject LonLat format
37911      * 
37912      * Returns:
37913      * {Object} MapObject Pixel transtlated from MapObject LonLat
37914      */
37915     getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
37916         var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
37917         var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
37918         var res = this.map.getResolution();
37919         var extent = this.map.getExtent();
37920         return this.getMapObjectPixelFromXY((1/res * (lon - extent.left)),
37921                                             (1/res * (extent.top - lat)));
37922     },
37923
37924   
37925     /** 
37926      * APIMethod: setMapObjectCenter
37927      * Set the mapObject to the specified center and zoom
37928      * 
37929      * Parameters:
37930      * center - {Object} MapObject LonLat format
37931      * zoom - {int} MapObject zoom format
37932      */
37933     setMapObjectCenter: function(center, zoom) {
37934         if (this.animationEnabled === false && zoom != this.mapObject.zoom) {
37935             var mapContainer = this.getMapContainer();
37936             google.maps.event.addListenerOnce(
37937                 this.mapObject, 
37938                 "idle", 
37939                 function() {
37940                     mapContainer.style.visibility = "";
37941                 }
37942             );
37943             mapContainer.style.visibility = "hidden";
37944         }
37945         this.mapObject.setOptions({
37946             center: center,
37947             zoom: zoom
37948         });
37949     },
37950    
37951     
37952   // Bounds
37953   
37954     /** 
37955      * APIMethod: getMapObjectZoomFromMapObjectBounds
37956      * 
37957      * Parameters:
37958      * moBounds - {Object} MapObject Bounds format
37959      * 
37960      * Returns:
37961      * {Object} MapObject Zoom for specified MapObject Bounds
37962      */
37963     getMapObjectZoomFromMapObjectBounds: function(moBounds) {
37964         return this.mapObject.getBoundsZoomLevel(moBounds);
37965     },
37966
37967     /************************************
37968      *                                  *
37969      *       MapObject Primitives       *
37970      *                                  *
37971      ************************************/
37972
37973
37974   // LonLat
37975     
37976     /**
37977      * APIMethod: getMapObjectLonLatFromLonLat
37978      * 
37979      * Parameters:
37980      * lon - {Float}
37981      * lat - {Float}
37982      * 
37983      * Returns:
37984      * {Object} MapObject LonLat built from lon and lat params
37985      */
37986     getMapObjectLonLatFromLonLat: function(lon, lat) {
37987         var gLatLng;
37988         if(this.sphericalMercator) {
37989             var lonlat = this.inverseMercator(lon, lat);
37990             gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);
37991         } else {
37992             gLatLng = new google.maps.LatLng(lat, lon);
37993         }
37994         return gLatLng;
37995     },
37996     
37997   // Pixel
37998     
37999     /**
38000      * APIMethod: getMapObjectPixelFromXY
38001      * 
38002      * Parameters:
38003      * x - {Integer}
38004      * y - {Integer}
38005      * 
38006      * Returns:
38007      * {Object} MapObject Pixel from x and y parameters
38008      */
38009     getMapObjectPixelFromXY: function(x, y) {
38010         return new google.maps.Point(x, y);
38011     }
38012     
38013 };
38014 /* ======================================================================
38015     OpenLayers/Request.js
38016    ====================================================================== */
38017
38018 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
38019  * full list of contributors). Published under the 2-clause BSD license.
38020  * See license.txt in the OpenLayers distribution or repository for the
38021  * full text of the license. */
38022
38023 /**
38024  * @requires OpenLayers/Events.js
38025  * @requires OpenLayers/Request/XMLHttpRequest.js
38026  */
38027
38028 /**
38029  * TODO: deprecate me
38030  * Use OpenLayers.Request.proxy instead.
38031  */
38032 OpenLayers.ProxyHost = "";
38033
38034 /**
38035  * Namespace: OpenLayers.Request
38036  * The OpenLayers.Request namespace contains convenience methods for working
38037  *     with XMLHttpRequests.  These methods work with a cross-browser
38038  *     W3C compliant <OpenLayers.Request.XMLHttpRequest> class.
38039  */
38040 if (!OpenLayers.Request) {
38041     /**
38042      * This allows for OpenLayers/Request/XMLHttpRequest.js to be included
38043      * before or after this script.
38044      */
38045     OpenLayers.Request = {};
38046 }
38047 OpenLayers.Util.extend(OpenLayers.Request, {
38048     
38049     /**
38050      * Constant: DEFAULT_CONFIG
38051      * {Object} Default configuration for all requests.
38052      */
38053     DEFAULT_CONFIG: {
38054         method: "GET",
38055         url: window.location.href,
38056         async: true,
38057         user: undefined,
38058         password: undefined,
38059         params: null,
38060         proxy: OpenLayers.ProxyHost,
38061         headers: {},
38062         data: null,
38063         callback: function() {},
38064         success: null,
38065         failure: null,
38066         scope: null
38067     },
38068     
38069     /**
38070      * Constant: URL_SPLIT_REGEX
38071      */
38072     URL_SPLIT_REGEX: /([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/,
38073     
38074     /**
38075      * APIProperty: events
38076      * {<OpenLayers.Events>} An events object that handles all 
38077      *     events on the {<OpenLayers.Request>} object.
38078      *
38079      * All event listeners will receive an event object with three properties:
38080      * request - {<OpenLayers.Request.XMLHttpRequest>} The request object.
38081      * config - {Object} The config object sent to the specific request method.
38082      * requestUrl - {String} The request url.
38083      * 
38084      * Supported event types:
38085      * complete - Triggered when we have a response from the request, if a
38086      *     listener returns false, no further response processing will take
38087      *     place.
38088      * success - Triggered when the HTTP response has a success code (200-299).
38089      * failure - Triggered when the HTTP response does not have a success code.
38090      */
38091     events: new OpenLayers.Events(this),
38092     
38093     /**
38094      * Method: makeSameOrigin
38095      * Using the specified proxy, returns a same origin url of the provided url.
38096      *
38097      * Parameters:
38098      * url - {String} An arbitrary url
38099      * proxy {String|Function} The proxy to use to make the provided url a
38100      *     same origin url.
38101      *
38102      * Returns
38103      * {String} the same origin url. If no proxy is provided, the returned url
38104      *     will be the same as the provided url.
38105      */
38106     makeSameOrigin: function(url, proxy) {
38107         var sameOrigin = url.indexOf("http") !== 0;
38108         var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);
38109         if (urlParts) {
38110             var location = window.location;
38111             sameOrigin =
38112                 urlParts[1] == location.protocol &&
38113                 urlParts[3] == location.hostname;
38114             var uPort = urlParts[4], lPort = location.port;
38115             if (uPort != 80 && uPort != "" || lPort != "80" && lPort != "") {
38116                 sameOrigin = sameOrigin && uPort == lPort;
38117             }
38118         }
38119         if (!sameOrigin) {
38120             if (proxy) {
38121                 if (typeof proxy == "function") {
38122                     url = proxy(url);
38123                 } else {
38124                     url = proxy + encodeURIComponent(url);
38125                 }
38126             }
38127         }
38128         return url;
38129     },
38130
38131     /**
38132      * APIMethod: issue
38133      * Create a new XMLHttpRequest object, open it, set any headers, bind
38134      *     a callback to done state, and send any data.  It is recommended that
38135      *     you use one <GET>, <POST>, <PUT>, <DELETE>, <OPTIONS>, or <HEAD>.
38136      *     This method is only documented to provide detail on the configuration
38137      *     options available to all request methods.
38138      *
38139      * Parameters:
38140      * config - {Object} Object containing properties for configuring the
38141      *     request.  Allowed configuration properties are described below.
38142      *     This object is modified and should not be reused.
38143      *
38144      * Allowed config properties:
38145      * method - {String} One of GET, POST, PUT, DELETE, HEAD, or
38146      *     OPTIONS.  Default is GET.
38147      * url - {String} URL for the request.
38148      * async - {Boolean} Open an asynchronous request.  Default is true.
38149      * user - {String} User for relevant authentication scheme.  Set
38150      *     to null to clear current user.
38151      * password - {String} Password for relevant authentication scheme.
38152      *     Set to null to clear current password.
38153      * proxy - {String} Optional proxy.  Defaults to
38154      *     <OpenLayers.ProxyHost>.
38155      * params - {Object} Any key:value pairs to be appended to the
38156      *     url as a query string.  Assumes url doesn't already include a query
38157      *     string or hash.  Typically, this is only appropriate for <GET>
38158      *     requests where the query string will be appended to the url.
38159      *     Parameter values that are arrays will be
38160      *     concatenated with a comma (note that this goes against form-encoding)
38161      *     as is done with <OpenLayers.Util.getParameterString>.
38162      * headers - {Object} Object with header:value pairs to be set on
38163      *     the request.
38164      * data - {String | Document} Optional data to send with the request.
38165      *     Typically, this is only used with <POST> and <PUT> requests.
38166      *     Make sure to provide the appropriate "Content-Type" header for your
38167      *     data.  For <POST> and <PUT> requests, the content type defaults to
38168      *     "application-xml".  If your data is a different content type, or
38169      *     if you are using a different HTTP method, set the "Content-Type"
38170      *     header to match your data type.
38171      * callback - {Function} Function to call when request is done.
38172      *     To determine if the request failed, check request.status (200
38173      *     indicates success).
38174      * success - {Function} Optional function to call if request status is in
38175      *     the 200s.  This will be called in addition to callback above and
38176      *     would typically only be used as an alternative.
38177      * failure - {Function} Optional function to call if request status is not
38178      *     in the 200s.  This will be called in addition to callback above and
38179      *     would typically only be used as an alternative.
38180      * scope - {Object} If callback is a public method on some object,
38181      *     set the scope to that object.
38182      *
38183      * Returns:
38184      * {XMLHttpRequest} Request object.  To abort the request before a response
38185      *     is received, call abort() on the request object.
38186      */
38187     issue: function(config) {        
38188         // apply default config - proxy host may have changed
38189         var defaultConfig = OpenLayers.Util.extend(
38190             this.DEFAULT_CONFIG,
38191             {proxy: OpenLayers.ProxyHost}
38192         );
38193         config = config || {};
38194         config.headers = config.headers || {};
38195         config = OpenLayers.Util.applyDefaults(config, defaultConfig);
38196         config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers);
38197         // Always set the "X-Requested-With" header to signal that this request
38198         // was issued through the XHR-object. Since header keys are case 
38199         // insensitive and we want to allow overriding of the "X-Requested-With"
38200         // header through the user we cannot use applyDefaults, but have to 
38201         // check manually whether we were called with a "X-Requested-With"
38202         // header.
38203         var customRequestedWithHeader = false,
38204             headerKey;
38205         for(headerKey in config.headers) {
38206             if (config.headers.hasOwnProperty( headerKey )) {
38207                 if (headerKey.toLowerCase() === 'x-requested-with') {
38208                     customRequestedWithHeader = true;
38209                 }
38210             }
38211         }
38212         if (customRequestedWithHeader === false) {
38213             // we did not have a custom "X-Requested-With" header
38214             config.headers['X-Requested-With'] = 'XMLHttpRequest';
38215         }
38216
38217         // create request, open, and set headers
38218         var request = new OpenLayers.Request.XMLHttpRequest();
38219         var url = OpenLayers.Util.urlAppend(config.url, 
38220             OpenLayers.Util.getParameterString(config.params || {}));
38221         url = OpenLayers.Request.makeSameOrigin(url, config.proxy);
38222         request.open(
38223             config.method, url, config.async, config.user, config.password
38224         );
38225         for(var header in config.headers) {
38226             request.setRequestHeader(header, config.headers[header]);
38227         }
38228
38229         var events = this.events;
38230
38231         // we want to execute runCallbacks with "this" as the
38232         // execution scope
38233         var self = this;
38234         
38235         request.onreadystatechange = function() {
38236             if(request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {
38237                 var proceed = events.triggerEvent(
38238                     "complete",
38239                     {request: request, config: config, requestUrl: url}
38240                 );
38241                 if(proceed !== false) {
38242                     self.runCallbacks(
38243                         {request: request, config: config, requestUrl: url}
38244                     );
38245                 }
38246             }
38247         };
38248         
38249         // send request (optionally with data) and return
38250         // call in a timeout for asynchronous requests so the return is
38251         // available before readyState == 4 for cached docs
38252         if(config.async === false) {
38253             request.send(config.data);
38254         } else {
38255             window.setTimeout(function(){
38256                 if (request.readyState !== 0) { // W3C: 0-UNSENT
38257                     request.send(config.data);
38258                 }
38259             }, 0);
38260         }
38261         return request;
38262     },
38263     
38264     /**
38265      * Method: runCallbacks
38266      * Calls the complete, success and failure callbacks. Application
38267      *    can listen to the "complete" event, have the listener 
38268      *    display a confirm window and always return false, and
38269      *    execute OpenLayers.Request.runCallbacks if the user
38270      *    hits "yes" in the confirm window.
38271      *
38272      * Parameters:
38273      * options - {Object} Hash containing request, config and requestUrl keys
38274      */
38275     runCallbacks: function(options) {
38276         var request = options.request;
38277         var config = options.config;
38278         
38279         // bind callbacks to readyState 4 (done)
38280         var complete = (config.scope) ?
38281             OpenLayers.Function.bind(config.callback, config.scope) :
38282             config.callback;
38283         
38284         // optional success callback
38285         var success;
38286         if(config.success) {
38287             success = (config.scope) ?
38288                 OpenLayers.Function.bind(config.success, config.scope) :
38289                 config.success;
38290         }
38291
38292         // optional failure callback
38293         var failure;
38294         if(config.failure) {
38295             failure = (config.scope) ?
38296                 OpenLayers.Function.bind(config.failure, config.scope) :
38297                 config.failure;
38298         }
38299
38300         if (OpenLayers.Util.createUrlObject(config.url).protocol == "file:" &&
38301                                                         request.responseText) {
38302             request.status = 200;
38303         }
38304         complete(request);
38305
38306         if (!request.status || (request.status >= 200 && request.status < 300)) {
38307             this.events.triggerEvent("success", options);
38308             if(success) {
38309                 success(request);
38310             }
38311         }
38312         if(request.status && (request.status < 200 || request.status >= 300)) {                    
38313             this.events.triggerEvent("failure", options);
38314             if(failure) {
38315                 failure(request);
38316             }
38317         }
38318     },
38319     
38320     /**
38321      * APIMethod: GET
38322      * Send an HTTP GET request.  Additional configuration properties are
38323      *     documented in the <issue> method, with the method property set
38324      *     to GET.
38325      *
38326      * Parameters:
38327      * config - {Object} Object with properties for configuring the request.
38328      *     See the <issue> method for documentation of allowed properties.
38329      *     This object is modified and should not be reused.
38330      * 
38331      * Returns:
38332      * {XMLHttpRequest} Request object.
38333      */
38334     GET: function(config) {
38335         config = OpenLayers.Util.extend(config, {method: "GET"});
38336         return OpenLayers.Request.issue(config);
38337     },
38338     
38339     /**
38340      * APIMethod: POST
38341      * Send a POST request.  Additional configuration properties are
38342      *     documented in the <issue> method, with the method property set
38343      *     to POST and "Content-Type" header set to "application/xml".
38344      *
38345      * Parameters:
38346      * config - {Object} Object with properties for configuring the request.
38347      *     See the <issue> method for documentation of allowed properties.  The
38348      *     default "Content-Type" header will be set to "application-xml" if
38349      *     none is provided.  This object is modified and should not be reused.
38350      * 
38351      * Returns:
38352      * {XMLHttpRequest} Request object.
38353      */
38354     POST: function(config) {
38355         config = OpenLayers.Util.extend(config, {method: "POST"});
38356         // set content type to application/xml if it isn't already set
38357         config.headers = config.headers ? config.headers : {};
38358         if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
38359             config.headers["Content-Type"] = "application/xml";
38360         }
38361         return OpenLayers.Request.issue(config);
38362     },
38363     
38364     /**
38365      * APIMethod: PUT
38366      * Send an HTTP PUT request.  Additional configuration properties are
38367      *     documented in the <issue> method, with the method property set
38368      *     to PUT and "Content-Type" header set to "application/xml".
38369      *
38370      * Parameters:
38371      * config - {Object} Object with properties for configuring the request.
38372      *     See the <issue> method for documentation of allowed properties.  The
38373      *     default "Content-Type" header will be set to "application-xml" if
38374      *     none is provided.  This object is modified and should not be reused.
38375      * 
38376      * Returns:
38377      * {XMLHttpRequest} Request object.
38378      */
38379     PUT: function(config) {
38380         config = OpenLayers.Util.extend(config, {method: "PUT"});
38381         // set content type to application/xml if it isn't already set
38382         config.headers = config.headers ? config.headers : {};
38383         if(!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
38384             config.headers["Content-Type"] = "application/xml";
38385         }
38386         return OpenLayers.Request.issue(config);
38387     },
38388     
38389     /**
38390      * APIMethod: DELETE
38391      * Send an HTTP DELETE request.  Additional configuration properties are
38392      *     documented in the <issue> method, with the method property set
38393      *     to DELETE.
38394      *
38395      * Parameters:
38396      * config - {Object} Object with properties for configuring the request.
38397      *     See the <issue> method for documentation of allowed properties.
38398      *     This object is modified and should not be reused.
38399      * 
38400      * Returns:
38401      * {XMLHttpRequest} Request object.
38402      */
38403     DELETE: function(config) {
38404         config = OpenLayers.Util.extend(config, {method: "DELETE"});
38405         return OpenLayers.Request.issue(config);
38406     },
38407   
38408     /**
38409      * APIMethod: HEAD
38410      * Send an HTTP HEAD request.  Additional configuration properties are
38411      *     documented in the <issue> method, with the method property set
38412      *     to HEAD.
38413      *
38414      * Parameters:
38415      * config - {Object} Object with properties for configuring the request.
38416      *     See the <issue> method for documentation of allowed properties.
38417      *     This object is modified and should not be reused.
38418      * 
38419      * Returns:
38420      * {XMLHttpRequest} Request object.
38421      */
38422     HEAD: function(config) {
38423         config = OpenLayers.Util.extend(config, {method: "HEAD"});
38424         return OpenLayers.Request.issue(config);
38425     },
38426     
38427     /**
38428      * APIMethod: OPTIONS
38429      * Send an HTTP OPTIONS request.  Additional configuration properties are
38430      *     documented in the <issue> method, with the method property set
38431      *     to OPTIONS.
38432      *
38433      * Parameters:
38434      * config - {Object} Object with properties for configuring the request.
38435      *     See the <issue> method for documentation of allowed properties.
38436      *     This object is modified and should not be reused.
38437      * 
38438      * Returns:
38439      * {XMLHttpRequest} Request object.
38440      */
38441     OPTIONS: function(config) {
38442         config = OpenLayers.Util.extend(config, {method: "OPTIONS"});
38443         return OpenLayers.Request.issue(config);
38444     }
38445
38446 });
38447 /* ======================================================================
38448     OpenLayers/Request/XMLHttpRequest.js
38449    ====================================================================== */
38450
38451 // XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com)
38452 //
38453 // Licensed under the Apache License, Version 2.0 (the "License");
38454 // you may not use this file except in compliance with the License.
38455 // You may obtain a copy of the License at
38456 //
38457 //   http://www.apache.org/licenses/LICENSE-2.0
38458 //
38459 // Unless required by applicable law or agreed to in writing, software
38460 // distributed under the License is distributed on an "AS IS" BASIS,
38461 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
38462 // See the License for the specific language governing permissions and
38463 // limitations under the License.
38464
38465 /**
38466  * @requires OpenLayers/Request.js
38467  */
38468
38469 (function () {
38470
38471     // Save reference to earlier defined object implementation (if any)
38472     var oXMLHttpRequest    = window.XMLHttpRequest;
38473
38474     // Define on browser type
38475     var bGecko    = !!window.controllers,
38476         bIE        = window.document.all && !window.opera,
38477         bIE7    = bIE && window.navigator.userAgent.match(/MSIE 7.0/);
38478
38479     // Enables "XMLHttpRequest()" call next to "new XMLHttpReques()"
38480     function fXMLHttpRequest() {
38481         this._object    = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP");
38482         this._listeners    = [];
38483     };
38484
38485     // Constructor
38486     function cXMLHttpRequest() {
38487         return new fXMLHttpRequest;
38488     };
38489     cXMLHttpRequest.prototype    = fXMLHttpRequest.prototype;
38490
38491     // BUGFIX: Firefox with Firebug installed would break pages if not executed
38492     if (bGecko && oXMLHttpRequest.wrapped)
38493         cXMLHttpRequest.wrapped    = oXMLHttpRequest.wrapped;
38494
38495     // Constants
38496     cXMLHttpRequest.UNSENT                = 0;
38497     cXMLHttpRequest.OPENED                = 1;
38498     cXMLHttpRequest.HEADERS_RECEIVED    = 2;
38499     cXMLHttpRequest.LOADING                = 3;
38500     cXMLHttpRequest.DONE                = 4;
38501
38502     // Public Properties
38503     cXMLHttpRequest.prototype.readyState    = cXMLHttpRequest.UNSENT;
38504     cXMLHttpRequest.prototype.responseText    = '';
38505     cXMLHttpRequest.prototype.responseXML    = null;
38506     cXMLHttpRequest.prototype.status        = 0;
38507     cXMLHttpRequest.prototype.statusText    = '';
38508
38509     // Priority proposal
38510     cXMLHttpRequest.prototype.priority        = "NORMAL";
38511
38512     // Instance-level Events Handlers
38513     cXMLHttpRequest.prototype.onreadystatechange    = null;
38514
38515     // Class-level Events Handlers
38516     cXMLHttpRequest.onreadystatechange    = null;
38517     cXMLHttpRequest.onopen                = null;
38518     cXMLHttpRequest.onsend                = null;
38519     cXMLHttpRequest.onabort                = null;
38520
38521     // Public Methods
38522     cXMLHttpRequest.prototype.open    = function(sMethod, sUrl, bAsync, sUser, sPassword) {
38523         // Delete headers, required when object is reused
38524         delete this._headers;
38525
38526         // When bAsync parameter value is omitted, use true as default
38527         if (arguments.length < 3)
38528             bAsync    = true;
38529
38530         // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests
38531         this._async        = bAsync;
38532
38533         // Set the onreadystatechange handler
38534         var oRequest    = this,
38535             nState        = this.readyState,
38536             fOnUnload;
38537
38538         // BUGFIX: IE - memory leak on page unload (inter-page leak)
38539         if (bIE && bAsync) {
38540             fOnUnload = function() {
38541                 if (nState != cXMLHttpRequest.DONE) {
38542                     fCleanTransport(oRequest);
38543                     // Safe to abort here since onreadystatechange handler removed
38544                     oRequest.abort();
38545                 }
38546             };
38547             window.attachEvent("onunload", fOnUnload);
38548         }
38549
38550         // Add method sniffer
38551         if (cXMLHttpRequest.onopen)
38552             cXMLHttpRequest.onopen.apply(this, arguments);
38553
38554         if (arguments.length > 4)
38555             this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
38556         else
38557         if (arguments.length > 3)
38558             this._object.open(sMethod, sUrl, bAsync, sUser);
38559         else
38560             this._object.open(sMethod, sUrl, bAsync);
38561
38562         this.readyState    = cXMLHttpRequest.OPENED;
38563         fReadyStateChange(this);
38564
38565         this._object.onreadystatechange    = function() {
38566             if (bGecko && !bAsync)
38567                 return;
38568
38569             // Synchronize state
38570             oRequest.readyState        = oRequest._object.readyState;
38571
38572             //
38573             fSynchronizeValues(oRequest);
38574
38575             // BUGFIX: Firefox fires unnecessary DONE when aborting
38576             if (oRequest._aborted) {
38577                 // Reset readyState to UNSENT
38578                 oRequest.readyState    = cXMLHttpRequest.UNSENT;
38579
38580                 // Return now
38581                 return;
38582             }
38583
38584             if (oRequest.readyState == cXMLHttpRequest.DONE) {
38585                 // Free up queue
38586                 delete oRequest._data;
38587 /*                if (bAsync)
38588                     fQueue_remove(oRequest);*/
38589                 //
38590                 fCleanTransport(oRequest);
38591 // Uncomment this block if you need a fix for IE cache
38592 /*
38593                 // BUGFIX: IE - cache issue
38594                 if (!oRequest._object.getResponseHeader("Date")) {
38595                     // Save object to cache
38596                     oRequest._cached    = oRequest._object;
38597
38598                     // Instantiate a new transport object
38599                     cXMLHttpRequest.call(oRequest);
38600
38601                     // Re-send request
38602                     if (sUser) {
38603                          if (sPassword)
38604                             oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
38605                         else
38606                             oRequest._object.open(sMethod, sUrl, bAsync, sUser);
38607                     }
38608                     else
38609                         oRequest._object.open(sMethod, sUrl, bAsync);
38610                     oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0));
38611                     // Copy headers set
38612                     if (oRequest._headers)
38613                         for (var sHeader in oRequest._headers)
38614                             if (typeof oRequest._headers[sHeader] == "string")    // Some frameworks prototype objects with functions
38615                                 oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]);
38616
38617                     oRequest._object.onreadystatechange    = function() {
38618                         // Synchronize state
38619                         oRequest.readyState        = oRequest._object.readyState;
38620
38621                         if (oRequest._aborted) {
38622                             //
38623                             oRequest.readyState    = cXMLHttpRequest.UNSENT;
38624
38625                             // Return
38626                             return;
38627                         }
38628
38629                         if (oRequest.readyState == cXMLHttpRequest.DONE) {
38630                             // Clean Object
38631                             fCleanTransport(oRequest);
38632
38633                             // get cached request
38634                             if (oRequest.status == 304)
38635                                 oRequest._object    = oRequest._cached;
38636
38637                             //
38638                             delete oRequest._cached;
38639
38640                             //
38641                             fSynchronizeValues(oRequest);
38642
38643                             //
38644                             fReadyStateChange(oRequest);
38645
38646                             // BUGFIX: IE - memory leak in interrupted
38647                             if (bIE && bAsync)
38648                                 window.detachEvent("onunload", fOnUnload);
38649                         }
38650                     };
38651                     oRequest._object.send(null);
38652
38653                     // Return now - wait until re-sent request is finished
38654                     return;
38655                 };
38656 */
38657                 // BUGFIX: IE - memory leak in interrupted
38658                 if (bIE && bAsync)
38659                     window.detachEvent("onunload", fOnUnload);
38660             }
38661
38662             // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice
38663             if (nState != oRequest.readyState)
38664                 fReadyStateChange(oRequest);
38665
38666             nState    = oRequest.readyState;
38667         }
38668     };
38669     function fXMLHttpRequest_send(oRequest) {
38670         oRequest._object.send(oRequest._data);
38671
38672         // BUGFIX: Gecko - missing readystatechange calls in synchronous requests
38673         if (bGecko && !oRequest._async) {
38674             oRequest.readyState    = cXMLHttpRequest.OPENED;
38675
38676             // Synchronize state
38677             fSynchronizeValues(oRequest);
38678
38679             // Simulate missing states
38680             while (oRequest.readyState < cXMLHttpRequest.DONE) {
38681                 oRequest.readyState++;
38682                 fReadyStateChange(oRequest);
38683                 // Check if we are aborted
38684                 if (oRequest._aborted)
38685                     return;
38686             }
38687         }
38688     };
38689     cXMLHttpRequest.prototype.send    = function(vData) {
38690         // Add method sniffer
38691         if (cXMLHttpRequest.onsend)
38692             cXMLHttpRequest.onsend.apply(this, arguments);
38693
38694         if (!arguments.length)
38695             vData    = null;
38696
38697         // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required
38698         // BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent
38699         // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard)
38700         if (vData && vData.nodeType) {
38701             vData    = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;
38702             if (!this._headers["Content-Type"])
38703                 this._object.setRequestHeader("Content-Type", "application/xml");
38704         }
38705
38706         this._data    = vData;
38707 /*
38708         // Add to queue
38709         if (this._async)
38710             fQueue_add(this);
38711         else*/
38712             fXMLHttpRequest_send(this);
38713     };
38714     cXMLHttpRequest.prototype.abort    = function() {
38715         // Add method sniffer
38716         if (cXMLHttpRequest.onabort)
38717             cXMLHttpRequest.onabort.apply(this, arguments);
38718
38719         // BUGFIX: Gecko - unnecessary DONE when aborting
38720         if (this.readyState > cXMLHttpRequest.UNSENT)
38721             this._aborted    = true;
38722
38723         this._object.abort();
38724
38725         // BUGFIX: IE - memory leak
38726         fCleanTransport(this);
38727
38728         this.readyState    = cXMLHttpRequest.UNSENT;
38729
38730         delete this._data;
38731 /*        if (this._async)
38732             fQueue_remove(this);*/
38733     };
38734     cXMLHttpRequest.prototype.getAllResponseHeaders    = function() {
38735         return this._object.getAllResponseHeaders();
38736     };
38737     cXMLHttpRequest.prototype.getResponseHeader    = function(sName) {
38738         return this._object.getResponseHeader(sName);
38739     };
38740     cXMLHttpRequest.prototype.setRequestHeader    = function(sName, sValue) {
38741         // BUGFIX: IE - cache issue
38742         if (!this._headers)
38743             this._headers    = {};
38744         this._headers[sName]    = sValue;
38745
38746         return this._object.setRequestHeader(sName, sValue);
38747     };
38748
38749     // EventTarget interface implementation
38750     cXMLHttpRequest.prototype.addEventListener    = function(sName, fHandler, bUseCapture) {
38751         for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
38752             if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
38753                 return;
38754         // Add listener
38755         this._listeners.push([sName, fHandler, bUseCapture]);
38756     };
38757
38758     cXMLHttpRequest.prototype.removeEventListener    = function(sName, fHandler, bUseCapture) {
38759         for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
38760             if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture)
38761                 break;
38762         // Remove listener
38763         if (oListener)
38764             this._listeners.splice(nIndex, 1);
38765     };
38766
38767     cXMLHttpRequest.prototype.dispatchEvent    = function(oEvent) {
38768         var oEventPseudo    = {
38769             'type':            oEvent.type,
38770             'target':        this,
38771             'currentTarget':this,
38772             'eventPhase':    2,
38773             'bubbles':        oEvent.bubbles,
38774             'cancelable':    oEvent.cancelable,
38775             'timeStamp':    oEvent.timeStamp,
38776             'stopPropagation':    function() {},    // There is no flow
38777             'preventDefault':    function() {},    // There is no default action
38778             'initEvent':        function() {}    // Original event object should be initialized
38779         };
38780
38781         // Execute onreadystatechange
38782         if (oEventPseudo.type == "readystatechange" && this.onreadystatechange)
38783             (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);
38784
38785         // Execute listeners
38786         for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
38787             if (oListener[0] == oEventPseudo.type && !oListener[2])
38788                 (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]);
38789     };
38790
38791     //
38792     cXMLHttpRequest.prototype.toString    = function() {
38793         return '[' + "object" + ' ' + "XMLHttpRequest" + ']';
38794     };
38795
38796     cXMLHttpRequest.toString    = function() {
38797         return '[' + "XMLHttpRequest" + ']';
38798     };
38799
38800     // Helper function
38801     function fReadyStateChange(oRequest) {
38802         // Sniffing code
38803         if (cXMLHttpRequest.onreadystatechange)
38804             cXMLHttpRequest.onreadystatechange.apply(oRequest);
38805
38806         // Fake event
38807         oRequest.dispatchEvent({
38808             'type':            "readystatechange",
38809             'bubbles':        false,
38810             'cancelable':    false,
38811             'timeStamp':    new Date + 0
38812         });
38813     };
38814
38815     function fGetDocument(oRequest) {
38816         var oDocument    = oRequest.responseXML,
38817             sResponse    = oRequest.responseText;
38818         // Try parsing responseText
38819         if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) {
38820             oDocument    = new window.ActiveXObject("Microsoft.XMLDOM");
38821             oDocument.async                = false;
38822             oDocument.validateOnParse    = false;
38823             oDocument.loadXML(sResponse);
38824         }
38825         // Check if there is no error in document
38826         if (oDocument)
38827             if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror"))
38828                 return null;
38829         return oDocument;
38830     };
38831
38832     function fSynchronizeValues(oRequest) {
38833         try {    oRequest.responseText    = oRequest._object.responseText;    } catch (e) {}
38834         try {    oRequest.responseXML    = fGetDocument(oRequest._object);    } catch (e) {}
38835         try {    oRequest.status            = oRequest._object.status;            } catch (e) {}
38836         try {    oRequest.statusText        = oRequest._object.statusText;        } catch (e) {}
38837     };
38838
38839     function fCleanTransport(oRequest) {
38840         // BUGFIX: IE - memory leak (on-page leak)
38841         oRequest._object.onreadystatechange    = new window.Function;
38842     };
38843 /*
38844     // Queue manager
38845     var oQueuePending    = {"CRITICAL":[],"HIGH":[],"NORMAL":[],"LOW":[],"LOWEST":[]},
38846         aQueueRunning    = [];
38847     function fQueue_add(oRequest) {
38848         oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : "NORMAL"].push(oRequest);
38849         //
38850         setTimeout(fQueue_process);
38851     };
38852
38853     function fQueue_remove(oRequest) {
38854         for (var nIndex = 0, bFound    = false; nIndex < aQueueRunning.length; nIndex++)
38855             if (bFound)
38856                 aQueueRunning[nIndex - 1]    = aQueueRunning[nIndex];
38857             else
38858             if (aQueueRunning[nIndex] == oRequest)
38859                 bFound    = true;
38860         if (bFound)
38861             aQueueRunning.length--;
38862         //
38863         setTimeout(fQueue_process);
38864     };
38865
38866     function fQueue_process() {
38867         if (aQueueRunning.length < 6) {
38868             for (var sPriority in oQueuePending) {
38869                 if (oQueuePending[sPriority].length) {
38870                     var oRequest    = oQueuePending[sPriority][0];
38871                     oQueuePending[sPriority]    = oQueuePending[sPriority].slice(1);
38872                     //
38873                     aQueueRunning.push(oRequest);
38874                     // Send request
38875                     fXMLHttpRequest_send(oRequest);
38876                     break;
38877                 }
38878             }
38879         }
38880     };
38881 */
38882     // Internet Explorer 5.0 (missing apply)
38883     if (!window.Function.prototype.apply) {
38884         window.Function.prototype.apply    = function(oRequest, oArguments) {
38885             if (!oArguments)
38886                 oArguments    = [];
38887             oRequest.__func    = this;
38888             oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);
38889             delete oRequest.__func;
38890         };
38891     };
38892
38893     // Register new object with window
38894     /**
38895      * Class: OpenLayers.Request.XMLHttpRequest
38896      * Standard-compliant (W3C) cross-browser implementation of the
38897      *     XMLHttpRequest object.  From
38898      *     http://code.google.com/p/xmlhttprequest/.
38899      */
38900     if (!OpenLayers.Request) {
38901         /**
38902          * This allows for OpenLayers/Request.js to be included
38903          * before or after this script.
38904          */
38905         OpenLayers.Request = {};
38906     }
38907     OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;
38908 })();
38909 /* ======================================================================
38910     OpenLayers/Lang/de.js
38911    ====================================================================== */
38912
38913 /* Translators (2009 onwards):
38914  *  - Grille chompa
38915  *  - Nikiwaibel
38916  *  - Umherirrender
38917  */
38918
38919 /**
38920  * @requires OpenLayers/Lang.js
38921  */
38922
38923 /**
38924  * Namespace: OpenLayers.Lang["de"]
38925  * Dictionary for Deutsch.  Keys for entries are used in calls to
38926  *     <OpenLayers.Lang.translate>.  Entry bodies are normal strings or
38927  *     strings formatted for use with <OpenLayers.String.format> calls.
38928  */
38929 OpenLayers.Lang["de"] = OpenLayers.Util.applyDefaults({
38930
38931     'unhandledRequest': "Unbehandelte Anfragerückmeldung ${statusText}",
38932
38933     'Permalink': "Permalink",
38934
38935     'Overlays': "Overlays",
38936
38937     'Base Layer': "Grundkarte",
38938
38939     'noFID': "Ein Feature, für das keine FID existiert, kann nicht aktualisiert werden.",
38940
38941     'browserNotSupported': "Ihr Browser unterstützt keine Vektordarstellung. Aktuell unterstützte Renderer:\n${renderers}",
38942
38943     '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.",
38944
38945     'commitSuccess': "WFS-Transaktion: Erfolgreich ${response}",
38946
38947     'commitFailed': "WFS-Transaktion: Fehlgeschlagen ${response}",
38948
38949     '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",
38950
38951     '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",
38952
38953     'Scale = 1 : ${scaleDenom}': "Maßstab = 1 : ${scaleDenom}",
38954
38955     'W': "W",
38956
38957     'E': "O",
38958
38959     'N': "N",
38960
38961     'S': "S",
38962
38963     '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.",
38964
38965     'methodDeprecated': "Die Methode ist veraltet und wird in 3.0 entfernt. Bitte verwende stattdessen ${newMethod}."
38966
38967 });
38968 /* ======================================================================
38969     OpenLayers/Popup/FramedCloud.js
38970    ====================================================================== */
38971
38972 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
38973  * full list of contributors). Published under the 2-clause BSD license.
38974  * See license.txt in the OpenLayers distribution or repository for the
38975  * full text of the license. */
38976
38977 /**
38978  * @requires OpenLayers/Popup/Framed.js
38979  * @requires OpenLayers/Util.js
38980  * @requires OpenLayers/BaseTypes/Bounds.js
38981  * @requires OpenLayers/BaseTypes/Pixel.js
38982  * @requires OpenLayers/BaseTypes/Size.js
38983  */
38984
38985 /**
38986  * Class: OpenLayers.Popup.FramedCloud
38987  * 
38988  * Inherits from: 
38989  *  - <OpenLayers.Popup.Framed>
38990  */
38991 OpenLayers.Popup.FramedCloud = 
38992   OpenLayers.Class(OpenLayers.Popup.Framed, {
38993
38994     /** 
38995      * Property: contentDisplayClass
38996      * {String} The CSS class of the popup content div.
38997      */
38998     contentDisplayClass: "olFramedCloudPopupContent",
38999
39000     /**
39001      * APIProperty: autoSize
39002      * {Boolean} Framed Cloud is autosizing by default.
39003      */
39004     autoSize: true,
39005
39006     /**
39007      * APIProperty: panMapIfOutOfView
39008      * {Boolean} Framed Cloud does pan into view by default.
39009      */
39010     panMapIfOutOfView: true,
39011
39012     /**
39013      * APIProperty: imageSize
39014      * {<OpenLayers.Size>}
39015      */
39016     imageSize: new OpenLayers.Size(1276, 736),
39017
39018     /**
39019      * APIProperty: isAlphaImage
39020      * {Boolean} The FramedCloud does not use an alpha image (in honor of the 
39021      *     good ie6 folk out there)
39022      */
39023     isAlphaImage: false,
39024
39025     /** 
39026      * APIProperty: fixedRelativePosition
39027      * {Boolean} The Framed Cloud popup works in just one fixed position.
39028      */
39029     fixedRelativePosition: false,
39030
39031     /**
39032      * Property: positionBlocks
39033      * {Object} Hash of differen position blocks, keyed by relativePosition
39034      *     two-character code string (ie "tl", "tr", "bl", "br")
39035      */
39036     positionBlocks: {
39037         "tl": {
39038             'offset': new OpenLayers.Pixel(44, 0),
39039             'padding': new OpenLayers.Bounds(8, 40, 8, 9),
39040             'blocks': [
39041                 { // top-left
39042                     size: new OpenLayers.Size('auto', 'auto'),
39043                     anchor: new OpenLayers.Bounds(0, 51, 22, 0),
39044                     position: new OpenLayers.Pixel(0, 0)
39045                 },
39046                 { //top-right
39047                     size: new OpenLayers.Size(22, 'auto'),
39048                     anchor: new OpenLayers.Bounds(null, 50, 0, 0),
39049                     position: new OpenLayers.Pixel(-1238, 0)
39050                 },
39051                 { //bottom-left
39052                     size: new OpenLayers.Size('auto', 19),
39053                     anchor: new OpenLayers.Bounds(0, 32, 22, null),
39054                     position: new OpenLayers.Pixel(0, -631)
39055                 },
39056                 { //bottom-right
39057                     size: new OpenLayers.Size(22, 18),
39058                     anchor: new OpenLayers.Bounds(null, 32, 0, null),
39059                     position: new OpenLayers.Pixel(-1238, -632)
39060                 },
39061                 { // stem
39062                     size: new OpenLayers.Size(81, 35),
39063                     anchor: new OpenLayers.Bounds(null, 0, 0, null),
39064                     position: new OpenLayers.Pixel(0, -688)
39065                 }
39066             ]
39067         },
39068         "tr": {
39069             'offset': new OpenLayers.Pixel(-45, 0),
39070             'padding': new OpenLayers.Bounds(8, 40, 8, 9),
39071             'blocks': [
39072                 { // top-left
39073                     size: new OpenLayers.Size('auto', 'auto'),
39074                     anchor: new OpenLayers.Bounds(0, 51, 22, 0),
39075                     position: new OpenLayers.Pixel(0, 0)
39076                 },
39077                 { //top-right
39078                     size: new OpenLayers.Size(22, 'auto'),
39079                     anchor: new OpenLayers.Bounds(null, 50, 0, 0),
39080                     position: new OpenLayers.Pixel(-1238, 0)
39081                 },
39082                 { //bottom-left
39083                     size: new OpenLayers.Size('auto', 19),
39084                     anchor: new OpenLayers.Bounds(0, 32, 22, null),
39085                     position: new OpenLayers.Pixel(0, -631)
39086                 },
39087                 { //bottom-right
39088                     size: new OpenLayers.Size(22, 19),
39089                     anchor: new OpenLayers.Bounds(null, 32, 0, null),
39090                     position: new OpenLayers.Pixel(-1238, -631)
39091                 },
39092                 { // stem
39093                     size: new OpenLayers.Size(81, 35),
39094                     anchor: new OpenLayers.Bounds(0, 0, null, null),
39095                     position: new OpenLayers.Pixel(-215, -687)
39096                 }
39097             ]
39098         },
39099         "bl": {
39100             'offset': new OpenLayers.Pixel(45, 0),
39101             'padding': new OpenLayers.Bounds(8, 9, 8, 40),
39102             'blocks': [
39103                 { // top-left
39104                     size: new OpenLayers.Size('auto', 'auto'),
39105                     anchor: new OpenLayers.Bounds(0, 21, 22, 32),
39106                     position: new OpenLayers.Pixel(0, 0)
39107                 },
39108                 { //top-right
39109                     size: new OpenLayers.Size(22, 'auto'),
39110                     anchor: new OpenLayers.Bounds(null, 21, 0, 32),
39111                     position: new OpenLayers.Pixel(-1238, 0)
39112                 },
39113                 { //bottom-left
39114                     size: new OpenLayers.Size('auto', 21),
39115                     anchor: new OpenLayers.Bounds(0, 0, 22, null),
39116                     position: new OpenLayers.Pixel(0, -629)
39117                 },
39118                 { //bottom-right
39119                     size: new OpenLayers.Size(22, 21),
39120                     anchor: new OpenLayers.Bounds(null, 0, 0, null),
39121                     position: new OpenLayers.Pixel(-1238, -629)
39122                 },
39123                 { // stem
39124                     size: new OpenLayers.Size(81, 33),
39125                     anchor: new OpenLayers.Bounds(null, null, 0, 0),
39126                     position: new OpenLayers.Pixel(-101, -674)
39127                 }
39128             ]
39129         },
39130         "br": {
39131             'offset': new OpenLayers.Pixel(-44, 0),
39132             'padding': new OpenLayers.Bounds(8, 9, 8, 40),
39133             'blocks': [
39134                 { // top-left
39135                     size: new OpenLayers.Size('auto', 'auto'),
39136                     anchor: new OpenLayers.Bounds(0, 21, 22, 32),
39137                     position: new OpenLayers.Pixel(0, 0)
39138                 },
39139                 { //top-right
39140                     size: new OpenLayers.Size(22, 'auto'),
39141                     anchor: new OpenLayers.Bounds(null, 21, 0, 32),
39142                     position: new OpenLayers.Pixel(-1238, 0)
39143                 },
39144                 { //bottom-left
39145                     size: new OpenLayers.Size('auto', 21),
39146                     anchor: new OpenLayers.Bounds(0, 0, 22, null),
39147                     position: new OpenLayers.Pixel(0, -629)
39148                 },
39149                 { //bottom-right
39150                     size: new OpenLayers.Size(22, 21),
39151                     anchor: new OpenLayers.Bounds(null, 0, 0, null),
39152                     position: new OpenLayers.Pixel(-1238, -629)
39153                 },
39154                 { // stem
39155                     size: new OpenLayers.Size(81, 33),
39156                     anchor: new OpenLayers.Bounds(0, null, null, 0),
39157                     position: new OpenLayers.Pixel(-311, -674)
39158                 }
39159             ]
39160         }
39161     },
39162
39163     /**
39164      * APIProperty: minSize
39165      * {<OpenLayers.Size>}
39166      */
39167     minSize: new OpenLayers.Size(105, 10),
39168
39169     /**
39170      * APIProperty: maxSize
39171      * {<OpenLayers.Size>}
39172      */
39173     maxSize: new OpenLayers.Size(1200, 660),
39174
39175     /** 
39176      * Constructor: OpenLayers.Popup.FramedCloud
39177      * 
39178      * Parameters:
39179      * id - {String}
39180      * lonlat - {<OpenLayers.LonLat>}
39181      * contentSize - {<OpenLayers.Size>}
39182      * contentHTML - {String}
39183      * anchor - {Object} Object to which we'll anchor the popup. Must expose 
39184      *     a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) 
39185      *     (Note that this is generally an <OpenLayers.Icon>).
39186      * closeBox - {Boolean}
39187      * closeBoxCallback - {Function} Function to be called on closeBox click.
39188      */
39189     initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox, 
39190                         closeBoxCallback) {
39191
39192         this.imageSrc = OpenLayers.Util.getImageLocation('cloud-popup-relative.png');
39193         OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);
39194         this.contentDiv.className = this.contentDisplayClass;
39195     },
39196
39197     CLASS_NAME: "OpenLayers.Popup.FramedCloud"
39198 });
39199 /* ======================================================================
39200     OpenLayers/Rule.js
39201    ====================================================================== */
39202
39203 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
39204  * full list of contributors). Published under the 2-clause BSD license.
39205  * See license.txt in the OpenLayers distribution or repository for the
39206  * full text of the license. */
39207
39208
39209 /**
39210  * @requires OpenLayers/BaseTypes/Class.js
39211  * @requires OpenLayers/Util.js
39212  * @requires OpenLayers/Style.js
39213  */
39214
39215 /**
39216  * Class: OpenLayers.Rule
39217  * This class represents an SLD Rule, as being used for rule-based SLD styling.
39218  */
39219 OpenLayers.Rule = OpenLayers.Class({
39220     
39221     /**
39222      * Property: id
39223      * {String} A unique id for this session.
39224      */
39225     id: null,
39226     
39227     /**
39228      * APIProperty: name
39229      * {String} name of this rule
39230      */
39231     name: null,
39232     
39233     /**
39234      * Property: title
39235      * {String} Title of this rule (set if included in SLD)
39236      */
39237     title: null,
39238     
39239     /**
39240      * Property: description
39241      * {String} Description of this rule (set if abstract is included in SLD)
39242      */
39243     description: null,
39244
39245     /**
39246      * Property: context
39247      * {Object} An optional object with properties that the rule should be
39248      * evaluated against. If no context is specified, feature.attributes will
39249      * be used.
39250      */
39251     context: null,
39252     
39253     /**
39254      * Property: filter
39255      * {<OpenLayers.Filter>} Optional filter for the rule.
39256      */
39257     filter: null,
39258
39259     /**
39260      * Property: elseFilter
39261      * {Boolean} Determines whether this rule is only to be applied only if
39262      * no other rules match (ElseFilter according to the SLD specification). 
39263      * Default is false.  For instances of OpenLayers.Rule, if elseFilter is
39264      * false, the rule will always apply.  For subclasses, the else property is 
39265      * ignored.
39266      */
39267     elseFilter: false,
39268     
39269     /**
39270      * Property: symbolizer
39271      * {Object} Symbolizer or hash of symbolizers for this rule. If hash of
39272      * symbolizers, keys are one or more of ["Point", "Line", "Polygon"]. The
39273      * latter if useful if it is required to style e.g. vertices of a line
39274      * with a point symbolizer. Note, however, that this is not implemented
39275      * yet in OpenLayers, but it is the way how symbolizers are defined in
39276      * SLD.
39277      */
39278     symbolizer: null,
39279     
39280     /**
39281      * Property: symbolizers
39282      * {Array} Collection of symbolizers associated with this rule.  If 
39283      *     provided at construction, the symbolizers array has precedence
39284      *     over the deprecated symbolizer property.  Note that multiple 
39285      *     symbolizers are not currently supported by the vector renderers.
39286      *     Rules with multiple symbolizers are currently only useful for
39287      *     maintaining elements in an SLD document.
39288      */
39289     symbolizers: null,
39290     
39291     /**
39292      * APIProperty: minScaleDenominator
39293      * {Number} or {String} minimum scale at which to draw the feature.
39294      * In the case of a String, this can be a combination of text and
39295      * propertyNames in the form "literal ${propertyName}"
39296      */
39297     minScaleDenominator: null,
39298
39299     /**
39300      * APIProperty: maxScaleDenominator
39301      * {Number} or {String} maximum scale at which to draw the feature.
39302      * In the case of a String, this can be a combination of text and
39303      * propertyNames in the form "literal ${propertyName}"
39304      */
39305     maxScaleDenominator: null,
39306     
39307     /** 
39308      * Constructor: OpenLayers.Rule
39309      * Creates a Rule.
39310      *
39311      * Parameters:
39312      * options - {Object} An optional object with properties to set on the
39313      *           rule
39314      * 
39315      * Returns:
39316      * {<OpenLayers.Rule>}
39317      */
39318     initialize: function(options) {
39319         this.symbolizer = {};
39320         OpenLayers.Util.extend(this, options);
39321         if (this.symbolizers) {
39322             delete this.symbolizer;
39323         }
39324         this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
39325     },
39326
39327     /** 
39328      * APIMethod: destroy
39329      * nullify references to prevent circular references and memory leaks
39330      */
39331     destroy: function() {
39332         for (var i in this.symbolizer) {
39333             this.symbolizer[i] = null;
39334         }
39335         this.symbolizer = null;
39336         delete this.symbolizers;
39337     },
39338     
39339     /**
39340      * APIMethod: evaluate
39341      * evaluates this rule for a specific feature
39342      * 
39343      * Parameters:
39344      * feature - {<OpenLayers.Feature>} feature to apply the rule to.
39345      * 
39346      * Returns:
39347      * {Boolean} true if the rule applies, false if it does not.
39348      * This rule is the default rule and always returns true.
39349      */
39350     evaluate: function(feature) {
39351         var context = this.getContext(feature);
39352         var applies = true;
39353
39354         if (this.minScaleDenominator || this.maxScaleDenominator) {
39355             var scale = feature.layer.map.getScale();
39356         }
39357         
39358         // check if within minScale/maxScale bounds
39359         if (this.minScaleDenominator) {
39360             applies = scale >= OpenLayers.Style.createLiteral(
39361                     this.minScaleDenominator, context);
39362         }
39363         if (applies && this.maxScaleDenominator) {
39364             applies = scale < OpenLayers.Style.createLiteral(
39365                     this.maxScaleDenominator, context);
39366         }
39367         
39368         // check if optional filter applies
39369         if(applies && this.filter) {
39370             // feature id filters get the feature, others get the context
39371             if(this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") {
39372                 applies = this.filter.evaluate(feature);
39373             } else {
39374                 applies = this.filter.evaluate(context);
39375             }
39376         }
39377
39378         return applies;
39379     },
39380     
39381     /**
39382      * Method: getContext
39383      * Gets the context for evaluating this rule
39384      * 
39385      * Paramters:
39386      * feature - {<OpenLayers.Feature>} feature to take the context from if
39387      *           none is specified.
39388      */
39389     getContext: function(feature) {
39390         var context = this.context;
39391         if (!context) {
39392             context = feature.attributes || feature.data;
39393         }
39394         if (typeof this.context == "function") {
39395             context = this.context(feature);
39396         }
39397         return context;
39398     },
39399     
39400     /**
39401      * APIMethod: clone
39402      * Clones this rule.
39403      * 
39404      * Returns:
39405      * {<OpenLayers.Rule>} Clone of this rule.
39406      */
39407     clone: function() {
39408         var options = OpenLayers.Util.extend({}, this);
39409         if (this.symbolizers) {
39410             // clone symbolizers
39411             var len = this.symbolizers.length;
39412             options.symbolizers = new Array(len);
39413             for (var i=0; i<len; ++i) {
39414                 options.symbolizers[i] = this.symbolizers[i].clone();
39415             }
39416         } else {
39417             // clone symbolizer
39418             options.symbolizer = {};
39419             var value, type;
39420             for(var key in this.symbolizer) {
39421                 value = this.symbolizer[key];
39422                 type = typeof value;
39423                 if(type === "object") {
39424                     options.symbolizer[key] = OpenLayers.Util.extend({}, value);
39425                 } else if(type === "string") {
39426                     options.symbolizer[key] = value;
39427                 }
39428             }
39429         }
39430         // clone filter
39431         options.filter = this.filter && this.filter.clone();
39432         // clone context
39433         options.context = this.context && OpenLayers.Util.extend({}, this.context);
39434         return new OpenLayers.Rule(options);
39435     },
39436         
39437     CLASS_NAME: "OpenLayers.Rule"
39438 });
39439 /* ======================================================================
39440     OpenLayers/Handler/Pinch.js
39441    ====================================================================== */
39442
39443 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
39444  * full list of contributors). Published under the 2-clause BSD license.
39445  * See license.txt in the OpenLayers distribution or repository for the
39446  * full text of the license. */
39447
39448 /**
39449  * @requires OpenLayers/Handler.js
39450  */
39451
39452 /**
39453  * Class: OpenLayers.Handler.Pinch
39454  * The pinch handler is used to deal with sequences of browser events related
39455  *     to pinch gestures. The handler is used by controls that want to know
39456  *     when a pinch sequence begins, when a pinch is happening, and when it has
39457  *     finished.
39458  *
39459  * Controls that use the pinch handler typically construct it with callbacks
39460  *     for 'start', 'move', and 'done'.  Callbacks for these keys are
39461  *     called when the pinch begins, with each change, and when the pinch is
39462  *     done.
39463  *
39464  * Create a new pinch handler with the <OpenLayers.Handler.Pinch> constructor.
39465  *
39466  * Inherits from:
39467  *  - <OpenLayers.Handler>
39468  */
39469 OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, {
39470
39471     /**
39472      * Property: started
39473      * {Boolean} When a touchstart event is received, we want to record it,
39474      *     but not set 'pinching' until the touchmove get started after
39475      *     starting.
39476      */
39477     started: false,
39478
39479     /**
39480      * Property: stopDown
39481      * {Boolean} Stop propagation of touchstart events from getting to
39482      *     listeners on the same element. Default is false.
39483      */
39484     stopDown: false,
39485
39486     /**
39487      * Property: pinching
39488      * {Boolean}
39489      */
39490     pinching: false,
39491
39492     /**
39493      * Property: last
39494      * {Object} Object that store informations related to pinch last touch.
39495      */
39496     last: null,
39497
39498     /**
39499      * Property: start
39500      * {Object} Object that store informations related to pinch touchstart.
39501      */
39502     start: null,
39503
39504     /**
39505      * Constructor: OpenLayers.Handler.Pinch
39506      * Returns OpenLayers.Handler.Pinch
39507      *
39508      * Parameters:
39509      * control - {<OpenLayers.Control>} The control that is making use of
39510      *     this handler.  If a handler is being used without a control, the
39511      *     handlers setMap method must be overridden to deal properly with
39512      *     the map.
39513      * callbacks - {Object} An object containing functions to be called when
39514      *     the pinch operation start, change, or is finished. The callbacks
39515      *     should expect to receive an object argument, which contains
39516      *     information about scale, distance, and position of touch points.
39517      * options - {Object}
39518      */
39519
39520     /**
39521      * Method: touchstart
39522      * Handle touchstart events
39523      *
39524      * Parameters:
39525      * evt - {Event}
39526      *
39527      * Returns:
39528      * {Boolean} Let the event propagate.
39529      */
39530     touchstart: function(evt) {
39531         var propagate = true;
39532         this.pinching = false;
39533         if (OpenLayers.Event.isMultiTouch(evt)) {
39534             this.started = true;
39535             this.last = this.start = {
39536                 distance: this.getDistance(evt.touches),
39537                 delta: 0,
39538                 scale: 1
39539             };
39540             this.callback("start", [evt, this.start]);
39541             propagate = !this.stopDown;
39542         } else if (this.started) {
39543             // Some webkit versions send fake single-touch events during
39544             // multitouch, which cause the drag handler to trigger
39545             return false;
39546         } else {
39547             this.started = false;
39548             this.start = null;
39549             this.last = null;
39550         }
39551         // prevent document dragging
39552         OpenLayers.Event.preventDefault(evt);
39553         return propagate;
39554     },
39555
39556     /**
39557      * Method: touchmove
39558      * Handle touchmove events
39559      *
39560      * Parameters:
39561      * evt - {Event}
39562      *
39563      * Returns:
39564      * {Boolean} Let the event propagate.
39565      */
39566     touchmove: function(evt) {
39567         if (this.started && OpenLayers.Event.isMultiTouch(evt)) {
39568             this.pinching = true;
39569             var current = this.getPinchData(evt);
39570             this.callback("move", [evt, current]);
39571             this.last = current;
39572             // prevent document dragging
39573             OpenLayers.Event.stop(evt);
39574         } else if (this.started) {
39575             // Some webkit versions send fake single-touch events during
39576             // multitouch, which cause the drag handler to trigger
39577             return false;
39578         }
39579         return true;
39580     },
39581
39582     /**
39583      * Method: touchend
39584      * Handle touchend events
39585      *
39586      * Parameters:
39587      * evt - {Event}
39588      *
39589      * Returns:
39590      * {Boolean} Let the event propagate.
39591      */
39592     touchend: function(evt) {
39593         if (this.started && !OpenLayers.Event.isMultiTouch(evt)) {
39594             this.started = false;
39595             this.pinching = false;
39596             this.callback("done", [evt, this.start, this.last]);
39597             this.start = null;
39598             this.last = null;
39599             return false;
39600         }
39601         return true;
39602     },
39603
39604     /**
39605      * Method: activate
39606      * Activate the handler.
39607      *
39608      * Returns:
39609      * {Boolean} The handler was successfully activated.
39610      */
39611     activate: function() {
39612         var activated = false;
39613         if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
39614             this.pinching = false;
39615             activated = true;
39616         }
39617         return activated;
39618     },
39619
39620     /**
39621      * Method: deactivate
39622      * Deactivate the handler.
39623      *
39624      * Returns:
39625      * {Boolean} The handler was successfully deactivated.
39626      */
39627     deactivate: function() {
39628         var deactivated = false;
39629         if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
39630             this.started = false;
39631             this.pinching = false;
39632             this.start = null;
39633             this.last = null;
39634             deactivated = true;
39635         }
39636         return deactivated;
39637     },
39638
39639     /**
39640      * Method: getDistance
39641      * Get the distance in pixels between two touches.
39642      *
39643      * Parameters:
39644      * touches - {Array(Object)}
39645      *
39646      * Returns:
39647      * {Number} The distance in pixels.
39648      */
39649     getDistance: function(touches) {
39650         var t0 = touches[0];
39651         var t1 = touches[1];
39652         return Math.sqrt(
39653             Math.pow(t0.olClientX - t1.olClientX, 2) +
39654             Math.pow(t0.olClientY - t1.olClientY, 2)
39655         );
39656     },
39657
39658
39659     /**
39660      * Method: getPinchData
39661      * Get informations about the pinch event.
39662      *
39663      * Parameters:
39664      * evt - {Event}
39665      *
39666      * Returns:
39667      * {Object} Object that contains data about the current pinch.
39668      */
39669     getPinchData: function(evt) {
39670         var distance = this.getDistance(evt.touches);
39671         var scale = distance / this.start.distance;
39672         return {
39673             distance: distance,
39674             delta: this.last.distance - distance,
39675             scale: scale
39676         };
39677     },
39678
39679     CLASS_NAME: "OpenLayers.Handler.Pinch"
39680 });
39681
39682 /* ======================================================================
39683     OpenLayers/Lang/en.js
39684    ====================================================================== */
39685
39686 /**
39687  * @requires OpenLayers/Lang.js
39688  */
39689
39690 /**
39691  * Namespace: OpenLayers.Lang["en"]
39692  * Dictionary for English.  Keys for entries are used in calls to
39693  *     <OpenLayers.Lang.translate>.  Entry bodies are normal strings or
39694  *     strings formatted for use with <OpenLayers.String.format> calls.
39695  */
39696 OpenLayers.Lang.en = {
39697
39698     'unhandledRequest': "Unhandled request return ${statusText}",
39699
39700     'Permalink': "Permalink",
39701
39702     'Overlays': "Overlays",
39703
39704     'Base Layer': "Base Layer",
39705
39706     'noFID': "Can't update a feature for which there is no FID.",
39707
39708     'browserNotSupported':
39709         "Your browser does not support vector rendering. Currently supported renderers are:\n${renderers}",
39710
39711     // console message
39712     'minZoomLevelError':
39713         "The minZoomLevel property is only intended for use " +
39714         "with the FixedZoomLevels-descendent layers. That this " +
39715         "wfs layer checks for minZoomLevel is a relic of the" +
39716         "past. We cannot, however, remove it without possibly " +
39717         "breaking OL based applications that may depend on it." +
39718         " Therefore we are deprecating it -- the minZoomLevel " +
39719         "check below will be removed at 3.0. Please instead " +
39720         "use min/max resolution setting as described here: " +
39721         "http://trac.openlayers.org/wiki/SettingZoomLevels",
39722
39723     'commitSuccess': "WFS Transaction: SUCCESS ${response}",
39724
39725     'commitFailed': "WFS Transaction: FAILED ${response}",
39726
39727     'googleWarning':
39728         "The Google Layer was unable to load correctly.<br><br>" +
39729         "To get rid of this message, select a new BaseLayer " +
39730         "in the layer switcher in the upper-right corner.<br><br>" +
39731         "Most likely, this is because the Google Maps library " +
39732         "script was either not included, or does not contain the " +
39733         "correct API key for your site.<br><br>" +
39734         "Developers: For help getting this working correctly, " +
39735         "<a href='http://trac.openlayers.org/wiki/Google' " +
39736         "target='_blank'>click here</a>",
39737
39738     'getLayerWarning':
39739         "The ${layerType} Layer was unable to load correctly.<br><br>" +
39740         "To get rid of this message, select a new BaseLayer " +
39741         "in the layer switcher in the upper-right corner.<br><br>" +
39742         "Most likely, this is because the ${layerLib} library " +
39743         "script was not correctly included.<br><br>" +
39744         "Developers: For help getting this working correctly, " +
39745         "<a href='http://trac.openlayers.org/wiki/${layerLib}' " +
39746         "target='_blank'>click here</a>",
39747
39748     'Scale = 1 : ${scaleDenom}': "Scale = 1 : ${scaleDenom}",
39749     
39750     //labels for the graticule control
39751     'W': 'W',
39752     'E': 'E',
39753     'N': 'N',
39754     'S': 'S',
39755     'Graticule': 'Graticule',
39756
39757     // console message
39758     'reprojectDeprecated':
39759         "You are using the 'reproject' option " +
39760         "on the ${layerName} layer. This option is deprecated: " +
39761         "its use was designed to support displaying data over commercial " + 
39762         "basemaps, but that functionality should now be achieved by using " +
39763         "Spherical Mercator support. More information is available from " +
39764         "http://trac.openlayers.org/wiki/SphericalMercator.",
39765
39766     // console message
39767     'methodDeprecated':
39768         "This method has been deprecated and will be removed in 3.0. " +
39769         "Please use ${newMethod} instead.",
39770
39771     // **** end ****
39772     'end': ''
39773     
39774 };
39775 /* ======================================================================
39776     OpenLayers/Control/PinchZoom.js
39777    ====================================================================== */
39778
39779 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
39780  * full list of contributors). Published under the 2-clause BSD license.
39781  * See license.txt in the OpenLayers distribution or repository for the
39782  * full text of the license. */
39783
39784 /**
39785  * @requires OpenLayers/Handler/Pinch.js
39786  */
39787
39788 /**
39789  * Class: OpenLayers.Control.PinchZoom
39790  *
39791  * Inherits:
39792  *  - <OpenLayers.Control>
39793  */
39794 OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, {
39795
39796     /** 
39797      * Property: type
39798      * {OpenLayers.Control.TYPES}
39799      */
39800     type: OpenLayers.Control.TYPE_TOOL,
39801
39802     /**
39803      * Property: pinchOrigin
39804      * {Object} Cached object representing the pinch start (in pixels).
39805      */
39806     pinchOrigin: null,    
39807     
39808     /**
39809      * Property: currentCenter
39810      * {Object} Cached object representing the latest pinch center (in pixels).
39811      */
39812     currentCenter: null,    
39813
39814     /**
39815      * APIProperty: autoActivate
39816      * {Boolean} Activate the control when it is added to a map.  Default is
39817      *     true.
39818      */
39819     autoActivate: true,
39820
39821     /**
39822      * APIProperty: preserveCenter
39823      * {Boolean} Set this to true if you don't want the map center to change
39824      *     while pinching. For example you may want to set preserveCenter to
39825      *     true when the user location is being watched and you want to preserve
39826      *     the user location at the center of the map even if he zooms in or
39827      *     out using pinch. This property's value can be changed any time on an
39828      *     existing instance. Default is false.
39829      */
39830     preserveCenter: false,
39831     
39832     /**
39833      * APIProperty: handlerOptions
39834      * {Object} Used to set non-default properties on the pinch handler
39835      */
39836
39837     /**
39838      * Constructor: OpenLayers.Control.PinchZoom
39839      * Create a control for zooming with pinch gestures.  This works on devices
39840      *     with multi-touch support.
39841      *
39842      * Parameters:
39843      * options - {Object} An optional object whose properties will be set on
39844      *                    the control
39845      */
39846     initialize: function(options) {
39847         OpenLayers.Control.prototype.initialize.apply(this, arguments);
39848         this.handler = new OpenLayers.Handler.Pinch(this, {
39849             start: this.pinchStart,
39850             move: this.pinchMove,
39851             done: this.pinchDone
39852         }, this.handlerOptions);
39853     },
39854     
39855     /**
39856      * Method: pinchStart
39857      *
39858      * Parameters:
39859      * evt - {Event}
39860      * pinchData - {Object} pinch data object related to the current touchmove
39861      *     of the pinch gesture. This give us the current scale of the pinch.
39862      */
39863     pinchStart: function(evt, pinchData) {
39864         var xy = (this.preserveCenter) ?
39865             this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;
39866         this.pinchOrigin = xy;
39867         this.currentCenter = xy;
39868     },
39869     
39870     /**
39871      * Method: pinchMove
39872      *
39873      * Parameters:
39874      * evt - {Event}
39875      * pinchData - {Object} pinch data object related to the current touchmove
39876      *     of the pinch gesture. This give us the current scale of the pinch.
39877      */
39878     pinchMove: function(evt, pinchData) {
39879         var scale = pinchData.scale;
39880         var containerOrigin = this.map.layerContainerOriginPx;
39881         var pinchOrigin = this.pinchOrigin;
39882         var current = (this.preserveCenter) ?
39883             this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy;
39884
39885         var dx = Math.round((containerOrigin.x + current.x - pinchOrigin.x) + (scale - 1) * (containerOrigin.x - pinchOrigin.x));
39886         var dy = Math.round((containerOrigin.y + current.y - pinchOrigin.y) + (scale - 1) * (containerOrigin.y - pinchOrigin.y));
39887
39888         this.map.applyTransform(dx, dy, scale);
39889         this.currentCenter = current;
39890     },
39891
39892     /**
39893      * Method: pinchDone
39894      *
39895      * Parameters:
39896      * evt - {Event}
39897      * start - {Object} pinch data object related to the touchstart event that
39898      *     started the pinch gesture.
39899      * last - {Object} pinch data object related to the last touchmove event
39900      *     of the pinch gesture. This give us the final scale of the pinch.
39901      */
39902     pinchDone: function(evt, start, last) {
39903         this.map.applyTransform();
39904         var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true);
39905         if (zoom !== this.map.getZoom() || !this.currentCenter.equals(this.pinchOrigin)) {
39906             var resolution = this.map.getResolutionForZoom(zoom);
39907
39908             var location = this.map.getLonLatFromPixel(this.pinchOrigin);
39909             var zoomPixel = this.currentCenter;        
39910             var size = this.map.getSize();
39911
39912             location.lon += resolution * ((size.w / 2) - zoomPixel.x);
39913             location.lat -= resolution * ((size.h / 2) - zoomPixel.y);
39914
39915             // Force a reflow before calling setCenter. This is to work
39916             // around an issue occuring in iOS.
39917             //
39918             // See https://github.com/openlayers/openlayers/pull/351.
39919             //
39920             // Without a reflow setting the layer container div's top left
39921             // style properties to "0px" - as done in Map.moveTo when zoom
39922             // is changed - won't actually correctly reposition the layer
39923             // container div.
39924             //
39925             // Also, we need to use a statement that the Google Closure
39926             // compiler won't optimize away.
39927             this.map.div.clientWidth = this.map.div.clientWidth;
39928
39929             this.map.setCenter(location, zoom);
39930         }
39931     },
39932
39933     CLASS_NAME: "OpenLayers.Control.PinchZoom"
39934
39935 });
39936 /* ======================================================================
39937     OpenLayers/Control/TouchNavigation.js
39938    ====================================================================== */
39939
39940 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
39941  * full list of contributors). Published under the 2-clause BSD license.
39942  * See license.txt in the OpenLayers distribution or repository for the
39943  * full text of the license. */
39944
39945 /**
39946  * @requires OpenLayers/Control/DragPan.js
39947  * @requires OpenLayers/Control/PinchZoom.js
39948  * @requires OpenLayers/Handler/Click.js
39949  */
39950
39951 /**
39952  * Class: OpenLayers.Control.TouchNavigation
39953  * The navigation control handles map browsing with touch events (dragging,
39954  *     double-tapping, tap with two fingers, and pinch zoom).  Create a new 
39955  *     control with the <OpenLayers.Control.TouchNavigation> constructor.
39956  *
39957  * If you’re only targeting touch enabled devices with your mapping application,
39958  *     you can create a map with only a TouchNavigation control. The 
39959  *     <OpenLayers.Control.Navigation> control is mobile ready by default, but 
39960  *     you can generate a smaller build of the library by only including this
39961  *     touch navigation control if you aren't concerned about mouse interaction.
39962  *
39963  * Inherits:
39964  *  - <OpenLayers.Control>
39965  */
39966 OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, {
39967
39968     /**
39969      * Property: dragPan
39970      * {<OpenLayers.Control.DragPan>}
39971      */
39972     dragPan: null,
39973
39974     /**
39975      * APIProperty: dragPanOptions
39976      * {Object} Options passed to the DragPan control.
39977      */
39978     dragPanOptions: null,
39979
39980     /**
39981      * Property: pinchZoom
39982      * {<OpenLayers.Control.PinchZoom>}
39983      */
39984     pinchZoom: null,
39985
39986     /**
39987      * APIProperty: pinchZoomOptions
39988      * {Object} Options passed to the PinchZoom control.
39989      */
39990     pinchZoomOptions: null,
39991
39992     /**
39993      * APIProperty: clickHandlerOptions
39994      * {Object} Options passed to the Click handler.
39995      */
39996     clickHandlerOptions: null,
39997
39998     /**
39999      * APIProperty: documentDrag
40000      * {Boolean} Allow panning of the map by dragging outside map viewport.
40001      *     Default is false.
40002      */
40003     documentDrag: false,
40004
40005     /**
40006      * APIProperty: autoActivate
40007      * {Boolean} Activate the control when it is added to a map.  Default is
40008      *     true.
40009      */
40010     autoActivate: true,
40011
40012     /**
40013      * Constructor: OpenLayers.Control.TouchNavigation
40014      * Create a new navigation control
40015      *
40016      * Parameters:
40017      * options - {Object} An optional object whose properties will be set on
40018      *                    the control
40019      */
40020     initialize: function(options) {
40021         this.handlers = {};
40022         OpenLayers.Control.prototype.initialize.apply(this, arguments);
40023     },
40024
40025     /**
40026      * Method: destroy
40027      * The destroy method is used to perform any clean up before the control
40028      * is dereferenced.  Typically this is where event listeners are removed
40029      * to prevent memory leaks.
40030      */
40031     destroy: function() {
40032         this.deactivate();
40033         if(this.dragPan) {
40034             this.dragPan.destroy();
40035         }
40036         this.dragPan = null;
40037         if (this.pinchZoom) {
40038             this.pinchZoom.destroy();
40039             delete this.pinchZoom;
40040         }
40041         OpenLayers.Control.prototype.destroy.apply(this,arguments);
40042     },
40043
40044     /**
40045      * Method: activate
40046      */
40047     activate: function() {
40048         if(OpenLayers.Control.prototype.activate.apply(this,arguments)) {
40049             this.dragPan.activate();
40050             this.handlers.click.activate();
40051             this.pinchZoom.activate();
40052             return true;
40053         }
40054         return false;
40055     },
40056
40057     /**
40058      * Method: deactivate
40059      */
40060     deactivate: function() {
40061         if(OpenLayers.Control.prototype.deactivate.apply(this,arguments)) {
40062             this.dragPan.deactivate();
40063             this.handlers.click.deactivate();
40064             this.pinchZoom.deactivate();
40065             return true;
40066         }
40067         return false;
40068     },
40069     
40070     /**
40071      * Method: draw
40072      */
40073     draw: function() {
40074         var clickCallbacks = {
40075             click: this.defaultClick,
40076             dblclick: this.defaultDblClick
40077         };
40078         var clickOptions = OpenLayers.Util.extend({
40079             "double": true,
40080             stopDouble: true,
40081             pixelTolerance: 2
40082         }, this.clickHandlerOptions);
40083         this.handlers.click = new OpenLayers.Handler.Click(
40084             this, clickCallbacks, clickOptions
40085         );
40086         this.dragPan = new OpenLayers.Control.DragPan(
40087             OpenLayers.Util.extend({
40088                 map: this.map,
40089                 documentDrag: this.documentDrag
40090             }, this.dragPanOptions)
40091         );
40092         this.dragPan.draw();
40093         this.pinchZoom = new OpenLayers.Control.PinchZoom(
40094             OpenLayers.Util.extend({map: this.map}, this.pinchZoomOptions)
40095         );
40096     },
40097
40098     /**
40099      * Method: defaultClick
40100      *
40101      * Parameters:
40102      * evt - {Event}
40103      */
40104     defaultClick: function (evt) {
40105         if(evt.lastTouches && evt.lastTouches.length == 2) {
40106             this.map.zoomOut();
40107         }
40108     },
40109
40110     /**
40111      * Method: defaultDblClick
40112      *
40113      * Parameters:
40114      * evt - {Event}
40115      */
40116     defaultDblClick: function (evt) {
40117         this.map.zoomTo(this.map.zoom + 1, evt.xy);
40118     },
40119
40120     CLASS_NAME: "OpenLayers.Control.TouchNavigation"
40121 });
40122 /* ======================================================================
40123     OpenLayers/Renderer/VML.js
40124    ====================================================================== */
40125
40126 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
40127  * full list of contributors). Published under the 2-clause BSD license.
40128  * See license.txt in the OpenLayers distribution or repository for the
40129  * full text of the license. */
40130
40131 /**
40132  * @requires OpenLayers/Renderer/Elements.js
40133  */
40134
40135 /**
40136  * Class: OpenLayers.Renderer.VML
40137  * Render vector features in browsers with VML capability.  Construct a new
40138  * VML renderer with the <OpenLayers.Renderer.VML> constructor.
40139  * 
40140  * Note that for all calculations in this class, we use (num | 0) to truncate a 
40141  * float value to an integer. This is done because it seems that VML doesn't 
40142  * support float values.
40143  *
40144  * Inherits from:
40145  *  - <OpenLayers.Renderer.Elements>
40146  */
40147 OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
40148
40149     /**
40150      * Property: xmlns
40151      * {String} XML Namespace URN
40152      */
40153     xmlns: "urn:schemas-microsoft-com:vml",
40154     
40155     /**
40156      * Property: symbolCache
40157      * {DOMElement} node holding symbols. This hash is keyed by symbol name,
40158      *     and each value is a hash with a "path" and an "extent" property.
40159      */
40160     symbolCache: {},
40161
40162     /**
40163      * Property: offset
40164      * {Object} Hash with "x" and "y" properties
40165      */
40166     offset: null,
40167     
40168     /**
40169      * Constructor: OpenLayers.Renderer.VML
40170      * Create a new VML renderer.
40171      *
40172      * Parameters:
40173      * containerID - {String} The id for the element that contains the renderer
40174      */
40175     initialize: function(containerID) {
40176         if (!this.supported()) { 
40177             return; 
40178         }
40179         if (!document.namespaces.olv) {
40180             document.namespaces.add("olv", this.xmlns);
40181             var style = document.createStyleSheet();
40182             var shapes = ['shape','rect', 'oval', 'fill', 'stroke', 'imagedata', 'group','textbox']; 
40183             for (var i = 0, len = shapes.length; i < len; i++) {
40184
40185                 style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " +
40186                               "position: absolute; display: inline-block;");
40187             }                  
40188         }
40189         
40190         OpenLayers.Renderer.Elements.prototype.initialize.apply(this, 
40191                                                                 arguments);
40192     },
40193
40194     /**
40195      * APIMethod: supported
40196      * Determine whether a browser supports this renderer.
40197      *
40198      * Returns:
40199      * {Boolean} The browser supports the VML renderer
40200      */
40201     supported: function() {
40202         return !!(document.namespaces);
40203     },    
40204
40205     /**
40206      * Method: setExtent
40207      * Set the renderer's extent
40208      *
40209      * Parameters:
40210      * extent - {<OpenLayers.Bounds>}
40211      * resolutionChanged - {Boolean}
40212      * 
40213      * Returns:
40214      * {Boolean} true to notify the layer that the new extent does not exceed
40215      *     the coordinate range, and the features will not need to be redrawn.
40216      */
40217     setExtent: function(extent, resolutionChanged) {
40218         var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);
40219         var resolution = this.getResolution();
40220     
40221         var left = (extent.left/resolution) | 0;
40222         var top = (extent.top/resolution - this.size.h) | 0;
40223         if (resolutionChanged || !this.offset) {
40224             this.offset = {x: left, y: top};
40225             left = 0;
40226             top = 0;
40227         } else {
40228             left = left - this.offset.x;
40229             top = top - this.offset.y;
40230         }
40231
40232         
40233         var org = (left - this.xOffset) + " " + top;
40234         this.root.coordorigin = org;
40235         var roots = [this.root, this.vectorRoot, this.textRoot];
40236         var root;
40237         for(var i=0, len=roots.length; i<len; ++i) {
40238             root = roots[i];
40239
40240             var size = this.size.w + " " + this.size.h;
40241             root.coordsize = size;
40242             
40243         }
40244         // flip the VML display Y axis upside down so it 
40245         // matches the display Y axis of the map
40246         this.root.style.flip = "y";
40247         
40248         return coordSysUnchanged;
40249     },
40250
40251
40252     /**
40253      * Method: setSize
40254      * Set the size of the drawing surface
40255      *
40256      * Parameters:
40257      * size - {<OpenLayers.Size>} the size of the drawing surface
40258      */
40259     setSize: function(size) {
40260         OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
40261         
40262         // setting width and height on all roots to avoid flicker which we
40263         // would get with 100% width and height on child roots
40264         var roots = [
40265             this.rendererRoot,
40266             this.root,
40267             this.vectorRoot,
40268             this.textRoot
40269         ];
40270         var w = this.size.w + "px";
40271         var h = this.size.h + "px";
40272         var root;
40273         for(var i=0, len=roots.length; i<len; ++i) {
40274             root = roots[i];
40275             root.style.width = w;
40276             root.style.height = h;
40277         }
40278     },
40279
40280     /**
40281      * Method: getNodeType
40282      * Get the node type for a geometry and style
40283      *
40284      * Parameters:
40285      * geometry - {<OpenLayers.Geometry>}
40286      * style - {Object}
40287      *
40288      * Returns:
40289      * {String} The corresponding node type for the specified geometry
40290      */
40291     getNodeType: function(geometry, style) {
40292         var nodeType = null;
40293         switch (geometry.CLASS_NAME) {
40294             case "OpenLayers.Geometry.Point":
40295                 if (style.externalGraphic) {
40296                     nodeType = "olv:rect";
40297                 } else if (this.isComplexSymbol(style.graphicName)) {
40298                     nodeType = "olv:shape";
40299                 } else {
40300                     nodeType = "olv:oval";
40301                 }
40302                 break;
40303             case "OpenLayers.Geometry.Rectangle":
40304                 nodeType = "olv:rect";
40305                 break;
40306             case "OpenLayers.Geometry.LineString":
40307             case "OpenLayers.Geometry.LinearRing":
40308             case "OpenLayers.Geometry.Polygon":
40309             case "OpenLayers.Geometry.Curve":
40310                 nodeType = "olv:shape";
40311                 break;
40312             default:
40313                 break;
40314         }
40315         return nodeType;
40316     },
40317
40318     /**
40319      * Method: setStyle
40320      * Use to set all the style attributes to a VML node.
40321      *
40322      * Parameters:
40323      * node - {DOMElement} An VML element to decorate
40324      * style - {Object}
40325      * options - {Object} Currently supported options include 
40326      *                              'isFilled' {Boolean} and
40327      *                              'isStroked' {Boolean}
40328      * geometry - {<OpenLayers.Geometry>}
40329      */
40330     setStyle: function(node, style, options, geometry) {
40331         style = style  || node._style;
40332         options = options || node._options;
40333         var fillColor = style.fillColor;
40334
40335         var title = style.title || style.graphicTitle;
40336         if (title) {
40337             node.title = title;
40338         } 
40339
40340         if (node._geometryClass === "OpenLayers.Geometry.Point") {
40341             if (style.externalGraphic) {
40342                 options.isFilled = true;
40343                 var width = style.graphicWidth || style.graphicHeight;
40344                 var height = style.graphicHeight || style.graphicWidth;
40345                 width = width ? width : style.pointRadius*2;
40346                 height = height ? height : style.pointRadius*2;
40347
40348                 var resolution = this.getResolution();
40349                 var xOffset = (style.graphicXOffset != undefined) ?
40350                     style.graphicXOffset : -(0.5 * width);
40351                 var yOffset = (style.graphicYOffset != undefined) ?
40352                     style.graphicYOffset : -(0.5 * height);
40353                 
40354                 node.style.left = ((((geometry.x - this.featureDx)/resolution - this.offset.x)+xOffset) | 0) + "px";
40355                 node.style.top = (((geometry.y/resolution - this.offset.y)-(yOffset+height)) | 0) + "px";
40356                 node.style.width = width + "px";
40357                 node.style.height = height + "px";
40358                 node.style.flip = "y";
40359                 
40360                 // modify fillColor and options for stroke styling below
40361                 fillColor = "none";
40362                 options.isStroked = false;
40363             } else if (this.isComplexSymbol(style.graphicName)) {
40364                 var cache = this.importSymbol(style.graphicName);
40365                 node.path = cache.path;
40366                 node.coordorigin = cache.left + "," + cache.bottom;
40367                 var size = cache.size;
40368                 node.coordsize = size + "," + size;        
40369                 this.drawCircle(node, geometry, style.pointRadius);
40370                 node.style.flip = "y";
40371             } else {
40372                 this.drawCircle(node, geometry, style.pointRadius);
40373             }
40374         }
40375
40376         // fill 
40377         if (options.isFilled) { 
40378             node.fillcolor = fillColor; 
40379         } else { 
40380             node.filled = "false"; 
40381         }
40382         var fills = node.getElementsByTagName("fill");
40383         var fill = (fills.length == 0) ? null : fills[0];
40384         if (!options.isFilled) {
40385             if (fill) {
40386                 node.removeChild(fill);
40387             }
40388         } else {
40389             if (!fill) {
40390                 fill = this.createNode('olv:fill', node.id + "_fill");
40391             }
40392             fill.opacity = style.fillOpacity;
40393
40394             if (node._geometryClass === "OpenLayers.Geometry.Point" &&
40395                     style.externalGraphic) {
40396
40397                 // override fillOpacity
40398                 if (style.graphicOpacity) {
40399                     fill.opacity = style.graphicOpacity;
40400                 }
40401                 
40402                 fill.src = style.externalGraphic;
40403                 fill.type = "frame";
40404                 
40405                 if (!(style.graphicWidth && style.graphicHeight)) {
40406                   fill.aspect = "atmost";
40407                 }                
40408             }
40409             if (fill.parentNode != node) {
40410                 node.appendChild(fill);
40411             }
40412         }
40413
40414         // additional rendering for rotated graphics or symbols
40415         var rotation = style.rotation;
40416         if ((rotation !== undefined || node._rotation !== undefined)) {
40417             node._rotation = rotation;
40418             if (style.externalGraphic) {
40419                 this.graphicRotate(node, xOffset, yOffset, style);
40420                 // make the fill fully transparent, because we now have
40421                 // the graphic as imagedata element. We cannot just remove
40422                 // the fill, because this is part of the hack described
40423                 // in graphicRotate
40424                 fill.opacity = 0;
40425             } else if(node._geometryClass === "OpenLayers.Geometry.Point") {
40426                 node.style.rotation = rotation || 0;
40427             }
40428         }
40429
40430         // stroke 
40431         var strokes = node.getElementsByTagName("stroke");
40432         var stroke = (strokes.length == 0) ? null : strokes[0];
40433         if (!options.isStroked) {
40434             node.stroked = false;
40435             if (stroke) {
40436                 stroke.on = false;
40437             }
40438         } else {
40439             if (!stroke) {
40440                 stroke = this.createNode('olv:stroke', node.id + "_stroke");
40441                 node.appendChild(stroke);
40442             }
40443             stroke.on = true;
40444             stroke.color = style.strokeColor; 
40445             stroke.weight = style.strokeWidth + "px"; 
40446             stroke.opacity = style.strokeOpacity;
40447             stroke.endcap = style.strokeLinecap == 'butt' ? 'flat' :
40448                 (style.strokeLinecap || 'round');
40449             if (style.strokeDashstyle) {
40450                 stroke.dashstyle = this.dashStyle(style);
40451             }
40452         }
40453         
40454         if (style.cursor != "inherit" && style.cursor != null) {
40455             node.style.cursor = style.cursor;
40456         }
40457         return node;
40458     },
40459
40460     /**
40461      * Method: graphicRotate
40462      * If a point is to be styled with externalGraphic and rotation, VML fills
40463      * cannot be used to display the graphic, because rotation of graphic
40464      * fills is not supported by the VML implementation of Internet Explorer.
40465      * This method creates a olv:imagedata element inside the VML node,
40466      * DXImageTransform.Matrix and BasicImage filters for rotation and
40467      * opacity, and a 3-step hack to remove rendering artefacts from the
40468      * graphic and preserve the ability of graphics to trigger events.
40469      * Finally, OpenLayers methods are used to determine the correct
40470      * insertion point of the rotated image, because DXImageTransform.Matrix
40471      * does the rotation without the ability to specify a rotation center
40472      * point.
40473      * 
40474      * Parameters:
40475      * node    - {DOMElement}
40476      * xOffset - {Number} rotation center relative to image, x coordinate
40477      * yOffset - {Number} rotation center relative to image, y coordinate
40478      * style   - {Object}
40479      */
40480     graphicRotate: function(node, xOffset, yOffset, style) {
40481         var style = style || node._style;
40482         var rotation = style.rotation || 0;
40483         
40484         var aspectRatio, size;
40485         if (!(style.graphicWidth && style.graphicHeight)) {
40486             // load the image to determine its size
40487             var img = new Image();
40488             img.onreadystatechange = OpenLayers.Function.bind(function() {
40489                 if(img.readyState == "complete" ||
40490                         img.readyState == "interactive") {
40491                     aspectRatio = img.width / img.height;
40492                     size = Math.max(style.pointRadius * 2, 
40493                         style.graphicWidth || 0,
40494                         style.graphicHeight || 0);
40495                     xOffset = xOffset * aspectRatio;
40496                     style.graphicWidth = size * aspectRatio;
40497                     style.graphicHeight = size;
40498                     this.graphicRotate(node, xOffset, yOffset, style);
40499                 }
40500             }, this);
40501             img.src = style.externalGraphic;
40502             
40503             // will be called again by the onreadystate handler
40504             return;
40505         } else {
40506             size = Math.max(style.graphicWidth, style.graphicHeight);
40507             aspectRatio = style.graphicWidth / style.graphicHeight;
40508         }
40509         
40510         var width = Math.round(style.graphicWidth || size * aspectRatio);
40511         var height = Math.round(style.graphicHeight || size);
40512         node.style.width = width + "px";
40513         node.style.height = height + "px";
40514         
40515         // Three steps are required to remove artefacts for images with
40516         // transparent backgrounds (resulting from using DXImageTransform
40517         // filters on svg objects), while preserving awareness for browser
40518         // events on images:
40519         // - Use the fill as usual (like for unrotated images) to handle
40520         //   events
40521         // - specify an imagedata element with the same src as the fill
40522         // - style the imagedata element with an AlphaImageLoader filter
40523         //   with empty src
40524         var image = document.getElementById(node.id + "_image");
40525         if (!image) {
40526             image = this.createNode("olv:imagedata", node.id + "_image");
40527             node.appendChild(image);
40528         }
40529         image.style.width = width + "px";
40530         image.style.height = height + "px";
40531         image.src = style.externalGraphic;
40532         image.style.filter =
40533             "progid:DXImageTransform.Microsoft.AlphaImageLoader(" + 
40534             "src='', sizingMethod='scale')";
40535
40536         var rot = rotation * Math.PI / 180;
40537         var sintheta = Math.sin(rot);
40538         var costheta = Math.cos(rot);
40539
40540         // do the rotation on the image
40541         var filter =
40542             "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta +
40543             ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta +
40544             ",SizingMethod='auto expand')\n";
40545
40546         // set the opacity (needed for the imagedata)
40547         var opacity = style.graphicOpacity || style.fillOpacity;
40548         if (opacity && opacity != 1) {
40549             filter += 
40550                 "progid:DXImageTransform.Microsoft.BasicImage(opacity=" + 
40551                 opacity+")\n";
40552         }
40553         node.style.filter = filter;
40554
40555         // do the rotation again on a box, so we know the insertion point
40556         var centerPoint = new OpenLayers.Geometry.Point(-xOffset, -yOffset);
40557         var imgBox = new OpenLayers.Bounds(0, 0, width, height).toGeometry();
40558         imgBox.rotate(style.rotation, centerPoint);
40559         var imgBounds = imgBox.getBounds();
40560
40561         node.style.left = Math.round(
40562             parseInt(node.style.left) + imgBounds.left) + "px";
40563         node.style.top = Math.round(
40564             parseInt(node.style.top) - imgBounds.bottom) + "px";
40565     },
40566
40567     /**
40568      * Method: postDraw
40569      * Does some node postprocessing to work around browser issues:
40570      * - Some versions of Internet Explorer seem to be unable to set fillcolor
40571      *   and strokecolor to "none" correctly before the fill node is appended
40572      *   to a visible vml node. This method takes care of that and sets
40573      *   fillcolor and strokecolor again if needed.
40574      * - In some cases, a node won't become visible after being drawn. Setting
40575      *   style.visibility to "visible" works around that.
40576      * 
40577      * Parameters:
40578      * node - {DOMElement}
40579      */
40580     postDraw: function(node) {
40581         node.style.visibility = "visible";
40582         var fillColor = node._style.fillColor;
40583         var strokeColor = node._style.strokeColor;
40584         if (fillColor == "none" &&
40585                 node.fillcolor != fillColor) {
40586             node.fillcolor = fillColor;
40587         }
40588         if (strokeColor == "none" &&
40589                 node.strokecolor != strokeColor) {
40590             node.strokecolor = strokeColor;
40591         }
40592     },
40593
40594
40595     /**
40596      * Method: setNodeDimension
40597      * Get the geometry's bounds, convert it to our vml coordinate system, 
40598      * then set the node's position, size, and local coordinate system.
40599      *   
40600      * Parameters:
40601      * node - {DOMElement}
40602      * geometry - {<OpenLayers.Geometry>}
40603      */
40604     setNodeDimension: function(node, geometry) {
40605
40606         var bbox = geometry.getBounds();
40607         if(bbox) {
40608             var resolution = this.getResolution();
40609         
40610             var scaledBox = 
40611                 new OpenLayers.Bounds(((bbox.left - this.featureDx)/resolution - this.offset.x) | 0,
40612                                       (bbox.bottom/resolution - this.offset.y) | 0,
40613                                       ((bbox.right - this.featureDx)/resolution - this.offset.x) | 0,
40614                                       (bbox.top/resolution - this.offset.y) | 0);
40615             
40616             // Set the internal coordinate system to draw the path
40617             node.style.left = scaledBox.left + "px";
40618             node.style.top = scaledBox.top + "px";
40619             node.style.width = scaledBox.getWidth() + "px";
40620             node.style.height = scaledBox.getHeight() + "px";
40621     
40622             node.coordorigin = scaledBox.left + " " + scaledBox.top;
40623             node.coordsize = scaledBox.getWidth()+ " " + scaledBox.getHeight();
40624         }
40625     },
40626     
40627     /** 
40628      * Method: dashStyle
40629      * 
40630      * Parameters:
40631      * style - {Object}
40632      * 
40633      * Returns:
40634      * {String} A VML compliant 'stroke-dasharray' value
40635      */
40636     dashStyle: function(style) {
40637         var dash = style.strokeDashstyle;
40638         switch (dash) {
40639             case 'solid':
40640             case 'dot':
40641             case 'dash':
40642             case 'dashdot':
40643             case 'longdash':
40644             case 'longdashdot':
40645                 return dash;
40646             default:
40647                 // very basic guessing of dash style patterns
40648                 var parts = dash.split(/[ ,]/);
40649                 if (parts.length == 2) {
40650                     if (1*parts[0] >= 2*parts[1]) {
40651                         return "longdash";
40652                     }
40653                     return (parts[0] == 1 || parts[1] == 1) ? "dot" : "dash";
40654                 } else if (parts.length == 4) {
40655                     return (1*parts[0] >= 2*parts[1]) ? "longdashdot" :
40656                         "dashdot";
40657                 }
40658                 return "solid";
40659         }
40660     },
40661
40662     /**
40663      * Method: createNode
40664      * Create a new node
40665      *
40666      * Parameters:
40667      * type - {String} Kind of node to draw
40668      * id - {String} Id for node
40669      *
40670      * Returns:
40671      * {DOMElement} A new node of the given type and id
40672      */
40673     createNode: function(type, id) {
40674         var node = document.createElement(type);
40675         if (id) {
40676             node.id = id;
40677         }
40678         
40679         // IE hack to make elements unselectable, to prevent 'blue flash'
40680         // while dragging vectors; #1410
40681         node.unselectable = 'on';
40682         node.onselectstart = OpenLayers.Function.False;
40683         
40684         return node;    
40685     },
40686     
40687     /**
40688      * Method: nodeTypeCompare
40689      * Determine whether a node is of a given type
40690      *
40691      * Parameters:
40692      * node - {DOMElement} An VML element
40693      * type - {String} Kind of node
40694      *
40695      * Returns:
40696      * {Boolean} Whether or not the specified node is of the specified type
40697      */
40698     nodeTypeCompare: function(node, type) {
40699
40700         //split type
40701         var subType = type;
40702         var splitIndex = subType.indexOf(":");
40703         if (splitIndex != -1) {
40704             subType = subType.substr(splitIndex+1);
40705         }
40706
40707         //split nodeName
40708         var nodeName = node.nodeName;
40709         splitIndex = nodeName.indexOf(":");
40710         if (splitIndex != -1) {
40711             nodeName = nodeName.substr(splitIndex+1);
40712         }
40713
40714         return (subType == nodeName);
40715     },
40716
40717     /**
40718      * Method: createRenderRoot
40719      * Create the renderer root
40720      *
40721      * Returns:
40722      * {DOMElement} The specific render engine's root element
40723      */
40724     createRenderRoot: function() {
40725         return this.nodeFactory(this.container.id + "_vmlRoot", "div");
40726     },
40727
40728     /**
40729      * Method: createRoot
40730      * Create the main root element
40731      * 
40732      * Parameters:
40733      * suffix - {String} suffix to append to the id
40734      *
40735      * Returns:
40736      * {DOMElement}
40737      */
40738     createRoot: function(suffix) {
40739         return this.nodeFactory(this.container.id + suffix, "olv:group");
40740     },
40741     
40742     /**************************************
40743      *                                    *
40744      *     GEOMETRY DRAWING FUNCTIONS     *
40745      *                                    *
40746      **************************************/
40747     
40748     /**
40749      * Method: drawPoint
40750      * Render a point
40751      * 
40752      * Parameters:
40753      * node - {DOMElement}
40754      * geometry - {<OpenLayers.Geometry>}
40755      * 
40756      * Returns:
40757      * {DOMElement} or false if the point could not be drawn
40758      */
40759     drawPoint: function(node, geometry) {
40760         return this.drawCircle(node, geometry, 1);
40761     },
40762
40763     /**
40764      * Method: drawCircle
40765      * Render a circle.
40766      * Size and Center a circle given geometry (x,y center) and radius
40767      * 
40768      * Parameters:
40769      * node - {DOMElement}
40770      * geometry - {<OpenLayers.Geometry>}
40771      * radius - {float}
40772      * 
40773      * Returns:
40774      * {DOMElement} or false if the circle could not ne drawn
40775      */
40776     drawCircle: function(node, geometry, radius) {
40777         if(!isNaN(geometry.x)&& !isNaN(geometry.y)) {
40778             var resolution = this.getResolution();
40779
40780             node.style.left = ((((geometry.x - this.featureDx) /resolution - this.offset.x) | 0) - radius) + "px";
40781             node.style.top = (((geometry.y /resolution - this.offset.y) | 0) - radius) + "px";
40782     
40783             var diameter = radius * 2;
40784             
40785             node.style.width = diameter + "px";
40786             node.style.height = diameter + "px";
40787             return node;
40788         }
40789         return false;
40790     },
40791
40792
40793     /**
40794      * Method: drawLineString
40795      * Render a linestring.
40796      * 
40797      * Parameters:
40798      * node - {DOMElement}
40799      * geometry - {<OpenLayers.Geometry>}
40800      * 
40801      * Returns:
40802      * {DOMElement}
40803      */
40804     drawLineString: function(node, geometry) {
40805         return this.drawLine(node, geometry, false);
40806     },
40807
40808     /**
40809      * Method: drawLinearRing
40810      * Render a linearring
40811      * 
40812      * Parameters:
40813      * node - {DOMElement}
40814      * geometry - {<OpenLayers.Geometry>}
40815      * 
40816      * Returns:
40817      * {DOMElement}
40818      */
40819     drawLinearRing: function(node, geometry) {
40820         return this.drawLine(node, geometry, true);
40821     },
40822
40823     /**
40824      * Method: DrawLine
40825      * Render a line.
40826      * 
40827      * Parameters:
40828      * node - {DOMElement}
40829      * geometry - {<OpenLayers.Geometry>}
40830      * closeLine - {Boolean} Close the line? (make it a ring?)
40831      * 
40832      * Returns:
40833      * {DOMElement}
40834      */
40835     drawLine: function(node, geometry, closeLine) {
40836
40837         this.setNodeDimension(node, geometry);
40838
40839         var resolution = this.getResolution();
40840         var numComponents = geometry.components.length;
40841         var parts = new Array(numComponents);
40842
40843         var comp, x, y;
40844         for (var i = 0; i < numComponents; i++) {
40845             comp = geometry.components[i];
40846             x = ((comp.x - this.featureDx)/resolution - this.offset.x) | 0;
40847             y = (comp.y/resolution - this.offset.y) | 0;
40848             parts[i] = " " + x + "," + y + " l ";
40849         }
40850         var end = (closeLine) ? " x e" : " e";
40851         node.path = "m" + parts.join("") + end;
40852         return node;
40853     },
40854
40855     /**
40856      * Method: drawPolygon
40857      * Render a polygon
40858      * 
40859      * Parameters:
40860      * node - {DOMElement}
40861      * geometry - {<OpenLayers.Geometry>}
40862      * 
40863      * Returns:
40864      * {DOMElement}
40865      */
40866     drawPolygon: function(node, geometry) {
40867         this.setNodeDimension(node, geometry);
40868
40869         var resolution = this.getResolution();
40870     
40871         var path = [];
40872         var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y;
40873         for (j=0, jj=geometry.components.length; j<jj; j++) {
40874             path.push("m");
40875             points = geometry.components[j].components;
40876             // we only close paths of interior rings with area
40877             area = (j === 0);
40878             first = null;
40879             second = null;
40880             for (i=0, ii=points.length; i<ii; i++) {
40881                 comp = points[i];
40882                 x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0;
40883                 y = (comp.y / resolution - this.offset.y) | 0;
40884                 pathComp = " " + x + "," + y;
40885                 path.push(pathComp);
40886                 if (i==0) {
40887                     path.push(" l");
40888                 }
40889                 if (!area) {
40890                     // IE improperly renders sub-paths that have no area.
40891                     // Instead of checking the area of every ring, we confirm
40892                     // the ring has at least three distinct points.  This does
40893                     // not catch all non-zero area cases, but it greatly improves
40894                     // interior ring digitizing and is a minor performance hit
40895                     // when rendering rings with many points.
40896                     if (!first) {
40897                         first = pathComp;
40898                     } else if (first != pathComp) {
40899                         if (!second) {
40900                             second = pathComp;
40901                         } else if (second != pathComp) {
40902                             // stop looking
40903                             area = true;
40904                         }
40905                     }
40906                 }
40907             }
40908             path.push(area ? " x " : " ");
40909         }
40910         path.push("e");
40911         node.path = path.join("");
40912         return node;
40913     },
40914
40915     /**
40916      * Method: drawRectangle
40917      * Render a rectangle
40918      * 
40919      * Parameters:
40920      * node - {DOMElement}
40921      * geometry - {<OpenLayers.Geometry>}
40922      * 
40923      * Returns:
40924      * {DOMElement}
40925      */
40926     drawRectangle: function(node, geometry) {
40927         var resolution = this.getResolution();
40928     
40929         node.style.left = (((geometry.x - this.featureDx)/resolution - this.offset.x) | 0) + "px";
40930         node.style.top = ((geometry.y/resolution - this.offset.y) | 0) + "px";
40931         node.style.width = ((geometry.width/resolution) | 0) + "px";
40932         node.style.height = ((geometry.height/resolution) | 0) + "px";
40933         
40934         return node;
40935     },
40936     
40937     /**
40938      * Method: drawText
40939      * This method is only called by the renderer itself.
40940      * 
40941      * Parameters: 
40942      * featureId - {String}
40943      * style -
40944      * location - {<OpenLayers.Geometry.Point>}
40945      */
40946     drawText: function(featureId, style, location) {
40947         var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect");
40948         var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox");
40949         
40950         var resolution = this.getResolution();
40951         label.style.left = (((location.x - this.featureDx)/resolution - this.offset.x) | 0) + "px";
40952         label.style.top = ((location.y/resolution - this.offset.y) | 0) + "px";
40953         label.style.flip = "y";
40954
40955         textbox.innerText = style.label;
40956
40957         if (style.cursor != "inherit" && style.cursor != null) {
40958             textbox.style.cursor = style.cursor;
40959         }
40960         if (style.fontColor) {
40961             textbox.style.color = style.fontColor;
40962         }
40963         if (style.fontOpacity) {
40964             textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')';
40965         }
40966         if (style.fontFamily) {
40967             textbox.style.fontFamily = style.fontFamily;
40968         }
40969         if (style.fontSize) {
40970             textbox.style.fontSize = style.fontSize;
40971         }
40972         if (style.fontWeight) {
40973             textbox.style.fontWeight = style.fontWeight;
40974         }
40975         if (style.fontStyle) {
40976             textbox.style.fontStyle = style.fontStyle;
40977         }
40978         if(style.labelSelect === true) {
40979             label._featureId = featureId;
40980             textbox._featureId = featureId;
40981             textbox._geometry = location;
40982             textbox._geometryClass = location.CLASS_NAME;
40983         }
40984         textbox.style.whiteSpace = "nowrap";
40985         // fun with IE: IE7 in standards compliant mode does not display any
40986         // text with a left inset of 0. So we set this to 1px and subtract one
40987         // pixel later when we set label.style.left
40988         textbox.inset = "1px,0px,0px,0px";
40989
40990         if(!label.parentNode) {
40991             label.appendChild(textbox);
40992             this.textRoot.appendChild(label);
40993         }
40994
40995         var align = style.labelAlign || "cm";
40996         if (align.length == 1) {
40997             align += "m";
40998         }
40999         var xshift = textbox.clientWidth *
41000             (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0,1)]);
41001         var yshift = textbox.clientHeight *
41002             (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1,1)]);
41003         label.style.left = parseInt(label.style.left)-xshift-1+"px";
41004         label.style.top = parseInt(label.style.top)+yshift+"px";
41005         
41006     },
41007     
41008     /**
41009      * Method: moveRoot
41010      * moves this renderer's root to a different renderer.
41011      * 
41012      * Parameters:
41013      * renderer - {<OpenLayers.Renderer>} target renderer for the moved root
41014      * root - {DOMElement} optional root node. To be used when this renderer
41015      *     holds roots from multiple layers to tell this method which one to
41016      *     detach
41017      * 
41018      * Returns:
41019      * {Boolean} true if successful, false otherwise
41020      */
41021     moveRoot: function(renderer) {
41022         var layer = this.map.getLayer(renderer.container.id);
41023         if(layer instanceof OpenLayers.Layer.Vector.RootContainer) {
41024             layer = this.map.getLayer(this.container.id);
41025         }
41026         layer && layer.renderer.clear();
41027         OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments);
41028         layer && layer.redraw();
41029     },
41030     
41031     /**
41032      * Method: importSymbol
41033      * add a new symbol definition from the rendererer's symbol hash
41034      * 
41035      * Parameters:
41036      * graphicName - {String} name of the symbol to import
41037      * 
41038      * Returns:
41039      * {Object} - hash of {DOMElement} "symbol" and {Number} "size"
41040      */      
41041     importSymbol: function (graphicName)  {
41042         var id = this.container.id + "-" + graphicName;
41043         
41044         // check if symbol already exists in the cache
41045         var cache = this.symbolCache[id];
41046         if (cache) {
41047             return cache;
41048         }
41049         
41050         var symbol = OpenLayers.Renderer.symbol[graphicName];
41051         if (!symbol) {
41052             throw new Error(graphicName + ' is not a valid symbol name');
41053         }
41054
41055         var symbolExtent = new OpenLayers.Bounds(
41056                                     Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
41057         
41058         var pathitems = ["m"];
41059         for (var i=0; i<symbol.length; i=i+2) {
41060             var x = symbol[i];
41061             var y = symbol[i+1];
41062             symbolExtent.left = Math.min(symbolExtent.left, x);
41063             symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
41064             symbolExtent.right = Math.max(symbolExtent.right, x);
41065             symbolExtent.top = Math.max(symbolExtent.top, y);
41066
41067             pathitems.push(x);
41068             pathitems.push(y);
41069             if (i == 0) {
41070                 pathitems.push("l");
41071             }
41072         }
41073         pathitems.push("x e");
41074         var path = pathitems.join(" ");
41075
41076         var diff = (symbolExtent.getWidth() - symbolExtent.getHeight()) / 2;
41077         if(diff > 0) {
41078             symbolExtent.bottom = symbolExtent.bottom - diff;
41079             symbolExtent.top = symbolExtent.top + diff;
41080         } else {
41081             symbolExtent.left = symbolExtent.left + diff;
41082             symbolExtent.right = symbolExtent.right - diff;
41083         }
41084         
41085         cache = {
41086             path: path,
41087             size: symbolExtent.getWidth(), // equals getHeight() now
41088             left: symbolExtent.left,
41089             bottom: symbolExtent.bottom
41090         };
41091         this.symbolCache[id] = cache;
41092         
41093         return cache;
41094     },
41095     
41096     CLASS_NAME: "OpenLayers.Renderer.VML"
41097 });
41098
41099 /**
41100  * Constant: OpenLayers.Renderer.VML.LABEL_SHIFT
41101  * {Object}
41102  */
41103 OpenLayers.Renderer.VML.LABEL_SHIFT = {
41104     "l": 0,
41105     "c": .5,
41106     "r": 1,
41107     "t": 0,
41108     "m": .5,
41109     "b": 1
41110 };
41111 /* ======================================================================
41112     OpenLayers/Protocol/WFS/v1_0_0.js
41113    ====================================================================== */
41114
41115 /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
41116  * full list of contributors). Published under the 2-clause BSD license.
41117  * See license.txt in the OpenLayers distribution or repository for the
41118  * full text of the license. */
41119
41120 /**
41121  * @requires OpenLayers/Protocol/WFS/v1.js
41122  * @requires OpenLayers/Format/WFST/v1_0_0.js
41123  */
41124
41125 /**
41126  * Class: OpenLayers.Protocol.WFS.v1_0_0
41127  * A WFS v1.0.0 protocol for vector layers.  Create a new instance with the
41128  *     <OpenLayers.Protocol.WFS.v1_0_0> constructor.
41129  *
41130  * Inherits from:
41131  *  - <OpenLayers.Protocol.WFS.v1>
41132  */
41133 OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {
41134     
41135     /**
41136      * Property: version
41137      * {String} WFS version number.
41138      */
41139     version: "1.0.0",
41140     
41141     /**
41142      * Constructor: OpenLayers.Protocol.WFS.v1_0_0
41143      * A class for giving layers WFS v1.0.0 protocol.
41144      *
41145      * Parameters:
41146      * options - {Object} Optional object whose properties will be set on the
41147      *     instance.
41148      *
41149      * Valid options properties:
41150      * featureType - {String} Local (without prefix) feature typeName (required).
41151      * featureNS - {String} Feature namespace (optional).
41152      * featurePrefix - {String} Feature namespace alias (optional - only used
41153      *     if featureNS is provided).  Default is 'feature'.
41154      * geometryName - {String} Name of geometry attribute.  Default is 'the_geom'.
41155      */
41156    
41157     CLASS_NAME: "OpenLayers.Protocol.WFS.v1_0_0" 
41158 });