source: ether_iasi/trunk/web/resources/js/OpenLayers-2.12/lib/deprecated.js

Last change on this file was 739, checked in by vmipsl, 11 years ago

OpenLayers?

File size: 170.9 KB
Line 
1/**
2 * @requires OpenLayers/BaseTypes/Class.js
3 * @requires OpenLayers/Util.js
4 * @requires OpenLayers/Control.js
5 * @requires OpenLayers/Format.js
6 * @requires OpenLayers/Request.js
7 * @requires OpenLayers/Layer/WMS.js
8 * @requires OpenLayers/Layer/MapServer.js
9 * @requires OpenLayers/Tile.js
10 * @requires OpenLayers/Request/XMLHttpRequest.js
11 * @requires OpenLayers/Layer/Vector.js
12 * @requires OpenLayers/Layer/Markers.js
13 * @requires OpenLayers/Console.js
14 * @requires OpenLayers/Lang.js
15 * @requires OpenLayers/Feature.js
16 * @requires OpenLayers/Layer/EventPane.js
17 * @requires OpenLayers/Layer/FixedZoomLevels.js
18 * @requires OpenLayers/Layer/SphericalMercator.js
19 * @requires OpenLayers/Protocol.js
20 * @requires OpenLayers/Format/JSON.js
21 * @requires OpenLayers/Format/WKT.js
22 * @requires OpenLayers/Format/XML.js
23 * @requires OpenLayers/Geometry.js
24 * @requires OpenLayers/Renderer/Elements.js
25 */
26
27/**
28 * About: Deprecated
29 * The deprecated.js script includes all methods, properties, and constructors
30 * that are not supported as part of the long-term API.  If you use any of
31 * these, you have to explicitly include this script in your application.
32 *
33 * For example:
34 * (code)
35 *     <script src="deprecated.js" type="text/javascript"></script>
36 * (end)
37 *
38 * You are strongly encouraged to avoid using deprecated functionality.  The
39 * documentation here should point you to the supported alternatives.
40 */
41
42/**
43 * Namespace: OpenLayers.Class
44 */
45
46/**
47 * Property: isPrototype
48 * *Deprecated*.  This is no longer needed and will be removed at 3.0.
49 */
50OpenLayers.Class.isPrototype = function () {};
51
52/**
53 * APIFunction: OpenLayers.create
54 * *Deprecated*.  Old method to create an OpenLayers style class.  Use the
55 *     <OpenLayers.Class> constructor instead.
56 *
57 * Returns:
58 * An OpenLayers class
59 */
60OpenLayers.Class.create = function() {
61    return function() {
62        if (arguments && arguments[0] != OpenLayers.Class.isPrototype) {
63            this.initialize.apply(this, arguments);
64        }
65    };
66};
67
68/**
69 * APIFunction: inherit
70 * *Deprecated*.  Old method to inherit from one or more OpenLayers style
71 *     classes.  Use the <OpenLayers.Class> constructor instead.
72 *
73 * Parameters:
74 * class - One or more classes can be provided as arguments
75 *
76 * Returns:
77 * An object prototype
78 */
79OpenLayers.Class.inherit = function (P) {
80    var C = function() {
81       P.call(this);
82    };
83    var newArgs = [C].concat(Array.prototype.slice.call(arguments));
84    OpenLayers.inherit.apply(null, newArgs);
85    return C.prototype;
86};
87
88/**
89 * Namespace: OpenLayers.Util
90 */
91
92/**
93 * Function: clearArray
94 * *Deprecated*. This function will disappear in 3.0.
95 * Please use "array.length = 0" instead.
96 *
97 * Parameters:
98 * array - {Array}
99 */
100OpenLayers.Util.clearArray = function(array) {
101    OpenLayers.Console.warn(
102        OpenLayers.i18n(
103            "methodDeprecated", {'newMethod': 'array = []'}
104        )
105    );
106    array.length = 0;
107};
108
109/**
110 * Function: setOpacity
111 * *Deprecated*.  This function has been deprecated. Instead, please use
112 *     <OpenLayers.Util.modifyDOMElement>
113 *     or
114 *     <OpenLayers.Util.modifyAlphaImageDiv>
115 *
116 * Set the opacity of a DOM Element
117 *     Note that for this function to work in IE, elements must "have layout"
118 *     according to:
119 *     http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/haslayout.asp
120 *
121 * Parameters:
122 * element - {DOMElement} Set the opacity on this DOM element
123 * opacity - {Float} Opacity value (0.0 - 1.0)
124 */
125OpenLayers.Util.setOpacity = function(element, opacity) {
126    OpenLayers.Util.modifyDOMElement(element, null, null, null,
127                                     null, null, null, opacity);
128};
129
130/**
131 * Function: safeStopPropagation
132 * *Deprecated*. This function has been deprecated. Please use directly
133 *     <OpenLayers.Event.stop> passing 'true' as the 2nd
134 *     argument (preventDefault)
135 *
136 * Safely stop the propagation of an event *without* preventing
137 *   the default browser action from occurring.
138 *
139 * Parameters:
140 * evt - {Event}
141 */
142OpenLayers.Util.safeStopPropagation = function(evt) {
143    OpenLayers.Event.stop(evt, true);
144};
145
146/**
147 * Function: getArgs
148 * *Deprecated*.  Will be removed in 3.0.  Please use instead
149 *     <OpenLayers.Util.getParameters>
150 *
151 * Parameters:
152 * url - {String} Optional url used to extract the query string.
153 *                If null, query string is taken from page location.
154 *
155 * Returns:
156 * {Object} An object of key/value pairs from the query string.
157 */
158OpenLayers.Util.getArgs = function(url) {
159    OpenLayers.Console.warn(
160        OpenLayers.i18n(
161            "methodDeprecated", {'newMethod': 'OpenLayers.Util.getParameters'}
162        )
163    );
164    return OpenLayers.Util.getParameters(url);
165};
166
167/**
168 * Namespace: OpenLayers.Ajax
169 */
170
171/**
172 * Function: OpenLayers.nullHandler
173 * @param {} request
174 */
175OpenLayers.nullHandler = function(request) {
176    OpenLayers.Console.userError(OpenLayers.i18n("unhandledRequest", {'statusText':request.statusText}));
177};
178
179/**
180 * APIFunction: OpenLayers.loadURL
181 * Background load a document.
182 * *Deprecated*.  Use <OpenLayers.Request.GET> method instead.
183 *
184 * Parameters:
185 * uri - {String} URI of source doc
186 * params - {String} or {Object} GET params. Either a string in the form
187 *     "?hello=world&foo=bar" (do not forget the leading question mark)
188 *     or an object in the form {'hello': 'world', 'foo': 'bar}
189 * caller - {Object} object which gets callbacks
190 * onComplete - {Function} Optional callback for success.  The callback
191 *     will be called with this set to caller and will receive the request
192 *     object as an argument.  Note that if you do not specify an onComplete
193 *     function, <OpenLayers.nullHandler> will be called (which pops up a
194 *     user friendly error message dialog).
195 * onFailure - {Function} Optional callback for failure.  In the event of
196 *     a failure, the callback will be called with this set to caller and will
197 *     receive the request object as an argument.  Note that if you do not
198 *     specify an onComplete function, <OpenLayers.nullHandler> will be called
199 *     (which pops up a user friendly error message dialog).
200 *
201 * Returns:
202 * {<OpenLayers.Request.XMLHttpRequest>}  The request object. To abort loading,
203 *     call request.abort().
204 */
205OpenLayers.loadURL = function(uri, params, caller,
206                                  onComplete, onFailure) {
207   
208    if(typeof params == 'string') {
209        params = OpenLayers.Util.getParameters(params);
210    }
211    var success = (onComplete) ? onComplete : OpenLayers.nullHandler;
212    var failure = (onFailure) ? onFailure : OpenLayers.nullHandler;
213   
214    return OpenLayers.Request.GET({
215        url: uri, params: params,
216        success: success, failure: failure, scope: caller
217    });
218};
219
220/**
221 * Function: OpenLayers.parseXMLString
222 * Parse XML into a doc structure
223 *
224 * Parameters:
225 * text - {String}
226 *
227 * Returns:
228 * {?} Parsed AJAX Responsev
229 */
230OpenLayers.parseXMLString = function(text) {
231
232    //MS sucks, if the server is bad it dies
233    var index = text.indexOf('<');
234    if (index > 0) {
235        text = text.substring(index);
236    }
237
238    var ajaxResponse = OpenLayers.Util.Try(
239        function() {
240            var xmldom = new ActiveXObject('Microsoft.XMLDOM');
241            xmldom.loadXML(text);
242            return xmldom;
243        },
244        function() {
245            return new DOMParser().parseFromString(text, 'text/xml');
246        },
247        function() {
248            var req = new XMLHttpRequest();
249            req.open("GET", "data:" + "text/xml" +
250                     ";charset=utf-8," + encodeURIComponent(text), false);
251            if (req.overrideMimeType) {
252                req.overrideMimeType("text/xml");
253            }
254            req.send(null);
255            return req.responseXML;
256        }
257    );
258
259    return ajaxResponse;
260};
261
262OpenLayers.Ajax = {
263
264    /**
265     * Method: emptyFunction
266     */
267    emptyFunction: function () {},
268
269    /**
270     * Method: getTransport
271     *
272     * Returns:
273     * {Object} Transport mechanism for whichever browser we're in, or false if
274     *          none available.
275     */
276    getTransport: function() {
277        return OpenLayers.Util.Try(
278            function() {return new XMLHttpRequest();},
279            function() {return new ActiveXObject('Msxml2.XMLHTTP');},
280            function() {return new ActiveXObject('Microsoft.XMLHTTP');}
281        ) || false;
282    },
283
284    /**
285     * Property: activeRequestCount
286     * {Integer}
287     */
288    activeRequestCount: 0
289};
290
291/**
292 * Namespace: OpenLayers.Ajax.Responders
293 * {Object}
294 */
295OpenLayers.Ajax.Responders = {
296 
297    /**
298     * Property: responders
299     * {Array}
300     */
301    responders: [],
302
303    /**
304     * Method: register
305     * 
306     * Parameters:
307     * responderToAdd - {?}
308     */
309    register: function(responderToAdd) {
310        for (var i = 0; i < this.responders.length; i++){
311            if (responderToAdd == this.responders[i]){
312                return;
313            }
314        }
315        this.responders.push(responderToAdd);
316    },
317
318    /**
319     * Method: unregister
320     * 
321     * Parameters:
322     * responderToRemove - {?}
323     */
324    unregister: function(responderToRemove) {
325        OpenLayers.Util.removeItem(this.reponders, responderToRemove);
326    },
327
328    /**
329     * Method: dispatch
330     *
331     * Parameters:
332     * callback - {?}
333     * request - {?}
334     * transport - {?}
335     */
336    dispatch: function(callback, request, transport) {
337        var responder;
338        for (var i = 0; i < this.responders.length; i++) {
339            responder = this.responders[i];
340     
341            if (responder[callback] && 
342                typeof responder[callback] == 'function') {
343                try {
344                    responder[callback].apply(responder, 
345                                              [request, transport]);
346                } catch (e) {}
347            }
348        }
349    }
350};
351
352OpenLayers.Ajax.Responders.register({
353    /**
354     * Function: onCreate
355     */
356    onCreate: function() {
357        OpenLayers.Ajax.activeRequestCount++;
358    },
359
360    /**
361     * Function: onComplete
362     */
363     onComplete: function() {
364         OpenLayers.Ajax.activeRequestCount--;
365     }
366});
367
368/**
369 * Class: OpenLayers.Ajax.Base
370 */
371OpenLayers.Ajax.Base = OpenLayers.Class({
372     
373    /**
374     * Constructor: OpenLayers.Ajax.Base
375     *
376     * Parameters:
377     * options - {Object}
378     */
379    initialize: function(options) {
380        this.options = {
381            method:       'post',
382            asynchronous: true,
383            contentType:  'application/xml',
384            parameters:   ''
385        };
386        OpenLayers.Util.extend(this.options, options || {});
387       
388        this.options.method = this.options.method.toLowerCase();
389       
390        if (typeof this.options.parameters == 'string') {
391            this.options.parameters = 
392                OpenLayers.Util.getParameters(this.options.parameters);
393        }
394    }
395});
396
397/**
398 * Class: OpenLayers.Ajax.Request
399 * *Deprecated*.  Use <OpenLayers.Request> method instead.
400 *
401 * Inherit:
402 *  - <OpenLayers.Ajax.Base>
403 */
404OpenLayers.Ajax.Request = OpenLayers.Class(OpenLayers.Ajax.Base, {
405
406    /**
407     * Property: _complete
408     *
409     * {Boolean}
410     */
411    _complete: false,
412     
413    /**
414     * Constructor: OpenLayers.Ajax.Request
415     *
416     * Parameters:
417     * url - {String}
418     * options - {Object}
419     */
420    initialize: function(url, options) {
421        OpenLayers.Ajax.Base.prototype.initialize.apply(this, [options]);
422       
423        if (OpenLayers.ProxyHost && OpenLayers.String.startsWith(url, "http")) {
424            url = OpenLayers.ProxyHost + encodeURIComponent(url);
425        }
426       
427        this.transport = OpenLayers.Ajax.getTransport();
428        this.request(url);
429    },
430
431    /**
432     * Method: request
433     *
434     * Parameters:
435     * url - {String}
436     */
437    request: function(url) {
438        this.url = url;
439        this.method = this.options.method;
440        var params = OpenLayers.Util.extend({}, this.options.parameters);
441       
442        if (this.method != 'get' && this.method != 'post') {
443            // simulate other verbs over post
444            params['_method'] = this.method;
445            this.method = 'post';
446        }
447
448        this.parameters = params;       
449       
450        if (params = OpenLayers.Util.getParameterString(params)) {
451            // when GET, append parameters to URL
452            if (this.method == 'get') {
453                this.url += ((this.url.indexOf('?') > -1) ? '&' : '?') + params;
454            } else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
455                params += '&_=';
456            }
457        }
458        try {
459            var response = new OpenLayers.Ajax.Response(this);
460            if (this.options.onCreate) {
461                this.options.onCreate(response);
462            }
463           
464            OpenLayers.Ajax.Responders.dispatch('onCreate', 
465                                                this, 
466                                                response);
467   
468            this.transport.open(this.method.toUpperCase(), 
469                                this.url,
470                                this.options.asynchronous);
471   
472            if (this.options.asynchronous) {
473                window.setTimeout(
474                    OpenLayers.Function.bind(this.respondToReadyState, this, 1),
475                    10);
476            }
477           
478            this.transport.onreadystatechange = 
479                OpenLayers.Function.bind(this.onStateChange, this);   
480            this.setRequestHeaders();
481   
482            this.body =  this.method == 'post' ?
483                (this.options.postBody || params) : null;
484            this.transport.send(this.body);
485   
486            // Force Firefox to handle ready state 4 for synchronous requests
487            if (!this.options.asynchronous && 
488                this.transport.overrideMimeType) {
489                this.onStateChange();
490            }
491        } catch (e) {
492            this.dispatchException(e);
493        }
494    },
495
496    /**
497     * Method: onStateChange
498     */
499    onStateChange: function() {
500        var readyState = this.transport.readyState;
501        if (readyState > 1 && !((readyState == 4) && this._complete)) {
502            this.respondToReadyState(this.transport.readyState);
503        }
504    },
505     
506    /**
507     * Method: setRequestHeaders
508     */
509    setRequestHeaders: function() {
510        var headers = {
511            'X-Requested-With': 'XMLHttpRequest',
512            'Accept': 'text/javascript, text/html, application/xml, text/xml, */*',
513            'OpenLayers': true
514        };
515
516        if (this.method == 'post') {
517            headers['Content-type'] = this.options.contentType +
518                (this.options.encoding ? '; charset=' + this.options.encoding : '');
519   
520            /* Force "Connection: close" for older Mozilla browsers to work
521             * around a bug where XMLHttpRequest sends an incorrect
522             * Content-length header. See Mozilla Bugzilla #246651.
523             */
524            if (this.transport.overrideMimeType &&
525                (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) {
526                headers['Connection'] = 'close';
527            }
528        }
529        // user-defined headers
530        if (typeof this.options.requestHeaders == 'object') {   
531            var extras = this.options.requestHeaders;
532           
533            if (typeof extras.push == 'function') {
534                for (var i = 0, length = extras.length; i < length; i += 2) {
535                    headers[extras[i]] = extras[i+1];
536                }
537            } else {
538                for (var i in extras) {
539                    headers[i] = extras[i];
540                }
541            }
542        }
543       
544        for (var name in headers) {
545            this.transport.setRequestHeader(name, headers[name]);
546        }
547    },
548   
549    /**
550     * Method: success
551     *
552     * Returns:
553     * {Boolean} -
554     */
555    success: function() {
556        var status = this.getStatus();
557        return !status || (status >=200 && status < 300);
558    },
559   
560    /**
561     * Method: getStatus
562     *
563     * Returns:
564     * {Integer} - Status
565     */
566    getStatus: function() {
567        try {
568            return this.transport.status || 0;
569        } catch (e) {
570            return 0;
571        }
572    },
573
574    /**
575     * Method: respondToReadyState
576     *
577     * Parameters:
578     * readyState - {?}
579     */
580    respondToReadyState: function(readyState) {
581        var state = OpenLayers.Ajax.Request.Events[readyState];
582        var response = new OpenLayers.Ajax.Response(this);
583   
584        if (state == 'Complete') {
585            try {
586                this._complete = true;
587                (this.options['on' + response.status] ||
588                    this.options['on' + (this.success() ? 'Success' : 'Failure')] ||
589                    OpenLayers.Ajax.emptyFunction)(response);
590            } catch (e) {
591                this.dispatchException(e);
592            }
593   
594            var contentType = response.getHeader('Content-type');
595        }
596   
597        try {
598            (this.options['on' + state] || 
599             OpenLayers.Ajax.emptyFunction)(response);
600             OpenLayers.Ajax.Responders.dispatch('on' + state, 
601                                                 this, 
602                                                 response);
603        } catch (e) {
604            this.dispatchException(e);
605        }
606   
607        if (state == 'Complete') {
608            // avoid memory leak in MSIE: clean up
609            this.transport.onreadystatechange = OpenLayers.Ajax.emptyFunction;
610        }
611    },
612   
613    /**
614     * Method: getHeader
615     *
616     * Parameters:
617     * name - {String} Header name
618     *
619     * Returns:
620     * {?} - response header for the given name
621     */
622    getHeader: function(name) {
623        try {
624            return this.transport.getResponseHeader(name);
625        } catch (e) {
626            return null;
627        }
628    },
629
630    /**
631     * Method: dispatchException
632     * If the optional onException function is set, execute it
633     * and then dispatch the call to any other listener registered
634     * for onException.
635     *
636     * If no optional onException function is set, we suspect that
637     * the user may have also not used
638     * OpenLayers.Ajax.Responders.register to register a listener
639     * for the onException call.  To make sure that something
640     * gets done with this exception, only dispatch the call if there
641     * are listeners.
642     *
643     * If you explicitly want to swallow exceptions, set
644     * request.options.onException to an empty function (function(){})
645     * or register an empty function with <OpenLayers.Ajax.Responders>
646     * for onException.
647     *
648     * Parameters:
649     * exception - {?}
650     */
651    dispatchException: function(exception) {
652        var handler = this.options.onException;
653        if(handler) {
654            // call options.onException and alert any other listeners
655            handler(this, exception);
656            OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
657        } else {
658            // check if there are any other listeners
659            var listener = false;
660            var responders = OpenLayers.Ajax.Responders.responders;
661            for (var i = 0; i < responders.length; i++) {
662                if(responders[i].onException) {
663                    listener = true;
664                    break;
665                }
666            }
667            if(listener) {
668                // call all listeners
669                OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
670            } else {
671                // let the exception through
672                throw exception;
673            }
674        }
675    }
676});
677
678/**
679 * Property: Events
680 * {Array(String)}
681 */
682OpenLayers.Ajax.Request.Events =
683  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
684
685/**
686 * Class: OpenLayers.Ajax.Response
687 */
688OpenLayers.Ajax.Response = OpenLayers.Class({
689
690    /**
691     * Property: status
692     *
693     * {Integer}
694     */
695    status: 0,
696   
697
698    /**
699     * Property: statusText
700     *
701     * {String}
702     */
703    statusText: '',
704     
705    /**
706     * Constructor: OpenLayers.Ajax.Response
707     *
708     * Parameters:
709     * request - {Object}
710     */
711    initialize: function(request) {
712        this.request = request;
713        var transport = this.transport = request.transport,
714            readyState = this.readyState = transport.readyState;
715       
716        if ((readyState > 2 &&
717            !(!!(window.attachEvent && !window.opera))) ||
718            readyState == 4) {
719            this.status       = this.getStatus();
720            this.statusText   = this.getStatusText();
721            this.responseText = transport.responseText == null ?
722                '' : String(transport.responseText);
723        }
724       
725        if(readyState == 4) {
726            var xml = transport.responseXML;
727            this.responseXML  = xml === undefined ? null : xml;
728        }
729    },
730   
731    /**
732     * Method: getStatus
733     */
734    getStatus: OpenLayers.Ajax.Request.prototype.getStatus,
735   
736    /**
737     * Method: getStatustext
738     *
739     * Returns:
740     * {String} - statusText
741     */
742    getStatusText: function() {
743        try {
744            return this.transport.statusText || '';
745        } catch (e) {
746            return '';
747        }
748    },
749   
750    /**
751     * Method: getHeader
752     */
753    getHeader: OpenLayers.Ajax.Request.prototype.getHeader,
754   
755    /**
756     * Method: getResponseHeader
757     *
758     * Returns:
759     * {?} - response header for given name
760     */
761    getResponseHeader: function(name) {
762        return this.transport.getResponseHeader(name);
763    }
764});
765
766
767/**
768 * Function: getElementsByTagNameNS
769 *
770 * Parameters:
771 * parentnode - {?}
772 * nsuri - {?}
773 * nsprefix - {?}
774 * tagname - {?}
775 *
776 * Returns:
777 * {?}
778 */
779OpenLayers.Ajax.getElementsByTagNameNS  = function(parentnode, nsuri, 
780                                                   nsprefix, tagname) {
781    var elem = null;
782    if (parentnode.getElementsByTagNameNS) {
783        elem = parentnode.getElementsByTagNameNS(nsuri, tagname);
784    } else {
785        elem = parentnode.getElementsByTagName(nsprefix + ':' + tagname);
786    }
787    return elem;
788};
789
790
791/**
792 * Function: serializeXMLToString
793 * Wrapper function around XMLSerializer, which doesn't exist/work in
794 *     IE/Safari. We need to come up with a way to serialize in those browser:
795 *     for now, these browsers will just fail. #535, #536
796 *
797 * Parameters:
798 * xmldom {XMLNode} xml dom to serialize
799 *
800 * Returns:
801 * {?}
802 */
803OpenLayers.Ajax.serializeXMLToString = function(xmldom) {
804    var serializer = new XMLSerializer();
805    var data = serializer.serializeToString(xmldom);
806    return data;
807};
808
809/**
810 * Namespace: OpenLayers.Element
811 */
812OpenLayers.Util.extend(OpenLayers.Element, {
813
814    /**
815     * APIFunction: hide
816     * *Deprecated*. Hide element(s) passed in
817     *
818     * Parameters:
819     * element - {DOMElement} Actually user can pass any number of elements
820     */
821    hide: function() {
822        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
823            newMethod: "element.style.display = 'none';"
824        }));
825
826        for (var i=0, len=arguments.length; i<len; i++) {
827            var element = OpenLayers.Util.getElement(arguments[i]);
828            if (element) {
829                element.style.display = 'none';
830            }
831        }
832    },
833
834    /**
835     * APIFunction: show
836     * *Deprecated*. Show element(s) passed in
837     *
838     * Parameters:
839     * element - {DOMElement} Actually user can pass any number of elements
840     */
841    show: function() {
842        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
843            newMethod: "element.style.display = '';"
844        }));
845
846        for (var i=0, len=arguments.length; i<len; i++) {
847            var element = OpenLayers.Util.getElement(arguments[i]);
848            if (element) {
849                element.style.display = '';
850            }
851        }
852    },
853
854    /**
855     * APIFunction: getDimensions
856     * *Deprecated*. Returns dimensions of the element passed in.
857     * 
858     * Parameters:
859     * element - {DOMElement}
860     *
861     * Returns:
862     * {Object} Object with 'width' and 'height' properties which are the
863     *          dimensions of the element passed in.
864     */
865    getDimensions: function(element) {
866        element = OpenLayers.Util.getElement(element);
867        if (OpenLayers.Element.getStyle(element, 'display') != 'none') {
868            return {width: element.offsetWidth, height: element.offsetHeight};
869        }
870   
871        // All *Width and *Height properties give 0 on elements with display none,
872        // so enable the element temporarily
873        var els = element.style;
874        var originalVisibility = els.visibility;
875        var originalPosition = els.position;
876        var originalDisplay = els.display;
877        els.visibility = 'hidden';
878        els.position = 'absolute';
879        els.display = '';
880        var originalWidth = element.clientWidth;
881        var originalHeight = element.clientHeight;
882        els.display = originalDisplay;
883        els.position = originalPosition;
884        els.visibility = originalVisibility;
885        return {width: originalWidth, height: originalHeight};
886    }
887   
888});
889
890if (!String.prototype.startsWith) {
891    /**
892     * APIMethod: String.startsWith
893     * *Deprecated*. Whether or not a string starts with another string.
894     *
895     * Parameters:
896     * sStart - {String} The string we're testing for.
897     * 
898     * Returns:
899     * {Boolean} Whether or not this string starts with the string passed in.
900     */
901    String.prototype.startsWith = function(sStart) {
902        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
903                                {'newMethod':'OpenLayers.String.startsWith'}));
904        return OpenLayers.String.startsWith(this, sStart);
905    };
906}
907
908if (!String.prototype.contains) {
909    /**
910     * APIMethod: String.contains
911     * *Deprecated*. Whether or not a string contains another string.
912     *
913     * Parameters:
914     * str - {String} The string that we're testing for.
915     *
916     * Returns:
917     * {Boolean} Whether or not this string contains with the string passed in.
918     */
919    String.prototype.contains = function(str) {
920        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
921                                  {'newMethod':'OpenLayers.String.contains'}));
922        return OpenLayers.String.contains(this, str);
923    };
924}
925
926if (!String.prototype.trim) {
927    /**
928     * APIMethod: String.trim
929     * *Deprecated*. Removes leading and trailing whitespace characters from a string.
930     *
931     * Returns:
932     * {String} A trimmed version of the string - all leading and
933     *          trailing spaces removed
934     */
935    String.prototype.trim = function() {
936        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
937                                      {'newMethod':'OpenLayers.String.trim'}));
938        return OpenLayers.String.trim(this);
939    };
940}
941
942if (!String.prototype.camelize) {
943    /**
944     * APIMethod: String.camelize
945     * *Deprecated*. Camel-case a hyphenated string.
946     *     Ex. "chicken-head" becomes "chickenHead", and
947     *     "-chicken-head" becomes "ChickenHead".
948     *
949     * Returns:
950     * {String} The string, camelized
951     */
952    String.prototype.camelize = function() {
953        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
954                                  {'newMethod':'OpenLayers.String.camelize'}));
955        return OpenLayers.String.camelize(this);
956    };
957}
958
959if (!Function.prototype.bind) {
960    /**
961     * APIMethod: Function.bind
962     * *Deprecated*. Bind a function to an object.
963     * Method to easily create closures with 'this' altered.
964     *
965     * Parameters:
966     * object - {Object} the this parameter
967     *
968     * Returns:
969     * {Function} A closure with 'this' altered to the first
970     *            argument.
971     */
972    Function.prototype.bind = function() {
973        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
974                                {'newMethod':'OpenLayers.Function.bind'}));
975        // new function takes the same arguments with this function up front
976        Array.prototype.unshift.apply(arguments, [this]);
977        return OpenLayers.Function.bind.apply(null, arguments);
978    };
979}
980
981if (!Function.prototype.bindAsEventListener) {
982    /**
983     * APIMethod: Function.bindAsEventListener
984     * *Deprecated*. Bind a function to an object, and configure it to receive the
985     *     event object as first parameter when called.
986     *
987     * Parameters:
988     * object - {Object} A reference to this.
989     *
990     * Returns:
991     * {Function}
992     */
993    Function.prototype.bindAsEventListener = function(object) {
994        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
995                        {'newMethod':'OpenLayers.Function.bindAsEventListener'}));
996        return OpenLayers.Function.bindAsEventListener(this, object);
997    };
998}
999
1000// FIXME: Remove this in 3.0. In 3.0, Event.stop will no longer be provided
1001// by OpenLayers.
1002if (window.Event) {
1003    OpenLayers.Util.applyDefaults(window.Event, OpenLayers.Event);
1004} else {
1005    var Event = OpenLayers.Event;
1006}
1007
1008/**
1009 * Namespace: OpenLayers.Tile
1010 */
1011OpenLayers.Util.extend(OpenLayers.Tile.prototype, {
1012    /**   
1013     * Method: getBoundsFromBaseLayer
1014     * Take the pixel locations of the corner of the tile, and pass them to
1015     *     the base layer and ask for the location of those pixels, so that
1016     *     displaying tiles over Google works fine.
1017     *
1018     * Parameters:
1019     * position - {<OpenLayers.Pixel>}
1020     *
1021     * Returns:
1022     * bounds - {<OpenLayers.Bounds>}
1023     */
1024    getBoundsFromBaseLayer: function(position) {
1025        var msg = OpenLayers.i18n('reprojectDeprecated',
1026                                              {'layerName':this.layer.name});
1027        OpenLayers.Console.warn(msg);
1028        var topLeft = this.layer.map.getLonLatFromLayerPx(position); 
1029        var bottomRightPx = position.clone();
1030        bottomRightPx.x += this.size.w;
1031        bottomRightPx.y += this.size.h;
1032        var bottomRight = this.layer.map.getLonLatFromLayerPx(bottomRightPx); 
1033        // Handle the case where the base layer wraps around the date line.
1034        // Google does this, and it breaks WMS servers to request bounds in
1035        // that fashion. 
1036        if (topLeft.lon > bottomRight.lon) {
1037            if (topLeft.lon < 0) {
1038                topLeft.lon = -180 - (topLeft.lon+180);
1039            } else {
1040                bottomRight.lon = 180+bottomRight.lon+180;
1041            }       
1042        }
1043        var bounds = new OpenLayers.Bounds(topLeft.lon, 
1044                                       bottomRight.lat, 
1045                                       bottomRight.lon, 
1046                                       topLeft.lat); 
1047        return bounds;
1048    }   
1049});
1050
1051/**
1052 * Class: OpenLayers.Control.MouseDefaults
1053 * This class is DEPRECATED in 2.4 and will be removed by 3.0.
1054 * If you need this functionality, use <OpenLayers.Control.Navigation>
1055 * instead!!!
1056 *
1057 * Inherits from:
1058 *  - <OpenLayers.Control>
1059 */
1060OpenLayers.Control.MouseDefaults = OpenLayers.Class(OpenLayers.Control, {
1061
1062    /** WARNING WARNING WARNING!!!
1063        This class is DEPRECATED in 2.4 and will be removed by 3.0.
1064        If you need this functionality, use Control.Navigation instead!!! */
1065
1066    /**
1067     * Property: performedDrag
1068     * {Boolean}
1069     */
1070    performedDrag: false,
1071
1072    /**
1073     * Property: wheelObserver
1074     * {Function}
1075     */
1076    wheelObserver: null,
1077
1078    /**
1079     * Constructor: OpenLayers.Control.MouseDefaults
1080     */
1081    initialize: function() {
1082        OpenLayers.Control.prototype.initialize.apply(this, arguments);
1083    },
1084
1085    /**
1086     * APIMethod: destroy
1087     */   
1088    destroy: function() {
1089       
1090        if (this.handler) {
1091            this.handler.destroy();
1092        }
1093        this.handler = null;
1094
1095        this.map.events.un({
1096            "click": this.defaultClick,
1097            "dblclick": this.defaultDblClick,
1098            "mousedown": this.defaultMouseDown,
1099            "mouseup": this.defaultMouseUp,
1100            "mousemove": this.defaultMouseMove,
1101            "mouseout": this.defaultMouseOut,
1102            scope: this
1103        });
1104
1105        //unregister mousewheel events specifically on the window and document
1106        OpenLayers.Event.stopObserving(window, "DOMMouseScroll", 
1107                                        this.wheelObserver);
1108        OpenLayers.Event.stopObserving(window, "mousewheel", 
1109                                        this.wheelObserver);
1110        OpenLayers.Event.stopObserving(document, "mousewheel", 
1111                                        this.wheelObserver);
1112        this.wheelObserver = null;
1113                     
1114        OpenLayers.Control.prototype.destroy.apply(this, arguments);       
1115    },
1116
1117    /**
1118     * Method: draw
1119     */
1120    draw: function() {
1121        this.map.events.on({
1122            "click": this.defaultClick,
1123            "dblclick": this.defaultDblClick,
1124            "mousedown": this.defaultMouseDown,
1125            "mouseup": this.defaultMouseUp,
1126            "mousemove": this.defaultMouseMove,
1127            "mouseout": this.defaultMouseOut,
1128            scope: this
1129        });
1130
1131        this.registerWheelEvents();
1132
1133    },
1134
1135    /**
1136     * Method: registerWheelEvents
1137     */
1138    registerWheelEvents: function() {
1139
1140        this.wheelObserver = OpenLayers.Function.bindAsEventListener(
1141            this.onWheelEvent, this
1142        );
1143       
1144        //register mousewheel events specifically on the window and document
1145        OpenLayers.Event.observe(window, "DOMMouseScroll", this.wheelObserver);
1146        OpenLayers.Event.observe(window, "mousewheel", this.wheelObserver);
1147        OpenLayers.Event.observe(document, "mousewheel", this.wheelObserver);
1148    },
1149
1150    /**
1151     * Method: defaultClick
1152     *
1153     * Parameters:
1154     * evt - {Event}
1155     *
1156     * Returns:
1157     * {Boolean}
1158     */
1159    defaultClick: function (evt) {
1160        if (!OpenLayers.Event.isLeftClick(evt)) {
1161            return;
1162        }
1163        var notAfterDrag = !this.performedDrag;
1164        this.performedDrag = false;
1165        return notAfterDrag;
1166    },
1167
1168    /**
1169     * Method: defaultDblClick
1170     *
1171     * Parameters:
1172     * evt - {Event}
1173     */
1174    defaultDblClick: function (evt) {
1175        var newCenter = this.map.getLonLatFromViewPortPx( evt.xy ); 
1176        this.map.setCenter(newCenter, this.map.zoom + 1);
1177        OpenLayers.Event.stop(evt);
1178        return false;
1179    },
1180
1181    /**
1182     * Method: defaultMouseDown
1183     *
1184     * Parameters:
1185     * evt - {Event}
1186     */
1187    defaultMouseDown: function (evt) {
1188        if (!OpenLayers.Event.isLeftClick(evt)) {
1189            return;
1190        }
1191        this.mouseDragStart = evt.xy.clone();
1192        this.performedDrag  = false;
1193        if (evt.shiftKey) {
1194            this.map.div.style.cursor = "crosshair";
1195            this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
1196                                                     this.mouseDragStart,
1197                                                     null,
1198                                                     null,
1199                                                     "absolute",
1200                                                     "2px solid red");
1201            this.zoomBox.style.backgroundColor = "white";
1202            this.zoomBox.style.filter = "alpha(opacity=50)"; // IE
1203            this.zoomBox.style.opacity = "0.50";
1204            this.zoomBox.style.fontSize = "1px";
1205            this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
1206            this.map.viewPortDiv.appendChild(this.zoomBox);
1207        }
1208        document.onselectstart = OpenLayers.Function.False;
1209        OpenLayers.Event.stop(evt);
1210    },
1211
1212    /**
1213     * Method: defaultMouseMove
1214     *
1215     * Parameters:
1216     * evt - {Event}
1217     */
1218    defaultMouseMove: function (evt) {
1219        // record the mouse position, used in onWheelEvent
1220        this.mousePosition = evt.xy.clone();
1221
1222        if (this.mouseDragStart != null) {
1223            if (this.zoomBox) {
1224                var deltaX = Math.abs(this.mouseDragStart.x - evt.xy.x);
1225                var deltaY = Math.abs(this.mouseDragStart.y - evt.xy.y);
1226                this.zoomBox.style.width = Math.max(1, deltaX) + "px";
1227                this.zoomBox.style.height = Math.max(1, deltaY) + "px";
1228                if (evt.xy.x < this.mouseDragStart.x) {
1229                    this.zoomBox.style.left = evt.xy.x+"px";
1230                }
1231                if (evt.xy.y < this.mouseDragStart.y) {
1232                    this.zoomBox.style.top = evt.xy.y+"px";
1233                }
1234            } else {
1235                var deltaX = this.mouseDragStart.x - evt.xy.x;
1236                var deltaY = this.mouseDragStart.y - evt.xy.y;
1237                var size = this.map.getSize();
1238                var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX,
1239                                                 size.h / 2 + deltaY);
1240                var newCenter = this.map.getLonLatFromViewPortPx( newXY ); 
1241                this.map.setCenter(newCenter, null, true);
1242                this.mouseDragStart = evt.xy.clone();
1243                this.map.div.style.cursor = "move";
1244            }
1245            this.performedDrag = true;
1246        }
1247    },
1248
1249    /**
1250     * Method: defaultMouseUp
1251     *
1252     * Parameters:
1253     * evt - {<OpenLayers.Event>}
1254     */
1255    defaultMouseUp: function (evt) {
1256        if (!OpenLayers.Event.isLeftClick(evt)) {
1257            return;
1258        }
1259        if (this.zoomBox) {
1260            this.zoomBoxEnd(evt);   
1261        } else {
1262            if (this.performedDrag) {
1263                this.map.setCenter(this.map.center);
1264            }
1265        }
1266        document.onselectstart=null;
1267        this.mouseDragStart = null;
1268        this.map.div.style.cursor = "";
1269    },
1270
1271    /**
1272     * Method: defaultMouseOut
1273     *
1274     * Parameters:
1275     * evt - {Event}
1276     */
1277    defaultMouseOut: function (evt) {
1278        if (this.mouseDragStart != null && 
1279            OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {
1280            if (this.zoomBox) {
1281                this.removeZoomBox();
1282            }
1283            this.mouseDragStart = null;
1284        }
1285    },
1286
1287
1288    /**
1289     * Method: defaultWheelUp
1290     * User spun scroll wheel up
1291     *
1292     */
1293    defaultWheelUp: function(evt) {
1294        if (this.map.getZoom() <= this.map.getNumZoomLevels()) {
1295            this.map.setCenter(this.map.getLonLatFromPixel(evt.xy),
1296                               this.map.getZoom() + 1);
1297        }
1298    },
1299
1300    /**
1301     * Method: defaultWheelDown
1302     * User spun scroll wheel down
1303     */
1304    defaultWheelDown: function(evt) {
1305        if (this.map.getZoom() > 0) {
1306            this.map.setCenter(this.map.getLonLatFromPixel(evt.xy),
1307                               this.map.getZoom() - 1);
1308        }
1309    },
1310
1311    /**
1312     * Method: zoomBoxEnd
1313     * Zoombox function.
1314     */
1315    zoomBoxEnd: function(evt) {
1316        if (this.mouseDragStart != null) {
1317            if (Math.abs(this.mouseDragStart.x - evt.xy.x) > 5 ||   
1318                Math.abs(this.mouseDragStart.y - evt.xy.y) > 5) {   
1319                var start = this.map.getLonLatFromViewPortPx( this.mouseDragStart ); 
1320                var end = this.map.getLonLatFromViewPortPx( evt.xy );
1321                var top = Math.max(start.lat, end.lat);
1322                var bottom = Math.min(start.lat, end.lat);
1323                var left = Math.min(start.lon, end.lon);
1324                var right = Math.max(start.lon, end.lon);
1325                var bounds = new OpenLayers.Bounds(left, bottom, right, top);
1326                this.map.zoomToExtent(bounds);
1327            } else {
1328                var end = this.map.getLonLatFromViewPortPx( evt.xy );
1329                this.map.setCenter(new OpenLayers.LonLat(
1330                  (end.lon),
1331                  (end.lat)
1332                 ), this.map.getZoom() + 1);
1333            }   
1334            this.removeZoomBox();
1335       }
1336    },
1337
1338    /**
1339     * Method: removeZoomBox
1340     * Remove the zoombox from the screen and nullify our reference to it.
1341     */
1342    removeZoomBox: function() {
1343        this.map.viewPortDiv.removeChild(this.zoomBox);
1344        this.zoomBox = null;
1345    },
1346
1347
1348/**
1349 *  Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
1350 */
1351
1352
1353    /**
1354     * Method: onWheelEvent
1355     * Catch the wheel event and handle it xbrowserly
1356     *
1357     * Parameters:
1358     * e - {Event}
1359     */
1360    onWheelEvent: function(e){
1361   
1362        // first determine whether or not the wheeling was inside the map
1363        var inMap = false;
1364        var elem = OpenLayers.Event.element(e);
1365        while(elem != null) {
1366            if (this.map && elem == this.map.div) {
1367                inMap = true;
1368                break;
1369            }
1370            elem = elem.parentNode;
1371        }
1372       
1373        if (inMap) {
1374           
1375            var delta = 0;
1376            if (!e) {
1377                e = window.event;
1378            }
1379            if (e.wheelDelta) {
1380                delta = e.wheelDelta/120; 
1381                if (window.opera && window.opera.version() < 9.2) {
1382                    delta = -delta;
1383                }
1384            } else if (e.detail) {
1385                delta = -e.detail / 3;
1386            }
1387            if (delta) {
1388                // add the mouse position to the event because mozilla has a bug
1389                // with clientX and clientY (see https://bugzilla.mozilla.org/show_bug.cgi?id=352179)
1390                // getLonLatFromViewPortPx(e) returns wrong values
1391                e.xy = this.mousePosition;
1392
1393                if (delta < 0) {
1394                   this.defaultWheelDown(e);
1395                } else {
1396                   this.defaultWheelUp(e);
1397                }
1398            }
1399           
1400            //only wheel the map, not the window
1401            OpenLayers.Event.stop(e);
1402        }
1403    },
1404
1405    CLASS_NAME: "OpenLayers.Control.MouseDefaults"
1406});
1407
1408/**
1409 * Class: OpenLayers.Control.MouseToolbar
1410 * This class is DEPRECATED in 2.4 and will be removed by 3.0.
1411 * If you need this functionality, use <OpenLayers.Control.NavToolbar>
1412 * instead!!!
1413 */
1414OpenLayers.Control.MouseToolbar = OpenLayers.Class(
1415                                            OpenLayers.Control.MouseDefaults, {
1416   
1417    /**
1418     * Property: mode
1419     */ 
1420    mode: null,
1421    /**
1422     * Property: buttons
1423     */
1424    buttons: null,
1425   
1426    /**
1427     * APIProperty: direction
1428     * {String} 'vertical' or 'horizontal'
1429     */
1430    direction: "vertical",
1431   
1432    /**
1433     * Property: buttonClicked
1434     * {String}
1435     */
1436    buttonClicked: null,
1437   
1438    /**
1439     * Constructor: OpenLayers.Control.MouseToolbar
1440     *
1441     * Parameters:
1442     * position - {<OpenLayers.Pixel>}
1443     * direction - {String}
1444     */
1445    initialize: function(position, direction) {
1446        OpenLayers.Control.prototype.initialize.apply(this, arguments);
1447        this.position = new OpenLayers.Pixel(OpenLayers.Control.MouseToolbar.X,
1448                                             OpenLayers.Control.MouseToolbar.Y);
1449        if (position) {
1450            this.position = position;
1451        }
1452        if (direction) {
1453            this.direction = direction; 
1454        }
1455        this.measureDivs = [];
1456    },
1457   
1458    /**
1459     * APIMethod: destroy
1460     */
1461    destroy: function() {
1462        for( var btnId in this.buttons) {
1463            var btn = this.buttons[btnId];
1464            btn.map = null;
1465            btn.events.destroy();
1466        }
1467        OpenLayers.Control.MouseDefaults.prototype.destroy.apply(this, 
1468                                                                 arguments);
1469    },
1470   
1471    /**
1472     * Method: draw
1473     */
1474    draw: function() {
1475        OpenLayers.Control.prototype.draw.apply(this, arguments); 
1476        OpenLayers.Control.MouseDefaults.prototype.draw.apply(this, arguments);
1477        this.buttons = {};
1478        var sz = new OpenLayers.Size(28,28);
1479        var centered = new OpenLayers.Pixel(OpenLayers.Control.MouseToolbar.X,0);
1480        this._addButton("zoombox", "drag-rectangle-off.png", "drag-rectangle-on.png", centered, sz, "Shift->Drag to zoom to area");
1481        centered = centered.add((this.direction == "vertical" ? 0 : sz.w), (this.direction == "vertical" ? sz.h : 0));
1482        this._addButton("pan", "panning-hand-off.png", "panning-hand-on.png", centered, sz, "Drag the map to pan.");
1483        centered = centered.add((this.direction == "vertical" ? 0 : sz.w), (this.direction == "vertical" ? sz.h : 0));
1484        this.switchModeTo("pan");
1485
1486        return this.div;
1487    },
1488   
1489    /**
1490     * Method: _addButton
1491     */
1492    _addButton:function(id, img, activeImg, xy, sz, title) {
1493        var imgLocation = OpenLayers.Util.getImageLocation(img);
1494        var activeImgLocation = OpenLayers.Util.getImageLocation(activeImg);
1495        // var btn = new ol.AlphaImage("_"+id, imgLocation, xy, sz);
1496        var btn = OpenLayers.Util.createAlphaImageDiv(
1497                                    "OpenLayers_Control_MouseToolbar_" + id, 
1498                                    xy, sz, imgLocation, "absolute");
1499
1500        //we want to add the outer div
1501        this.div.appendChild(btn);
1502        btn.imgLocation = imgLocation;
1503        btn.activeImgLocation = activeImgLocation;
1504       
1505        btn.events = new OpenLayers.Events(this, btn, null, true);
1506        btn.events.on({
1507            "mousedown": this.buttonDown,
1508            "mouseup": this.buttonUp,
1509            "dblclick": OpenLayers.Event.stop,
1510            scope: this
1511        });
1512        btn.action = id;
1513        btn.title = title;
1514        btn.alt = title;
1515        btn.map = this.map;
1516
1517        //we want to remember/reference the outer div
1518        this.buttons[id] = btn;
1519        return btn;
1520    },
1521
1522    /**
1523     * Method: buttonDown
1524     *
1525     * Parameters:
1526     * evt - {Event}
1527     */
1528    buttonDown: function(evt) {
1529        if (!OpenLayers.Event.isLeftClick(evt)) {
1530            return;
1531        }
1532        this.buttonClicked = evt.element.action;
1533        OpenLayers.Event.stop(evt);
1534    },
1535
1536    /**
1537     * Method: buttonUp
1538     *
1539     * Parameters:
1540     * evt - {Event}
1541     */
1542    buttonUp: function(evt) {
1543        if (!OpenLayers.Event.isLeftClick(evt)) {
1544            return;
1545        }
1546        if (this.buttonClicked != null) {
1547            if (this.buttonClicked == evt.element.action) {
1548                this.switchModeTo(evt.element.action);
1549            }
1550            OpenLayers.Event.stop(evt);
1551            this.buttonClicked = null;
1552        }
1553    },
1554   
1555    /**
1556     * Method: defaultDblClick
1557     *
1558     * Parameters:
1559     * evt - {Event}
1560     */
1561    defaultDblClick: function (evt) {
1562        this.switchModeTo("pan");
1563        this.performedDrag = false;
1564        var newCenter = this.map.getLonLatFromViewPortPx( evt.xy ); 
1565        this.map.setCenter(newCenter, this.map.zoom + 1);
1566        OpenLayers.Event.stop(evt);
1567        return false;
1568    },
1569
1570    /**
1571     * Method: defaultMouseDown
1572     *
1573     * Parameters:
1574     * evt - {Event}
1575     */
1576    defaultMouseDown: function (evt) {
1577        if (!OpenLayers.Event.isLeftClick(evt)) {
1578            return;
1579        }
1580        this.mouseDragStart = evt.xy.clone();
1581        this.performedDrag = false;
1582        this.startViaKeyboard = false;
1583        if (evt.shiftKey && this.mode !="zoombox") {
1584            this.switchModeTo("zoombox");
1585            this.startViaKeyboard = true;
1586        } else if (evt.altKey && this.mode !="measure") {
1587            this.switchModeTo("measure");
1588        } else if (!this.mode) {
1589            this.switchModeTo("pan");
1590        }
1591       
1592        switch (this.mode) {
1593            case "zoombox":
1594                this.map.div.style.cursor = "crosshair";
1595                this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
1596                                                         this.mouseDragStart,
1597                                                         null,
1598                                                         null,
1599                                                         "absolute",
1600                                                         "2px solid red");
1601                this.zoomBox.style.backgroundColor = "white";
1602                this.zoomBox.style.filter = "alpha(opacity=50)"; // IE
1603                this.zoomBox.style.opacity = "0.50";
1604                this.zoomBox.style.fontSize = "1px";
1605                this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
1606                this.map.viewPortDiv.appendChild(this.zoomBox);
1607                this.performedDrag = true;
1608                break;
1609            case "measure":
1610                var distance = "";
1611                if (this.measureStart) {
1612                    var measureEnd = this.map.getLonLatFromViewPortPx(this.mouseDragStart);
1613                    distance = OpenLayers.Util.distVincenty(this.measureStart, measureEnd);
1614                    distance = Math.round(distance * 100) / 100;
1615                    distance = distance + "km";
1616                    this.measureStartBox = this.measureBox;
1617                }   
1618                this.measureStart = this.map.getLonLatFromViewPortPx(this.mouseDragStart);;
1619                this.measureBox = OpenLayers.Util.createDiv(null,
1620                                                         this.mouseDragStart.add(
1621                                                           -2-parseInt(this.map.layerContainerDiv.style.left),
1622                                                           -2-parseInt(this.map.layerContainerDiv.style.top)),
1623                                                         null,
1624                                                         null,
1625                                                         "absolute");
1626                this.measureBox.style.width="4px";
1627                this.measureBox.style.height="4px";
1628                this.measureBox.style.fontSize = "1px";
1629                this.measureBox.style.backgroundColor="red";
1630                this.measureBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
1631                this.map.layerContainerDiv.appendChild(this.measureBox);
1632                if (distance) {
1633                    this.measureBoxDistance = OpenLayers.Util.createDiv(null,
1634                                                         this.mouseDragStart.add(
1635                                                           -2-parseInt(this.map.layerContainerDiv.style.left),
1636                                                           2-parseInt(this.map.layerContainerDiv.style.top)),
1637                                                         null,
1638                                                         null,
1639                                                         "absolute");
1640                   
1641                    this.measureBoxDistance.innerHTML = distance;
1642                    this.measureBoxDistance.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
1643                    this.map.layerContainerDiv.appendChild(this.measureBoxDistance);
1644                    this.measureDivs.push(this.measureBoxDistance);
1645                }
1646                this.measureBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
1647                this.map.layerContainerDiv.appendChild(this.measureBox);
1648                this.measureDivs.push(this.measureBox);
1649                break;
1650            default:
1651                this.map.div.style.cursor = "move";
1652                break;
1653        }
1654        document.onselectstart = OpenLayers.Function.False;
1655        OpenLayers.Event.stop(evt);
1656    },
1657
1658    /**
1659     * Method: switchModeTo
1660     *
1661     * Parameters:
1662     * mode - {String}
1663     */
1664    switchModeTo: function(mode) {
1665        if (mode != this.mode) {
1666           
1667
1668            if (this.mode && this.buttons[this.mode]) {
1669                OpenLayers.Util.modifyAlphaImageDiv(this.buttons[this.mode], null, null, null, this.buttons[this.mode].imgLocation);
1670            }
1671            if (this.mode == "measure" && mode != "measure") {
1672                for(var i=0, len=this.measureDivs.length; i<len; i++) {
1673                    if (this.measureDivs[i]) { 
1674                        this.map.layerContainerDiv.removeChild(this.measureDivs[i]);
1675                    }
1676                }
1677                this.measureDivs = [];
1678                this.measureStart = null;
1679            }
1680            this.mode = mode;
1681            if (this.buttons[mode]) {
1682                OpenLayers.Util.modifyAlphaImageDiv(this.buttons[mode], null, null, null, this.buttons[mode].activeImgLocation);
1683            }
1684            switch (this.mode) {
1685                case "zoombox":
1686                    this.map.div.style.cursor = "crosshair";
1687                    break;
1688                default:
1689                    this.map.div.style.cursor = "";
1690                    break;
1691            }
1692
1693        } 
1694    }, 
1695
1696    /**
1697     * Method: leaveMode
1698     */
1699    leaveMode: function() {
1700        this.switchModeTo("pan");
1701    },
1702   
1703    /**
1704     * Method: defaultMouseMove
1705     *
1706     * Parameters:
1707     * evt - {Event}
1708     */
1709    defaultMouseMove: function (evt) {
1710        if (this.mouseDragStart != null) {
1711            switch (this.mode) {
1712                case "zoombox": 
1713                    var deltaX = Math.abs(this.mouseDragStart.x - evt.xy.x);
1714                    var deltaY = Math.abs(this.mouseDragStart.y - evt.xy.y);
1715                    this.zoomBox.style.width = Math.max(1, deltaX) + "px";
1716                    this.zoomBox.style.height = Math.max(1, deltaY) + "px";
1717                    if (evt.xy.x < this.mouseDragStart.x) {
1718                        this.zoomBox.style.left = evt.xy.x+"px";
1719                    }
1720                    if (evt.xy.y < this.mouseDragStart.y) {
1721                        this.zoomBox.style.top = evt.xy.y+"px";
1722                    }
1723                    break;
1724                default:
1725                    var deltaX = this.mouseDragStart.x - evt.xy.x;
1726                    var deltaY = this.mouseDragStart.y - evt.xy.y;
1727                    var size = this.map.getSize();
1728                    var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX,
1729                                                     size.h / 2 + deltaY);
1730                    var newCenter = this.map.getLonLatFromViewPortPx( newXY ); 
1731                    this.map.setCenter(newCenter, null, true);
1732                    this.mouseDragStart = evt.xy.clone();
1733            }
1734            this.performedDrag = true;
1735        }
1736    },
1737
1738    /**
1739     * Method: defaultMouseUp
1740     *
1741     * Parameters:
1742     * evt - {Event}
1743     */
1744    defaultMouseUp: function (evt) {
1745        if (!OpenLayers.Event.isLeftClick(evt)) {
1746            return;
1747        }
1748        switch (this.mode) {
1749            case "zoombox":
1750                this.zoomBoxEnd(evt);
1751                if (this.startViaKeyboard) {
1752                    this.leaveMode();
1753                }
1754                break;
1755            case "pan":
1756                if (this.performedDrag) {
1757                    this.map.setCenter(this.map.center);
1758                }       
1759        }
1760        document.onselectstart = null;
1761        this.mouseDragStart = null;
1762        this.map.div.style.cursor = "default";
1763    },
1764
1765    /**
1766     * Method: defaultMouseOut
1767     *
1768     * Parameters:
1769     * evt - {Event}
1770     */
1771    defaultMouseOut: function (evt) {
1772        if (this.mouseDragStart != null
1773            && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {
1774            if (this.zoomBox) {
1775                this.removeZoomBox();
1776                if (this.startViaKeyboard) {
1777                    this.leaveMode();
1778                }
1779            }
1780            this.mouseDragStart = null;
1781            this.map.div.style.cursor = "default";
1782        }
1783    },
1784
1785    /**
1786     * Method: defaultClick
1787     *
1788     * Parameters:
1789     * evt - {Event}
1790     */
1791    defaultClick: function (evt) {
1792        if (this.performedDrag)  {
1793            this.performedDrag = false;
1794            return false;
1795        }
1796    },
1797   
1798    CLASS_NAME: "OpenLayers.Control.MouseToolbar"
1799});
1800
1801OpenLayers.Control.MouseToolbar.X = 6;
1802OpenLayers.Control.MouseToolbar.Y = 300;
1803
1804/**
1805 * Class: OpenLayers.Layer.Grid
1806 */
1807
1808OpenLayers.Util.extend(OpenLayers.Layer.Grid.prototype, {
1809
1810    /**
1811     * Method: getGridBounds
1812     * Deprecated. This function will be removed in 3.0. Please use
1813     *     getTilesBounds() instead.
1814     *
1815     * Returns:
1816     * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the
1817     * currently loaded tiles (including those partially or not at all seen
1818     * onscreen)
1819     */
1820    getGridBounds: function() {
1821        var msg = "The getGridBounds() function is deprecated. It will be " +
1822                  "removed in 3.0. Please use getTilesBounds() instead.";
1823        OpenLayers.Console.warn(msg);
1824        return this.getTilesBounds();
1825    }
1826});
1827
1828/**
1829 * Class: OpenLayers.Format.XML
1830 */
1831OpenLayers.Util.extend(OpenLayers.Format.XML.prototype, {
1832
1833    /**
1834     * APIMethod: concatChildValues
1835     * *Deprecated*. Use <getChildValue> instead.
1836     *
1837     * Concatenate the value of all child nodes if any exist, or return an
1838     *     optional default string.  Returns an empty string if no children
1839     *     exist and no default value is supplied.  Not optimized for large
1840     *     numbers of child nodes.
1841     *
1842     * Parameters:
1843     * node - {DOMElement} The element used to look for child values.
1844     * def - {String} Optional string to return in the event that no
1845     *     child exist.
1846     *
1847     * Returns:
1848     * {String} The concatenated value of all child nodes of the given node.
1849     */
1850    concatChildValues: function(node, def) {
1851        var value = "";
1852        var child = node.firstChild;
1853        var childValue;
1854        while(child) {
1855            childValue = child.nodeValue;
1856            if(childValue) {
1857                value += childValue;
1858            }
1859            child = child.nextSibling;
1860        }
1861        if(value == "" && def != undefined) {
1862            value = def;
1863        }
1864        return value;
1865    }
1866
1867});
1868
1869/**
1870 * Class: OpenLayers.Layer.WMS.Post
1871 * Instances of OpenLayers.Layer.WMS.Post are used to retrieve data from OGC
1872 * Web Mapping Services via HTTP-POST (application/x-www-form-urlencoded).
1873 * Create a new WMS layer with the <OpenLayers.Layer.WMS.Post> constructor.
1874 *
1875 * *Deprecated*. Instead of this layer, use <OpenLayers.Layer.WMS> with
1876 * <OpenLayers.Tile.Image.maxGetUrlLength> configured in the layer's
1877 * <OpenLayers.Layer.WMS.tileOptions>.
1878 *
1879 * Inherits from:
1880 *  - <OpenLayers.Layer.WMS>
1881 */
1882OpenLayers.Layer.WMS.Post = OpenLayers.Class(OpenLayers.Layer.WMS, {
1883
1884    /**
1885     * APIProperty: unsupportedBrowsers
1886     * {Array} Array with browsers, which should use the HTTP-GET protocol
1887     * instead of HTTP-POST for fetching tiles from a WMS .
1888     * Defaults to ["mozilla", "firefox", "opera"], because Opera is not able
1889     * to show transparent images in IFrames and Firefox/Mozilla has some ugly
1890     * effects of viewport-shaking when panning the map. Both browsers, Opera
1891     * and Firefox/Mozilla, have no problem with long urls, which is the reason
1892     * for using POST instead of GET. The strings to pass to this array are
1893     * the ones returned by <OpenLayers.BROWSER_NAME>.
1894     */
1895    unsupportedBrowsers: ["mozilla", "firefox", "opera"],
1896
1897    /**
1898     * Property: SUPPORTED_TRANSITIONS
1899     * {Array}
1900     * no supported transitions for this type of layer, because it is not
1901     * possible to modify the initialized tiles (iframes)
1902     */
1903    SUPPORTED_TRANSITIONS: [],
1904   
1905    /**
1906     * Property: usePost
1907     * {Boolean}
1908     */
1909    usePost: null,
1910
1911    /**
1912     * Constructor: OpenLayers.Layer.WMS.Post
1913     * Creates a new WMS layer object.
1914     *
1915     * Example:
1916     * (code)
1917     * var wms = new OpenLayers.Layer.WMS.Post(
1918     *  "NASA Global Mosaic",
1919     *  "http://wms.jpl.nasa.gov/wms.cgi",
1920     *  {layers: "modis, global_mosaic"});
1921     * (end)
1922     *
1923     * Parameters:
1924     * name - {String} A name for the layer
1925     * url - {String} Base url for the WMS
1926     *                (e.g. http://wms.jpl.nasa.gov/wms.cgi)
1927     * params - {Object} An object with key/value pairs representing the
1928     *                   GetMap query string parameters and parameter values.
1929     * options - {Object} Hashtable of extra options to tag onto the layer.
1930     */
1931    initialize: function(name, url, params, options) {
1932        var newArguments = [];
1933        newArguments.push(name, url, params, options);
1934        OpenLayers.Layer.WMS.prototype.initialize.apply(this, newArguments);
1935
1936        this.usePost = OpenLayers.Util.indexOf(
1937            this.unsupportedBrowsers, OpenLayers.BROWSER_NAME) == -1;
1938    },
1939   
1940    /**
1941     * Method: addTile
1942     * addTile creates a tile, initializes it and adds it as iframe to the
1943     * layer div.
1944     *
1945     * Parameters:
1946     * bounds - {<OpenLayers.Bounds>}
1947     * position - {<OpenLayers.Pixel>}
1948     *
1949     * Returns:
1950     * {<OpenLayers.Tile.Image.IFrame>} The added OpenLayers.Tile.Image.IFrame
1951     */
1952    addTile: function(bounds,position) {
1953        return new OpenLayers.Tile.Image(
1954            this, position, bounds, null, this.tileSize, {
1955                maxGetUrlLength: this.usePost ? 0 : null
1956            });
1957    },
1958
1959    CLASS_NAME: 'OpenLayers.Layer.WMS.Post'
1960});
1961
1962/**
1963 * Class: OpenLayers.Layer.WMS.Untiled
1964 * *Deprecated*.  To be removed in 3.0.  Instead use OpenLayers.Layer.WMS and
1965 *     pass the option 'singleTile' as true.
1966 *
1967 * Inherits from:
1968 *  - <OpenLayers.Layer.WMS>
1969 */
1970OpenLayers.Layer.WMS.Untiled = OpenLayers.Class(OpenLayers.Layer.WMS, {
1971
1972    /**
1973     * APIProperty: singleTile
1974     * {singleTile} Always true for untiled.
1975     */
1976    singleTile: true,
1977
1978    /**
1979     * Constructor: OpenLayers.Layer.WMS.Untiled
1980     *
1981     * Parameters:
1982     * name - {String}
1983     * url - {String}
1984     * params - {Object}
1985     * options - {Object}
1986     */
1987    initialize: function(name, url, params, options) {
1988        OpenLayers.Layer.WMS.prototype.initialize.apply(this, arguments);
1989       
1990        var msg = "The OpenLayers.Layer.WMS.Untiled class is deprecated and " +
1991                  "will be removed in 3.0. Instead, you should use the " +
1992                  "normal OpenLayers.Layer.WMS class, passing it the option " +
1993                  "'singleTile' as true.";
1994        OpenLayers.Console.warn(msg);
1995    },   
1996
1997    /**
1998     * Method: clone
1999     * Create a clone of this layer
2000     *
2001     * Returns:
2002     * {<OpenLayers.Layer.WMS.Untiled>} An exact clone of this layer
2003     */
2004    clone: function (obj) {
2005       
2006        if (obj == null) {
2007            obj = new OpenLayers.Layer.WMS.Untiled(this.name,
2008                                                   this.url,
2009                                                   this.params,
2010                                                   this.getOptions());
2011        }
2012
2013        //get all additions from superclasses
2014        obj = OpenLayers.Layer.WMS.prototype.clone.apply(this, [obj]);
2015
2016        // copy/set any non-init, non-simple values here
2017
2018        return obj;
2019    }, 
2020
2021    CLASS_NAME: "OpenLayers.Layer.WMS.Untiled"
2022});
2023
2024/**
2025 * Class: OpenLayers.Layer.MapServer.Untiled
2026 * *Deprecated*.  To be removed in 3.0.  Instead use OpenLayers.Layer.MapServer
2027 *     and pass the option 'singleTile' as true.
2028 *
2029 * Inherits from:
2030 *  - <OpenLayers.Layer.MapServer>
2031 */
2032OpenLayers.Layer.MapServer.Untiled = OpenLayers.Class(OpenLayers.Layer.MapServer, {
2033
2034    /**
2035     * APIProperty: singleTile
2036     * {singleTile} Always true for untiled.
2037     */
2038    singleTile: true,
2039
2040    /**
2041     * Constructor: OpenLayers.Layer.MapServer.Untiled
2042     *
2043     * Parameters:
2044     * name - {String}
2045     * url - {String}
2046     * params - {Object}
2047     * options - {Object}
2048     */
2049    initialize: function(name, url, params, options) {
2050        OpenLayers.Layer.MapServer.prototype.initialize.apply(this, arguments);
2051       
2052        var msg = "The OpenLayers.Layer.MapServer.Untiled class is deprecated and " +
2053                  "will be removed in 3.0. Instead, you should use the " +
2054                  "normal OpenLayers.Layer.MapServer class, passing it the option " +
2055                  "'singleTile' as true.";
2056        OpenLayers.Console.warn(msg);
2057    },   
2058
2059    /**
2060     * Method: clone
2061     * Create a clone of this layer
2062     *
2063     * Returns:
2064     * {<OpenLayers.Layer.MapServer.Untiled>} An exact clone of this layer
2065     */
2066    clone: function (obj) {
2067       
2068        if (obj == null) {
2069            obj = new OpenLayers.Layer.MapServer.Untiled(this.name,
2070                                                         this.url,
2071                                                         this.params,
2072                                                         this.getOptions());
2073        }
2074
2075        //get all additions from superclasses
2076        obj = OpenLayers.Layer.MapServer.prototype.clone.apply(this, [obj]);
2077
2078        // copy/set any non-init, non-simple values here
2079       
2080        return obj;
2081    }, 
2082
2083    CLASS_NAME: "OpenLayers.Layer.MapServer.Untiled"
2084});
2085
2086/**
2087 * Class: OpenLayers.Tile.WFS
2088 * Instances of OpenLayers.Tile.WFS are used to manage the image tiles
2089 * used by various layers.  Create a new image tile with the
2090 * <OpenLayers.Tile.WFS> constructor.
2091 *
2092 * Inherits from:
2093 *  - <OpenLayers.Tile>
2094 */
2095OpenLayers.Tile.WFS = OpenLayers.Class(OpenLayers.Tile, {
2096
2097    /**
2098     * Property: features
2099     * {Array(<OpenLayers.Feature>)} list of features in this tile
2100     */
2101    features: null,
2102
2103    /**
2104     * Property: url
2105     * {String}
2106     */
2107    url: null,
2108
2109    /**
2110     * Property: request
2111     * {<OpenLayers.Request.XMLHttpRequest>}
2112     */
2113    request: null,
2114
2115    /** TBD 3.0 - reorder the parameters to the init function to put URL
2116     *             as last, so we can continue to call tile.initialize()
2117     *             without changing the arguments.
2118     *
2119     * Constructor: OpenLayers.Tile.WFS
2120     * Constructor for a new <OpenLayers.Tile.WFS> instance.
2121     *
2122     * Parameters:
2123     * layer - {<OpenLayers.Layer>} layer that the tile will go in.
2124     * position - {<OpenLayers.Pixel>}
2125     * bounds - {<OpenLayers.Bounds>}
2126     * url - {<String>}
2127     * size - {<OpenLayers.Size>}
2128     */
2129    initialize: function(layer, position, bounds, url, size) {
2130        OpenLayers.Tile.prototype.initialize.apply(this, arguments);
2131        this.url = url;
2132        this.features = [];
2133    },
2134
2135    /**
2136     * APIMethod: destroy
2137     * nullify references to prevent circular references and memory leaks
2138     */
2139    destroy: function() {
2140        OpenLayers.Tile.prototype.destroy.apply(this, arguments);
2141        this.destroyAllFeatures();
2142        this.features = null;
2143        this.url = null;
2144        if(this.request) {
2145            this.request.abort();
2146            //this.request.destroy();
2147            this.request = null;
2148        }
2149    },
2150
2151    /**
2152     * Method: clear
2153     *  Clear the tile of any bounds/position-related data so that it can
2154     *   be reused in a new location.
2155     */
2156    clear: function() {
2157        this.destroyAllFeatures();
2158    },
2159
2160    /**
2161     * Method: draw
2162     * Check that a tile should be drawn, and load features for it.
2163     */
2164    draw:function() {
2165        if (OpenLayers.Tile.prototype.draw.apply(this, arguments)) {
2166            if (this.isLoading) {
2167                //if already loading, send 'reload' instead of 'loadstart'.
2168                this.events.triggerEvent("reload");
2169            } else {
2170                this.isLoading = true;
2171                this.events.triggerEvent("loadstart");
2172            }
2173            this.loadFeaturesForRegion(this.requestSuccess);
2174        }
2175    },
2176
2177    /**
2178    * Method: loadFeaturesForRegion
2179    * Abort any pending requests and issue another request for data.
2180    *
2181    * Input are function pointers for what to do on success and failure.
2182    *
2183    * Parameters:
2184    * success - {function}
2185    * failure - {function}
2186    */
2187    loadFeaturesForRegion:function(success, failure) {
2188        if(this.request) {
2189            this.request.abort();
2190        }
2191        this.request = OpenLayers.Request.GET({
2192            url: this.url,
2193            success: success,
2194            failure: failure,
2195            scope: this
2196        });
2197    },
2198
2199    /**
2200    * Method: requestSuccess
2201    * Called on return from request succcess. Adds results via
2202    * layer.addFeatures in vector mode, addResults otherwise.
2203    *
2204    * Parameters:
2205    * request - {<OpenLayers.Request.XMLHttpRequest>}
2206    */
2207    requestSuccess:function(request) {
2208        if (this.features) {
2209            var doc = request.responseXML;
2210            if (!doc || !doc.documentElement) {
2211                doc = request.responseText;
2212            }
2213            if (this.layer.vectorMode) {
2214                this.layer.addFeatures(this.layer.formatObject.read(doc));
2215            } else {
2216                var xml = new OpenLayers.Format.XML();
2217                if (typeof doc == "string") {
2218                    doc = xml.read(doc);
2219                }
2220                var resultFeatures = xml.getElementsByTagNameNS(
2221                    doc, "http://www.opengis.net/gml", "featureMember"
2222                );
2223                this.addResults(resultFeatures);
2224            }
2225        }
2226        if (this.events) {
2227            this.events.triggerEvent("loadend");
2228        }
2229
2230        //request produced with success, we can delete the request object.
2231        //this.request.destroy();
2232        this.request = null;
2233    },
2234
2235    /**
2236     * Method: addResults
2237     * Construct new feature via layer featureClass constructor, and add to
2238     * this.features.
2239     *
2240     * Parameters:
2241     * results - {Object}
2242     */
2243    addResults: function(results) {
2244        for (var i=0; i < results.length; i++) {
2245            var feature = new this.layer.featureClass(this.layer,
2246                                                      results[i]);
2247            this.features.push(feature);
2248        }
2249    },
2250
2251
2252    /**
2253     * Method: destroyAllFeatures
2254     * Iterate through and call destroy() on each feature, removing it from
2255     *   the local array
2256     */
2257    destroyAllFeatures: function() {
2258        while(this.features.length > 0) {
2259            var feature = this.features.shift();
2260            feature.destroy();
2261        }
2262    },
2263
2264    CLASS_NAME: "OpenLayers.Tile.WFS"
2265  }
2266);
2267
2268/**
2269 * Class: OpenLayers.Feature.WFS
2270 * WFS handling class, for use as a featureClass on the WFS layer for handling
2271 * 'point' WFS types. Good for subclassing when creating a custom WFS like
2272 * XML application.
2273 *
2274 * Inherits from:
2275 *  - <OpenLayers.Feature>
2276 */
2277OpenLayers.Feature.WFS = OpenLayers.Class(OpenLayers.Feature, {
2278
2279    /**
2280     * Constructor: OpenLayers.Feature.WFS
2281     * Create a WFS feature.
2282     *
2283     * Parameters:
2284     * layer - {<OpenLayers.Layer>}
2285     * xmlNode - {XMLNode}
2286     */
2287    initialize: function(layer, xmlNode) {
2288        var newArguments = arguments;
2289        var data = this.processXMLNode(xmlNode);
2290        newArguments = new Array(layer, data.lonlat, data);
2291        OpenLayers.Feature.prototype.initialize.apply(this, newArguments);
2292        this.createMarker();
2293        this.layer.addMarker(this.marker);
2294    },
2295
2296    /**
2297     * Method: destroy
2298     * nullify references to prevent circular references and memory leaks
2299     */
2300    destroy: function() {
2301        if (this.marker != null) {
2302            this.layer.removeMarker(this.marker);
2303        }
2304        OpenLayers.Feature.prototype.destroy.apply(this, arguments);
2305    },
2306
2307    /**
2308     * Method: processXMLNode
2309     * When passed an xmlNode, parses it for a GML point, and passes
2310     * back an object describing that point.
2311     *
2312     * For subclasses of Feature.WFS, this is the feature to change.
2313     *
2314     * Parameters:
2315     * xmlNode - {XMLNode}
2316     *
2317     * Returns:
2318     * {Object} Data Object with 'id', 'lonlat', and private properties set
2319     */
2320    processXMLNode: function(xmlNode) {
2321        //this should be overridden by subclasses
2322        // must return an Object with 'id' and 'lonlat' values set
2323        var point = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, "http://www.opengis.net/gml", "gml", "Point");
2324        var text  = OpenLayers.Util.getXmlNodeValue(OpenLayers.Ajax.getElementsByTagNameNS(point[0], "http://www.opengis.net/gml","gml", "coordinates")[0]);
2325        var floats = text.split(",");
2326        return {lonlat: new OpenLayers.LonLat(parseFloat(floats[0]),
2327                                              parseFloat(floats[1])),
2328                id: null};
2329
2330    },
2331
2332    CLASS_NAME: "OpenLayers.Feature.WFS"
2333});
2334
2335
2336/**
2337 * Class: OpenLayers.Layer.WFS
2338 * *Deprecated*.  To be removed in 3.0.  Instead use OpenLayers.Layer.Vector
2339 *     with a Protocol.WFS and one or more Strategies.
2340 *
2341 * Inherits from:
2342 *  - <OpenLayers.Layer.Vector>
2343 *  - <OpenLayers.Layer.Markers>
2344 */
2345OpenLayers.Layer.WFS = OpenLayers.Class(
2346  OpenLayers.Layer.Vector, OpenLayers.Layer.Markers, {
2347
2348    /**
2349     * APIProperty: isBaseLayer
2350     * {Boolean} WFS layer is not a base layer by default.
2351     */
2352    isBaseLayer: false,
2353
2354    /**
2355     * Property: tile
2356     * {<OpenLayers.Tile.WFS>}
2357     */
2358    tile: null,
2359
2360    /**
2361     * APIProperty: ratio
2362     * {Float} The ratio property determines the size of the serverside query
2363     *    relative to the map viewport size. By default, we load an area twice
2364     *    as big as the map, to allow for panning without immediately reload.
2365     *    Setting this to 1 will cause the area of the WFS request to match
2366     *    the map area exactly. It is recommended to set this to some number
2367     *    at least slightly larger than 1, otherwise accidental clicks can
2368     *    cause a data reload, by moving the map only 1 pixel.
2369     */
2370    ratio: 2,
2371
2372    /**
2373     * Property: DEFAULT_PARAMS
2374     * {Object} Hashtable of default key/value parameters
2375     */
2376    DEFAULT_PARAMS: { service: "WFS",
2377                      version: "1.0.0",
2378                      request: "GetFeature"
2379                    },
2380
2381    /**
2382     * APIProperty: featureClass
2383     * {<OpenLayers.Feature>} If featureClass is defined, an old-style markers
2384     *     based WFS layer is created instead of a new-style vector layer. If
2385     *     sent, this should be a subclass of OpenLayers.Feature
2386     */
2387    featureClass: null,
2388
2389    /**
2390      * APIProperty: format
2391      * {<OpenLayers.Format>} The format you want the data to be parsed with.
2392      * Must be passed in the constructor. Should be a class, not an instance.
2393      * This option can only be used if no featureClass is passed / vectorMode
2394      * is false: if a featureClass is passed, then this parameter is ignored.
2395      */
2396    format: null,
2397
2398    /**
2399     * Property: formatObject
2400     * {<OpenLayers.Format>} Internally created/managed format object, used by
2401     * the Tile to parse data.
2402     */
2403    formatObject: null,
2404
2405    /**
2406     * APIProperty: formatOptions
2407     * {Object} Hash of options which should be passed to the format when it is
2408     * created. Must be passed in the constructor.
2409     */
2410    formatOptions: null,
2411
2412    /**
2413     * Property: vectorMode
2414     * {Boolean} Should be calculated automatically. Determines whether the
2415     *     layer is in vector mode or marker mode.
2416     */
2417    vectorMode: true,
2418
2419    /**
2420     * APIProperty: encodeBBOX
2421     * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no',
2422     *     but some services want it that way. Default false.
2423     */
2424    encodeBBOX: false,
2425
2426    /**
2427     * APIProperty: extractAttributes
2428     * {Boolean} Should the WFS layer parse attributes from the retrieved
2429     *     GML? Defaults to false. If enabled, parsing is slower, but
2430     *     attributes are available in the attributes property of
2431     *     layer features.
2432     */
2433    extractAttributes: false,
2434
2435    /**
2436     * Constructor: OpenLayers.Layer.WFS
2437     *
2438     * Parameters:
2439     * name - {String}
2440     * url - {String}
2441     * params - {Object}
2442     * options - {Object} Hashtable of extra options to tag onto the layer
2443     */
2444    initialize: function(name, url, params, options) {
2445        if (options == undefined) { options = {}; }
2446
2447        if (options.featureClass ||
2448            !OpenLayers.Layer.Vector ||
2449            !OpenLayers.Feature.Vector) {
2450            this.vectorMode = false;
2451        }
2452
2453        // Uppercase params
2454        params = OpenLayers.Util.upperCaseObject(params);
2455
2456        // Turn off error reporting, browsers like Safari may work
2457        // depending on the setup, and we don't want an unneccesary alert.
2458        OpenLayers.Util.extend(options, {'reportError': false});
2459        var newArguments = [];
2460        newArguments.push(name, options);
2461        OpenLayers.Layer.Vector.prototype.initialize.apply(this, newArguments);
2462        if (!this.renderer || !this.vectorMode) {
2463            this.vectorMode = false;
2464            if (!options.featureClass) {
2465                options.featureClass = OpenLayers.Feature.WFS;
2466            }
2467            OpenLayers.Layer.Markers.prototype.initialize.apply(this,
2468                                                                newArguments);
2469        }
2470
2471        if (this.params && this.params.typename && !this.options.typename) {
2472            this.options.typename = this.params.typename;
2473        }
2474
2475        if (!this.options.geometry_column) {
2476            this.options.geometry_column = "the_geom";
2477        }
2478
2479        this.params = OpenLayers.Util.applyDefaults(
2480            params,
2481            OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
2482        );
2483        this.url = url;
2484    },
2485
2486
2487    /**
2488     * APIMethod: destroy
2489     */
2490    destroy: function() {
2491        if (this.vectorMode) {
2492            OpenLayers.Layer.Vector.prototype.destroy.apply(this, arguments);
2493        } else {
2494            OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
2495        }
2496        if (this.tile) {
2497            this.tile.destroy();
2498        }
2499        this.tile = null;
2500
2501        this.ratio = null;
2502        this.featureClass = null;
2503        this.format = null;
2504
2505        if (this.formatObject && this.formatObject.destroy) {
2506            this.formatObject.destroy();
2507        }
2508        this.formatObject = null;
2509
2510        this.formatOptions = null;
2511        this.vectorMode = null;
2512        this.encodeBBOX = null;
2513        this.extractAttributes = null;
2514    },
2515
2516    /**
2517     * Method: setMap
2518     *
2519     * Parameters:
2520     * map - {<OpenLayers.Map>}
2521     */
2522    setMap: function(map) {
2523        if (this.vectorMode) {
2524            OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
2525
2526            var options = {
2527              'extractAttributes': this.extractAttributes
2528            };
2529
2530            OpenLayers.Util.extend(options, this.formatOptions);
2531            if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
2532                options.externalProjection = this.projection;
2533                options.internalProjection = this.map.getProjectionObject();
2534            }
2535
2536            this.formatObject = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
2537        } else {
2538            OpenLayers.Layer.Markers.prototype.setMap.apply(this, arguments);
2539        }
2540    },
2541
2542    /**
2543     * Method: moveTo
2544     *
2545     * Parameters:
2546     * bounds - {<OpenLayers.Bounds>}
2547     * zoomChanged - {Boolean}
2548     * dragging - {Boolean}
2549     */
2550    moveTo:function(bounds, zoomChanged, dragging) {
2551        if (this.vectorMode) {
2552            OpenLayers.Layer.Vector.prototype.moveTo.apply(this, arguments);
2553        } else {
2554            OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
2555        }
2556
2557        // don't load wfs features while dragging, wait for drag end
2558        if (dragging) {
2559            // TBD try to hide the vector layer while dragging
2560            // this.setVisibility(false);
2561            // this will probably help for panning performances
2562            return false;
2563        }
2564
2565        if ( zoomChanged ) {
2566            if (this.vectorMode) {
2567                this.renderer.clear();
2568            }
2569        }
2570
2571    //DEPRECATED - REMOVE IN 3.0
2572        // don't load data if current zoom level doesn't match
2573        if (this.options.minZoomLevel) {
2574            OpenLayers.Console.warn(OpenLayers.i18n('minZoomLevelError'));
2575
2576            if (this.map.getZoom() < this.options.minZoomLevel) {
2577                return null;
2578            }
2579        }
2580
2581        if (bounds == null) {
2582            bounds = this.map.getExtent();
2583        }
2584
2585        var firstRendering = (this.tile == null);
2586
2587        //does the new bounds to which we need to move fall outside of the
2588        // current tile's bounds?
2589        var outOfBounds = (!firstRendering &&
2590                           !this.tile.bounds.containsBounds(bounds));
2591
2592        if (zoomChanged || firstRendering || (!dragging && outOfBounds)) {
2593            //determine new tile bounds
2594            var center = bounds.getCenterLonLat();
2595            var tileWidth = bounds.getWidth() * this.ratio;
2596            var tileHeight = bounds.getHeight() * this.ratio;
2597            var tileBounds =
2598                new OpenLayers.Bounds(center.lon - (tileWidth / 2),
2599                                      center.lat - (tileHeight / 2),
2600                                      center.lon + (tileWidth / 2),
2601                                      center.lat + (tileHeight / 2));
2602
2603            //determine new tile size
2604            var tileSize = this.map.getSize();
2605            tileSize.w = tileSize.w * this.ratio;
2606            tileSize.h = tileSize.h * this.ratio;
2607
2608            //determine new position (upper left corner of new bounds)
2609            var ul = new OpenLayers.LonLat(tileBounds.left, tileBounds.top);
2610            var pos = this.map.getLayerPxFromLonLat(ul);
2611
2612            //formulate request url string
2613            var url = this.getFullRequestString();
2614
2615            var params = null;
2616
2617            // Cant combine "filter" and "BBOX". This is a cheap hack to help
2618            // people out who can't migrate to the WFS protocol immediately.
2619            var filter = this.params.filter || this.params.FILTER;
2620            if (filter) {
2621                params = {FILTER: filter};
2622            }
2623            else {
2624                params = {BBOX: this.encodeBBOX ? tileBounds.toBBOX()
2625                                                    : tileBounds.toArray()};
2626            }
2627
2628            if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
2629                var projectedBounds = tileBounds.clone();
2630                projectedBounds.transform(this.map.getProjectionObject(),
2631                                          this.projection);
2632                if (!filter){
2633                    params.BBOX = this.encodeBBOX ? projectedBounds.toBBOX()
2634                                                : projectedBounds.toArray();
2635                }
2636            }
2637
2638            url += "&" + OpenLayers.Util.getParameterString(params);
2639
2640            if (!this.tile) {
2641                this.tile = new OpenLayers.Tile.WFS(this, pos, tileBounds,
2642                                                     url, tileSize);
2643                this.addTileMonitoringHooks(this.tile);
2644                this.tile.draw();
2645            } else {
2646                if (this.vectorMode) {
2647                    this.destroyFeatures();
2648                    this.renderer.clear();
2649                } else {
2650                    this.clearMarkers();
2651                }
2652                this.removeTileMonitoringHooks(this.tile);
2653                this.tile.destroy();
2654
2655                this.tile = null;
2656                this.tile = new OpenLayers.Tile.WFS(this, pos, tileBounds,
2657                                                     url, tileSize);
2658                this.addTileMonitoringHooks(this.tile);
2659                this.tile.draw();
2660            }
2661        }
2662    },
2663
2664    /**
2665     * Method: addTileMonitoringHooks
2666     * This function takes a tile as input and adds the appropriate hooks to
2667     *     the tile so that the layer can keep track of the loading tile
2668     *     (making sure to check that the tile is always the layer's current
2669     *     tile before taking any action).
2670     *
2671     * Parameters:
2672     * tile - {<OpenLayers.Tile>}
2673     */
2674    addTileMonitoringHooks: function(tile) {
2675        tile.onLoadStart = function() {
2676            //if this is the the layer's current tile, then trigger
2677            // a 'loadstart'
2678            if (this == this.layer.tile) {
2679                this.layer.events.triggerEvent("loadstart");
2680            }
2681        };
2682        tile.events.register("loadstart", tile, tile.onLoadStart);
2683
2684        tile.onLoadEnd = function() {
2685            //if this is the the layer's current tile, then trigger
2686            // a 'tileloaded' and 'loadend'
2687            if (this == this.layer.tile) {
2688                this.layer.events.triggerEvent("tileloaded");
2689                this.layer.events.triggerEvent("loadend");
2690            }
2691        };
2692        tile.events.register("loadend", tile, tile.onLoadEnd);
2693        tile.events.register("unload", tile, tile.onLoadEnd);
2694    },
2695
2696    /**
2697     * Method: removeTileMonitoringHooks
2698     * This function takes a tile as input and removes the tile hooks
2699     *     that were added in addTileMonitoringHooks()
2700     *
2701     * Parameters:
2702     * tile - {<OpenLayers.Tile>}
2703     */
2704    removeTileMonitoringHooks: function(tile) {
2705        tile.unload();
2706        tile.events.un({
2707            "loadstart": tile.onLoadStart,
2708            "loadend": tile.onLoadEnd,
2709            "unload": tile.onLoadEnd,
2710            scope: tile
2711        });
2712    },
2713
2714    /**
2715     * Method: onMapResize
2716     * Call the onMapResize method of the appropriate parent class.
2717     */
2718    onMapResize: function() {
2719        if(this.vectorMode) {
2720            OpenLayers.Layer.Vector.prototype.onMapResize.apply(this,
2721                                                                arguments);
2722        } else {
2723            OpenLayers.Layer.Markers.prototype.onMapResize.apply(this,
2724                                                                 arguments);
2725        }
2726    },
2727
2728    /**
2729     * Method: display
2730     * Call the display method of the appropriate parent class.
2731     */
2732    display: function() {
2733        if(this.vectorMode) {
2734            OpenLayers.Layer.Vector.prototype.display.apply(this,
2735                                                                arguments);
2736        } else {
2737            OpenLayers.Layer.Markers.prototype.display.apply(this,
2738                                                                 arguments);
2739        }
2740    },
2741
2742    /**
2743     * APIMethod: mergeNewParams
2744     * Modify parameters for the layer and redraw.
2745     *
2746     * Parameters:
2747     * newParams - {Object}
2748     */
2749    mergeNewParams:function(newParams) {
2750        var upperParams = OpenLayers.Util.upperCaseObject(newParams);
2751        var newArguments = [upperParams];
2752        return OpenLayers.Layer.HTTPRequest.prototype.mergeNewParams.apply(this,
2753                                                                 newArguments);
2754    },
2755
2756    /**
2757     * APIMethod: clone
2758     *
2759     * Parameters:
2760     * obj - {Object}
2761     *
2762     * Returns:
2763     * {<OpenLayers.Layer.WFS>} An exact clone of this OpenLayers.Layer.WFS
2764     */
2765    clone: function (obj) {
2766
2767        if (obj == null) {
2768            obj = new OpenLayers.Layer.WFS(this.name,
2769                                           this.url,
2770                                           this.params,
2771                                           this.getOptions());
2772        }
2773
2774        //get all additions from superclasses
2775        if (this.vectorMode) {
2776            obj = OpenLayers.Layer.Vector.prototype.clone.apply(this, [obj]);
2777        } else {
2778            obj = OpenLayers.Layer.Markers.prototype.clone.apply(this, [obj]);
2779        }
2780
2781        // copy/set any non-init, non-simple values here
2782
2783        return obj;
2784    },
2785
2786    /**
2787     * APIMethod: getFullRequestString
2788     * combine the layer's url with its params and these newParams.
2789     *
2790     *    Add the SRS parameter from 'projection' -- this is probably
2791     *     more eloquently done via a setProjection() method, but this
2792     *     works for now and always.
2793     *
2794     * Parameters:
2795     * newParams - {Object}
2796     * altUrl - {String} Use this as the url instead of the layer's url
2797     */
2798    getFullRequestString:function(newParams, altUrl) {
2799        var projectionCode = this.projection.getCode() || this.map.getProjection();
2800        this.params.SRS = (projectionCode == "none") ? null : projectionCode;
2801
2802        return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
2803                                                    this, arguments);
2804    },
2805
2806    /**
2807     * APIMethod: commit
2808     * Write out the data to a WFS server.
2809     */
2810    commit: function() {
2811        if (!this.writer) {
2812            var options = {};
2813            if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
2814                options.externalProjection = this.projection;
2815                options.internalProjection = this.map.getProjectionObject();
2816            }
2817
2818            this.writer = new OpenLayers.Format.WFS(options,this);
2819        }
2820
2821        var data = this.writer.write(this.features);
2822
2823        OpenLayers.Request.POST({
2824            url: this.url,
2825            data: data,
2826            success: this.commitSuccess,
2827            failure: this.commitFailure,
2828            scope: this
2829        });
2830    },
2831
2832    /**
2833     * Method: commitSuccess
2834     * Called when the Ajax request returns a response
2835     *
2836     * Parameters:
2837     * response - {XmlNode} from server
2838     */
2839    commitSuccess: function(request) {
2840        var response = request.responseText;
2841        if (response.indexOf('SUCCESS') != -1) {
2842            this.commitReport(OpenLayers.i18n("commitSuccess", {'response':response}));
2843
2844            for(var i = 0; i < this.features.length; i++) {
2845                this.features[i].state = null;
2846            }
2847            // TBD redraw the layer or reset the state of features
2848            // foreach features: set state to null
2849        } else if (response.indexOf('FAILED') != -1 ||
2850            response.indexOf('Exception') != -1) {
2851            this.commitReport(OpenLayers.i18n("commitFailed", {'response':response}));
2852        }
2853    },
2854
2855    /**
2856     * Method: commitFailure
2857     * Called when the Ajax request fails
2858     *
2859     * Parameters:
2860     * response - {XmlNode} from server
2861     */
2862    commitFailure: function(request) {},
2863
2864    /**
2865     * APIMethod: commitReport
2866     * Called with a 'success' message if the commit succeeded, otherwise
2867     *     a failure message, and the full request text as a second parameter.
2868     *     Override this function to provide custom transaction reporting.
2869     *
2870     * string - {String} reporting string
2871     * response - {String} full XML response
2872     */
2873    commitReport: function(string, response) {
2874        OpenLayers.Console.userError(string);
2875    },
2876
2877
2878    /**
2879     * APIMethod: refresh
2880     * Refreshes all the features of the layer
2881     */
2882    refresh: function() {
2883        if (this.tile) {
2884            if (this.vectorMode) {
2885                this.renderer.clear();
2886                this.features.length = 0;
2887            } else {
2888                this.clearMarkers();
2889                this.markers.length = 0;
2890            }
2891            this.tile.draw();
2892        }
2893    },
2894
2895    /**
2896     * APIMethod: getDataExtent
2897     * Calculates the max extent which includes all of the layer data.
2898     *
2899     * Returns:
2900     * {<OpenLayers.Bounds>}
2901     */
2902    getDataExtent: function () {
2903        var extent;
2904        //get all additions from superclasses
2905        if (this.vectorMode) {
2906            extent = OpenLayers.Layer.Vector.prototype.getDataExtent.apply(this);
2907        } else {
2908            extent = OpenLayers.Layer.Markers.prototype.getDataExtent.apply(this);
2909        }
2910
2911        return extent;
2912    },
2913
2914    /**
2915     * APIMethod: setOpacity
2916     * Call the setOpacity method of the appropriate parent class to set the
2917     *     opacity.
2918     *
2919     * Parameters:
2920     * opacity - {Float}
2921     */
2922    setOpacity: function (opacity) {
2923        if (this.vectorMode) {
2924            OpenLayers.Layer.Vector.prototype.setOpacity.apply(this, [opacity]);
2925        } else {
2926            OpenLayers.Layer.Markers.prototype.setOpacity.apply(this, [opacity]);
2927        }
2928    },
2929
2930    CLASS_NAME: "OpenLayers.Layer.WFS"
2931});
2932
2933/**
2934 * Class: OpenLayers.Layer.VirtualEarth
2935 * *Deprecated*. Use <OpenLayers.Layer.Bing> instead.
2936 *
2937 * Instances of OpenLayers.Layer.VirtualEarth are used to display the data from
2938 *     the Bing Maps AJAX Control (see e.g.
2939 *     http://msdn.microsoft.com/library/bb429619.aspx). Create a VirtualEarth
2940 *     layer with the <OpenLayers.Layer.VirtualEarth> constructor.
2941 *
2942 * Inherits from:
2943 *  - <OpenLayers.Layer.EventPane>
2944 *  - <OpenLayers.Layer.FixedZoomLevels>
2945 */
2946OpenLayers.Layer.VirtualEarth = OpenLayers.Class(
2947    OpenLayers.Layer.EventPane,
2948    OpenLayers.Layer.FixedZoomLevels, {
2949
2950    /**
2951     * Constant: MIN_ZOOM_LEVEL
2952     * {Integer} 1
2953     */
2954    MIN_ZOOM_LEVEL: 1,
2955
2956    /**
2957     * Constant: MAX_ZOOM_LEVEL
2958     * {Integer} 19
2959     */
2960    MAX_ZOOM_LEVEL: 19,
2961
2962    /**
2963     * Constant: RESOLUTIONS
2964     * {Array(Float)} Hardcode these resolutions so that they are more closely
2965     *                tied with the standard wms projection
2966     */
2967    RESOLUTIONS: [
2968        1.40625,
2969        0.703125,
2970        0.3515625,
2971        0.17578125,
2972        0.087890625,
2973        0.0439453125,
2974        0.02197265625,
2975        0.010986328125,
2976        0.0054931640625,
2977        0.00274658203125,
2978        0.001373291015625,
2979        0.0006866455078125,
2980        0.00034332275390625,
2981        0.000171661376953125,
2982        0.0000858306884765625,
2983        0.00004291534423828125,
2984        0.00002145767211914062,
2985        0.00001072883605957031,
2986        0.00000536441802978515
2987    ],
2988
2989    /**
2990     * APIProperty: type
2991     * {VEMapType}
2992     */
2993    type: null,
2994
2995    /**
2996     * APIProperty: wrapDateLine
2997     * {Boolean} Allow user to pan forever east/west.  Default is true.
2998     *     Setting this to false only restricts panning if
2999     *     <sphericalMercator> is true.
3000     */
3001    wrapDateLine: true,
3002
3003    /**
3004     * APIProperty: sphericalMercator
3005     * {Boolean} Should the map act as a mercator-projected map? This will
3006     *     cause all interactions with the map to be in the actual map
3007     *     projection, which allows support for vector drawing, overlaying
3008     *     other maps, etc.
3009     */
3010    sphericalMercator: false,
3011
3012    /**
3013     * APIProperty: animationEnabled
3014     * {Boolean} If set to true, the transition between zoom levels will be
3015     *     animated. Set to false to match the zooming experience of other
3016     *     layer types. Default is true.
3017     */
3018    animationEnabled: true,
3019
3020    /**
3021     * Constructor: OpenLayers.Layer.VirtualEarth
3022     * Creates a new instance of a OpenLayers.Layer.VirtualEarth. If you use an
3023     *     instance of OpenLayers.Layer.VirtualEarth in you map, you should set
3024     *     the <OpenLayers.Map> option restrictedExtent to a meaningful value,
3025     *     e.g.:
3026     * (code)
3027     * var map = new OpenLayers.Map( 'map', {
3028     *     // other map options
3029     *     restrictedExtent : OpenLayers.Bounds(-20037508, -20037508, 20037508, 20037508)
3030     * } );
3031     *
3032     * var veLayer = new OpenLayers.Layer.VirtualEarth (
3033     *     "Virtual Earth Layer"
3034     * );
3035     *
3036     * map.addLayer( veLayer );
3037     * (end)
3038     *
3039     * Parameters:
3040     * name - {String}
3041     * options - {Object}
3042     */
3043    initialize: function(name, options) {
3044        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
3045        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
3046                                                                    arguments);
3047        if(this.sphericalMercator) {
3048            OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
3049            this.initMercatorParameters();
3050        }
3051    },
3052
3053    /**
3054     * Method: loadMapObject
3055     */
3056    loadMapObject:function() {
3057
3058        // create div and set to same size as map
3059        var veDiv = OpenLayers.Util.createDiv(this.name);
3060        var sz = this.map.getSize();
3061        veDiv.style.width = sz.w + "px";
3062        veDiv.style.height = sz.h + "px";
3063        this.div.appendChild(veDiv);
3064
3065        try { // crash prevention
3066            this.mapObject = new VEMap(this.name);
3067        } catch (e) { }
3068
3069        if (this.mapObject != null) {
3070            try { // this is to catch a Mozilla bug without falling apart
3071
3072                // The fourth argument is whether the map is 'fixed' -- not
3073                // draggable. See:
3074                // http://blogs.msdn.com/virtualearth/archive/2007/09/28/locking-a-virtual-earth-map.aspx
3075                //
3076                this.mapObject.LoadMap(null, null, this.type, true);
3077                this.mapObject.AttachEvent("onmousedown", OpenLayers.Function.True);
3078
3079            } catch (e) { }
3080            this.mapObject.HideDashboard();
3081            if(typeof this.mapObject.SetAnimationEnabled == "function") {
3082                this.mapObject.SetAnimationEnabled(this.animationEnabled);
3083            }
3084        }
3085
3086        //can we do smooth panning? this is an unpublished method, so we need
3087        // to be careful
3088        if ( !this.mapObject ||
3089             !this.mapObject.vemapcontrol ||
3090             !this.mapObject.vemapcontrol.PanMap ||
3091             (typeof this.mapObject.vemapcontrol.PanMap != "function")) {
3092
3093            this.dragPanMapObject = null;
3094        }
3095
3096    },
3097
3098    /**
3099     * Method: onMapResize
3100     */
3101    onMapResize: function() {
3102        this.mapObject.Resize(this.map.size.w, this.map.size.h);
3103    },
3104
3105    /**
3106     * APIMethod: getWarningHTML
3107     *
3108     * Returns:
3109     * {String} String with information on why layer is broken, how to get
3110     *          it working.
3111     */
3112    getWarningHTML:function() {
3113        return OpenLayers.i18n(
3114            "getLayerWarning", {'layerType':'VE', 'layerLib':'VirtualEarth'}
3115        );
3116    },
3117
3118
3119
3120    /************************************
3121     *                                  *
3122     *   MapObject Interface Controls   *
3123     *                                  *
3124     ************************************/
3125
3126
3127  // Get&Set Center, Zoom
3128
3129    /**
3130     * APIMethod: setMapObjectCenter
3131     * Set the mapObject to the specified center and zoom
3132     *
3133     * Parameters:
3134     * center - {Object} MapObject LonLat format
3135     * zoom - {int} MapObject zoom format
3136     */
3137    setMapObjectCenter: function(center, zoom) {
3138        this.mapObject.SetCenterAndZoom(center, zoom);
3139    },
3140
3141    /**
3142     * APIMethod: getMapObjectCenter
3143     *
3144     * Returns:
3145     * {Object} The mapObject's current center in Map Object format
3146     */
3147    getMapObjectCenter: function() {
3148        return this.mapObject.GetCenter();
3149    },
3150
3151    /**
3152     * APIMethod: dragPanMapObject
3153     *
3154     * Parameters:
3155     * dX - {Integer}
3156     * dY - {Integer}
3157     */
3158    dragPanMapObject: function(dX, dY) {
3159        this.mapObject.vemapcontrol.PanMap(dX, -dY);
3160    },
3161
3162    /**
3163     * APIMethod: getMapObjectZoom
3164     *
3165     * Returns:
3166     * {Integer} The mapObject's current zoom, in Map Object format
3167     */
3168    getMapObjectZoom: function() {
3169        return this.mapObject.GetZoomLevel();
3170    },
3171
3172
3173  // LonLat - Pixel Translation
3174
3175    /**
3176     * APIMethod: getMapObjectLonLatFromMapObjectPixel
3177     *
3178     * Parameters:
3179     * moPixel - {Object} MapObject Pixel format
3180     *
3181     * Returns:
3182     * {Object} MapObject LonLat translated from MapObject Pixel
3183     */
3184    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
3185        //the conditional here is to test if we are running the v6 of VE
3186        return (typeof VEPixel != 'undefined')
3187            ? this.mapObject.PixelToLatLong(moPixel)
3188            : this.mapObject.PixelToLatLong(moPixel.x, moPixel.y);
3189    },
3190
3191    /**
3192     * APIMethod: getMapObjectPixelFromMapObjectLonLat
3193     *
3194     * Parameters:
3195     * moLonLat - {Object} MapObject LonLat format
3196     *
3197     * Returns:
3198     * {Object} MapObject Pixel transtlated from MapObject LonLat
3199     */
3200    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
3201        return this.mapObject.LatLongToPixel(moLonLat);
3202    },
3203
3204
3205    /************************************
3206     *                                  *
3207     *       MapObject Primitives       *
3208     *                                  *
3209     ************************************/
3210
3211
3212  // LonLat
3213
3214    /**
3215     * APIMethod: getLongitudeFromMapObjectLonLat
3216     *
3217     * Parameters:
3218     * moLonLat - {Object} MapObject LonLat format
3219     *
3220     * Returns:
3221     * {Float} Longitude of the given MapObject LonLat
3222     */
3223    getLongitudeFromMapObjectLonLat: function(moLonLat) {
3224        return this.sphericalMercator ?
3225            this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lon :
3226            moLonLat.Longitude;
3227    },
3228
3229    /**
3230     * APIMethod: getLatitudeFromMapObjectLonLat
3231     *
3232     * Parameters:
3233     * moLonLat - {Object} MapObject LonLat format
3234     *
3235     * Returns:
3236     * {Float} Latitude of the given MapObject LonLat
3237     */
3238    getLatitudeFromMapObjectLonLat: function(moLonLat) {
3239        return this.sphericalMercator ?
3240            this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lat :
3241            moLonLat.Latitude;
3242    },
3243
3244    /**
3245     * APIMethod: getMapObjectLonLatFromLonLat
3246     *
3247     * Parameters:
3248     * lon - {Float}
3249     * lat - {Float}
3250     *
3251     * Returns:
3252     * {Object} MapObject LonLat built from lon and lat params
3253     */
3254    getMapObjectLonLatFromLonLat: function(lon, lat) {
3255        var veLatLong;
3256        if(this.sphericalMercator) {
3257            var lonlat = this.inverseMercator(lon, lat);
3258            veLatLong = new VELatLong(lonlat.lat, lonlat.lon);
3259        } else {
3260            veLatLong = new VELatLong(lat, lon);
3261        }
3262        return veLatLong;
3263    },
3264
3265  // Pixel
3266
3267    /**
3268     * APIMethod: getXFromMapObjectPixel
3269     *
3270     * Parameters:
3271     * moPixel - {Object} MapObject Pixel format
3272     *
3273     * Returns:
3274     * {Integer} X value of the MapObject Pixel
3275     */
3276    getXFromMapObjectPixel: function(moPixel) {
3277        return moPixel.x;
3278    },
3279
3280    /**
3281     * APIMethod: getYFromMapObjectPixel
3282     *
3283     * Parameters:
3284     * moPixel - {Object} MapObject Pixel format
3285     *
3286     * Returns:
3287     * {Integer} Y value of the MapObject Pixel
3288     */
3289    getYFromMapObjectPixel: function(moPixel) {
3290        return moPixel.y;
3291    },
3292
3293    /**
3294     * APIMethod: getMapObjectPixelFromXY
3295     *
3296     * Parameters:
3297     * x - {Integer}
3298     * y - {Integer}
3299     *
3300     * Returns:
3301     * {Object} MapObject Pixel from x and y parameters
3302     */
3303    getMapObjectPixelFromXY: function(x, y) {
3304        //the conditional here is to test if we are running the v6 of VE
3305        return (typeof VEPixel != 'undefined') ? new VEPixel(x, y)
3306                         : new Msn.VE.Pixel(x, y);
3307    },
3308
3309    CLASS_NAME: "OpenLayers.Layer.VirtualEarth"
3310});
3311
3312/*
3313 * Copyright 2007, Google Inc.
3314 *
3315 * Redistribution and use in source and binary forms, with or without
3316 * modification, are permitted provided that the following conditions are met:
3317 *
3318 *  1. Redistributions of source code must retain the above copyright notice,
3319 *     this list of conditions and the following disclaimer.
3320 *  2. Redistributions in binary form must reproduce the above copyright notice,
3321 *     this list of conditions and the following disclaimer in the documentation
3322 *     and/or other materials provided with the distribution.
3323 *  3. Neither the name of Google Inc. nor the names of its contributors may be
3324 *     used to endorse or promote products derived from this software without
3325 *     specific prior written permission.
3326 *
3327 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
3328 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
3329 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
3330 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3331 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
3332 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
3333 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
3334 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3335 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3336 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3337 *
3338 * Sets up google.gears.*, which is *the only* supported way to access Gears.
3339 *
3340 * Circumvent this file at your own risk!
3341 *
3342 * In the future, Gears may automatically define google.gears.* without this
3343 * file. Gears may use these objects to transparently fix bugs and compatibility
3344 * issues. Applications that use the code below will continue to work seamlessly
3345 * when that happens.
3346 */
3347
3348(function() {
3349  // We are already defined. Hooray!
3350  if (window.google && google.gears) {
3351    return;
3352  }
3353
3354  var factory = null;
3355
3356  // Firefox
3357  if (typeof GearsFactory != 'undefined') {
3358    factory = new GearsFactory();
3359  } else {
3360    // IE
3361    try {
3362      factory = new ActiveXObject('Gears.Factory');
3363      // privateSetGlobalObject is only required and supported on WinCE.
3364      if (factory.getBuildInfo().indexOf('ie_mobile') != -1) {
3365        factory.privateSetGlobalObject(this);
3366      }
3367    } catch (e) {
3368      // Safari
3369      if ((typeof navigator.mimeTypes != 'undefined')
3370           && navigator.mimeTypes["application/x-googlegears"]) {
3371        factory = document.createElement("object");
3372        factory.style.display = "none";
3373        factory.width = 0;
3374        factory.height = 0;
3375        factory.type = "application/x-googlegears";
3376        document.documentElement.appendChild(factory);
3377      }
3378    }
3379  }
3380
3381  // *Do not* define any objects if Gears is not installed. This mimics the
3382  // behavior of Gears defining the objects in the future.
3383  if (!factory) {
3384    return;
3385  }
3386
3387  // Now set up the objects, being careful not to overwrite anything.
3388  //
3389  // Note: In Internet Explorer for Windows Mobile, you can't add properties to
3390  // the window object. However, global objects are automatically added as
3391  // properties of the window object in all browsers.
3392  if (!window.google) {
3393    google = {};
3394  }
3395
3396  if (!google.gears) {
3397    google.gears = {factory: factory};
3398  }
3399})();
3400
3401/**
3402 * Class: OpenLayers.Protocol.SQL
3403 * Abstract SQL protocol class.  Not to be instantiated directly.  Use
3404 *     one of the SQL protocol subclasses instead.
3405 *
3406 * Inherits from:
3407 *  - <OpenLayers.Protocol>
3408 */
3409OpenLayers.Protocol.SQL = OpenLayers.Class(OpenLayers.Protocol, {
3410
3411    /**
3412     * APIProperty: databaseName
3413     * {String}
3414     */
3415    databaseName: 'ol',
3416
3417    /**
3418     * APIProperty: tableName
3419     * Name of the database table into which Features should be saved.
3420     */
3421    tableName: "ol_vector_features",
3422
3423    /**
3424     * Property: postReadFiltering
3425     * {Boolean} Whether the filter (if there's one) must be applied after
3426     *      the features have been read from the database; for example the
3427     *      BBOX strategy passes the read method a BBOX spatial filter, if
3428     *      postReadFiltering is true every feature read from the database
3429     *      will go through the BBOX spatial filter, which can be costly;
3430     *      defaults to true.
3431     */
3432    postReadFiltering: true,
3433
3434    /**
3435     * Constructor: OpenLayers.Protocol.SQL
3436     */
3437    initialize: function(options) {
3438        OpenLayers.Protocol.prototype.initialize.apply(this, [options]);
3439    },
3440
3441    /**
3442     * APIMethod: destroy
3443     * Clean up the protocol.
3444     */
3445    destroy: function() {
3446        OpenLayers.Protocol.prototype.destroy.apply(this);
3447    },
3448
3449    /**
3450     * APIMethod: supported
3451     * This should be overridden by specific subclasses
3452     *
3453     * Returns:
3454     * {Boolean} Whether or not the browser supports the SQL backend
3455     */
3456    supported: function() {
3457        return false;
3458    },
3459
3460    /**
3461     * Method: evaluateFilter
3462     * If postReadFiltering is true evaluate the filter against the feature
3463     * and return the result of the evaluation, otherwise return true.
3464     *
3465     * Parameters:
3466     * {<OpenLayers.Feature.Vector>} The feature.
3467     * {<OpenLayers.Filter>} The filter.
3468     *
3469     * Returns:
3470     * {Boolean} true if postReadFiltering if false, the result of the
3471     * filter evaluation otherwise.
3472     */
3473    evaluateFilter: function(feature, filter) {
3474        return filter && this.postReadFiltering ?
3475            filter.evaluate(feature) : true;
3476    },
3477
3478    CLASS_NAME: "OpenLayers.Protocol.SQL"
3479});
3480
3481/**
3482 * Class: OpenLayers.Protocol.SQL.Gears
3483 * This Protocol stores feature in the browser via the Gears Database module
3484 * <http://code.google.com/apis/gears/api_database.html>.
3485 *
3486 * The main advantage is that all the read, create, update and delete operations
3487 * can be done offline.
3488 *
3489 * Inherits from:
3490 *  - <OpenLayers.Protocol.SQL>
3491 */
3492OpenLayers.Protocol.SQL.Gears = OpenLayers.Class(OpenLayers.Protocol.SQL, {
3493
3494    /**
3495     * Property: FID_PREFIX
3496     * {String}
3497     */
3498    FID_PREFIX: '__gears_fid__',
3499
3500    /**
3501     * Property: NULL_GEOMETRY
3502     * {String}
3503     */
3504    NULL_GEOMETRY: '__gears_null_geometry__',
3505
3506    /**
3507     * Property: NULL_FEATURE_STATE
3508     * {String}
3509     */
3510    NULL_FEATURE_STATE: '__gears_null_feature_state__',
3511
3512    /**
3513     * Property: jsonParser
3514     * {<OpenLayers.Format.JSON>}
3515     */
3516    jsonParser: null,
3517
3518    /**
3519     * Property: wktParser
3520     * {<OpenLayers.Format.WKT>}
3521     */
3522    wktParser: null,
3523
3524    /**
3525     * Property: fidRegExp
3526     * {RegExp} Regular expression to know whether a feature was
3527     *      created in offline mode.
3528     */
3529    fidRegExp: null,
3530
3531    /**
3532     * Property: saveFeatureState
3533     * {Boolean} Whether to save the feature state (<OpenLayers.State>)
3534     *      into the database, defaults to true.
3535     */
3536    saveFeatureState: true,
3537
3538    /**
3539     * Property: typeOfFid
3540     * {String} The type of the feature identifier, either "number" or
3541     *      "string", defaults to "string".
3542     */
3543    typeOfFid: "string",
3544
3545    /**
3546     * Property: db
3547     * {GearsDatabase}
3548     */
3549    db: null,
3550
3551    /**
3552     * Constructor: OpenLayers.Protocol.SQL.Gears
3553     */
3554    initialize: function(options) {
3555        if (!this.supported()) {
3556            return;
3557        }
3558        OpenLayers.Protocol.SQL.prototype.initialize.apply(this, [options]);
3559        this.jsonParser = new OpenLayers.Format.JSON();
3560        this.wktParser = new OpenLayers.Format.WKT();
3561
3562        this.fidRegExp = new RegExp('^' + this.FID_PREFIX);
3563        this.initializeDatabase();
3564
3565
3566    },
3567
3568    /**
3569     * Method: initializeDatabase
3570     */
3571    initializeDatabase: function() {
3572        this.db = google.gears.factory.create('beta.database');
3573        this.db.open(this.databaseName);
3574        this.db.execute(
3575            "CREATE TABLE IF NOT EXISTS " + this.tableName +
3576            " (fid TEXT UNIQUE, geometry TEXT, properties TEXT," +
3577            "  state TEXT)");
3578   },
3579
3580    /**
3581     * APIMethod: destroy
3582     * Clean up the protocol.
3583     */
3584    destroy: function() {
3585        this.db.close();
3586        this.db = null;
3587
3588        this.jsonParser = null;
3589        this.wktParser = null;
3590
3591        OpenLayers.Protocol.SQL.prototype.destroy.apply(this);
3592    },
3593
3594    /**
3595     * APIMethod: supported
3596     * Determine whether a browser supports Gears
3597     *
3598     * Returns:
3599     * {Boolean} The browser supports Gears
3600     */
3601    supported: function() {
3602        return !!(window.google && google.gears);
3603    },
3604
3605    /**
3606     * APIMethod: read
3607     * Read all features from the database and return a
3608     * <OpenLayers.Protocol.Response> instance. If the options parameter
3609     * contains a callback attribute, the function is called with the response
3610     * as a parameter.
3611     *
3612     * Parameters:
3613     * options - {Object} Optional object for configuring the request; it
3614     *      can have the {Boolean} property "noFeatureStateReset" which
3615     *      specifies if the state of features read from the Gears
3616     *      database must be reset to null, if "noFeatureStateReset"
3617     *      is undefined or false then each feature's state is reset
3618     *      to null, if "noFeatureStateReset" is true the feature state
3619     *      is preserved.
3620     *
3621     * Returns:
3622     * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
3623     *      object.
3624     */
3625    read: function(options) {
3626        OpenLayers.Protocol.prototype.read.apply(this, arguments);
3627        options = OpenLayers.Util.applyDefaults(options, this.options);
3628
3629        var feature, features = [];
3630        var rs = this.db.execute("SELECT * FROM " + this.tableName);
3631        while (rs.isValidRow()) {
3632            feature = this.unfreezeFeature(rs);
3633            if (this.evaluateFilter(feature, options.filter)) {
3634                if (!options.noFeatureStateReset) {
3635                    feature.state = null;
3636                }
3637                features.push(feature);
3638            }
3639            rs.next();
3640        }
3641        rs.close();
3642
3643        var resp = new OpenLayers.Protocol.Response({
3644            code: OpenLayers.Protocol.Response.SUCCESS,
3645            requestType: "read",
3646            features: features
3647        });
3648
3649        if (options && options.callback) {
3650            options.callback.call(options.scope, resp);
3651        }
3652
3653        return resp;
3654    },
3655
3656    /**
3657     * Method: unfreezeFeature
3658     *
3659     * Parameters:
3660     * row - {ResultSet}
3661     *
3662     * Returns:
3663     * {<OpenLayers.Feature.Vector>}
3664     */
3665    unfreezeFeature: function(row) {
3666        var feature;
3667        var wkt = row.fieldByName('geometry');
3668        if (wkt == this.NULL_GEOMETRY) {
3669            feature = new OpenLayers.Feature.Vector();
3670        } else {
3671            feature = this.wktParser.read(wkt);
3672        }
3673
3674        feature.attributes = this.jsonParser.read(
3675            row.fieldByName('properties'));
3676
3677        feature.fid = this.extractFidFromField(row.fieldByName('fid'));
3678
3679        var state = row.fieldByName('state');
3680        if (state == this.NULL_FEATURE_STATE) {
3681            state = null;
3682        }
3683        feature.state = state;
3684
3685        return feature;
3686    },
3687
3688    /**
3689     * Method: extractFidFromField
3690     *
3691     * Parameters:
3692     * field - {String}
3693     *
3694     * Returns
3695     * {String} or {Number} The fid.
3696     */
3697    extractFidFromField: function(field) {
3698        if (!field.match(this.fidRegExp) && this.typeOfFid == "number") {
3699            field = parseFloat(field);
3700        }
3701        return field;
3702    },
3703
3704    /**
3705     * APIMethod: create
3706     * Create new features into the database.
3707     *
3708     * Parameters:
3709     * features - {Array({<OpenLayers.Feature.Vector>})} or
3710     *            {<OpenLayers.Feature.Vector>} The features to create in
3711     *            the database.
3712     * options - {Object} Optional object for configuring the request.
3713     *
3714     * Returns:
3715     *  {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
3716     *          object.
3717     */
3718    create: function(features, options) {
3719        options = OpenLayers.Util.applyDefaults(options, this.options);
3720
3721        var resp = this.createOrUpdate(features);
3722        resp.requestType = "create";
3723
3724        if (options && options.callback) {
3725            options.callback.call(options.scope, resp);
3726        }
3727
3728        return resp;
3729    },
3730
3731    /**
3732     * APIMethod: update
3733     * Construct a request updating modified feature.
3734     *
3735     * Parameters:
3736     * features - {Array({<OpenLayers.Feature.Vector>})} or
3737     *            {<OpenLayers.Feature.Vector>} The features to update in
3738     *            the database.
3739     * options - {Object} Optional object for configuring the request.
3740     *
3741     * Returns:
3742     *  {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
3743     *          object.
3744     */
3745    update: function(features, options) {
3746        options = OpenLayers.Util.applyDefaults(options, this.options);
3747
3748        var resp = this.createOrUpdate(features);
3749        resp.requestType = "update";
3750
3751        if (options && options.callback) {
3752            options.callback.call(options.scope, resp);
3753        }
3754
3755        return resp;
3756    },
3757
3758    /**
3759     * Method: createOrUpdate
3760     * Construct a request for updating or creating features in the
3761     * database.
3762     *
3763     * Parameters:
3764     * features - {Array({<OpenLayers.Feature.Vector>})} or
3765     *      {<OpenLayers.Feature.Vector>} The feature to create or update
3766     *      in the database.
3767     *
3768     * Returns:
3769     *  {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
3770     *          object.
3771     */
3772    createOrUpdate: function(features) {
3773        if (!(OpenLayers.Util.isArray(features))) {
3774            features = [features];
3775        }
3776
3777        var i, len = features.length, feature;
3778        var insertedFeatures = new Array(len);
3779
3780        for (i = 0; i < len; i++) {
3781            feature = features[i];
3782            var params = this.freezeFeature(feature);
3783            this.db.execute(
3784                "REPLACE INTO " + this.tableName +
3785                " (fid, geometry, properties, state)" +
3786                " VALUES (?, ?, ?, ?)",
3787                params);
3788
3789            var clone = feature.clone();
3790            clone.fid = this.extractFidFromField(params[0]);
3791            insertedFeatures[i] = clone;
3792        }
3793
3794        return new OpenLayers.Protocol.Response({
3795            code: OpenLayers.Protocol.Response.SUCCESS,
3796            features: insertedFeatures,
3797            reqFeatures: features
3798        });
3799    },
3800
3801    /**
3802     * Method: freezeFeature
3803     *
3804     * Parameters:
3805     * feature - {<OpenLayers.Feature.Vector>}
3806     * state - {String} The feature state to store in the database.
3807     *
3808     * Returns:
3809     * {Array}
3810     */
3811    freezeFeature: function(feature) {
3812        // 2 notes:
3813        // - fid might not be a string
3814        // - getFeatureStateForFreeze needs the feature fid to it's stored
3815        //   in the feature here
3816        feature.fid = feature.fid != null ?
3817            "" + feature.fid : OpenLayers.Util.createUniqueID(this.FID_PREFIX);
3818
3819        var geometry = feature.geometry != null ?
3820            feature.geometry.toString() : this.NULL_GEOMETRY;
3821
3822        var properties = this.jsonParser.write(feature.attributes);
3823
3824        var state = this.getFeatureStateForFreeze(feature);
3825
3826        return [feature.fid, geometry, properties, state];
3827    },
3828
3829    /**
3830     * Method: getFeatureStateForFreeze
3831     * Get the state of the feature to store into the database.
3832     *
3833     * Parameters:
3834     * feature - {<OpenLayers.Feature.Vector>} The feature.
3835     *
3836     * Returns
3837     * {String} The state
3838     */
3839    getFeatureStateForFreeze: function(feature) {
3840        var state;
3841        if (!this.saveFeatureState) {
3842            state = this.NULL_FEATURE_STATE;
3843        } else if (this.createdOffline(feature)) {
3844            // if the feature was created in offline mode, its
3845            // state must remain INSERT
3846            state = OpenLayers.State.INSERT;
3847        } else {
3848            state = feature.state;
3849        }
3850        return state;
3851    },
3852
3853    /**
3854     * APIMethod: delete
3855     * Delete features from the database.
3856     *
3857     * Parameters:
3858     * features - {Array({<OpenLayers.Feature.Vector>})} or
3859     *            {<OpenLayers.Feature.Vector>}
3860     * options - {Object} Optional object for configuring the request.
3861     *       This object is modified and should not be reused.
3862     *
3863     * Returns:
3864     *  {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
3865     *          object.
3866     */
3867    "delete": function(features, options) {
3868        if (!(OpenLayers.Util.isArray(features))) {
3869            features = [features];
3870        }
3871
3872        options = OpenLayers.Util.applyDefaults(options, this.options);
3873
3874        var i, len, feature;
3875        for (i = 0, len = features.length; i < len; i++) {
3876            feature = features[i];
3877
3878            // if saveFeatureState is set to true and if the feature wasn't created
3879            // in offline mode we don't delete it in the database but just update
3880            // it state column
3881            if (this.saveFeatureState && !this.createdOffline(feature)) {
3882                var toDelete = feature.clone();
3883                toDelete.fid = feature.fid;
3884                if (toDelete.geometry) {
3885                    toDelete.geometry.destroy();
3886                    toDelete.geometry = null;
3887                }
3888                toDelete.state = feature.state;
3889                this.createOrUpdate(toDelete);
3890            } else {
3891                this.db.execute(
3892                    "DELETE FROM " + this.tableName +
3893                    " WHERE fid = ?", [feature.fid]);
3894            }
3895        }
3896
3897        var resp = new OpenLayers.Protocol.Response({
3898            code: OpenLayers.Protocol.Response.SUCCESS,
3899            requestType: "delete",
3900            reqFeatures: features
3901        });
3902
3903        if (options && options.callback) {
3904            options.callback.call(options.scope, resp);
3905        }
3906
3907        return resp;
3908    },
3909
3910    /**
3911     * Method: createdOffline
3912     * Returns true if the feature had a feature id when it was created in
3913     *      the Gears database, false otherwise; this is determined by
3914     *      checking the form of the feature's fid value.
3915     *
3916     * Parameters:
3917     * feature - {<OpenLayers.Feature.Vector>}
3918     *
3919     * Returns:
3920     * {Boolean}
3921     */
3922    createdOffline: function(feature) {
3923        return (typeof feature.fid == "string" &&
3924                !!(feature.fid.match(this.fidRegExp)));
3925    },
3926
3927    /**
3928     * APIMethod: commit
3929     * Go over the features and for each take action
3930     * based on the feature state. Possible actions are create,
3931     * update and delete.
3932     *
3933     * Parameters:
3934     * features - {Array({<OpenLayers.Feature.Vector>})}
3935     * options - {Object} Object whose possible keys are "create", "update",
3936     *      "delete", "callback" and "scope", the values referenced by the
3937     *      first three are objects as passed to the "create", "update", and
3938     *      "delete" methods, the value referenced by the "callback" key is
3939     *      a function which is called when the commit operation is complete
3940     *      using the scope referenced by the "scope" key.
3941     *
3942     * Returns:
3943     * {Array({<OpenLayers.Protocol.Response>})} An array of
3944     *       <OpenLayers.Protocol.Response> objects, one per request made
3945     *       to the database.
3946     */
3947    commit: function(features, options) {
3948        var opt, resp = [], nRequests = 0, nResponses = 0;
3949
3950        function callback(resp) {
3951            if (++nResponses < nRequests) {
3952                resp.last = false;
3953            }
3954            this.callUserCallback(options, resp);
3955        }
3956
3957        var feature, toCreate = [], toUpdate = [], toDelete = [];
3958        for (var i = features.length - 1; i >= 0; i--) {
3959            feature = features[i];
3960            switch (feature.state) {
3961            case OpenLayers.State.INSERT:
3962                toCreate.push(feature);
3963                break;
3964            case OpenLayers.State.UPDATE:
3965                toUpdate.push(feature);
3966                break;
3967            case OpenLayers.State.DELETE:
3968                toDelete.push(feature);
3969                break;
3970            }
3971        }
3972        if (toCreate.length > 0) {
3973            nRequests++;
3974            opt = OpenLayers.Util.applyDefaults(
3975                {"callback": callback, "scope": this},
3976                options.create
3977            );
3978            resp.push(this.create(toCreate, opt));
3979        }
3980        if (toUpdate.length > 0) {
3981            nRequests++;
3982            opt = OpenLayers.Util.applyDefaults(
3983                {"callback": callback, "scope": this},
3984                options.update
3985            );
3986            resp.push(this.update(toUpdate, opt));
3987        }
3988        if (toDelete.length > 0) {
3989            nRequests++;
3990            opt = OpenLayers.Util.applyDefaults(
3991                {"callback": callback, "scope": this},
3992                options["delete"]
3993            );
3994            resp.push(this["delete"](toDelete, opt));
3995        }
3996
3997        return resp;
3998    },
3999
4000    /**
4001     * Method: clear
4002     * Removes all rows of the table.
4003     */
4004    clear: function() {
4005        this.db.execute("DELETE FROM " + this.tableName);
4006    },
4007
4008    /**
4009     * Method: callUserCallback
4010     * This method is called from within commit each time a request is made
4011     * to the database, it is responsible for calling the user-supplied
4012     * callbacks.
4013     *
4014     * Parameters:
4015     * options - {Object} The map of options passed to the commit call.
4016     * resp - {<OpenLayers.Protocol.Response>}
4017     */
4018    callUserCallback: function(options, resp) {
4019        var opt = options[resp.requestType];
4020        if (opt && opt.callback) {
4021            opt.callback.call(opt.scope, resp);
4022        }
4023        if (resp.last && options.callback) {
4024            options.callback.call(options.scope);
4025        }
4026    },
4027
4028    CLASS_NAME: "OpenLayers.Protocol.SQL.Gears"
4029});
4030
4031/**
4032 * Class: OpenLayers.Layer.Yahoo
4033 *
4034 * Inherits from:
4035 *  - <OpenLayers.Layer.EventPane>
4036 *  - <OpenLayers.Layer.FixedZoomLevels>
4037 */
4038OpenLayers.Layer.Yahoo = OpenLayers.Class(
4039  OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {
4040
4041    /**
4042     * Constant: MIN_ZOOM_LEVEL
4043     * {Integer} 0
4044     */
4045    MIN_ZOOM_LEVEL: 0,
4046
4047    /**
4048     * Constant: MAX_ZOOM_LEVEL
4049     * {Integer} 17
4050     */
4051    MAX_ZOOM_LEVEL: 17,
4052
4053    /**
4054     * Constant: RESOLUTIONS
4055     * {Array(Float)} Hardcode these resolutions so that they are more closely
4056     *                tied with the standard wms projection
4057     */
4058    RESOLUTIONS: [
4059        1.40625,
4060        0.703125,
4061        0.3515625,
4062        0.17578125,
4063        0.087890625,
4064        0.0439453125,
4065        0.02197265625,
4066        0.010986328125,
4067        0.0054931640625,
4068        0.00274658203125,
4069        0.001373291015625,
4070        0.0006866455078125,
4071        0.00034332275390625,
4072        0.000171661376953125,
4073        0.0000858306884765625,
4074        0.00004291534423828125,
4075        0.00002145767211914062,
4076        0.00001072883605957031
4077    ],
4078
4079    /**
4080     * APIProperty: type
4081     * {YahooMapType}
4082     */
4083    type: null,
4084
4085    /**
4086     * APIProperty: wrapDateLine
4087     * {Boolean} Allow user to pan forever east/west.  Default is true.
4088     *     Setting this to false only restricts panning if
4089     *     <sphericalMercator> is true.
4090     */
4091    wrapDateLine: true,
4092
4093    /**
4094     * APIProperty: sphericalMercator
4095     * {Boolean} Should the map act as a mercator-projected map? This will
4096     * cause all interactions with the map to be in the actual map projection,
4097     * which allows support for vector drawing, overlaying other maps, etc.
4098     */
4099    sphericalMercator: false,
4100
4101    /**
4102     * Constructor: OpenLayers.Layer.Yahoo
4103     *
4104     * Parameters:
4105     * name - {String}
4106     * options - {Object}
4107     */
4108    initialize: function(name, options) {
4109        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
4110        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
4111                                                                    arguments);
4112        if(this.sphericalMercator) {
4113            OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
4114            this.initMercatorParameters();
4115        }
4116    },
4117
4118    /**
4119     * Method: loadMapObject
4120     */
4121    loadMapObject:function() {
4122        try { //do not crash!
4123            var size = this.getMapObjectSizeFromOLSize(this.map.getSize());
4124            this.mapObject = new YMap(this.div, this.type, size);
4125            this.mapObject.disableKeyControls();
4126            this.mapObject.disableDragMap();
4127
4128            //can we do smooth panning? (moveByXY is not an API function)
4129            if ( !this.mapObject.moveByXY ||
4130                 (typeof this.mapObject.moveByXY != "function" ) ) {
4131
4132                this.dragPanMapObject = null;
4133            }
4134        } catch(e) {}
4135    },
4136
4137    /**
4138     * Method: onMapResize
4139     *
4140     */
4141    onMapResize: function() {
4142        try {
4143            var size = this.getMapObjectSizeFromOLSize(this.map.getSize());
4144            this.mapObject.resizeTo(size);
4145        } catch(e) {}
4146    },
4147
4148
4149    /**
4150     * APIMethod: setMap
4151     * Overridden from EventPane because we need to remove this yahoo event
4152     *     pane which prohibits our drag and drop, and we can only do this
4153     *     once the map has been loaded and centered.
4154     *
4155     * Parameters:
4156     * map - {<OpenLayers.Map>}
4157     */
4158    setMap: function(map) {
4159        OpenLayers.Layer.EventPane.prototype.setMap.apply(this, arguments);
4160
4161        this.map.events.register("moveend", this, this.fixYahooEventPane);
4162    },
4163
4164    /**
4165     * Method: fixYahooEventPane
4166     * The map has been centered, so the mysterious yahoo eventpane has been
4167     *     added. we remove it so that it doesnt mess with *our* event pane.
4168     */
4169    fixYahooEventPane: function() {
4170        var yahooEventPane = OpenLayers.Util.getElement("ygddfdiv");
4171        if (yahooEventPane != null) {
4172            if (yahooEventPane.parentNode != null) {
4173                yahooEventPane.parentNode.removeChild(yahooEventPane);
4174            }
4175            this.map.events.unregister("moveend", this,
4176                                       this.fixYahooEventPane);
4177        }
4178    },
4179
4180    /**
4181     * APIMethod: getWarningHTML
4182     *
4183     * Returns:
4184     * {String} String with information on why layer is broken, how to get
4185     *          it working.
4186     */
4187    getWarningHTML:function() {
4188        return OpenLayers.i18n(
4189            "getLayerWarning", {'layerType':'Yahoo', 'layerLib':'Yahoo'}
4190        );
4191    },
4192
4193  /********************************************************/
4194  /*                                                      */
4195  /*             Translation Functions                    */
4196  /*                                                      */
4197  /*    The following functions translate GMaps and OL    */
4198  /*     formats for Pixel, LonLat, Bounds, and Zoom      */
4199  /*                                                      */
4200  /********************************************************/
4201
4202
4203  //
4204  // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom
4205  //
4206
4207    /**
4208     * APIMethod: getOLZoomFromMapObjectZoom
4209     *
4210     * Parameters:
4211     * gZoom - {Integer}
4212     *
4213     * Returns:
4214     * {Integer} An OpenLayers Zoom level, translated from the passed in gZoom
4215     *           Returns null if null value is passed in.
4216     */
4217    getOLZoomFromMapObjectZoom: function(moZoom) {
4218        var zoom = null;
4219        if (moZoom != null) {
4220            zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getOLZoomFromMapObjectZoom.apply(this, [moZoom]);
4221            zoom = 18 - zoom;
4222        }
4223        return zoom;
4224    },
4225
4226    /**
4227     * APIMethod: getMapObjectZoomFromOLZoom
4228     *
4229     * Parameters:
4230     * olZoom - {Integer}
4231     *
4232     * Returns:
4233     * {Integer} A MapObject level, translated from the passed in olZoom
4234     *           Returns null if null value is passed in
4235     */
4236    getMapObjectZoomFromOLZoom: function(olZoom) {
4237        var zoom = null;
4238        if (olZoom != null) {
4239            zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getMapObjectZoomFromOLZoom.apply(this, [olZoom]);
4240            zoom = 18 - zoom;
4241        }
4242        return zoom;
4243    },
4244
4245    /************************************
4246     *                                  *
4247     *   MapObject Interface Controls   *
4248     *                                  *
4249     ************************************/
4250
4251
4252  // Get&Set Center, Zoom
4253
4254    /**
4255     * APIMethod: setMapObjectCenter
4256     * Set the mapObject to the specified center and zoom
4257     *
4258     * Parameters:
4259     * center - {Object} MapObject LonLat format
4260     * zoom - {int} MapObject zoom format
4261     */
4262    setMapObjectCenter: function(center, zoom) {
4263        this.mapObject.drawZoomAndCenter(center, zoom);
4264    },
4265
4266    /**
4267     * APIMethod: getMapObjectCenter
4268     *
4269     * Returns:
4270     * {Object} The mapObject's current center in Map Object format
4271     */
4272    getMapObjectCenter: function() {
4273        return this.mapObject.getCenterLatLon();
4274    },
4275
4276    /**
4277     * APIMethod: dragPanMapObject
4278     *
4279     * Parameters:
4280     * dX - {Integer}
4281     * dY - {Integer}
4282     */
4283    dragPanMapObject: function(dX, dY) {
4284        this.mapObject.moveByXY({
4285            'x': -dX,
4286            'y': dY
4287        });
4288    },
4289
4290    /**
4291     * APIMethod: getMapObjectZoom
4292     *
4293     * Returns:
4294     * {Integer} The mapObject's current zoom, in Map Object format
4295     */
4296    getMapObjectZoom: function() {
4297        return this.mapObject.getZoomLevel();
4298    },
4299
4300
4301  // LonLat - Pixel Translation
4302
4303    /**
4304     * APIMethod: getMapObjectLonLatFromMapObjectPixel
4305     *
4306     * Parameters:
4307     * moPixel - {Object} MapObject Pixel format
4308     *
4309     * Returns:
4310     * {Object} MapObject LonLat translated from MapObject Pixel
4311     */
4312    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
4313        return this.mapObject.convertXYLatLon(moPixel);
4314    },
4315
4316    /**
4317     * APIMethod: getMapObjectPixelFromMapObjectLonLat
4318     *
4319     * Parameters:
4320     * moLonLat - {Object} MapObject LonLat format
4321     *
4322     * Returns:
4323     * {Object} MapObject Pixel transtlated from MapObject LonLat
4324     */
4325    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
4326        return this.mapObject.convertLatLonXY(moLonLat);
4327    },
4328
4329
4330    /************************************
4331     *                                  *
4332     *       MapObject Primitives       *
4333     *                                  *
4334     ************************************/
4335
4336
4337  // LonLat
4338
4339    /**
4340     * APIMethod: getLongitudeFromMapObjectLonLat
4341     *
4342     * Parameters:
4343     * moLonLat - {Object} MapObject LonLat format
4344     *
4345     * Returns:
4346     * {Float} Longitude of the given MapObject LonLat
4347     */
4348    getLongitudeFromMapObjectLonLat: function(moLonLat) {
4349        return this.sphericalMercator ?
4350            this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lon :
4351            moLonLat.Lon;
4352    },
4353
4354    /**
4355     * APIMethod: getLatitudeFromMapObjectLonLat
4356     *
4357     * Parameters:
4358     * moLonLat - {Object} MapObject LonLat format
4359     *
4360     * Returns:
4361     * {Float} Latitude of the given MapObject LonLat
4362     */
4363    getLatitudeFromMapObjectLonLat: function(moLonLat) {
4364        return this.sphericalMercator ?
4365            this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lat :
4366            moLonLat.Lat;
4367    },
4368
4369    /**
4370     * APIMethod: getMapObjectLonLatFromLonLat
4371     *
4372     * Parameters:
4373     * lon - {Float}
4374     * lat - {Float}
4375     *
4376     * Returns:
4377     * {Object} MapObject LonLat built from lon and lat params
4378     */
4379    getMapObjectLonLatFromLonLat: function(lon, lat) {
4380        var yLatLong;
4381        if(this.sphericalMercator) {
4382            var lonlat = this.inverseMercator(lon, lat);
4383            yLatLong = new YGeoPoint(lonlat.lat, lonlat.lon);
4384        } else {
4385            yLatLong = new YGeoPoint(lat, lon);
4386        }
4387        return yLatLong;
4388    },
4389
4390  // Pixel
4391
4392    /**
4393     * APIMethod: getXFromMapObjectPixel
4394     *
4395     * Parameters:
4396     * moPixel - {Object} MapObject Pixel format
4397     *
4398     * Returns:
4399     * {Integer} X value of the MapObject Pixel
4400     */
4401    getXFromMapObjectPixel: function(moPixel) {
4402        return moPixel.x;
4403    },
4404
4405    /**
4406     * APIMethod: getYFromMapObjectPixel
4407     *
4408     * Parameters:
4409     * moPixel - {Object} MapObject Pixel format
4410     *
4411     * Returns:
4412     * {Integer} Y value of the MapObject Pixel
4413     */
4414    getYFromMapObjectPixel: function(moPixel) {
4415        return moPixel.y;
4416    },
4417
4418    /**
4419     * APIMethod: getMapObjectPixelFromXY
4420     *
4421     * Parameters:
4422     * x - {Integer}
4423     * y - {Integer}
4424     *
4425     * Returns:
4426     * {Object} MapObject Pixel from x and y parameters
4427     */
4428    getMapObjectPixelFromXY: function(x, y) {
4429        return new YCoordPoint(x, y);
4430    },
4431
4432  // Size
4433
4434    /**
4435     * APIMethod: getMapObjectSizeFromOLSize
4436     *
4437     * Parameters:
4438     * olSize - {<OpenLayers.Size>}
4439     *
4440     * Returns:
4441     * {Object} MapObject Size from olSize parameter
4442     */
4443    getMapObjectSizeFromOLSize: function(olSize) {
4444        return new YSize(olSize.w, olSize.h);
4445    },
4446
4447    CLASS_NAME: "OpenLayers.Layer.Yahoo"
4448});
4449
4450/**
4451 * Class: OpenLayers.Layer.GML
4452 * Create a vector layer by parsing a GML file. The GML file is
4453 *     passed in as a parameter.
4454 * *Deprecated*.  To be removed in 3.0.  Instead use OpenLayers.Layer.Vector
4455 *     with Protocol.HTTP and Strategy.Fixed. Provide the protocol with a
4456 *     format parameter to get the parser you want for your data.
4457 *
4458 * Inherits from:
4459 *  - <OpenLayers.Layer.Vector>
4460 */
4461OpenLayers.Layer.GML = OpenLayers.Class(OpenLayers.Layer.Vector, {
4462
4463    /**
4464      * Property: loaded
4465      * {Boolean} Flag for whether the GML data has been loaded yet.
4466      */
4467    loaded: false,
4468
4469    /**
4470      * APIProperty: format
4471      * {<OpenLayers.Format>} The format you want the data to be parsed with.
4472      */
4473    format: null,
4474
4475    /**
4476     * APIProperty: formatOptions
4477     * {Object} Hash of options which should be passed to the format when it is
4478     * created. Must be passed in the constructor.
4479     */
4480    formatOptions: null,
4481
4482    /**
4483     * Constructor: OpenLayers.Layer.GML
4484     * Load and parse a single file on the web, according to the format
4485     * provided via the 'format' option, defaulting to GML.
4486     *
4487     * Parameters:
4488     * name - {String}
4489     * url - {String} URL of a GML file.
4490     * options - {Object} Hashtable of extra options to tag onto the layer.
4491     */
4492     initialize: function(name, url, options) {
4493        var newArguments = [];
4494        newArguments.push(name, options);
4495        OpenLayers.Layer.Vector.prototype.initialize.apply(this, newArguments);
4496        this.url = url;
4497    },
4498
4499    /**
4500     * APIMethod: setVisibility
4501     * Set the visibility flag for the layer and hide/show&redraw accordingly.
4502     * Fire event unless otherwise specified
4503     * GML will be loaded if the layer is being made visible for the first
4504     * time.
4505     *
4506     * Parameters:
4507     * visible - {Boolean} Whether or not to display the layer
4508     *                          (if in range)
4509     * noEvent - {Boolean}
4510     */
4511    setVisibility: function(visibility, noEvent) {
4512        OpenLayers.Layer.Vector.prototype.setVisibility.apply(this, arguments);
4513        if(this.visibility && !this.loaded){
4514            // Load the GML
4515            this.loadGML();
4516        }
4517    },
4518
4519    /**
4520     * Method: moveTo
4521     * If layer is visible and GML has not been loaded, load GML, then load GML
4522     * and call OpenLayers.Layer.Vector.moveTo() to redraw at the new location.
4523     *
4524     * Parameters:
4525     * bounds - {Object}
4526     * zoomChanged - {Object}
4527     * minor - {Object}
4528     */
4529    moveTo:function(bounds, zoomChanged, minor) {
4530        OpenLayers.Layer.Vector.prototype.moveTo.apply(this, arguments);
4531        // Wait until initialisation is complete before loading GML
4532        // otherwise we can get a race condition where the root HTML DOM is
4533        // loaded after the GML is paited.
4534        // See http://trac.openlayers.org/ticket/404
4535        if(this.visibility && !this.loaded){
4536            this.loadGML();
4537        }
4538    },
4539
4540    /**
4541     * Method: loadGML
4542     */
4543    loadGML: function() {
4544        if (!this.loaded) {
4545            this.events.triggerEvent("loadstart");
4546            OpenLayers.Request.GET({
4547                url: this.url,
4548                success: this.requestSuccess,
4549                failure: this.requestFailure,
4550                scope: this
4551            });
4552            this.loaded = true;
4553        }
4554    },
4555
4556    /**
4557     * Method: setUrl
4558     * Change the URL and reload the GML
4559     *
4560     * Parameters:
4561     * url - {String} URL of a GML file.
4562     */
4563    setUrl:function(url) {
4564        this.url = url;
4565        this.destroyFeatures();
4566        this.loaded = false;
4567        this.loadGML();
4568    },
4569
4570    /**
4571     * Method: requestSuccess
4572     * Process GML after it has been loaded.
4573     * Called by initialize() and loadUrl() after the GML has been loaded.
4574     *
4575     * Parameters:
4576     * request - {String}
4577     */
4578    requestSuccess:function(request) {
4579        var doc = request.responseXML;
4580
4581        if (!doc || !doc.documentElement) {
4582            doc = request.responseText;
4583        }
4584
4585        var options = {};
4586
4587        OpenLayers.Util.extend(options, this.formatOptions);
4588        if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
4589            options.externalProjection = this.projection;
4590            options.internalProjection = this.map.getProjectionObject();
4591        }
4592
4593        var gml = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
4594        this.addFeatures(gml.read(doc));
4595        this.events.triggerEvent("loadend");
4596    },
4597
4598    /**
4599     * Method: requestFailure
4600     * Process a failed loading of GML.
4601     * Called by initialize() and loadUrl() if there was a problem loading GML.
4602     *
4603     * Parameters:
4604     * request - {String}
4605     */
4606    requestFailure: function(request) {
4607        OpenLayers.Console.userError('Error in loading GML file ' +  this.url);
4608        this.events.triggerEvent("loadend");
4609    },
4610
4611    CLASS_NAME: "OpenLayers.Layer.GML"
4612});
4613
4614/**
4615 * Class: OpenLayers.Geometry.Rectangle
4616 * This class is *not supported*, and probably isn't what you're looking for.
4617 *     Instead, most users probably want something like:
4618 *     (code)
4619 *     var poly = new OpenLayers.Bounds(0,0,10,10).toGeometry();
4620 *     (end)
4621 *     This will create a rectangular Polygon geometry.
4622 *
4623 * Inherits:
4624 *  - <OpenLayers.Geometry>
4625 */
4626
4627OpenLayers.Geometry.Rectangle = OpenLayers.Class(OpenLayers.Geometry, {
4628
4629    /**
4630     * Property: x
4631     * {Float}
4632     */
4633    x: null,
4634
4635    /**
4636     * Property: y
4637     * {Float}
4638     */
4639    y: null,
4640
4641    /**
4642     * Property: width
4643     * {Float}
4644     */
4645    width: null,
4646
4647    /**
4648     * Property: height
4649     * {Float}
4650     */
4651    height: null,
4652
4653    /**
4654     * Constructor: OpenLayers.Geometry.Rectangle
4655     *
4656     * Parameters:
4657     * points - {Array(<OpenLayers.Geometry.Point>)}
4658     */
4659    initialize: function(x, y, width, height) {
4660        OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
4661       
4662        this.x = x;
4663        this.y = y;
4664
4665        this.width = width;
4666        this.height = height;
4667    },
4668   
4669    /**
4670     * Method: calculateBounds
4671     * Recalculate the bounds for the geometry.
4672     */
4673    calculateBounds: function() {
4674        this.bounds = new OpenLayers.Bounds(this.x, this.y,
4675                                            this.x + this.width, 
4676                                            this.y + this.height);
4677    },
4678   
4679   
4680    /**
4681     * APIMethod: getLength
4682     *
4683     * Returns:
4684     * {Float} The length of the geometry
4685     */
4686    getLength: function() {
4687        var length = (2 * this.width) + (2 * this.height);
4688        return length;
4689    },
4690
4691    /**
4692     * APIMethod: getArea
4693     *
4694     * Returns:
4695     * {Float} The area of the geometry
4696     */
4697    getArea: function() {
4698        var area = this.width * this.height;
4699        return area;
4700    },   
4701
4702    CLASS_NAME: "OpenLayers.Geometry.Rectangle"
4703});
4704
4705/**
4706 * Class: OpenLayers.Renderer.NG
4707 *
4708 * Inherits from:
4709 *  - <OpenLayers.Renderer.Elements>
4710 */
4711OpenLayers.Renderer.NG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
4712
4713    /**
4714     * Constant: labelNodeType
4715     * {String} The node type for text label containers. To be defined by
4716     * subclasses.
4717     */
4718    labelNodeType: null,
4719
4720    /**
4721     * Constructor: OpenLayers.Renderer.NG
4722     *
4723     * Parameters:
4724     * containerID - {String}
4725     * options - {Object} options for this renderer. Supported options are:
4726     *     * yOrdering - {Boolean} Whether to use y-ordering
4727     *     * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored
4728     *         if yOrdering is set to true.
4729     */
4730
4731    /**
4732     * Method: updateDimensions
4733     * To be extended by subclasses - here we set positioning related styles
4734     * on HTML elements, subclasses have to do the same for renderer specific
4735     * elements (e.g. viewBox, width and height of the rendererRoot)
4736     *
4737     * Parameters:
4738     * zoomChanged - {Boolean} Has the zoom changed? If so, subclasses may have
4739     *     to update feature styles/dimensions.
4740     */
4741    updateDimensions: function(zoomChanged) {
4742        var mapExtent = this.map.getExtent();
4743        var renderExtent = mapExtent.scale(3);
4744        this.setExtent(renderExtent, true);
4745        var res = this.getResolution();
4746        var div = this.rendererRoot.parentNode;
4747        var layerLeft = parseFloat(div.parentNode.style.left);
4748        var layerTop = parseFloat(div.parentNode.style.top);
4749        div.style.left = ((renderExtent.left - mapExtent.left) / res - layerLeft) + "px";
4750        div.style.top = ((mapExtent.top - renderExtent.top) / res - layerTop) + "px";
4751    },
4752
4753    /**
4754     * Method: resize
4755     */
4756    setSize: function() {
4757        this.map.getExtent() && this.updateDimensions();
4758    },
4759
4760    /**
4761     * Method: drawFeature
4762     * Draw the feature.  The optional style argument can be used
4763     * to override the feature's own style.  This method should only
4764     * be called from layer.drawFeature().
4765     *
4766     * Parameters:
4767     * feature - {<OpenLayers.Feature.Vector>}
4768     * style - {<Object>}
4769     *
4770     * Returns:
4771     * {Boolean} true if the feature has been drawn completely, false if not,
4772     *     undefined if the feature had no geometry
4773     */
4774    drawFeature: function(feature, style) {
4775        if(style == null) {
4776            style = feature.style;
4777        }
4778        if (feature.geometry) {
4779            var rendered = this.drawGeometry(feature.geometry, style, feature.id);
4780            if(rendered !== false && style.label) {
4781                var location = feature.geometry.getCentroid();
4782                this.drawText(feature.id, style, location);
4783            } else {
4784                this.removeText(feature.id);
4785            }
4786            return rendered;
4787        }
4788    },
4789
4790    /**
4791     * Method: drawText
4792     * Function for drawing text labels.
4793     * This method is only called by the renderer itself.
4794     *
4795     * Parameters:
4796     * featureId - {String|DOMElement}
4797     * style - {Object}
4798     * location - {<OpenLayers.Geometry.Point>}, will be modified inline
4799     *
4800     * Returns:
4801     * {DOMElement} container holding the text label (to be populated by
4802     * subclasses)
4803     */
4804    drawText: function(featureId, style, location) {
4805        var label;
4806        if (typeof featureId !== "string") {
4807            label = featureId;
4808        } else {
4809            label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, this.labelNodeType);
4810            label._featureId = featureId;
4811        }
4812        label._style = style;
4813        label._x = location.x;
4814        label._y = location.y;
4815        if(style.labelXOffset || style.labelYOffset) {
4816            var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;
4817            var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;
4818            var res = this.getResolution();
4819            location.move(xOffset*res, yOffset*res);
4820        }
4821
4822        if(label.parentNode !== this.textRoot) {
4823            this.textRoot.appendChild(label);
4824        }
4825
4826        return label;
4827    },
4828
4829    CLASS_NAME: "OpenLayers.Renderer.NG"
4830});
4831
4832// Monkey-patching Layer.Vector for Renderer.NG support
4833(function() {
4834    var moveTo = OpenLayers.Layer.Vector.prototype.moveTo;
4835    OpenLayers.Layer.Vector.prototype.moveTo = function(bounds, zoomChanged, dragging) {
4836        if (OpenLayers.Renderer.NG && this.renderer instanceof OpenLayers.Renderer.NG) {
4837            OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
4838            dragging || this.renderer.updateDimensions(zoomChanged);
4839            if (!this.drawn) {
4840                this.drawn = true;
4841                var feature;
4842                for(var i=0, len=this.features.length; i<len; i++) {
4843                    this.renderer.locked = (i !== (len - 1));
4844                    feature = this.features[i];
4845                    this.drawFeature(feature);
4846                }
4847            }
4848        } else {
4849            moveTo.apply(this, arguments);
4850        }
4851    }
4852    var redraw = OpenLayers.Layer.Vector.prototype.redraw;
4853    OpenLayers.Layer.Vector.prototype.redraw = function() {
4854        if (OpenLayers.Renderer.NG && this.renderer instanceof OpenLayers.Renderer.NG) {
4855            this.drawn = false;
4856        }
4857        redraw.apply(this, arguments);
4858    }
4859})();
4860
4861/**
4862 * Class: OpenLayers.Renderer.SVG2
4863 *
4864 * Inherits from:
4865 *  - <OpenLayers.Renderer.NG>
4866 */
4867OpenLayers.Renderer.SVG2 = OpenLayers.Class(OpenLayers.Renderer.NG, {
4868
4869    /**
4870     * Property: xmlns
4871     * {String}
4872     */
4873    xmlns: "http://www.w3.org/2000/svg",
4874
4875    /**
4876     * Property: xlinkns
4877     * {String}
4878     */
4879    xlinkns: "http://www.w3.org/1999/xlink",
4880
4881    /**
4882     * Property: symbolMetrics
4883     * {Object} Cache for symbol metrics according to their svg coordinate
4884     *     space. This is an object keyed by the symbol's id, and values are
4885     *     an object with size, x and y properties.
4886     */
4887    symbolMetrics: null,
4888
4889    /**
4890     * Constant: labelNodeType
4891     * {String} The node type for text label containers.
4892     */
4893    labelNodeType: "g",
4894
4895    /**
4896     * Constructor: OpenLayers.Renderer.SVG2
4897     *
4898     * Parameters:
4899     * containerID - {String}
4900     */
4901    initialize: function(containerID) {
4902        if (!this.supported()) {
4903            return;
4904        }
4905        OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
4906                                                                arguments);
4907
4908        this.symbolMetrics = {};
4909    },
4910
4911    /**
4912     * APIMethod: supported
4913     *
4914     * Returns:
4915     * {Boolean} Whether or not the browser supports the SVG renderer
4916     */
4917    supported: function() {
4918        var svgFeature = "http://www.w3.org/TR/SVG11/feature#";
4919        return (document.implementation &&
4920           (document.implementation.hasFeature("org.w3c.svg", "1.0") ||
4921            document.implementation.hasFeature(svgFeature + "SVG", "1.1") ||
4922            document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1") ));
4923    },
4924
4925    /**
4926     * Method: updateDimensions
4927     *
4928     * Parameters:
4929     * zoomChanged - {Boolean}
4930     */
4931    updateDimensions: function(zoomChanged) {
4932        OpenLayers.Renderer.NG.prototype.updateDimensions.apply(this, arguments);
4933
4934        var res = this.getResolution();
4935
4936        var width = this.extent.getWidth();
4937        var height = this.extent.getHeight();
4938
4939        var extentString = [
4940            this.extent.left,
4941            -this.extent.top,
4942            width,
4943            height
4944        ].join(" ");
4945        this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
4946        this.rendererRoot.setAttributeNS(null, "width", width / res);
4947        this.rendererRoot.setAttributeNS(null, "height", height / res);
4948
4949        if (zoomChanged === true) {
4950            // update styles for the new resolution
4951            var i, len;
4952            var nodes = this.vectorRoot.childNodes;
4953            for (i=0, len=nodes.length; i<len; ++i) {
4954                this.setStyle(nodes[i]);
4955            }
4956            var textNodes = this.textRoot.childNodes;
4957            var label;
4958            for (i=0, len=textNodes.length; i<len; ++i) {
4959                label = textNodes[i];
4960                this.drawText(label, label._style,
4961                    new OpenLayers.Geometry.Point(label._x, label._y)
4962                );
4963            }
4964        }
4965    },
4966
4967    /**
4968     * Method: getNodeType
4969     *
4970     * Parameters:
4971     * geometry - {<OpenLayers.Geometry>}
4972     * style - {Object}
4973     *
4974     * Returns:
4975     * {String} The corresponding node type for the specified geometry
4976     */
4977    getNodeType: function(geometry, style) {
4978        var nodeType = null;
4979        switch (geometry.CLASS_NAME) {
4980            case "OpenLayers.Geometry.Point":
4981                if (style.externalGraphic) {
4982                    nodeType = "image";
4983                } else if (this.isComplexSymbol(style.graphicName)) {
4984                    nodeType = "svg";
4985                } else {
4986                    nodeType = "circle";
4987                }
4988                break;
4989            case "OpenLayers.Geometry.Rectangle":
4990                nodeType = "rect";
4991                break;
4992            case "OpenLayers.Geometry.LineString":
4993                nodeType = "polyline";
4994                break;
4995            case "OpenLayers.Geometry.LinearRing":
4996                nodeType = "polygon";
4997                break;
4998            case "OpenLayers.Geometry.Polygon":
4999            case "OpenLayers.Geometry.Curve":
5000                nodeType = "path";
5001                break;
5002            default:
5003                break;
5004        }
5005        return nodeType;
5006    },
5007
5008    /**
5009     * Method: setStyle
5010     * Use to set all the style attributes to a SVG node.
5011     *
5012     * Takes care to adjust stroke width and point radius to be
5013     * resolution-relative
5014     *
5015     * Parameters:
5016     * node - {SVGDomElement} An SVG element to decorate
5017     * style - {Object}
5018     * options - {Object} Currently supported options include
5019     *                              'isFilled' {Boolean} and
5020     *                              'isStroked' {Boolean}
5021     */
5022    setStyle: function(node, style, options) {
5023        style = style  || node._style;
5024        options = options || node._options;
5025        var resolution = this.getResolution();
5026        var r = node._radius;
5027        var widthFactor = resolution;
5028        if (node._geometryClass == "OpenLayers.Geometry.Point" && r) {
5029            node.style.visibility = "";
5030            if (style.graphic === false) {
5031                node.style.visibility = "hidden";
5032            } else if (style.externalGraphic) {
5033
5034                if (style.graphicTitle) {
5035                    node.setAttributeNS(null, "title", style.graphicTitle);
5036                    //Standards-conformant SVG
5037                    // Prevent duplicate nodes. See issue https://github.com/openlayers/openlayers/issues/92
5038                    var titleNode = node.getElementsByTagName("title");
5039                    if (titleNode.length > 0) {
5040                        titleNode[0].firstChild.textContent = style.graphicTitle;
5041                    } else {
5042                        var label = this.nodeFactory(null, "title");
5043                        label.textContent = style.graphicTitle;
5044                        node.appendChild(label);
5045                    }
5046                }
5047                if (style.graphicWidth && style.graphicHeight) {
5048                    node.setAttributeNS(null, "preserveAspectRatio", "none");
5049                }
5050                var width = style.graphicWidth || style.graphicHeight;
5051                var height = style.graphicHeight || style.graphicWidth;
5052                width = width ? width : style.pointRadius*2;
5053                height = height ? height : style.pointRadius*2;
5054                width *= resolution;
5055                height *= resolution;
5056
5057                var xOffset = (style.graphicXOffset != undefined) ?
5058                    style.graphicXOffset * resolution : -(0.5 * width);
5059                var yOffset = (style.graphicYOffset != undefined) ?
5060                    style.graphicYOffset * resolution : -(0.5 * height);
5061
5062                var opacity = style.graphicOpacity || style.fillOpacity;
5063
5064                node.setAttributeNS(null, "x", node._x + xOffset);
5065                node.setAttributeNS(null, "y", node._y + yOffset);
5066                node.setAttributeNS(null, "width", width);
5067                node.setAttributeNS(null, "height", height);
5068                node.setAttributeNS(this.xlinkns, "href", style.externalGraphic);
5069                node.setAttributeNS(null, "style", "opacity: "+opacity);
5070                node.onclick = OpenLayers.Renderer.SVG2.preventDefault;
5071            } else if (this.isComplexSymbol(style.graphicName)) {
5072                // the symbol viewBox is three times as large as the symbol
5073                var offset = style.pointRadius * 3 * resolution;
5074                var size = offset * 2;
5075                var src = this.importSymbol(style.graphicName);
5076                widthFactor = this.symbolMetrics[src.id].size * 3 / size * resolution;
5077
5078                // remove the node from the dom before we modify it. This
5079                // prevents various rendering issues in Safari and FF
5080                var parent = node.parentNode;
5081                var nextSibling = node.nextSibling;
5082                if(parent) {
5083                    parent.removeChild(node);
5084                }
5085
5086                // The more appropriate way to implement this would be use/defs,
5087                // but due to various issues in several browsers, it is safer to
5088                // copy the symbols instead of referencing them.
5089                // See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985
5090                // and this email thread
5091                // http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html
5092                node.firstChild && node.removeChild(node.firstChild);
5093                node.appendChild(src.firstChild.cloneNode(true));
5094                node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox"));
5095
5096                node.setAttributeNS(null, "width", size);
5097                node.setAttributeNS(null, "height", size);
5098                node.setAttributeNS(null, "x", node._x - offset);
5099                node.setAttributeNS(null, "y", node._y - offset);
5100
5101                // now that the node has all its new properties, insert it
5102                // back into the dom where it was
5103                if(nextSibling) {
5104                    parent.insertBefore(node, nextSibling);
5105                } else if(parent) {
5106                    parent.appendChild(node);
5107                }
5108            } else {
5109                node.setAttributeNS(null, "r", style.pointRadius * resolution);
5110            }
5111
5112            var rotation = style.rotation;
5113            if (rotation !== undefined || node._rotation !== undefined) {
5114                node._rotation = rotation;
5115                rotation |= 0;
5116                if (node.nodeName !== "svg") {
5117                    node.setAttributeNS(null, "transform",
5118                        ["rotate(", rotation, node._x, node._y, ")"].join(" ")
5119                    );
5120                } else {
5121                    var metrics = this.symbolMetrics[src.id];
5122                    node.firstChild.setAttributeNS(null, "transform",
5123                        ["rotate(", rotation, metrics.x, metrics.y, ")"].join(" ")
5124                    );
5125                }
5126            }
5127        }
5128
5129        if (options.isFilled) {
5130            node.setAttributeNS(null, "fill", style.fillColor);
5131            node.setAttributeNS(null, "fill-opacity", style.fillOpacity);
5132        } else {
5133            node.setAttributeNS(null, "fill", "none");
5134        }
5135
5136        if (options.isStroked) {
5137            node.setAttributeNS(null, "stroke", style.strokeColor);
5138            node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity);
5139            node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor);
5140            node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap || "round");
5141            // Hard-coded linejoin for now, to make it look the same as in VML.
5142            // There is no strokeLinejoin property yet for symbolizers.
5143            node.setAttributeNS(null, "stroke-linejoin", "round");
5144            style.strokeDashstyle && node.setAttributeNS(null,
5145                "stroke-dasharray", this.dashStyle(style, widthFactor));
5146        } else {
5147            node.setAttributeNS(null, "stroke", "none");
5148        }
5149
5150        if (style.pointerEvents) {
5151            node.setAttributeNS(null, "pointer-events", style.pointerEvents);
5152        }
5153
5154        if (style.cursor != null) {
5155            node.setAttributeNS(null, "cursor", style.cursor);
5156        }
5157
5158        return node;
5159    },
5160
5161    /**
5162     * Method: dashStyle
5163     *
5164     * Parameters:
5165     * style - {Object}
5166     * widthFactor - {Number}
5167     *
5168     * Returns:
5169     * {String} A SVG compliant 'stroke-dasharray' value
5170     */
5171    dashStyle: function(style, widthFactor) {
5172        var w = style.strokeWidth * widthFactor;
5173        var str = style.strokeDashstyle;
5174        switch (str) {
5175            case 'solid':
5176                return 'none';
5177            case 'dot':
5178                return [widthFactor, 4 * w].join();
5179            case 'dash':
5180                return [4 * w, 4 * w].join();
5181            case 'dashdot':
5182                return [4 * w, 4 * w, widthFactor, 4 * w].join();
5183            case 'longdash':
5184                return [8 * w, 4 * w].join();
5185            case 'longdashdot':
5186                return [8 * w, 4 * w, widthFactor, 4 * w].join();
5187            default:
5188                var parts = OpenLayers.String.trim(str).split(/\s+/g);
5189                for (var i=0, ii=parts.length; i<ii; i++) {
5190                    parts[i] = parts[i] * widthFactor;
5191                }
5192                return parts.join();
5193        }
5194    },
5195
5196    /**
5197     * Method: createNode
5198     *
5199     * Parameters:
5200     * type - {String} Kind of node to draw
5201     * id - {String} Id for node
5202     *
5203     * Returns:
5204     * {DOMElement} A new node of the given type and id
5205     */
5206    createNode: function(type, id) {
5207        var node = document.createElementNS(this.xmlns, type);
5208        if (id) {
5209            node.setAttributeNS(null, "id", id);
5210        }
5211        return node;
5212    },
5213
5214    /**
5215     * Method: nodeTypeCompare
5216     *
5217     * Parameters:
5218     * node - {SVGDomElement} An SVG element
5219     * type - {String} Kind of node
5220     *
5221     * Returns:
5222     * {Boolean} Whether or not the specified node is of the specified type
5223     */
5224    nodeTypeCompare: function(node, type) {
5225        return (type == node.nodeName);
5226    },
5227
5228    /**
5229     * Method: createRenderRoot
5230     *
5231     * Returns:
5232     * {DOMElement} The specific render engine's root element
5233     */
5234    createRenderRoot: function() {
5235        return this.nodeFactory(this.container.id + "_svgRoot", "svg");
5236    },
5237
5238    /**
5239     * Method: createRoot
5240     *
5241     * Parameters:
5242     * suffix - {String} suffix to append to the id
5243     *
5244     * Returns:
5245     * {DOMElement}
5246     */
5247    createRoot: function(suffix) {
5248        return this.nodeFactory(this.container.id + suffix, "g");
5249    },
5250
5251    /**
5252     * Method: createDefs
5253     *
5254     * Returns:
5255     * {DOMElement} The element to which we'll add the symbol definitions
5256     */
5257    createDefs: function() {
5258        var defs = this.nodeFactory(this.container.id + "_defs", "defs");
5259        this.rendererRoot.appendChild(defs);
5260        return defs;
5261    },
5262
5263    /**************************************
5264     *                                    *
5265     *     GEOMETRY DRAWING FUNCTIONS     *
5266     *                                    *
5267     **************************************/
5268
5269    /**
5270     * Method: drawPoint
5271     * This method is only called by the renderer itself.
5272     *
5273     * Parameters:
5274     * node - {DOMElement}
5275     * geometry - {<OpenLayers.Geometry>}
5276     *
5277     * Returns:
5278     * {DOMElement} or false if the renderer could not draw the point
5279     */
5280    drawPoint: function(node, geometry) {
5281        return this.drawCircle(node, geometry, 1);
5282    },
5283
5284    /**
5285     * Method: drawCircle
5286     * This method is only called by the renderer itself.
5287     *
5288     * Parameters:
5289     * node - {DOMElement}
5290     * geometry - {<OpenLayers.Geometry>}
5291     * radius - {Float}
5292     *
5293     * Returns:
5294     * {DOMElement} or false if the renderer could not draw the circle
5295     */
5296    drawCircle: function(node, geometry, radius) {
5297        var x = geometry.x;
5298        var y = -geometry.y;
5299        node.setAttributeNS(null, "cx", x);
5300        node.setAttributeNS(null, "cy", y);
5301        node._x = x;
5302        node._y = y;
5303        node._radius = radius;
5304        return node;
5305    },
5306
5307    /**
5308     * Method: drawLineString
5309     * This method is only called by the renderer itself.
5310     *
5311     * Parameters:
5312     * node - {DOMElement}
5313     * geometry - {<OpenLayers.Geometry>}
5314     *
5315     * Returns:
5316     * {DOMElement} or null if the renderer could not draw all components of
5317     *     the linestring, or false if nothing could be drawn
5318     */
5319    drawLineString: function(node, geometry) {
5320        var path = this.getComponentsString(geometry.components);
5321        node.setAttributeNS(null, "points", path);
5322        return node;
5323    },
5324
5325    /**
5326     * Method: drawLinearRing
5327     * This method is only called by the renderer itself.
5328     *
5329     * Parameters:
5330     * node - {DOMElement}
5331     * geometry - {<OpenLayers.Geometry>}
5332     *
5333     * Returns:
5334     * {DOMElement} or null if the renderer could not draw all components
5335     *     of the linear ring, or false if nothing could be drawn
5336     */
5337    drawLinearRing: function(node, geometry) {
5338        var path = this.getComponentsString(geometry.components);
5339        node.setAttributeNS(null, "points", path);
5340        return node;
5341    },
5342
5343    /**
5344     * Method: drawPolygon
5345     * This method is only called by the renderer itself.
5346     *
5347     * Parameters:
5348     * node - {DOMElement}
5349     * geometry - {<OpenLayers.Geometry>}
5350     *
5351     * Returns:
5352     * {DOMElement} or null if the renderer could not draw all components
5353     *     of the polygon, or false if nothing could be drawn
5354     */
5355    drawPolygon: function(node, geometry) {
5356        var d = [];
5357        var draw = true;
5358        var complete = true;
5359        var linearRingResult, path;
5360        for (var j=0, len=geometry.components.length; j<len; j++) {
5361            d.push("M");
5362            path = this.getComponentsString(
5363                geometry.components[j].components, " ");
5364            d.push(path);
5365        }
5366        d.push("z");
5367        node.setAttributeNS(null, "d", d.join(" "));
5368        node.setAttributeNS(null, "fill-rule", "evenodd");
5369        return node;
5370    },
5371
5372    /**
5373     * Method: drawRectangle
5374     * This method is only called by the renderer itself.
5375     *
5376     * Parameters:
5377     * node - {DOMElement}
5378     * geometry - {<OpenLayers.Geometry>}
5379     *
5380     * Returns:
5381     * {DOMElement} or false if the renderer could not draw the rectangle
5382     */
5383    drawRectangle: function(node, geometry) {
5384        node.setAttributeNS(null, "x", geometry.x);
5385        node.setAttributeNS(null, "y", -geometry.y);
5386        node.setAttributeNS(null, "width", geometry.width);
5387        node.setAttributeNS(null, "height", geometry.height);
5388        return node;
5389    },
5390
5391    /**
5392     * Method: drawText
5393     * Function for drawing text labels.
5394     * This method is only called by the renderer itself.
5395     *
5396     * Parameters:
5397     * featureId - {String|DOMElement}
5398     * style - {Object}
5399     * location - {<OpenLayers.Geometry.Point>}, will be modified inline
5400     *
5401     * Returns:
5402     * {DOMElement} container holding the text label
5403     */
5404    drawText: function(featureId, style, location) {
5405        var g = OpenLayers.Renderer.NG.prototype.drawText.apply(this, arguments);
5406        var text = g.firstChild ||
5407            this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_text", "text");
5408
5409        var res = this.getResolution();
5410        text.setAttributeNS(null, "x", location.x / res);
5411        text.setAttributeNS(null, "y", - location.y / res);
5412        g.setAttributeNS(null, "transform", "scale(" + res + ")");
5413
5414        if (style.fontColor) {
5415            text.setAttributeNS(null, "fill", style.fontColor);
5416        }
5417        if (style.fontOpacity) {
5418            text.setAttributeNS(null, "opacity", style.fontOpacity);
5419        }
5420        if (style.fontFamily) {
5421            text.setAttributeNS(null, "font-family", style.fontFamily);
5422        }
5423        if (style.fontSize) {
5424            text.setAttributeNS(null, "font-size", style.fontSize);
5425        }
5426        if (style.fontWeight) {
5427            text.setAttributeNS(null, "font-weight", style.fontWeight);
5428        }
5429        if (style.fontStyle) {
5430            text.setAttributeNS(null, "font-style", style.fontStyle);
5431        }
5432        if (style.labelSelect === true) {
5433            text.setAttributeNS(null, "pointer-events", "visible");
5434            text._featureId = featureId;
5435        } else {
5436            text.setAttributeNS(null, "pointer-events", "none");
5437        }
5438        var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign;
5439        text.setAttributeNS(null, "text-anchor",
5440            OpenLayers.Renderer.SVG2.LABEL_ALIGN[align[0]] || "middle");
5441
5442        if (OpenLayers.IS_GECKO === true) {
5443            text.setAttributeNS(null, "dominant-baseline",
5444                OpenLayers.Renderer.SVG2.LABEL_ALIGN[align[1]] || "central");
5445        }
5446
5447        var labelRows = style.label.split('\n');
5448        var numRows = labelRows.length;
5449        while (text.childNodes.length > numRows) {
5450            text.removeChild(text.lastChild);
5451        }
5452        for (var i = 0; i < numRows; i++) {
5453            var tspan = text.childNodes[i] ||
5454                this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan_" + i, "tspan");
5455            if (style.labelSelect === true) {
5456                tspan._featureId = featureId;
5457            }
5458            if (OpenLayers.IS_GECKO === false) {
5459                tspan.setAttributeNS(null, "baseline-shift",
5460                    OpenLayers.Renderer.SVG2.LABEL_VSHIFT[align[1]] || "-35%");
5461            }
5462            tspan.setAttribute("x", location.x / res);
5463            if (i == 0) {
5464                var vfactor = OpenLayers.Renderer.SVG2.LABEL_VFACTOR[align[1]];
5465                if (vfactor == null) {
5466                    vfactor = -.5;
5467                }
5468                tspan.setAttribute("dy", (vfactor*(numRows-1)) + "em");
5469            } else {
5470                tspan.setAttribute("dy", "1em");
5471            }
5472            tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i];
5473            if (!tspan.parentNode) {
5474                text.appendChild(tspan);
5475            }
5476        }
5477
5478        if (!text.parentNode) {
5479            g.appendChild(text);
5480        }
5481
5482        return g;
5483    },
5484
5485    /**
5486     * Method: getComponentString
5487     *
5488     * Parameters:
5489     * components - {Array(<OpenLayers.Geometry.Point>)} Array of points
5490     * separator - {String} character between coordinate pairs. Defaults to ","
5491     *
5492     * Returns:
5493     * {Object} hash with properties "path" (the string created from the
5494     *     components and "complete" (false if the renderer was unable to
5495     *     draw all components)
5496     */
5497    getComponentsString: function(components, separator) {
5498        var len = components.length;
5499        var strings = new Array(len);
5500        for (var i=0; i<len; i++) {
5501            strings[i] = this.getShortString(components[i]);
5502        }
5503
5504        return strings.join(separator || ",");
5505    },
5506
5507    /**
5508     * Method: getShortString
5509     *
5510     * Parameters:
5511     * point - {<OpenLayers.Geometry.Point>}
5512     *
5513     * Returns:
5514     * {String} or false if point is outside the valid range
5515     */
5516    getShortString: function(point) {
5517        return point.x + "," + (-point.y);
5518    },
5519
5520    /**
5521     * Method: importSymbol
5522     * add a new symbol definition from the rendererer's symbol hash
5523     *
5524     * Parameters:
5525     * graphicName - {String} name of the symbol to import
5526     *
5527     * Returns:
5528     * {DOMElement} - the imported symbol
5529     */
5530    importSymbol: function (graphicName)  {
5531        if (!this.defs) {
5532            // create svg defs tag
5533            this.defs = this.createDefs();
5534        }
5535        var id = this.container.id + "-" + graphicName;
5536
5537        // check if symbol already exists in the defs
5538        var existing = document.getElementById(id);
5539        if (existing != null) {
5540            return existing;
5541        }
5542
5543        var symbol = OpenLayers.Renderer.symbol[graphicName];
5544        if (!symbol) {
5545            throw new Error(graphicName + ' is not a valid symbol name');
5546        }
5547
5548        var symbolNode = this.nodeFactory(id, "symbol");
5549        var node = this.nodeFactory(null, "polygon");
5550        symbolNode.appendChild(node);
5551        var symbolExtent = new OpenLayers.Bounds(
5552                                    Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
5553
5554        var points = [];
5555        var x,y;
5556        for (var i=0, len=symbol.length; i<len; i=i+2) {
5557            x = symbol[i];
5558            y = symbol[i+1];
5559            symbolExtent.left = Math.min(symbolExtent.left, x);
5560            symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
5561            symbolExtent.right = Math.max(symbolExtent.right, x);
5562            symbolExtent.top = Math.max(symbolExtent.top, y);
5563            points.push(x, ",", y);
5564        }
5565
5566        node.setAttributeNS(null, "points", points.join(" "));
5567
5568        var width = symbolExtent.getWidth();
5569        var height = symbolExtent.getHeight();
5570        // create a viewBox three times as large as the symbol itself,
5571        // to allow for strokeWidth being displayed correctly at the corners.
5572        var viewBox = [symbolExtent.left - width,
5573                        symbolExtent.bottom - height, width * 3, height * 3];
5574        symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
5575        this.symbolMetrics[id] = {
5576            size: Math.max(width, height),
5577            x: symbolExtent.getCenterLonLat().lon,
5578            y: symbolExtent.getCenterLonLat().lat
5579        };
5580
5581        this.defs.appendChild(symbolNode);
5582        return symbolNode;
5583    },
5584
5585    /**
5586     * Method: getFeatureIdFromEvent
5587     *
5588     * Parameters:
5589     * evt - {Object} An <OpenLayers.Event> object
5590     *
5591     * Returns:
5592     * {String} A feature id or undefined.
5593     */
5594    getFeatureIdFromEvent: function(evt) {
5595        var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);
5596        if(!featureId) {
5597            var target = evt.target;
5598            featureId = target.parentNode && target != this.rendererRoot ?
5599                target.parentNode._featureId : undefined;
5600        }
5601        return featureId;
5602    },
5603
5604    CLASS_NAME: "OpenLayers.Renderer.SVG2"
5605});
5606
5607/**
5608 * Constant: OpenLayers.Renderer.SVG2.LABEL_ALIGN
5609 * {Object}
5610 */
5611OpenLayers.Renderer.SVG2.LABEL_ALIGN = {
5612    "l": "start",
5613    "r": "end",
5614    "b": "bottom",
5615    "t": "hanging"
5616};
5617
5618/**
5619 * Constant: OpenLayers.Renderer.SVG2.LABEL_VSHIFT
5620 * {Object}
5621 */
5622OpenLayers.Renderer.SVG2.LABEL_VSHIFT = {
5623    // according to
5624    // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html
5625    // a baseline-shift of -70% shifts the text exactly from the
5626    // bottom to the top of the baseline, so -35% moves the text to
5627    // the center of the baseline.
5628    "t": "-70%",
5629    "b": "0"
5630};
5631
5632/**
5633 * Constant: OpenLayers.Renderer.SVG2.LABEL_VFACTOR
5634 * {Object}
5635 */
5636OpenLayers.Renderer.SVG2.LABEL_VFACTOR = {
5637    "t": 0,
5638    "b": -1
5639};
5640
5641/**
5642 * Function: OpenLayers.Renderer.SVG2.preventDefault
5643 * Used to prevent default events (especially opening images in a new tab on
5644 * ctrl-click) from being executed for externalGraphic and graphicName symbols
5645 */
5646OpenLayers.Renderer.SVG2.preventDefault = function(e) {
5647    e.preventDefault && e.preventDefault();
5648};
Note: See TracBrowser for help on using the repository browser.