Project

General

Profile

Download (176 KB) Statistics
| Branch: | Tag: | Revision:
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
 * @requires OpenLayers/Popup/Anchored.js
26
 * @requires Rico/Corner.js
27
 */
28

    
29
/**
30
 * About: Deprecated
31
 * The deprecated.js script includes all methods, properties, and constructors
32
 * that are not supported as part of the long-term API.  If you use any of
33
 * these, you have to explicitly include this script in your application.
34
 *
35
 * For example:
36
 * (code)
37
 *     <script src="deprecated.js" type="text/javascript"></script>
38
 * (end)
39
 *
40
 * You are strongly encouraged to avoid using deprecated functionality.  The
41
 * documentation here should point you to the supported alternatives.
42
 */
43

    
44
/**
45
 * Namespace: OpenLayers.Class
46
 */
47

    
48
/**
49
 * Property: isPrototype
50
 * *Deprecated*.  This is no longer needed and will be removed at 3.0.
51
 */
52
OpenLayers.Class.isPrototype = function () {};
53

    
54
/**
55
 * APIFunction: OpenLayers.create
56
 * *Deprecated*.  Old method to create an OpenLayers style class.  Use the
57
 *     <OpenLayers.Class> constructor instead.
58
 *
59
 * Returns:
60
 * An OpenLayers class
61
 */
62
OpenLayers.Class.create = function() {
63
    return function() {
64
        if (arguments && arguments[0] != OpenLayers.Class.isPrototype) {
65
            this.initialize.apply(this, arguments);
66
        }
67
    };
68
};
69

    
70
/**
71
 * APIFunction: inherit
72
 * *Deprecated*.  Old method to inherit from one or more OpenLayers style
73
 *     classes.  Use the <OpenLayers.Class> constructor instead.
74
 *
75
 * Parameters:
76
 * class - One or more classes can be provided as arguments
77
 *
78
 * Returns:
79
 * An object prototype
80
 */
81
OpenLayers.Class.inherit = function (P) {
82
    var C = function() {
83
       P.call(this);
84
    };
85
    var newArgs = [C].concat(Array.prototype.slice.call(arguments));
86
    OpenLayers.inherit.apply(null, newArgs);
87
    return C.prototype;
88
};
89

    
90
/**
91
 * Namespace: OpenLayers.Util
92
 */
93

    
94
/**
95
 * Function: clearArray
96
 * *Deprecated*. This function will disappear in 3.0.
97
 * Please use "array.length = 0" instead.
98
 * 
99
 * Parameters:
100
 * array - {Array}
101
 */
102
OpenLayers.Util.clearArray = function(array) {
103
    OpenLayers.Console.warn(
104
        OpenLayers.i18n(
105
            "methodDeprecated", {'newMethod': 'array = []'}
106
        )
107
    );
108
    array.length = 0;
109
};
110

    
111
/**
112
 * Function: setOpacity
113
 * *Deprecated*.  This function has been deprecated. Instead, please use 
114
 *     <OpenLayers.Util.modifyDOMElement> 
115
 *     or 
116
 *     <OpenLayers.Util.modifyAlphaImageDiv>
117
 * 
118
 * Set the opacity of a DOM Element
119
 *     Note that for this function to work in IE, elements must "have layout"
120
 *     according to:
121
 *     http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/haslayout.asp
122
 *
123
 * Parameters:
124
 * element - {DOMElement} Set the opacity on this DOM element
125
 * opacity - {Float} Opacity value (0.0 - 1.0)
126
 */
127
OpenLayers.Util.setOpacity = function(element, opacity) {
128
    OpenLayers.Util.modifyDOMElement(element, null, null, null,
129
                                     null, null, null, opacity);
130
};
131

    
132
/**
133
 * Function: safeStopPropagation
134
 * *Deprecated*. This function has been deprecated. Please use directly 
135
 *     <OpenLayers.Event.stop> passing 'true' as the 2nd 
136
 *     argument (preventDefault)
137
 * 
138
 * Safely stop the propagation of an event *without* preventing
139
 *   the default browser action from occurring.
140
 * 
141
 * Parameters:
142
 * evt - {Event}
143
 */
144
OpenLayers.Util.safeStopPropagation = function(evt) {
145
    OpenLayers.Event.stop(evt, true);
146
};
147

    
148
/**
149
 * Function: getArgs
150
 * *Deprecated*.  Will be removed in 3.0.  Please use instead
151
 *     <OpenLayers.Util.getParameters>
152
 * 
153
 * Parameters:
154
 * url - {String} Optional url used to extract the query string.
155
 *                If null, query string is taken from page location.
156
 * 
157
 * Returns:
158
 * {Object} An object of key/value pairs from the query string.
159
 */
160
OpenLayers.Util.getArgs = function(url) {
161
    OpenLayers.Console.warn(
162
        OpenLayers.i18n(
163
            "methodDeprecated", {'newMethod': 'OpenLayers.Util.getParameters'}
164
        )
165
    );
166
    return OpenLayers.Util.getParameters(url);
167
};
168

    
169
/** 
170
 * Maintain existing definition of $.
171
 * 
172
 * The use of our $-method is deprecated and the mapping of 
173
 * OpenLayers.Util.getElement will eventually be removed. Do not depend on 
174
 * window.$ being defined by OpenLayers.
175
 */
176
if(typeof window.$  === "undefined") {
177
    window.$ = OpenLayers.Util.getElement;
178
}
179

    
180
/**
181
 * Namespace: OpenLayers.Ajax
182
 */
183

    
184
/**
185
 * Function: OpenLayers.nullHandler
186
 * @param {} request
187
 */
188
OpenLayers.nullHandler = function(request) {
189
    OpenLayers.Console.userError(OpenLayers.i18n("unhandledRequest", {'statusText':request.statusText}));
190
};
191

    
192
/** 
193
 * APIFunction: OpenLayers.loadURL
194
 * Background load a document.
195
 * *Deprecated*.  Use <OpenLayers.Request.GET> method instead.
196
 *
197
 * Parameters:
198
 * uri - {String} URI of source doc
199
 * params - {String} or {Object} GET params. Either a string in the form
200
 *     "?hello=world&foo=bar" (do not forget the leading question mark)
201
 *     or an object in the form {'hello': 'world', 'foo': 'bar}
202
 * caller - {Object} object which gets callbacks
203
 * onComplete - {Function} Optional callback for success.  The callback
204
 *     will be called with this set to caller and will receive the request
205
 *     object as an argument.  Note that if you do not specify an onComplete
206
 *     function, <OpenLayers.nullHandler> will be called (which pops up a 
207
 *     user friendly error message dialog).
208
 * onFailure - {Function} Optional callback for failure.  In the event of
209
 *     a failure, the callback will be called with this set to caller and will
210
 *     receive the request object as an argument.  Note that if you do not
211
 *     specify an onComplete function, <OpenLayers.nullHandler> will be called
212
 *     (which pops up a user friendly error message dialog).
213
 *
214
 * Returns:
215
 * {<OpenLayers.Request.XMLHttpRequest>}  The request object. To abort loading,
216
 *     call request.abort().
217
 */
218
OpenLayers.loadURL = function(uri, params, caller,
219
                                  onComplete, onFailure) {
220
    
221
    if(typeof params == 'string') {
222
        params = OpenLayers.Util.getParameters(params);
223
    }
224
    var success = (onComplete) ? onComplete : OpenLayers.nullHandler;
225
    var failure = (onFailure) ? onFailure : OpenLayers.nullHandler;
226
    
227
    return OpenLayers.Request.GET({
228
        url: uri, params: params,
229
        success: success, failure: failure, scope: caller
230
    });
231
};
232

    
233
/** 
234
 * Function: OpenLayers.parseXMLString
235
 * Parse XML into a doc structure
236
 * 
237
 * Parameters:
238
 * text - {String} 
239
 * 
240
 * Returns:
241
 * {?} Parsed AJAX Responsev
242
 */
243
OpenLayers.parseXMLString = function(text) {
244

    
245
    //MS sucks, if the server is bad it dies
246
    var index = text.indexOf('<');
247
    if (index > 0) {
248
        text = text.substring(index);
249
    }
250

    
251
    var ajaxResponse = OpenLayers.Util.Try(
252
        function() {
253
            var xmldom = new ActiveXObject('Microsoft.XMLDOM');
254
            xmldom.loadXML(text);
255
            return xmldom;
256
        },
257
        function() {
258
            return new DOMParser().parseFromString(text, 'text/xml');
259
        },
260
        function() {
261
            var req = new XMLHttpRequest();
262
            req.open("GET", "data:" + "text/xml" +
263
                     ";charset=utf-8," + encodeURIComponent(text), false);
264
            if (req.overrideMimeType) {
265
                req.overrideMimeType("text/xml");
266
            }
267
            req.send(null);
268
            return req.responseXML;
269
        }
270
    );
271

    
272
    return ajaxResponse;
273
};
274

    
275
OpenLayers.Ajax = {
276

    
277
    /**
278
     * Method: emptyFunction
279
     */
280
    emptyFunction: function () {},
281

    
282
    /**
283
     * Method: getTransport
284
     * 
285
     * Returns: 
286
     * {Object} Transport mechanism for whichever browser we're in, or false if
287
     *          none available.
288
     */
289
    getTransport: function() {
290
        return OpenLayers.Util.Try(
291
            function() {return new XMLHttpRequest();},
292
            function() {return new ActiveXObject('Msxml2.XMLHTTP');},
293
            function() {return new ActiveXObject('Microsoft.XMLHTTP');}
294
        ) || false;
295
    },
296

    
297
    /**
298
     * Property: activeRequestCount
299
     * {Integer}
300
     */
301
    activeRequestCount: 0
302
};
303

    
304
/**
305
 * Namespace: OpenLayers.Ajax.Responders
306
 * {Object}
307
 */
308
OpenLayers.Ajax.Responders = {
309
  
310
    /**
311
     * Property: responders
312
     * {Array}
313
     */
314
    responders: [],
315

    
316
    /**
317
     * Method: register
318
     *  
319
     * Parameters:
320
     * responderToAdd - {?}
321
     */
322
    register: function(responderToAdd) {
323
        for (var i = 0; i < this.responders.length; i++){
324
            if (responderToAdd == this.responders[i]){
325
                return;
326
            }
327
        }
328
        this.responders.push(responderToAdd);
329
    },
330

    
331
    /**
332
     * Method: unregister
333
     *  
334
     * Parameters:
335
     * responderToRemove - {?}
336
     */
337
    unregister: function(responderToRemove) {
338
        OpenLayers.Util.removeItem(this.reponders, responderToRemove);
339
    },
340

    
341
    /**
342
     * Method: dispatch
343
     * 
344
     * Parameters:
345
     * callback - {?}
346
     * request - {?}
347
     * transport - {?}
348
     */
349
    dispatch: function(callback, request, transport) {
350
        var responder;
351
        for (var i = 0; i < this.responders.length; i++) {
352
            responder = this.responders[i];
353
     
354
            if (responder[callback] && 
355
                typeof responder[callback] == 'function') {
356
                try {
357
                    responder[callback].apply(responder, 
358
                                              [request, transport]);
359
                } catch (e) {}
360
            }
361
        }
362
    }
363
};
364

    
365
OpenLayers.Ajax.Responders.register({
366
    /** 
367
     * Function: onCreate
368
     */
369
    onCreate: function() {
370
        OpenLayers.Ajax.activeRequestCount++;
371
    },
372

    
373
    /**
374
     * Function: onComplete
375
     */
376
     onComplete: function() {
377
         OpenLayers.Ajax.activeRequestCount--;
378
     }
379
});
380

    
381
/**
382
 * Class: OpenLayers.Ajax.Base
383
 */
384
OpenLayers.Ajax.Base = OpenLayers.Class({
385
      
386
    /**
387
     * Constructor: OpenLayers.Ajax.Base
388
     * 
389
     * Parameters: 
390
     * options - {Object}
391
     */
392
    initialize: function(options) {
393
        this.options = {
394
            method:       'post',
395
            asynchronous: true,
396
            contentType:  'application/xml',
397
            parameters:   ''
398
        };
399
        OpenLayers.Util.extend(this.options, options || {});
400
        
401
        this.options.method = this.options.method.toLowerCase();
402
        
403
        if (typeof this.options.parameters == 'string') {
404
            this.options.parameters = 
405
                OpenLayers.Util.getParameters(this.options.parameters);
406
        }
407
    }
408
});
409

    
410
/**
411
 * Class: OpenLayers.Ajax.Request
412
 * *Deprecated*.  Use <OpenLayers.Request> method instead.
413
 *
414
 * Inherit:
415
 *  - <OpenLayers.Ajax.Base>
416
 */
417
OpenLayers.Ajax.Request = OpenLayers.Class(OpenLayers.Ajax.Base, {
418

    
419
    /**
420
     * Property: _complete
421
     *
422
     * {Boolean}
423
     */
424
    _complete: false,
425
      
426
    /**
427
     * Constructor: OpenLayers.Ajax.Request
428
     * 
429
     * Parameters: 
430
     * url - {String}
431
     * options - {Object}
432
     */
433
    initialize: function(url, options) {
434
        OpenLayers.Ajax.Base.prototype.initialize.apply(this, [options]);
435
        
436
        if (OpenLayers.ProxyHost && OpenLayers.String.startsWith(url, "http")) {
437
            url = OpenLayers.ProxyHost + encodeURIComponent(url);
438
        }
439
        
440
        this.transport = OpenLayers.Ajax.getTransport();
441
        this.request(url);
442
    },
443

    
444
    /**
445
     * Method: request
446
     * 
447
     * Parameters:
448
     * url - {String}
449
     */
450
    request: function(url) {
451
        this.url = url;
452
        this.method = this.options.method;
453
        var params = OpenLayers.Util.extend({}, this.options.parameters);
454
        
455
        if (this.method != 'get' && this.method != 'post') {
456
            // simulate other verbs over post
457
            params['_method'] = this.method;
458
            this.method = 'post';
459
        }
460

    
461
        this.parameters = params;        
462
        
463
        if (params = OpenLayers.Util.getParameterString(params)) {
464
            // when GET, append parameters to URL
465
            if (this.method == 'get') {
466
                this.url += ((this.url.indexOf('?') > -1) ? '&' : '?') + params;
467
            } else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
468
                params += '&_=';
469
            }
470
        }
471
        try {
472
            var response = new OpenLayers.Ajax.Response(this);
473
            if (this.options.onCreate) {
474
                this.options.onCreate(response);
475
            }
476
            
477
            OpenLayers.Ajax.Responders.dispatch('onCreate', 
478
                                                this, 
479
                                                response);
480
    
481
            this.transport.open(this.method.toUpperCase(), 
482
                                this.url,
483
                                this.options.asynchronous);
484
    
485
            if (this.options.asynchronous) {
486
                window.setTimeout(
487
                    OpenLayers.Function.bind(this.respondToReadyState, this, 1),
488
                    10);
489
            }
490
            
491
            this.transport.onreadystatechange = 
492
                OpenLayers.Function.bind(this.onStateChange, this);    
493
            this.setRequestHeaders();
494
    
495
            this.body =  this.method == 'post' ?
496
                (this.options.postBody || params) : null;
497
            this.transport.send(this.body);
498
    
499
            // Force Firefox to handle ready state 4 for synchronous requests
500
            if (!this.options.asynchronous && 
501
                this.transport.overrideMimeType) {
502
                this.onStateChange();
503
            }
504
        } catch (e) {
505
            this.dispatchException(e);
506
        }
507
    },
508

    
509
    /**
510
     * Method: onStateChange
511
     */
512
    onStateChange: function() {
513
        var readyState = this.transport.readyState;
514
        if (readyState > 1 && !((readyState == 4) && this._complete)) {
515
            this.respondToReadyState(this.transport.readyState);
516
        }
517
    },
518
     
519
    /**
520
     * Method: setRequestHeaders
521
     */
522
    setRequestHeaders: function() {
523
        var headers = {
524
            'X-Requested-With': 'XMLHttpRequest',
525
            'Accept': 'text/javascript, text/html, application/xml, text/xml, */*',
526
            'OpenLayers': true
527
        };
528

    
529
        if (this.method == 'post') {
530
            headers['Content-type'] = this.options.contentType +
531
                (this.options.encoding ? '; charset=' + this.options.encoding : '');
532
    
533
            /* Force "Connection: close" for older Mozilla browsers to work
534
             * around a bug where XMLHttpRequest sends an incorrect
535
             * Content-length header. See Mozilla Bugzilla #246651.
536
             */
537
            if (this.transport.overrideMimeType &&
538
                (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) {
539
                headers['Connection'] = 'close';
540
            }
541
        }
542
        // user-defined headers
543
        if (typeof this.options.requestHeaders == 'object') {    
544
            var extras = this.options.requestHeaders;
545
            
546
            if (typeof extras.push == 'function') {
547
                for (var i = 0, length = extras.length; i < length; i += 2) {
548
                    headers[extras[i]] = extras[i+1];
549
                }
550
            } else {
551
                for (var i in extras) {
552
                    headers[i] = extras[i];
553
                }
554
            }
555
        }
556
        
557
        for (var name in headers) {
558
            this.transport.setRequestHeader(name, headers[name]);
559
        }
560
    },
561
    
562
    /**
563
     * Method: success
564
     *
565
     * Returns:
566
     * {Boolean} - 
567
     */
568
    success: function() {
569
        var status = this.getStatus();
570
        return !status || (status >=200 && status < 300);
571
    },
572
    
573
    /**
574
     * Method: getStatus
575
     *
576
     * Returns:
577
     * {Integer} - Status
578
     */
579
    getStatus: function() {
580
        try {
581
            return this.transport.status || 0;
582
        } catch (e) {
583
            return 0;
584
        }
585
    },
586

    
587
    /**
588
     * Method: respondToReadyState
589
     *
590
     * Parameters:
591
     * readyState - {?}
592
     */
593
    respondToReadyState: function(readyState) {
594
        var state = OpenLayers.Ajax.Request.Events[readyState];
595
        var response = new OpenLayers.Ajax.Response(this);
596
    
597
        if (state == 'Complete') {
598
            try {
599
                this._complete = true;
600
                (this.options['on' + response.status] ||
601
                    this.options['on' + (this.success() ? 'Success' : 'Failure')] ||
602
                    OpenLayers.Ajax.emptyFunction)(response);
603
            } catch (e) {
604
                this.dispatchException(e);
605
            }
606
    
607
            var contentType = response.getHeader('Content-type');
608
        }
609
    
610
        try {
611
            (this.options['on' + state] || 
612
             OpenLayers.Ajax.emptyFunction)(response);
613
             OpenLayers.Ajax.Responders.dispatch('on' + state, 
614
                                                 this, 
615
                                                 response);
616
        } catch (e) {
617
            this.dispatchException(e);
618
        }
619
    
620
        if (state == 'Complete') {
621
            // avoid memory leak in MSIE: clean up
622
            this.transport.onreadystatechange = OpenLayers.Ajax.emptyFunction;
623
        }
624
    },
625
    
626
    /**
627
     * Method: getHeader
628
     * 
629
     * Parameters:
630
     * name - {String} Header name
631
     *
632
     * Returns:
633
     * {?} - response header for the given name
634
     */
635
    getHeader: function(name) {
636
        try {
637
            return this.transport.getResponseHeader(name);
638
        } catch (e) {
639
            return null;
640
        }
641
    },
642

    
643
    /**
644
     * Method: dispatchException
645
     * If the optional onException function is set, execute it
646
     * and then dispatch the call to any other listener registered
647
     * for onException.
648
     * 
649
     * If no optional onException function is set, we suspect that
650
     * the user may have also not used
651
     * OpenLayers.Ajax.Responders.register to register a listener
652
     * for the onException call.  To make sure that something
653
     * gets done with this exception, only dispatch the call if there
654
     * are listeners.
655
     *
656
     * If you explicitly want to swallow exceptions, set
657
     * request.options.onException to an empty function (function(){})
658
     * or register an empty function with <OpenLayers.Ajax.Responders>
659
     * for onException.
660
     * 
661
     * Parameters:
662
     * exception - {?}
663
     */
664
    dispatchException: function(exception) {
665
        var handler = this.options.onException;
666
        if(handler) {
667
            // call options.onException and alert any other listeners
668
            handler(this, exception);
669
            OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
670
        } else {
671
            // check if there are any other listeners
672
            var listener = false;
673
            var responders = OpenLayers.Ajax.Responders.responders;
674
            for (var i = 0; i < responders.length; i++) {
675
                if(responders[i].onException) {
676
                    listener = true;
677
                    break;
678
                }
679
            }
680
            if(listener) {
681
                // call all listeners
682
                OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
683
            } else {
684
                // let the exception through
685
                throw exception;
686
            }
687
        }
688
    }
689
});
690

    
691
/** 
692
 * Property: Events
693
 * {Array(String)}
694
 */
695
OpenLayers.Ajax.Request.Events =
696
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
697

    
698
/**
699
 * Class: OpenLayers.Ajax.Response
700
 */
701
OpenLayers.Ajax.Response = OpenLayers.Class({
702

    
703
    /**
704
     * Property: status
705
     *
706
     * {Integer}
707
     */
708
    status: 0,
709
    
710

    
711
    /**
712
     * Property: statusText
713
     *
714
     * {String}
715
     */
716
    statusText: '',
717
      
718
    /**
719
     * Constructor: OpenLayers.Ajax.Response
720
     * 
721
     * Parameters: 
722
     * request - {Object}
723
     */
724
    initialize: function(request) {
725
        this.request = request;
726
        var transport = this.transport = request.transport,
727
            readyState = this.readyState = transport.readyState;
728
        
729
        if ((readyState > 2 &&
730
            !(!!(window.attachEvent && !window.opera))) ||
731
            readyState == 4) {
732
            this.status       = this.getStatus();
733
            this.statusText   = this.getStatusText();
734
            this.responseText = transport.responseText == null ?
735
                '' : String(transport.responseText);
736
        }
737
        
738
        if(readyState == 4) {
739
            var xml = transport.responseXML;
740
            this.responseXML  = xml === undefined ? null : xml;
741
        }
742
    },
743
    
744
    /**
745
     * Method: getStatus
746
     */
747
    getStatus: OpenLayers.Ajax.Request.prototype.getStatus,
748
    
749
    /**
750
     * Method: getStatustext
751
     *
752
     * Returns:
753
     * {String} - statusText
754
     */
755
    getStatusText: function() {
756
        try {
757
            return this.transport.statusText || '';
758
        } catch (e) {
759
            return '';
760
        }
761
    },
762
    
763
    /**
764
     * Method: getHeader
765
     */
766
    getHeader: OpenLayers.Ajax.Request.prototype.getHeader,
767
    
768
    /** 
769
     * Method: getResponseHeader
770
     *
771
     * Returns:
772
     * {?} - response header for given name
773
     */
774
    getResponseHeader: function(name) {
775
        return this.transport.getResponseHeader(name);
776
    }
777
});
778

    
779

    
780
/**
781
 * Function: getElementsByTagNameNS
782
 * 
783
 * Parameters:
784
 * parentnode - {?}
785
 * nsuri - {?}
786
 * nsprefix - {?}
787
 * tagname - {?}
788
 * 
789
 * Returns:
790
 * {?}
791
 */
792
OpenLayers.Ajax.getElementsByTagNameNS  = function(parentnode, nsuri, 
793
                                                   nsprefix, tagname) {
794
    var elem = null;
795
    if (parentnode.getElementsByTagNameNS) {
796
        elem = parentnode.getElementsByTagNameNS(nsuri, tagname);
797
    } else {
798
        elem = parentnode.getElementsByTagName(nsprefix + ':' + tagname);
799
    }
800
    return elem;
801
};
802

    
803

    
804
/**
805
 * Function: serializeXMLToString
806
 * Wrapper function around XMLSerializer, which doesn't exist/work in
807
 *     IE/Safari. We need to come up with a way to serialize in those browser:
808
 *     for now, these browsers will just fail. #535, #536
809
 *
810
 * Parameters: 
811
 * xmldom {XMLNode} xml dom to serialize
812
 * 
813
 * Returns:
814
 * {?}
815
 */
816
OpenLayers.Ajax.serializeXMLToString = function(xmldom) {
817
    var serializer = new XMLSerializer();
818
    var data = serializer.serializeToString(xmldom);
819
    return data;
820
};
821

    
822
/**
823
 * Namespace: OpenLayers.Element
824
 */
825
OpenLayers.Util.extend(OpenLayers.Element, {
826

    
827
    /**
828
     * APIFunction: hide
829
     * *Deprecated*. Hide element(s) passed in
830
     * 
831
     * Parameters:
832
     * element - {DOMElement} Actually user can pass any number of elements
833
     */
834
    hide: function() {
835
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
836
            newMethod: "element.style.display = 'none';"
837
        }));
838

    
839
        for (var i=0, len=arguments.length; i<len; i++) {
840
            var element = OpenLayers.Util.getElement(arguments[i]);
841
            if (element) {
842
                element.style.display = 'none';
843
            }
844
        }
845
    },
846

    
847
    /**
848
     * APIFunction: show
849
     * *Deprecated*. Show element(s) passed in
850
     * 
851
     * Parameters:
852
     * element - {DOMElement} Actually user can pass any number of elements
853
     */
854
    show: function() {
855
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
856
            newMethod: "element.style.display = '';"
857
        }));
858

    
859
        for (var i=0, len=arguments.length; i<len; i++) {
860
            var element = OpenLayers.Util.getElement(arguments[i]);
861
            if (element) {
862
                element.style.display = '';
863
            }
864
        }
865
    },
866

    
867
    /**
868
     * APIFunction: getDimensions
869
     * *Deprecated*. Returns dimensions of the element passed in.
870
     *  
871
     * Parameters:
872
     * element - {DOMElement}
873
     * 
874
     * Returns:
875
     * {Object} Object with 'width' and 'height' properties which are the 
876
     *          dimensions of the element passed in.
877
     */
878
    getDimensions: function(element) {
879
        element = OpenLayers.Util.getElement(element);
880
        if (OpenLayers.Element.getStyle(element, 'display') != 'none') {
881
            return {width: element.offsetWidth, height: element.offsetHeight};
882
        }
883
    
884
        // All *Width and *Height properties give 0 on elements with display none,
885
        // so enable the element temporarily
886
        var els = element.style;
887
        var originalVisibility = els.visibility;
888
        var originalPosition = els.position;
889
        var originalDisplay = els.display;
890
        els.visibility = 'hidden';
891
        els.position = 'absolute';
892
        els.display = '';
893
        var originalWidth = element.clientWidth;
894
        var originalHeight = element.clientHeight;
895
        els.display = originalDisplay;
896
        els.position = originalPosition;
897
        els.visibility = originalVisibility;
898
        return {width: originalWidth, height: originalHeight};
899
    }
900
    
901
});
902

    
903
if (!String.prototype.startsWith) {
904
    /**
905
     * APIMethod: String.startsWith
906
     * *Deprecated*. Whether or not a string starts with another string. 
907
     * 
908
     * Parameters:
909
     * sStart - {String} The string we're testing for.
910
     *  
911
     * Returns:
912
     * {Boolean} Whether or not this string starts with the string passed in.
913
     */
914
    String.prototype.startsWith = function(sStart) {
915
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
916
                                {'newMethod':'OpenLayers.String.startsWith'}));
917
        return OpenLayers.String.startsWith(this, sStart);
918
    };
919
}
920

    
921
if (!String.prototype.contains) {
922
    /**
923
     * APIMethod: String.contains
924
     * *Deprecated*. Whether or not a string contains another string.
925
     * 
926
     * Parameters:
927
     * str - {String} The string that we're testing for.
928
     * 
929
     * Returns:
930
     * {Boolean} Whether or not this string contains with the string passed in.
931
     */
932
    String.prototype.contains = function(str) {
933
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
934
                                  {'newMethod':'OpenLayers.String.contains'}));
935
        return OpenLayers.String.contains(this, str);
936
    };
937
}
938

    
939
if (!String.prototype.trim) {
940
    /**
941
     * APIMethod: String.trim
942
     * *Deprecated*. Removes leading and trailing whitespace characters from a string.
943
     * 
944
     * Returns:
945
     * {String} A trimmed version of the string - all leading and 
946
     *          trailing spaces removed
947
     */
948
    String.prototype.trim = function() {
949
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
950
                                      {'newMethod':'OpenLayers.String.trim'}));
951
        return OpenLayers.String.trim(this);
952
    };
953
}
954

    
955
if (!String.prototype.camelize) {
956
    /**
957
     * APIMethod: String.camelize
958
     * *Deprecated*. Camel-case a hyphenated string. 
959
     *     Ex. "chicken-head" becomes "chickenHead", and
960
     *     "-chicken-head" becomes "ChickenHead".
961
     * 
962
     * Returns:
963
     * {String} The string, camelized
964
     */
965
    String.prototype.camelize = function() {
966
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
967
                                  {'newMethod':'OpenLayers.String.camelize'}));
968
        return OpenLayers.String.camelize(this);
969
    };
970
}
971

    
972
if (!Function.prototype.bind) {
973
    /**
974
     * APIMethod: Function.bind
975
     * *Deprecated*. Bind a function to an object. 
976
     * Method to easily create closures with 'this' altered.
977
     * 
978
     * Parameters:
979
     * object - {Object} the this parameter
980
     * 
981
     * Returns:
982
     * {Function} A closure with 'this' altered to the first
983
     *            argument.
984
     */
985
    Function.prototype.bind = function() {
986
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
987
                                {'newMethod':'OpenLayers.Function.bind'}));
988
        // new function takes the same arguments with this function up front
989
        Array.prototype.unshift.apply(arguments, [this]);
990
        return OpenLayers.Function.bind.apply(null, arguments);
991
    };
992
}
993

    
994
if (!Function.prototype.bindAsEventListener) {
995
    /**
996
     * APIMethod: Function.bindAsEventListener
997
     * *Deprecated*. Bind a function to an object, and configure it to receive the
998
     *     event object as first parameter when called. 
999
     * 
1000
     * Parameters:
1001
     * object - {Object} A reference to this.
1002
     * 
1003
     * Returns:
1004
     * {Function}
1005
     */
1006
    Function.prototype.bindAsEventListener = function(object) {
1007
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
1008
                        {'newMethod':'OpenLayers.Function.bindAsEventListener'}));
1009
        return OpenLayers.Function.bindAsEventListener(this, object);
1010
    };
1011
}
1012

    
1013
// FIXME: Remove this in 3.0. In 3.0, Event.stop will no longer be provided
1014
// by OpenLayers.
1015
if (window.Event) {
1016
    OpenLayers.Util.applyDefaults(window.Event, OpenLayers.Event);
1017
} else {
1018
    var Event = OpenLayers.Event;
1019
}
1020

    
1021
/**
1022
 * Namespace: OpenLayers.Tile
1023
 */
1024
OpenLayers.Util.extend(OpenLayers.Tile.prototype, {
1025
    /**   
1026
     * Method: getBoundsFromBaseLayer
1027
     * Take the pixel locations of the corner of the tile, and pass them to 
1028
     *     the base layer and ask for the location of those pixels, so that 
1029
     *     displaying tiles over Google works fine.
1030
     *
1031
     * Parameters:
1032
     * position - {<OpenLayers.Pixel>}
1033
     *
1034
     * Returns:
1035
     * bounds - {<OpenLayers.Bounds>} 
1036
     */
1037
    getBoundsFromBaseLayer: function(position) {
1038
        var msg = OpenLayers.i18n('reprojectDeprecated',
1039
                                              {'layerName':this.layer.name});
1040
        OpenLayers.Console.warn(msg);
1041
        var topLeft = this.layer.map.getLonLatFromLayerPx(position); 
1042
        var bottomRightPx = position.clone();
1043
        bottomRightPx.x += this.size.w;
1044
        bottomRightPx.y += this.size.h;
1045
        var bottomRight = this.layer.map.getLonLatFromLayerPx(bottomRightPx); 
1046
        // Handle the case where the base layer wraps around the date line.
1047
        // Google does this, and it breaks WMS servers to request bounds in 
1048
        // that fashion.  
1049
        if (topLeft.lon > bottomRight.lon) {
1050
            if (topLeft.lon < 0) {
1051
                topLeft.lon = -180 - (topLeft.lon+180);
1052
            } else {
1053
                bottomRight.lon = 180+bottomRight.lon+180;
1054
            }        
1055
        }
1056
        var bounds = new OpenLayers.Bounds(topLeft.lon, 
1057
                                       bottomRight.lat, 
1058
                                       bottomRight.lon, 
1059
                                       topLeft.lat);  
1060
        return bounds;
1061
    }    
1062
});
1063

    
1064
/**
1065
 * Class: OpenLayers.Control.MouseDefaults
1066
 * This class is DEPRECATED in 2.4 and will be removed by 3.0.
1067
 * If you need this functionality, use <OpenLayers.Control.Navigation> 
1068
 * instead!!!
1069
 *
1070
 * Inherits from:
1071
 *  - <OpenLayers.Control>
1072
 */
1073
OpenLayers.Control.MouseDefaults = OpenLayers.Class(OpenLayers.Control, {
1074

    
1075
    /** WARNING WARNING WARNING!!!
1076
        This class is DEPRECATED in 2.4 and will be removed by 3.0.
1077
        If you need this functionality, use Control.Navigation instead!!! */
1078

    
1079
    /** 
1080
     * Property: performedDrag
1081
     * {Boolean}
1082
     */
1083
    performedDrag: false,
1084

    
1085
    /** 
1086
     * Property: wheelObserver 
1087
     * {Function}
1088
     */
1089
    wheelObserver: null,
1090

    
1091
    /** 
1092
     * Constructor: OpenLayers.Control.MouseDefaults
1093
     */
1094
    initialize: function() {
1095
        OpenLayers.Control.prototype.initialize.apply(this, arguments);
1096
    },
1097

    
1098
    /**
1099
     * APIMethod: destroy
1100
     */    
1101
    destroy: function() {
1102
        
1103
        if (this.handler) {
1104
            this.handler.destroy();
1105
        }
1106
        this.handler = null;
1107

    
1108
        this.map.events.un({
1109
            "click": this.defaultClick,
1110
            "dblclick": this.defaultDblClick,
1111
            "mousedown": this.defaultMouseDown,
1112
            "mouseup": this.defaultMouseUp,
1113
            "mousemove": this.defaultMouseMove,
1114
            "mouseout": this.defaultMouseOut,
1115
            scope: this
1116
        });
1117

    
1118
        //unregister mousewheel events specifically on the window and document
1119
        OpenLayers.Event.stopObserving(window, "DOMMouseScroll", 
1120
                                        this.wheelObserver);
1121
        OpenLayers.Event.stopObserving(window, "mousewheel", 
1122
                                        this.wheelObserver);
1123
        OpenLayers.Event.stopObserving(document, "mousewheel", 
1124
                                        this.wheelObserver);
1125
        this.wheelObserver = null;
1126
                      
1127
        OpenLayers.Control.prototype.destroy.apply(this, arguments);        
1128
    },
1129

    
1130
    /**
1131
     * Method: draw
1132
     */
1133
    draw: function() {
1134
        this.map.events.on({
1135
            "click": this.defaultClick,
1136
            "dblclick": this.defaultDblClick,
1137
            "mousedown": this.defaultMouseDown,
1138
            "mouseup": this.defaultMouseUp,
1139
            "mousemove": this.defaultMouseMove,
1140
            "mouseout": this.defaultMouseOut,
1141
            scope: this
1142
        });
1143

    
1144
        this.registerWheelEvents();
1145

    
1146
    },
1147

    
1148
    /**
1149
     * Method: registerWheelEvents
1150
     */
1151
    registerWheelEvents: function() {
1152

    
1153
        this.wheelObserver = OpenLayers.Function.bindAsEventListener(
1154
            this.onWheelEvent, this
1155
        );
1156
        
1157
        //register mousewheel events specifically on the window and document
1158
        OpenLayers.Event.observe(window, "DOMMouseScroll", this.wheelObserver);
1159
        OpenLayers.Event.observe(window, "mousewheel", this.wheelObserver);
1160
        OpenLayers.Event.observe(document, "mousewheel", this.wheelObserver);
1161
    },
1162

    
1163
    /**
1164
     * Method: defaultClick
1165
     * 
1166
     * Parameters:
1167
     * evt - {Event} 
1168
     *
1169
     * Returns:
1170
     * {Boolean}
1171
     */
1172
    defaultClick: function (evt) {
1173
        if (!OpenLayers.Event.isLeftClick(evt)) {
1174
            return;
1175
        }
1176
        var notAfterDrag = !this.performedDrag;
1177
        this.performedDrag = false;
1178
        return notAfterDrag;
1179
    },
1180

    
1181
    /**
1182
     * Method: defaultDblClick
1183
     * 
1184
     * Parameters:
1185
     * evt - {Event} 
1186
     */
1187
    defaultDblClick: function (evt) {
1188
        var newCenter = this.map.getLonLatFromViewPortPx( evt.xy ); 
1189
        this.map.setCenter(newCenter, this.map.zoom + 1);
1190
        OpenLayers.Event.stop(evt);
1191
        return false;
1192
    },
1193

    
1194
    /**
1195
     * Method: defaultMouseDown
1196
     * 
1197
     * Parameters:
1198
     * evt - {Event} 
1199
     */
1200
    defaultMouseDown: function (evt) {
1201
        if (!OpenLayers.Event.isLeftClick(evt)) {
1202
            return;
1203
        }
1204
        this.mouseDragStart = evt.xy.clone();
1205
        this.performedDrag  = false;
1206
        if (evt.shiftKey) {
1207
            this.map.div.style.cursor = "crosshair";
1208
            this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
1209
                                                     this.mouseDragStart,
1210
                                                     null,
1211
                                                     null,
1212
                                                     "absolute",
1213
                                                     "2px solid red");
1214
            this.zoomBox.style.backgroundColor = "white";
1215
            this.zoomBox.style.filter = "alpha(opacity=50)"; // IE
1216
            this.zoomBox.style.opacity = "0.50";
1217
            this.zoomBox.style.fontSize = "1px";
1218
            this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
1219
            this.map.viewPortDiv.appendChild(this.zoomBox);
1220
        }
1221
        document.onselectstart = OpenLayers.Function.False;
1222
        OpenLayers.Event.stop(evt);
1223
    },
1224

    
1225
    /**
1226
     * Method: defaultMouseMove
1227
     *
1228
     * Parameters:
1229
     * evt - {Event} 
1230
     */
1231
    defaultMouseMove: function (evt) {
1232
        // record the mouse position, used in onWheelEvent
1233
        this.mousePosition = evt.xy.clone();
1234

    
1235
        if (this.mouseDragStart != null) {
1236
            if (this.zoomBox) {
1237
                var deltaX = Math.abs(this.mouseDragStart.x - evt.xy.x);
1238
                var deltaY = Math.abs(this.mouseDragStart.y - evt.xy.y);
1239
                this.zoomBox.style.width = Math.max(1, deltaX) + "px";
1240
                this.zoomBox.style.height = Math.max(1, deltaY) + "px";
1241
                if (evt.xy.x < this.mouseDragStart.x) {
1242
                    this.zoomBox.style.left = evt.xy.x+"px";
1243
                }
1244
                if (evt.xy.y < this.mouseDragStart.y) {
1245
                    this.zoomBox.style.top = evt.xy.y+"px";
1246
                }
1247
            } else {
1248
                var deltaX = this.mouseDragStart.x - evt.xy.x;
1249
                var deltaY = this.mouseDragStart.y - evt.xy.y;
1250
                var size = this.map.getSize();
1251
                var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX,
1252
                                                 size.h / 2 + deltaY);
1253
                var newCenter = this.map.getLonLatFromViewPortPx( newXY ); 
1254
                this.map.setCenter(newCenter, null, true);
1255
                this.mouseDragStart = evt.xy.clone();
1256
                this.map.div.style.cursor = "move";
1257
            }
1258
            this.performedDrag = true;
1259
        }
1260
    },
1261

    
1262
    /**
1263
     * Method: defaultMouseUp
1264
     * 
1265
     * Parameters:
1266
     * evt - {<OpenLayers.Event>} 
1267
     */
1268
    defaultMouseUp: function (evt) {
1269
        if (!OpenLayers.Event.isLeftClick(evt)) {
1270
            return;
1271
        }
1272
        if (this.zoomBox) {
1273
            this.zoomBoxEnd(evt);    
1274
        } else {
1275
            if (this.performedDrag) {
1276
                this.map.setCenter(this.map.center);
1277
            }
1278
        }
1279
        document.onselectstart=null;
1280
        this.mouseDragStart = null;
1281
        this.map.div.style.cursor = "";
1282
    },
1283

    
1284
    /**
1285
     * Method: defaultMouseOut
1286
     * 
1287
     * Parameters:
1288
     * evt - {Event} 
1289
     */
1290
    defaultMouseOut: function (evt) {
1291
        if (this.mouseDragStart != null && 
1292
            OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {
1293
            if (this.zoomBox) {
1294
                this.removeZoomBox();
1295
            }
1296
            this.mouseDragStart = null;
1297
        }
1298
    },
1299

    
1300

    
1301
    /** 
1302
     * Method: defaultWheelUp
1303
     * User spun scroll wheel up
1304
     * 
1305
     */
1306
    defaultWheelUp: function(evt) {
1307
        if (this.map.getZoom() <= this.map.getNumZoomLevels()) {
1308
            this.map.setCenter(this.map.getLonLatFromPixel(evt.xy),
1309
                               this.map.getZoom() + 1);
1310
        }
1311
    },
1312

    
1313
    /**
1314
     * Method: defaultWheelDown
1315
     * User spun scroll wheel down
1316
     */
1317
    defaultWheelDown: function(evt) {
1318
        if (this.map.getZoom() > 0) {
1319
            this.map.setCenter(this.map.getLonLatFromPixel(evt.xy),
1320
                               this.map.getZoom() - 1);
1321
        }
1322
    },
1323

    
1324
    /**
1325
     * Method: zoomBoxEnd
1326
     * Zoombox function. 
1327
     */
1328
    zoomBoxEnd: function(evt) {
1329
        if (this.mouseDragStart != null) {
1330
            if (Math.abs(this.mouseDragStart.x - evt.xy.x) > 5 ||    
1331
                Math.abs(this.mouseDragStart.y - evt.xy.y) > 5) {   
1332
                var start = this.map.getLonLatFromViewPortPx( this.mouseDragStart ); 
1333
                var end = this.map.getLonLatFromViewPortPx( evt.xy );
1334
                var top = Math.max(start.lat, end.lat);
1335
                var bottom = Math.min(start.lat, end.lat);
1336
                var left = Math.min(start.lon, end.lon);
1337
                var right = Math.max(start.lon, end.lon);
1338
                var bounds = new OpenLayers.Bounds(left, bottom, right, top);
1339
                this.map.zoomToExtent(bounds);
1340
            } else {
1341
                var end = this.map.getLonLatFromViewPortPx( evt.xy );
1342
                this.map.setCenter(new OpenLayers.LonLat(
1343
                  (end.lon),
1344
                  (end.lat)
1345
                 ), this.map.getZoom() + 1);
1346
            }    
1347
            this.removeZoomBox();
1348
       }
1349
    },
1350

    
1351
    /**
1352
     * Method: removeZoomBox
1353
     * Remove the zoombox from the screen and nullify our reference to it.
1354
     */
1355
    removeZoomBox: function() {
1356
        this.map.viewPortDiv.removeChild(this.zoomBox);
1357
        this.zoomBox = null;
1358
    },
1359

    
1360

    
1361
/**
1362
 *  Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
1363
 */
1364

    
1365

    
1366
    /**
1367
     * Method: onWheelEvent
1368
     * Catch the wheel event and handle it xbrowserly
1369
     *
1370
     * Parameters: 
1371
     * e - {Event} 
1372
     */
1373
    onWheelEvent: function(e){
1374
    
1375
        // first determine whether or not the wheeling was inside the map
1376
        var inMap = false;
1377
        var elem = OpenLayers.Event.element(e);
1378
        while(elem != null) {
1379
            if (this.map && elem == this.map.div) {
1380
                inMap = true;
1381
                break;
1382
            }
1383
            elem = elem.parentNode;
1384
        }
1385
        
1386
        if (inMap) {
1387
            
1388
            var delta = 0;
1389
            if (!e) {
1390
                e = window.event;
1391
            }
1392
            if (e.wheelDelta) {
1393
                delta = e.wheelDelta/120; 
1394
                if (window.opera && window.opera.version() < 9.2) {
1395
                    delta = -delta;
1396
                }
1397
            } else if (e.detail) {
1398
                delta = -e.detail / 3;
1399
            }
1400
            if (delta) {
1401
                // add the mouse position to the event because mozilla has a bug
1402
                // with clientX and clientY (see https://bugzilla.mozilla.org/show_bug.cgi?id=352179)
1403
                // getLonLatFromViewPortPx(e) returns wrong values
1404
                e.xy = this.mousePosition;
1405

    
1406
                if (delta < 0) {
1407
                   this.defaultWheelDown(e);
1408
                } else {
1409
                   this.defaultWheelUp(e);
1410
                }
1411
            }
1412
            
1413
            //only wheel the map, not the window
1414
            OpenLayers.Event.stop(e);
1415
        }
1416
    },
1417

    
1418
    CLASS_NAME: "OpenLayers.Control.MouseDefaults"
1419
});
1420

    
1421
/**
1422
 * Class: OpenLayers.Control.MouseToolbar
1423
 * This class is DEPRECATED in 2.4 and will be removed by 3.0.
1424
 * If you need this functionality, use <OpenLayers.Control.NavToolbar>
1425
 * instead!!! 
1426
 */
1427
OpenLayers.Control.MouseToolbar = OpenLayers.Class(
1428
                                            OpenLayers.Control.MouseDefaults, {
1429
    
1430
    /**
1431
     * Property: mode
1432
     */ 
1433
    mode: null,
1434
    /**
1435
     * Property: buttons
1436
     */
1437
    buttons: null,
1438
    
1439
    /**
1440
     * APIProperty: direction
1441
     * {String} 'vertical' or 'horizontal'
1442
     */
1443
    direction: "vertical",
1444
    
1445
    /**
1446
     * Property: buttonClicked
1447
     * {String}
1448
     */
1449
    buttonClicked: null,
1450
    
1451
    /**
1452
     * Constructor: OpenLayers.Control.MouseToolbar
1453
     *
1454
     * Parameters:
1455
     * position - {<OpenLayers.Pixel>}
1456
     * direction - {String}
1457
     */
1458
    initialize: function(position, direction) {
1459
        OpenLayers.Control.prototype.initialize.apply(this, arguments);
1460
        this.position = new OpenLayers.Pixel(OpenLayers.Control.MouseToolbar.X,
1461
                                             OpenLayers.Control.MouseToolbar.Y);
1462
        if (position) {
1463
            this.position = position;
1464
        }
1465
        if (direction) {
1466
            this.direction = direction; 
1467
        }
1468
        this.measureDivs = [];
1469
    },
1470
    
1471
    /**
1472
     * APIMethod: destroy 
1473
     */
1474
    destroy: function() {
1475
        for( var btnId in this.buttons) {
1476
            var btn = this.buttons[btnId];
1477
            btn.map = null;
1478
            btn.events.destroy();
1479
        }
1480
        OpenLayers.Control.MouseDefaults.prototype.destroy.apply(this, 
1481
                                                                 arguments);
1482
    },
1483
    
1484
    /**
1485
     * Method: draw
1486
     */
1487
    draw: function() {
1488
        OpenLayers.Control.prototype.draw.apply(this, arguments); 
1489
        OpenLayers.Control.MouseDefaults.prototype.draw.apply(this, arguments);
1490
        this.buttons = {};
1491
        var sz = new OpenLayers.Size(28,28);
1492
        var centered = new OpenLayers.Pixel(OpenLayers.Control.MouseToolbar.X,0);
1493
        this._addButton("zoombox", "drag-rectangle-off.png", "drag-rectangle-on.png", centered, sz, "Shift->Drag to zoom to area");
1494
        centered = centered.add((this.direction == "vertical" ? 0 : sz.w), (this.direction == "vertical" ? sz.h : 0));
1495
        this._addButton("pan", "panning-hand-off.png", "panning-hand-on.png", centered, sz, "Drag the map to pan.");
1496
        centered = centered.add((this.direction == "vertical" ? 0 : sz.w), (this.direction == "vertical" ? sz.h : 0));
1497
        this.switchModeTo("pan");
1498

    
1499
        return this.div;
1500
    },
1501
    
1502
    /**
1503
     * Method: _addButton
1504
     */
1505
    _addButton:function(id, img, activeImg, xy, sz, title) {
1506
        var imgLocation = OpenLayers.Util.getImageLocation(img);
1507
        var activeImgLocation = OpenLayers.Util.getImageLocation(activeImg);
1508
        // var btn = new ol.AlphaImage("_"+id, imgLocation, xy, sz);
1509
        var btn = OpenLayers.Util.createAlphaImageDiv(
1510
                                    "OpenLayers_Control_MouseToolbar_" + id, 
1511
                                    xy, sz, imgLocation, "absolute");
1512

    
1513
        //we want to add the outer div
1514
        this.div.appendChild(btn);
1515
        btn.imgLocation = imgLocation;
1516
        btn.activeImgLocation = activeImgLocation;
1517
        
1518
        btn.events = new OpenLayers.Events(this, btn, null, true);
1519
        btn.events.on({
1520
            "mousedown": this.buttonDown,
1521
            "mouseup": this.buttonUp,
1522
            "dblclick": OpenLayers.Event.stop,
1523
            scope: this
1524
        });
1525
        btn.action = id;
1526
        btn.title = title;
1527
        btn.alt = title;
1528
        btn.map = this.map;
1529

    
1530
        //we want to remember/reference the outer div
1531
        this.buttons[id] = btn;
1532
        return btn;
1533
    },
1534

    
1535
    /**
1536
     * Method: buttonDown
1537
     *
1538
     * Parameters:
1539
     * evt - {Event} 
1540
     */
1541
    buttonDown: function(evt) {
1542
        if (!OpenLayers.Event.isLeftClick(evt)) {
1543
            return;
1544
        }
1545
        this.buttonClicked = evt.element.action;
1546
        OpenLayers.Event.stop(evt);
1547
    },
1548

    
1549
    /**
1550
     * Method: buttonUp
1551
     *
1552
     * Parameters:
1553
     * evt - {Event} 
1554
     */
1555
    buttonUp: function(evt) {
1556
        if (!OpenLayers.Event.isLeftClick(evt)) {
1557
            return;
1558
        }
1559
        if (this.buttonClicked != null) {
1560
            if (this.buttonClicked == evt.element.action) {
1561
                this.switchModeTo(evt.element.action);
1562
            }
1563
            OpenLayers.Event.stop(evt);
1564
            this.buttonClicked = null;
1565
        }
1566
    },
1567
    
1568
    /**
1569
     * Method: defaultDblClick 
1570
     *
1571
     * Parameters:
1572
     * evt - {Event} 
1573
     */
1574
    defaultDblClick: function (evt) {
1575
        this.switchModeTo("pan");
1576
        this.performedDrag = false;
1577
        var newCenter = this.map.getLonLatFromViewPortPx( evt.xy ); 
1578
        this.map.setCenter(newCenter, this.map.zoom + 1);
1579
        OpenLayers.Event.stop(evt);
1580
        return false;
1581
    },
1582

    
1583
    /**
1584
     * Method: defaultMouseDown
1585
     *
1586
     * Parameters:
1587
     * evt - {Event} 
1588
     */
1589
    defaultMouseDown: function (evt) {
1590
        if (!OpenLayers.Event.isLeftClick(evt)) {
1591
            return;
1592
        }
1593
        this.mouseDragStart = evt.xy.clone();
1594
        this.performedDrag = false;
1595
        this.startViaKeyboard = false;
1596
        if (evt.shiftKey && this.mode !="zoombox") {
1597
            this.switchModeTo("zoombox");
1598
            this.startViaKeyboard = true;
1599
        } else if (evt.altKey && this.mode !="measure") {
1600
            this.switchModeTo("measure");
1601
        } else if (!this.mode) {
1602
            this.switchModeTo("pan");
1603
        }
1604
        
1605
        switch (this.mode) {
1606
            case "zoombox":
1607
                this.map.div.style.cursor = "crosshair";
1608
                this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
1609
                                                         this.mouseDragStart,
1610
                                                         null,
1611
                                                         null,
1612
                                                         "absolute",
1613
                                                         "2px solid red");
1614
                this.zoomBox.style.backgroundColor = "white";
1615
                this.zoomBox.style.filter = "alpha(opacity=50)"; // IE
1616
                this.zoomBox.style.opacity = "0.50";
1617
                this.zoomBox.style.fontSize = "1px";
1618
                this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
1619
                this.map.viewPortDiv.appendChild(this.zoomBox);
1620
                this.performedDrag = true;
1621
                break;
1622
            case "measure":
1623
                var distance = "";
1624
                if (this.measureStart) {
1625
                    var measureEnd = this.map.getLonLatFromViewPortPx(this.mouseDragStart);
1626
                    distance = OpenLayers.Util.distVincenty(this.measureStart, measureEnd);
1627
                    distance = Math.round(distance * 100) / 100;
1628
                    distance = distance + "km";
1629
                    this.measureStartBox = this.measureBox;
1630
                }    
1631
                this.measureStart = this.map.getLonLatFromViewPortPx(this.mouseDragStart);;
1632
                this.measureBox = OpenLayers.Util.createDiv(null,
1633
                                                         this.mouseDragStart.add(
1634
                                                           -2-parseInt(this.map.layerContainerDiv.style.left),
1635
                                                           -2-parseInt(this.map.layerContainerDiv.style.top)),
1636
                                                         null,
1637
                                                         null,
1638
                                                         "absolute");
1639
                this.measureBox.style.width="4px";
1640
                this.measureBox.style.height="4px";
1641
                this.measureBox.style.fontSize = "1px";
1642
                this.measureBox.style.backgroundColor="red";
1643
                this.measureBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
1644
                this.map.layerContainerDiv.appendChild(this.measureBox);
1645
                if (distance) {
1646
                    this.measureBoxDistance = OpenLayers.Util.createDiv(null,
1647
                                                         this.mouseDragStart.add(
1648
                                                           -2-parseInt(this.map.layerContainerDiv.style.left),
1649
                                                           2-parseInt(this.map.layerContainerDiv.style.top)),
1650
                                                         null,
1651
                                                         null,
1652
                                                         "absolute");
1653
                    
1654
                    this.measureBoxDistance.innerHTML = distance;
1655
                    this.measureBoxDistance.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
1656
                    this.map.layerContainerDiv.appendChild(this.measureBoxDistance);
1657
                    this.measureDivs.push(this.measureBoxDistance);
1658
                }
1659
                this.measureBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
1660
                this.map.layerContainerDiv.appendChild(this.measureBox);
1661
                this.measureDivs.push(this.measureBox);
1662
                break;
1663
            default:
1664
                this.map.div.style.cursor = "move";
1665
                break;
1666
        }
1667
        document.onselectstart = OpenLayers.Function.False;
1668
        OpenLayers.Event.stop(evt);
1669
    },
1670

    
1671
    /**
1672
     * Method: switchModeTo 
1673
     *
1674
     * Parameters:
1675
     * mode - {String} 
1676
     */
1677
    switchModeTo: function(mode) {
1678
        if (mode != this.mode) {
1679
            
1680

    
1681
            if (this.mode && this.buttons[this.mode]) {
1682
                OpenLayers.Util.modifyAlphaImageDiv(this.buttons[this.mode], null, null, null, this.buttons[this.mode].imgLocation);
1683
            }
1684
            if (this.mode == "measure" && mode != "measure") {
1685
                for(var i=0, len=this.measureDivs.length; i<len; i++) {
1686
                    if (this.measureDivs[i]) { 
1687
                        this.map.layerContainerDiv.removeChild(this.measureDivs[i]);
1688
                    }
1689
                }
1690
                this.measureDivs = [];
1691
                this.measureStart = null;
1692
            }
1693
            this.mode = mode;
1694
            if (this.buttons[mode]) {
1695
                OpenLayers.Util.modifyAlphaImageDiv(this.buttons[mode], null, null, null, this.buttons[mode].activeImgLocation);
1696
            }
1697
            switch (this.mode) {
1698
                case "zoombox":
1699
                    this.map.div.style.cursor = "crosshair";
1700
                    break;
1701
                default:
1702
                    this.map.div.style.cursor = "";
1703
                    break;
1704
            }
1705

    
1706
        } 
1707
    }, 
1708

    
1709
    /**
1710
     * Method: leaveMode
1711
     */
1712
    leaveMode: function() {
1713
        this.switchModeTo("pan");
1714
    },
1715
    
1716
    /**
1717
     * Method: defaultMouseMove
1718
     *
1719
     * Parameters:
1720
     * evt - {Event} 
1721
     */
1722
    defaultMouseMove: function (evt) {
1723
        if (this.mouseDragStart != null) {
1724
            switch (this.mode) {
1725
                case "zoombox": 
1726
                    var deltaX = Math.abs(this.mouseDragStart.x - evt.xy.x);
1727
                    var deltaY = Math.abs(this.mouseDragStart.y - evt.xy.y);
1728
                    this.zoomBox.style.width = Math.max(1, deltaX) + "px";
1729
                    this.zoomBox.style.height = Math.max(1, deltaY) + "px";
1730
                    if (evt.xy.x < this.mouseDragStart.x) {
1731
                        this.zoomBox.style.left = evt.xy.x+"px";
1732
                    }
1733
                    if (evt.xy.y < this.mouseDragStart.y) {
1734
                        this.zoomBox.style.top = evt.xy.y+"px";
1735
                    }
1736
                    break;
1737
                default:
1738
                    var deltaX = this.mouseDragStart.x - evt.xy.x;
1739
                    var deltaY = this.mouseDragStart.y - evt.xy.y;
1740
                    var size = this.map.getSize();
1741
                    var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX,
1742
                                                     size.h / 2 + deltaY);
1743
                    var newCenter = this.map.getLonLatFromViewPortPx( newXY ); 
1744
                    this.map.setCenter(newCenter, null, true);
1745
                    this.mouseDragStart = evt.xy.clone();
1746
            }
1747
            this.performedDrag = true;
1748
        }
1749
    },
1750

    
1751
    /**
1752
     * Method: defaultMouseUp
1753
     *
1754
     * Parameters:
1755
     * evt - {Event} 
1756
     */
1757
    defaultMouseUp: function (evt) {
1758
        if (!OpenLayers.Event.isLeftClick(evt)) {
1759
            return;
1760
        }
1761
        switch (this.mode) {
1762
            case "zoombox":
1763
                this.zoomBoxEnd(evt);
1764
                if (this.startViaKeyboard) {
1765
                    this.leaveMode();
1766
                }
1767
                break;
1768
            case "pan":
1769
                if (this.performedDrag) {
1770
                    this.map.setCenter(this.map.center);
1771
                }        
1772
        }
1773
        document.onselectstart = null;
1774
        this.mouseDragStart = null;
1775
        this.map.div.style.cursor = "default";
1776
    },
1777

    
1778
    /**
1779
     * Method: defaultMouseOut
1780
     *
1781
     * Parameters:
1782
     * evt - {Event} 
1783
     */
1784
    defaultMouseOut: function (evt) {
1785
        if (this.mouseDragStart != null
1786
            && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) {
1787
            if (this.zoomBox) {
1788
                this.removeZoomBox();
1789
                if (this.startViaKeyboard) {
1790
                    this.leaveMode();
1791
                }
1792
            }
1793
            this.mouseDragStart = null;
1794
            this.map.div.style.cursor = "default";
1795
        }
1796
    },
1797

    
1798
    /**
1799
     * Method: defaultClick
1800
     *
1801
     * Parameters:
1802
     * evt - {Event} 
1803
     */
1804
    defaultClick: function (evt) {
1805
        if (this.performedDrag)  {
1806
            this.performedDrag = false;
1807
            return false;
1808
        }
1809
    },
1810
    
1811
    CLASS_NAME: "OpenLayers.Control.MouseToolbar"
1812
});
1813

    
1814
OpenLayers.Control.MouseToolbar.X = 6;
1815
OpenLayers.Control.MouseToolbar.Y = 300;
1816

    
1817
/**
1818
 * Class: OpenLayers.Layer.Grid
1819
 */
1820

    
1821
OpenLayers.Util.extend(OpenLayers.Layer.Grid.prototype, {
1822

    
1823
    /**
1824
     * Method: getGridBounds
1825
     * Deprecated. This function will be removed in 3.0. Please use 
1826
     *     getTilesBounds() instead.
1827
     * 
1828
     * Returns:
1829
     * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the
1830
     * currently loaded tiles (including those partially or not at all seen 
1831
     * onscreen)
1832
     */
1833
    getGridBounds: function() {
1834
        var msg = "The getGridBounds() function is deprecated. It will be " +
1835
                  "removed in 3.0. Please use getTilesBounds() instead.";
1836
        OpenLayers.Console.warn(msg);
1837
        return this.getTilesBounds();
1838
    }
1839
});
1840

    
1841
/**
1842
 * Class: OpenLayers.Format.XML
1843
 */
1844
OpenLayers.Util.extend(OpenLayers.Format.XML.prototype, {
1845

    
1846
    /**
1847
     * APIMethod: concatChildValues
1848
     * *Deprecated*. Use <getChildValue> instead.
1849
     *
1850
     * Concatenate the value of all child nodes if any exist, or return an
1851
     *     optional default string.  Returns an empty string if no children
1852
     *     exist and no default value is supplied.  Not optimized for large
1853
     *     numbers of child nodes.
1854
     *
1855
     * Parameters:
1856
     * node - {DOMElement} The element used to look for child values.
1857
     * def - {String} Optional string to return in the event that no
1858
     *     child exist.
1859
     *
1860
     * Returns:
1861
     * {String} The concatenated value of all child nodes of the given node.
1862
     */
1863
    concatChildValues: function(node, def) {
1864
        var value = "";
1865
        var child = node.firstChild;
1866
        var childValue;
1867
        while(child) {
1868
            childValue = child.nodeValue;
1869
            if(childValue) {
1870
                value += childValue;
1871
            }
1872
            child = child.nextSibling;
1873
        }
1874
        if(value == "" && def != undefined) {
1875
            value = def;
1876
        }
1877
        return value;
1878
    }
1879

    
1880
});
1881

    
1882
/**
1883
 * Class: OpenLayers.Layer.WMS.Post
1884
 * Instances of OpenLayers.Layer.WMS.Post are used to retrieve data from OGC
1885
 * Web Mapping Services via HTTP-POST (application/x-www-form-urlencoded). 
1886
 * Create a new WMS layer with the <OpenLayers.Layer.WMS.Post> constructor.
1887
 *
1888
 * *Deprecated*. Instead of this layer, use <OpenLayers.Layer.WMS> with
1889
 * <OpenLayers.Tile.Image.maxGetUrlLength> configured in the layer's
1890
 * <OpenLayers.Layer.WMS.tileOptions>.
1891
 *
1892
 * Inherits from:
1893
 *  - <OpenLayers.Layer.WMS>
1894
 */
1895
OpenLayers.Layer.WMS.Post = OpenLayers.Class(OpenLayers.Layer.WMS, {
1896

    
1897
    /**
1898
     * APIProperty: unsupportedBrowsers
1899
     * {Array} Array with browsers, which should use the HTTP-GET protocol 
1900
     * instead of HTTP-POST for fetching tiles from a WMS .
1901
     * Defaults to ["mozilla", "firefox", "opera"], because Opera is not able 
1902
     * to show transparent images in IFrames and Firefox/Mozilla has some ugly 
1903
     * effects of viewport-shaking when panning the map. Both browsers, Opera
1904
     * and Firefox/Mozilla, have no problem with long urls, which is the reason
1905
     * for using POST instead of GET. The strings to pass to this array are
1906
     * the ones returned by <OpenLayers.BROWSER_NAME>.
1907
     */
1908
    unsupportedBrowsers: ["mozilla", "firefox", "opera"],
1909

    
1910
    /**
1911
     * Property: SUPPORTED_TRANSITIONS
1912
     * {Array} 
1913
     * no supported transitions for this type of layer, because it is not
1914
     * possible to modify the initialized tiles (iframes)
1915
     */
1916
    SUPPORTED_TRANSITIONS: [],
1917
    
1918
    /**
1919
     * Property: usePost
1920
     * {Boolean}
1921
     */
1922
    usePost: null,
1923

    
1924
    /**
1925
     * Constructor: OpenLayers.Layer.WMS.Post
1926
     * Creates a new WMS layer object.
1927
     *
1928
     * Example:
1929
     * (code)
1930
     * var wms = new OpenLayers.Layer.WMS.Post(
1931
     *  "NASA Global Mosaic",
1932
     *  "http://wms.jpl.nasa.gov/wms.cgi",
1933
     *  {layers: "modis, global_mosaic"});
1934
     * (end)
1935
     *
1936
     * Parameters:
1937
     * name - {String} A name for the layer
1938
     * url - {String} Base url for the WMS
1939
     *                (e.g. http://wms.jpl.nasa.gov/wms.cgi)
1940
     * params - {Object} An object with key/value pairs representing the
1941
     *                   GetMap query string parameters and parameter values.
1942
     * options - {Object} Hashtable of extra options to tag onto the layer.
1943
     */
1944
    initialize: function(name, url, params, options) {
1945
        var newArguments = [];
1946
        newArguments.push(name, url, params, options);
1947
        OpenLayers.Layer.WMS.prototype.initialize.apply(this, newArguments);
1948

    
1949
        this.usePost = OpenLayers.Util.indexOf(
1950
            this.unsupportedBrowsers, OpenLayers.BROWSER_NAME) == -1;
1951
    },
1952
    
1953
    /**
1954
     * Method: addTile
1955
     * addTile creates a tile, initializes it and adds it as iframe to the
1956
     * layer div.
1957
     *
1958
     * Parameters:
1959
     * bounds - {<OpenLayers.Bounds>}
1960
     * position - {<OpenLayers.Pixel>}
1961
     *
1962
     * Returns:
1963
     * {<OpenLayers.Tile.Image.IFrame>} The added OpenLayers.Tile.Image.IFrame
1964
     */
1965
    addTile: function(bounds,position) {
1966
        return new OpenLayers.Tile.Image(
1967
            this, position, bounds, null, this.tileSize, {
1968
                maxGetUrlLength: this.usePost ? 0 : null
1969
            });
1970
    },
1971

    
1972
    CLASS_NAME: 'OpenLayers.Layer.WMS.Post'
1973
});
1974

    
1975
/**
1976
 * Class: OpenLayers.Layer.WMS.Untiled
1977
 * *Deprecated*.  To be removed in 3.0.  Instead use OpenLayers.Layer.WMS and 
1978
 *     pass the option 'singleTile' as true.
1979
 * 
1980
 * Inherits from: 
1981
 *  - <OpenLayers.Layer.WMS>
1982
 */
1983
OpenLayers.Layer.WMS.Untiled = OpenLayers.Class(OpenLayers.Layer.WMS, {
1984

    
1985
    /**
1986
     * APIProperty: singleTile
1987
     * {singleTile} Always true for untiled.
1988
     */
1989
    singleTile: true,
1990

    
1991
    /**
1992
     * Constructor: OpenLayers.Layer.WMS.Untiled
1993
     *
1994
     * Parameters:
1995
     * name - {String} 
1996
     * url - {String} 
1997
     * params - {Object} 
1998
     * options - {Object} 
1999
     */
2000
    initialize: function(name, url, params, options) {
2001
        OpenLayers.Layer.WMS.prototype.initialize.apply(this, arguments);
2002
        
2003
        var msg = "The OpenLayers.Layer.WMS.Untiled class is deprecated and " +
2004
                  "will be removed in 3.0. Instead, you should use the " +
2005
                  "normal OpenLayers.Layer.WMS class, passing it the option " +
2006
                  "'singleTile' as true.";
2007
        OpenLayers.Console.warn(msg);
2008
    },    
2009

    
2010
    /**
2011
     * Method: clone
2012
     * Create a clone of this layer
2013
     *
2014
     * Returns:
2015
     * {<OpenLayers.Layer.WMS.Untiled>} An exact clone of this layer
2016
     */
2017
    clone: function (obj) {
2018
        
2019
        if (obj == null) {
2020
            obj = new OpenLayers.Layer.WMS.Untiled(this.name,
2021
                                                   this.url,
2022
                                                   this.params,
2023
                                                   this.getOptions());
2024
        }
2025

    
2026
        //get all additions from superclasses
2027
        obj = OpenLayers.Layer.WMS.prototype.clone.apply(this, [obj]);
2028

    
2029
        // copy/set any non-init, non-simple values here
2030

    
2031
        return obj;
2032
    }, 
2033

    
2034
    CLASS_NAME: "OpenLayers.Layer.WMS.Untiled"
2035
});
2036

    
2037
/**
2038
 * Class: OpenLayers.Layer.MapServer.Untiled
2039
 * *Deprecated*.  To be removed in 3.0.  Instead use OpenLayers.Layer.MapServer
2040
 *     and pass the option 'singleTile' as true.
2041
 * 
2042
 * Inherits from: 
2043
 *  - <OpenLayers.Layer.MapServer>
2044
 */
2045
OpenLayers.Layer.MapServer.Untiled = OpenLayers.Class(OpenLayers.Layer.MapServer, {
2046

    
2047
    /**
2048
     * APIProperty: singleTile
2049
     * {singleTile} Always true for untiled.
2050
     */
2051
    singleTile: true,
2052

    
2053
    /**
2054
     * Constructor: OpenLayers.Layer.MapServer.Untiled
2055
     *
2056
     * Parameters:
2057
     * name - {String} 
2058
     * url - {String} 
2059
     * params - {Object} 
2060
     * options - {Object} 
2061
     */
2062
    initialize: function(name, url, params, options) {
2063
        OpenLayers.Layer.MapServer.prototype.initialize.apply(this, arguments);
2064
        
2065
        var msg = "The OpenLayers.Layer.MapServer.Untiled class is deprecated and " +
2066
                  "will be removed in 3.0. Instead, you should use the " +
2067
                  "normal OpenLayers.Layer.MapServer class, passing it the option " +
2068
                  "'singleTile' as true.";
2069
        OpenLayers.Console.warn(msg);
2070
    },    
2071

    
2072
    /**
2073
     * Method: clone
2074
     * Create a clone of this layer
2075
     *
2076
     * Returns:
2077
     * {<OpenLayers.Layer.MapServer.Untiled>} An exact clone of this layer
2078
     */
2079
    clone: function (obj) {
2080
        
2081
        if (obj == null) {
2082
            obj = new OpenLayers.Layer.MapServer.Untiled(this.name,
2083
                                                         this.url,
2084
                                                         this.params,
2085
                                                         this.getOptions());
2086
        }
2087

    
2088
        //get all additions from superclasses
2089
        obj = OpenLayers.Layer.MapServer.prototype.clone.apply(this, [obj]);
2090

    
2091
        // copy/set any non-init, non-simple values here
2092
        
2093
        return obj;
2094
    }, 
2095

    
2096
    CLASS_NAME: "OpenLayers.Layer.MapServer.Untiled"
2097
});
2098

    
2099
/**
2100
 * Class: OpenLayers.Tile.WFS
2101
 * Instances of OpenLayers.Tile.WFS are used to manage the image tiles
2102
 * used by various layers.  Create a new image tile with the
2103
 * <OpenLayers.Tile.WFS> constructor.
2104
 *
2105
 * Inherits from:
2106
 *  - <OpenLayers.Tile>
2107
 */
2108
OpenLayers.Tile.WFS = OpenLayers.Class(OpenLayers.Tile, {
2109

    
2110
    /**
2111
     * Property: features
2112
     * {Array(<OpenLayers.Feature>)} list of features in this tile
2113
     */
2114
    features: null,
2115

    
2116
    /**
2117
     * Property: url
2118
     * {String}
2119
     */
2120
    url: null,
2121

    
2122
    /**
2123
     * Property: request
2124
     * {<OpenLayers.Request.XMLHttpRequest>}
2125
     */
2126
    request: null,
2127

    
2128
    /** TBD 3.0 - reorder the parameters to the init function to put URL
2129
     *             as last, so we can continue to call tile.initialize()
2130
     *             without changing the arguments.
2131
     *
2132
     * Constructor: OpenLayers.Tile.WFS
2133
     * Constructor for a new <OpenLayers.Tile.WFS> instance.
2134
     *
2135
     * Parameters:
2136
     * layer - {<OpenLayers.Layer>} layer that the tile will go in.
2137
     * position - {<OpenLayers.Pixel>}
2138
     * bounds - {<OpenLayers.Bounds>}
2139
     * url - {<String>}
2140
     * size - {<OpenLayers.Size>}
2141
     */
2142
    initialize: function(layer, position, bounds, url, size) {
2143
        OpenLayers.Tile.prototype.initialize.apply(this, arguments);
2144
        this.url = url;
2145
        this.features = [];
2146
    },
2147

    
2148
    /**
2149
     * APIMethod: destroy
2150
     * nullify references to prevent circular references and memory leaks
2151
     */
2152
    destroy: function() {
2153
        OpenLayers.Tile.prototype.destroy.apply(this, arguments);
2154
        this.destroyAllFeatures();
2155
        this.features = null;
2156
        this.url = null;
2157
        if(this.request) {
2158
            this.request.abort();
2159
            //this.request.destroy();
2160
            this.request = null;
2161
        }
2162
    },
2163

    
2164
    /**
2165
     * Method: clear
2166
     *  Clear the tile of any bounds/position-related data so that it can
2167
     *   be reused in a new location.
2168
     */
2169
    clear: function() {
2170
        this.destroyAllFeatures();
2171
    },
2172

    
2173
    /**
2174
     * Method: draw
2175
     * Check that a tile should be drawn, and load features for it.
2176
     */
2177
    draw:function() {
2178
        if (OpenLayers.Tile.prototype.draw.apply(this, arguments)) {
2179
            if (this.isLoading) {
2180
                //if already loading, send 'reload' instead of 'loadstart'.
2181
                this.events.triggerEvent("reload");
2182
            } else {
2183
                this.isLoading = true;
2184
                this.events.triggerEvent("loadstart");
2185
            }
2186
            this.loadFeaturesForRegion(this.requestSuccess);
2187
        }
2188
    },
2189

    
2190
    /**
2191
    * Method: loadFeaturesForRegion
2192
    * Abort any pending requests and issue another request for data.
2193
    *
2194
    * Input are function pointers for what to do on success and failure.
2195
    *
2196
    * Parameters:
2197
    * success - {function}
2198
    * failure - {function}
2199
    */
2200
    loadFeaturesForRegion:function(success, failure) {
2201
        if(this.request) {
2202
            this.request.abort();
2203
        }
2204
        this.request = OpenLayers.Request.GET({
2205
            url: this.url,
2206
            success: success,
2207
            failure: failure,
2208
            scope: this
2209
        });
2210
    },
2211

    
2212
    /**
2213
    * Method: requestSuccess
2214
    * Called on return from request succcess. Adds results via
2215
    * layer.addFeatures in vector mode, addResults otherwise.
2216
    *
2217
    * Parameters:
2218
    * request - {<OpenLayers.Request.XMLHttpRequest>}
2219
    */
2220
    requestSuccess:function(request) {
2221
        if (this.features) {
2222
            var doc = request.responseXML;
2223
            if (!doc || !doc.documentElement) {
2224
                doc = request.responseText;
2225
            }
2226
            if (this.layer.vectorMode) {
2227
                this.layer.addFeatures(this.layer.formatObject.read(doc));
2228
            } else {
2229
                var xml = new OpenLayers.Format.XML();
2230
                if (typeof doc == "string") {
2231
                    doc = xml.read(doc);
2232
                }
2233
                var resultFeatures = xml.getElementsByTagNameNS(
2234
                    doc, "http://www.opengis.net/gml", "featureMember"
2235
                );
2236
                this.addResults(resultFeatures);
2237
            }
2238
        }
2239
        if (this.events) {
2240
            this.events.triggerEvent("loadend");
2241
        }
2242

    
2243
        //request produced with success, we can delete the request object.
2244
        //this.request.destroy();
2245
        this.request = null;
2246
    },
2247

    
2248
    /**
2249
     * Method: addResults
2250
     * Construct new feature via layer featureClass constructor, and add to
2251
     * this.features.
2252
     *
2253
     * Parameters:
2254
     * results - {Object}
2255
     */
2256
    addResults: function(results) {
2257
        for (var i=0; i < results.length; i++) {
2258
            var feature = new this.layer.featureClass(this.layer,
2259
                                                      results[i]);
2260
            this.features.push(feature);
2261
        }
2262
    },
2263

    
2264

    
2265
    /**
2266
     * Method: destroyAllFeatures
2267
     * Iterate through and call destroy() on each feature, removing it from
2268
     *   the local array
2269
     */
2270
    destroyAllFeatures: function() {
2271
        while(this.features.length > 0) {
2272
            var feature = this.features.shift();
2273
            feature.destroy();
2274
        }
2275
    },
2276

    
2277
    CLASS_NAME: "OpenLayers.Tile.WFS"
2278
  }
2279
);
2280

    
2281
/**
2282
 * Class: OpenLayers.Feature.WFS
2283
 * WFS handling class, for use as a featureClass on the WFS layer for handling
2284
 * 'point' WFS types. Good for subclassing when creating a custom WFS like
2285
 * XML application.
2286
 *
2287
 * Inherits from:
2288
 *  - <OpenLayers.Feature>
2289
 */
2290
OpenLayers.Feature.WFS = OpenLayers.Class(OpenLayers.Feature, {
2291

    
2292
    /**
2293
     * Constructor: OpenLayers.Feature.WFS
2294
     * Create a WFS feature.
2295
     *
2296
     * Parameters:
2297
     * layer - {<OpenLayers.Layer>}
2298
     * xmlNode - {XMLNode}
2299
     */
2300
    initialize: function(layer, xmlNode) {
2301
        var newArguments = arguments;
2302
        var data = this.processXMLNode(xmlNode);
2303
        newArguments = new Array(layer, data.lonlat, data);
2304
        OpenLayers.Feature.prototype.initialize.apply(this, newArguments);
2305
        this.createMarker();
2306
        this.layer.addMarker(this.marker);
2307
    },
2308

    
2309
    /**
2310
     * Method: destroy
2311
     * nullify references to prevent circular references and memory leaks
2312
     */
2313
    destroy: function() {
2314
        if (this.marker != null) {
2315
            this.layer.removeMarker(this.marker);
2316
        }
2317
        OpenLayers.Feature.prototype.destroy.apply(this, arguments);
2318
    },
2319

    
2320
    /**
2321
     * Method: processXMLNode
2322
     * When passed an xmlNode, parses it for a GML point, and passes
2323
     * back an object describing that point.
2324
     *
2325
     * For subclasses of Feature.WFS, this is the feature to change.
2326
     *
2327
     * Parameters:
2328
     * xmlNode - {XMLNode}
2329
     *
2330
     * Returns:
2331
     * {Object} Data Object with 'id', 'lonlat', and private properties set
2332
     */
2333
    processXMLNode: function(xmlNode) {
2334
        //this should be overridden by subclasses
2335
        // must return an Object with 'id' and 'lonlat' values set
2336
        var point = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, "http://www.opengis.net/gml", "gml", "Point");
2337
        var text  = OpenLayers.Util.getXmlNodeValue(OpenLayers.Ajax.getElementsByTagNameNS(point[0], "http://www.opengis.net/gml","gml", "coordinates")[0]);
2338
        var floats = text.split(",");
2339
        return {lonlat: new OpenLayers.LonLat(parseFloat(floats[0]),
2340
                                              parseFloat(floats[1])),
2341
                id: null};
2342

    
2343
    },
2344

    
2345
    CLASS_NAME: "OpenLayers.Feature.WFS"
2346
});
2347

    
2348

    
2349
/**
2350
 * Class: OpenLayers.Layer.WFS
2351
 * *Deprecated*.  To be removed in 3.0.  Instead use OpenLayers.Layer.Vector
2352
 *     with a Protocol.WFS and one or more Strategies.
2353
 *
2354
 * Inherits from:
2355
 *  - <OpenLayers.Layer.Vector>
2356
 *  - <OpenLayers.Layer.Markers>
2357
 */
2358
OpenLayers.Layer.WFS = OpenLayers.Class(
2359
  OpenLayers.Layer.Vector, OpenLayers.Layer.Markers, {
2360

    
2361
    /**
2362
     * APIProperty: isBaseLayer
2363
     * {Boolean} WFS layer is not a base layer by default.
2364
     */
2365
    isBaseLayer: false,
2366

    
2367
    /**
2368
     * Property: tile
2369
     * {<OpenLayers.Tile.WFS>}
2370
     */
2371
    tile: null,
2372

    
2373
    /**
2374
     * APIProperty: ratio
2375
     * {Float} The ratio property determines the size of the serverside query
2376
     *    relative to the map viewport size. By default, we load an area twice
2377
     *    as big as the map, to allow for panning without immediately reload.
2378
     *    Setting this to 1 will cause the area of the WFS request to match
2379
     *    the map area exactly. It is recommended to set this to some number
2380
     *    at least slightly larger than 1, otherwise accidental clicks can
2381
     *    cause a data reload, by moving the map only 1 pixel.
2382
     */
2383
    ratio: 2,
2384

    
2385
    /**
2386
     * Property: DEFAULT_PARAMS
2387
     * {Object} Hashtable of default key/value parameters
2388
     */
2389
    DEFAULT_PARAMS: { service: "WFS",
2390
                      version: "1.0.0",
2391
                      request: "GetFeature"
2392
                    },
2393

    
2394
    /**
2395
     * APIProperty: featureClass
2396
     * {<OpenLayers.Feature>} If featureClass is defined, an old-style markers
2397
     *     based WFS layer is created instead of a new-style vector layer. If
2398
     *     sent, this should be a subclass of OpenLayers.Feature
2399
     */
2400
    featureClass: null,
2401

    
2402
    /**
2403
      * APIProperty: format
2404
      * {<OpenLayers.Format>} The format you want the data to be parsed with.
2405
      * Must be passed in the constructor. Should be a class, not an instance.
2406
      * This option can only be used if no featureClass is passed / vectorMode
2407
      * is false: if a featureClass is passed, then this parameter is ignored.
2408
      */
2409
    format: null,
2410

    
2411
    /**
2412
     * Property: formatObject
2413
     * {<OpenLayers.Format>} Internally created/managed format object, used by
2414
     * the Tile to parse data.
2415
     */
2416
    formatObject: null,
2417

    
2418
    /**
2419
     * APIProperty: formatOptions
2420
     * {Object} Hash of options which should be passed to the format when it is
2421
     * created. Must be passed in the constructor.
2422
     */
2423
    formatOptions: null,
2424

    
2425
    /**
2426
     * Property: vectorMode
2427
     * {Boolean} Should be calculated automatically. Determines whether the
2428
     *     layer is in vector mode or marker mode.
2429
     */
2430
    vectorMode: true,
2431

    
2432
    /**
2433
     * APIProperty: encodeBBOX
2434
     * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no',
2435
     *     but some services want it that way. Default false.
2436
     */
2437
    encodeBBOX: false,
2438

    
2439
    /**
2440
     * APIProperty: extractAttributes
2441
     * {Boolean} Should the WFS layer parse attributes from the retrieved
2442
     *     GML? Defaults to false. If enabled, parsing is slower, but
2443
     *     attributes are available in the attributes property of
2444
     *     layer features.
2445
     */
2446
    extractAttributes: false,
2447

    
2448
    /**
2449
     * Constructor: OpenLayers.Layer.WFS
2450
     *
2451
     * Parameters:
2452
     * name - {String}
2453
     * url - {String}
2454
     * params - {Object}
2455
     * options - {Object} Hashtable of extra options to tag onto the layer
2456
     */
2457
    initialize: function(name, url, params, options) {
2458
        if (options == undefined) { options = {}; }
2459

    
2460
        if (options.featureClass ||
2461
            !OpenLayers.Layer.Vector ||
2462
            !OpenLayers.Feature.Vector) {
2463
            this.vectorMode = false;
2464
        }
2465

    
2466
        // Uppercase params
2467
        params = OpenLayers.Util.upperCaseObject(params);
2468

    
2469
        // Turn off error reporting, browsers like Safari may work
2470
        // depending on the setup, and we don't want an unneccesary alert.
2471
        OpenLayers.Util.extend(options, {'reportError': false});
2472
        var newArguments = [];
2473
        newArguments.push(name, options);
2474
        OpenLayers.Layer.Vector.prototype.initialize.apply(this, newArguments);
2475
        if (!this.renderer || !this.vectorMode) {
2476
            this.vectorMode = false;
2477
            if (!options.featureClass) {
2478
                options.featureClass = OpenLayers.Feature.WFS;
2479
            }
2480
            OpenLayers.Layer.Markers.prototype.initialize.apply(this,
2481
                                                                newArguments);
2482
        }
2483

    
2484
        if (this.params && this.params.typename && !this.options.typename) {
2485
            this.options.typename = this.params.typename;
2486
        }
2487

    
2488
        if (!this.options.geometry_column) {
2489
            this.options.geometry_column = "the_geom";
2490
        }
2491

    
2492
        this.params = OpenLayers.Util.applyDefaults(
2493
            params,
2494
            OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)
2495
        );
2496
        this.url = url;
2497
    },
2498

    
2499

    
2500
    /**
2501
     * APIMethod: destroy
2502
     */
2503
    destroy: function() {
2504
        if (this.vectorMode) {
2505
            OpenLayers.Layer.Vector.prototype.destroy.apply(this, arguments);
2506
        } else {
2507
            OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
2508
        }
2509
        if (this.tile) {
2510
            this.tile.destroy();
2511
        }
2512
        this.tile = null;
2513

    
2514
        this.ratio = null;
2515
        this.featureClass = null;
2516
        this.format = null;
2517

    
2518
        if (this.formatObject && this.formatObject.destroy) {
2519
            this.formatObject.destroy();
2520
        }
2521
        this.formatObject = null;
2522

    
2523
        this.formatOptions = null;
2524
        this.vectorMode = null;
2525
        this.encodeBBOX = null;
2526
        this.extractAttributes = null;
2527
    },
2528

    
2529
    /**
2530
     * Method: setMap
2531
     *
2532
     * Parameters:
2533
     * map - {<OpenLayers.Map>}
2534
     */
2535
    setMap: function(map) {
2536
        if (this.vectorMode) {
2537
            OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
2538

    
2539
            var options = {
2540
              'extractAttributes': this.extractAttributes
2541
            };
2542

    
2543
            OpenLayers.Util.extend(options, this.formatOptions);
2544
            if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
2545
                options.externalProjection = this.projection;
2546
                options.internalProjection = this.map.getProjectionObject();
2547
            }
2548

    
2549
            this.formatObject = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
2550
        } else {
2551
            OpenLayers.Layer.Markers.prototype.setMap.apply(this, arguments);
2552
        }
2553
    },
2554

    
2555
    /**
2556
     * Method: moveTo
2557
     *
2558
     * Parameters:
2559
     * bounds - {<OpenLayers.Bounds>}
2560
     * zoomChanged - {Boolean}
2561
     * dragging - {Boolean}
2562
     */
2563
    moveTo:function(bounds, zoomChanged, dragging) {
2564
        if (this.vectorMode) {
2565
            OpenLayers.Layer.Vector.prototype.moveTo.apply(this, arguments);
2566
        } else {
2567
            OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
2568
        }
2569

    
2570
        // don't load wfs features while dragging, wait for drag end
2571
        if (dragging) {
2572
            // TBD try to hide the vector layer while dragging
2573
            // this.setVisibility(false);
2574
            // this will probably help for panning performances
2575
            return false;
2576
        }
2577

    
2578
        if ( zoomChanged ) {
2579
            if (this.vectorMode) {
2580
                this.renderer.clear();
2581
            }
2582
        }
2583

    
2584
    //DEPRECATED - REMOVE IN 3.0
2585
        // don't load data if current zoom level doesn't match
2586
        if (this.options.minZoomLevel) {
2587
            OpenLayers.Console.warn(OpenLayers.i18n('minZoomLevelError'));
2588

    
2589
            if (this.map.getZoom() < this.options.minZoomLevel) {
2590
                return null;
2591
            }
2592
        }
2593

    
2594
        if (bounds == null) {
2595
            bounds = this.map.getExtent();
2596
        }
2597

    
2598
        var firstRendering = (this.tile == null);
2599

    
2600
        //does the new bounds to which we need to move fall outside of the
2601
        // current tile's bounds?
2602
        var outOfBounds = (!firstRendering &&
2603
                           !this.tile.bounds.containsBounds(bounds));
2604

    
2605
        if (zoomChanged || firstRendering || (!dragging && outOfBounds)) {
2606
            //determine new tile bounds
2607
            var center = bounds.getCenterLonLat();
2608
            var tileWidth = bounds.getWidth() * this.ratio;
2609
            var tileHeight = bounds.getHeight() * this.ratio;
2610
            var tileBounds =
2611
                new OpenLayers.Bounds(center.lon - (tileWidth / 2),
2612
                                      center.lat - (tileHeight / 2),
2613
                                      center.lon + (tileWidth / 2),
2614
                                      center.lat + (tileHeight / 2));
2615

    
2616
            //determine new tile size
2617
            var tileSize = this.map.getSize();
2618
            tileSize.w = tileSize.w * this.ratio;
2619
            tileSize.h = tileSize.h * this.ratio;
2620

    
2621
            //determine new position (upper left corner of new bounds)
2622
            var ul = new OpenLayers.LonLat(tileBounds.left, tileBounds.top);
2623
            var pos = this.map.getLayerPxFromLonLat(ul);
2624

    
2625
            //formulate request url string
2626
            var url = this.getFullRequestString();
2627

    
2628
            var params = null;
2629

    
2630
            // Cant combine "filter" and "BBOX". This is a cheap hack to help
2631
            // people out who can't migrate to the WFS protocol immediately.
2632
            var filter = this.params.filter || this.params.FILTER;
2633
            if (filter) {
2634
                params = {FILTER: filter};
2635
            }
2636
            else {
2637
                params = {BBOX: this.encodeBBOX ? tileBounds.toBBOX()
2638
                                                    : tileBounds.toArray()};
2639
            }
2640

    
2641
            if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
2642
                var projectedBounds = tileBounds.clone();
2643
                projectedBounds.transform(this.map.getProjectionObject(),
2644
                                          this.projection);
2645
                if (!filter){
2646
                    params.BBOX = this.encodeBBOX ? projectedBounds.toBBOX()
2647
                                                : projectedBounds.toArray();
2648
                }
2649
            }
2650

    
2651
            url += "&" + OpenLayers.Util.getParameterString(params);
2652

    
2653
            if (!this.tile) {
2654
                this.tile = new OpenLayers.Tile.WFS(this, pos, tileBounds,
2655
                                                     url, tileSize);
2656
                this.addTileMonitoringHooks(this.tile);
2657
                this.tile.draw();
2658
            } else {
2659
                if (this.vectorMode) {
2660
                    this.destroyFeatures();
2661
                    this.renderer.clear();
2662
                } else {
2663
                    this.clearMarkers();
2664
                }
2665
                this.removeTileMonitoringHooks(this.tile);
2666
                this.tile.destroy();
2667

    
2668
                this.tile = null;
2669
                this.tile = new OpenLayers.Tile.WFS(this, pos, tileBounds,
2670
                                                     url, tileSize);
2671
                this.addTileMonitoringHooks(this.tile);
2672
                this.tile.draw();
2673
            }
2674
        }
2675
    },
2676

    
2677
    /**
2678
     * Method: addTileMonitoringHooks
2679
     * This function takes a tile as input and adds the appropriate hooks to
2680
     *     the tile so that the layer can keep track of the loading tile
2681
     *     (making sure to check that the tile is always the layer's current
2682
     *     tile before taking any action).
2683
     *
2684
     * Parameters:
2685
     * tile - {<OpenLayers.Tile>}
2686
     */
2687
    addTileMonitoringHooks: function(tile) {
2688
        tile.onLoadStart = function() {
2689
            //if this is the the layer's current tile, then trigger
2690
            // a 'loadstart'
2691
            if (this == this.layer.tile) {
2692
                this.layer.events.triggerEvent("loadstart");
2693
            }
2694
        };
2695
        tile.events.register("loadstart", tile, tile.onLoadStart);
2696

    
2697
        tile.onLoadEnd = function() {
2698
            //if this is the the layer's current tile, then trigger
2699
            // a 'tileloaded' and 'loadend'
2700
            if (this == this.layer.tile) {
2701
                this.layer.events.triggerEvent("tileloaded");
2702
                this.layer.events.triggerEvent("loadend");
2703
            }
2704
        };
2705
        tile.events.register("loadend", tile, tile.onLoadEnd);
2706
        tile.events.register("unload", tile, tile.onLoadEnd);
2707
    },
2708

    
2709
    /**
2710
     * Method: removeTileMonitoringHooks
2711
     * This function takes a tile as input and removes the tile hooks
2712
     *     that were added in addTileMonitoringHooks()
2713
     *
2714
     * Parameters:
2715
     * tile - {<OpenLayers.Tile>}
2716
     */
2717
    removeTileMonitoringHooks: function(tile) {
2718
        tile.unload();
2719
        tile.events.un({
2720
            "loadstart": tile.onLoadStart,
2721
            "loadend": tile.onLoadEnd,
2722
            "unload": tile.onLoadEnd,
2723
            scope: tile
2724
        });
2725
    },
2726

    
2727
    /**
2728
     * Method: onMapResize
2729
     * Call the onMapResize method of the appropriate parent class.
2730
     */
2731
    onMapResize: function() {
2732
        if(this.vectorMode) {
2733
            OpenLayers.Layer.Vector.prototype.onMapResize.apply(this,
2734
                                                                arguments);
2735
        } else {
2736
            OpenLayers.Layer.Markers.prototype.onMapResize.apply(this,
2737
                                                                 arguments);
2738
        }
2739
    },
2740

    
2741
    /**
2742
     * Method: display
2743
     * Call the display method of the appropriate parent class.
2744
     */
2745
    display: function() {
2746
        if(this.vectorMode) {
2747
            OpenLayers.Layer.Vector.prototype.display.apply(this,
2748
                                                                arguments);
2749
        } else {
2750
            OpenLayers.Layer.Markers.prototype.display.apply(this,
2751
                                                                 arguments);
2752
        }
2753
    },
2754

    
2755
    /**
2756
     * APIMethod: mergeNewParams
2757
     * Modify parameters for the layer and redraw.
2758
     *
2759
     * Parameters:
2760
     * newParams - {Object}
2761
     */
2762
    mergeNewParams:function(newParams) {
2763
        var upperParams = OpenLayers.Util.upperCaseObject(newParams);
2764
        var newArguments = [upperParams];
2765
        return OpenLayers.Layer.HTTPRequest.prototype.mergeNewParams.apply(this,
2766
                                                                 newArguments);
2767
    },
2768

    
2769
    /**
2770
     * APIMethod: clone
2771
     *
2772
     * Parameters:
2773
     * obj - {Object}
2774
     *
2775
     * Returns:
2776
     * {<OpenLayers.Layer.WFS>} An exact clone of this OpenLayers.Layer.WFS
2777
     */
2778
    clone: function (obj) {
2779

    
2780
        if (obj == null) {
2781
            obj = new OpenLayers.Layer.WFS(this.name,
2782
                                           this.url,
2783
                                           this.params,
2784
                                           this.getOptions());
2785
        }
2786

    
2787
        //get all additions from superclasses
2788
        if (this.vectorMode) {
2789
            obj = OpenLayers.Layer.Vector.prototype.clone.apply(this, [obj]);
2790
        } else {
2791
            obj = OpenLayers.Layer.Markers.prototype.clone.apply(this, [obj]);
2792
        }
2793

    
2794
        // copy/set any non-init, non-simple values here
2795

    
2796
        return obj;
2797
    },
2798

    
2799
    /**
2800
     * APIMethod: getFullRequestString
2801
     * combine the layer's url with its params and these newParams.
2802
     *
2803
     *    Add the SRS parameter from 'projection' -- this is probably
2804
     *     more eloquently done via a setProjection() method, but this
2805
     *     works for now and always.
2806
     *
2807
     * Parameters:
2808
     * newParams - {Object}
2809
     * altUrl - {String} Use this as the url instead of the layer's url
2810
     */
2811
    getFullRequestString:function(newParams, altUrl) {
2812
        var projectionCode = this.projection.getCode() || this.map.getProjection();
2813
        this.params.SRS = (projectionCode == "none") ? null : projectionCode;
2814

    
2815
        return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(
2816
                                                    this, arguments);
2817
    },
2818

    
2819
    /**
2820
     * APIMethod: commit
2821
     * Write out the data to a WFS server.
2822
     */
2823
    commit: function() {
2824
        if (!this.writer) {
2825
            var options = {};
2826
            if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
2827
                options.externalProjection = this.projection;
2828
                options.internalProjection = this.map.getProjectionObject();
2829
            }
2830

    
2831
            this.writer = new OpenLayers.Format.WFS(options,this);
2832
        }
2833

    
2834
        var data = this.writer.write(this.features);
2835

    
2836
        OpenLayers.Request.POST({
2837
            url: this.url,
2838
            data: data,
2839
            success: this.commitSuccess,
2840
            failure: this.commitFailure,
2841
            scope: this
2842
        });
2843
    },
2844

    
2845
    /**
2846
     * Method: commitSuccess
2847
     * Called when the Ajax request returns a response
2848
     *
2849
     * Parameters:
2850
     * response - {XmlNode} from server
2851
     */
2852
    commitSuccess: function(request) {
2853
        var response = request.responseText;
2854
        if (response.indexOf('SUCCESS') != -1) {
2855
            this.commitReport(OpenLayers.i18n("commitSuccess", {'response':response}));
2856

    
2857
            for(var i = 0; i < this.features.length; i++) {
2858
                this.features[i].state = null;
2859
            }
2860
            // TBD redraw the layer or reset the state of features
2861
            // foreach features: set state to null
2862
        } else if (response.indexOf('FAILED') != -1 ||
2863
            response.indexOf('Exception') != -1) {
2864
            this.commitReport(OpenLayers.i18n("commitFailed", {'response':response}));
2865
        }
2866
    },
2867

    
2868
    /**
2869
     * Method: commitFailure
2870
     * Called when the Ajax request fails
2871
     *
2872
     * Parameters:
2873
     * response - {XmlNode} from server
2874
     */
2875
    commitFailure: function(request) {},
2876

    
2877
    /**
2878
     * APIMethod: commitReport
2879
     * Called with a 'success' message if the commit succeeded, otherwise
2880
     *     a failure message, and the full request text as a second parameter.
2881
     *     Override this function to provide custom transaction reporting.
2882
     *
2883
     * string - {String} reporting string
2884
     * response - {String} full XML response
2885
     */
2886
    commitReport: function(string, response) {
2887
        OpenLayers.Console.userError(string);
2888
    },
2889

    
2890

    
2891
    /**
2892
     * APIMethod: refresh
2893
     * Refreshes all the features of the layer
2894
     */
2895
    refresh: function() {
2896
        if (this.tile) {
2897
            if (this.vectorMode) {
2898
                this.renderer.clear();
2899
                this.features.length = 0;
2900
            } else {
2901
                this.clearMarkers();
2902
                this.markers.length = 0;
2903
            }
2904
            this.tile.draw();
2905
        }
2906
    },
2907

    
2908
    /**
2909
     * APIMethod: getDataExtent
2910
     * Calculates the max extent which includes all of the layer data.
2911
     *
2912
     * Returns:
2913
     * {<OpenLayers.Bounds>}
2914
     */
2915
    getDataExtent: function () {
2916
        var extent;
2917
        //get all additions from superclasses
2918
        if (this.vectorMode) {
2919
            extent = OpenLayers.Layer.Vector.prototype.getDataExtent.apply(this);
2920
        } else {
2921
            extent = OpenLayers.Layer.Markers.prototype.getDataExtent.apply(this);
2922
        }
2923

    
2924
        return extent;
2925
    },
2926

    
2927
    /**
2928
     * APIMethod: setOpacity
2929
     * Call the setOpacity method of the appropriate parent class to set the
2930
     *     opacity.
2931
     *
2932
     * Parameters:
2933
     * opacity - {Float}
2934
     */
2935
    setOpacity: function (opacity) {
2936
        if (this.vectorMode) {
2937
            OpenLayers.Layer.Vector.prototype.setOpacity.apply(this, [opacity]);
2938
        } else {
2939
            OpenLayers.Layer.Markers.prototype.setOpacity.apply(this, [opacity]);
2940
        }
2941
    },
2942

    
2943
    CLASS_NAME: "OpenLayers.Layer.WFS"
2944
});
2945

    
2946
/**
2947
 * Class: OpenLayers.Layer.VirtualEarth
2948
 * *Deprecated*. Use <OpenLayers.Layer.Bing> instead.
2949
 *
2950
 * Instances of OpenLayers.Layer.VirtualEarth are used to display the data from
2951
 *     the Bing Maps AJAX Control (see e.g.
2952
 *     http://msdn.microsoft.com/library/bb429619.aspx). Create a VirtualEarth
2953
 *     layer with the <OpenLayers.Layer.VirtualEarth> constructor.
2954
 *
2955
 * Inherits from:
2956
 *  - <OpenLayers.Layer.EventPane>
2957
 *  - <OpenLayers.Layer.FixedZoomLevels>
2958
 */
2959
OpenLayers.Layer.VirtualEarth = OpenLayers.Class(
2960
    OpenLayers.Layer.EventPane,
2961
    OpenLayers.Layer.FixedZoomLevels, {
2962

    
2963
    /**
2964
     * Constant: MIN_ZOOM_LEVEL
2965
     * {Integer} 1
2966
     */
2967
    MIN_ZOOM_LEVEL: 1,
2968

    
2969
    /**
2970
     * Constant: MAX_ZOOM_LEVEL
2971
     * {Integer} 19
2972
     */
2973
    MAX_ZOOM_LEVEL: 19,
2974

    
2975
    /**
2976
     * Constant: RESOLUTIONS
2977
     * {Array(Float)} Hardcode these resolutions so that they are more closely
2978
     *                tied with the standard wms projection
2979
     */
2980
    RESOLUTIONS: [
2981
        1.40625,
2982
        0.703125,
2983
        0.3515625,
2984
        0.17578125,
2985
        0.087890625,
2986
        0.0439453125,
2987
        0.02197265625,
2988
        0.010986328125,
2989
        0.0054931640625,
2990
        0.00274658203125,
2991
        0.001373291015625,
2992
        0.0006866455078125,
2993
        0.00034332275390625,
2994
        0.000171661376953125,
2995
        0.0000858306884765625,
2996
        0.00004291534423828125,
2997
        0.00002145767211914062,
2998
        0.00001072883605957031,
2999
        0.00000536441802978515
3000
    ],
3001

    
3002
    /**
3003
     * APIProperty: type
3004
     * {VEMapType}
3005
     */
3006
    type: null,
3007

    
3008
    /**
3009
     * APIProperty: wrapDateLine
3010
     * {Boolean} Allow user to pan forever east/west.  Default is true.
3011
     *     Setting this to false only restricts panning if
3012
     *     <sphericalMercator> is true.
3013
     */
3014
    wrapDateLine: true,
3015

    
3016
    /**
3017
     * APIProperty: sphericalMercator
3018
     * {Boolean} Should the map act as a mercator-projected map? This will
3019
     *     cause all interactions with the map to be in the actual map
3020
     *     projection, which allows support for vector drawing, overlaying
3021
     *     other maps, etc.
3022
     */
3023
    sphericalMercator: false,
3024

    
3025
    /**
3026
     * APIProperty: animationEnabled
3027
     * {Boolean} If set to true, the transition between zoom levels will be
3028
     *     animated. Set to false to match the zooming experience of other
3029
     *     layer types. Default is true.
3030
     */
3031
    animationEnabled: true,
3032

    
3033
    /**
3034
     * Constructor: OpenLayers.Layer.VirtualEarth
3035
     * Creates a new instance of a OpenLayers.Layer.VirtualEarth. If you use an
3036
     *     instance of OpenLayers.Layer.VirtualEarth in you map, you should set
3037
     *     the <OpenLayers.Map> option restrictedExtent to a meaningful value,
3038
     *     e.g.:
3039
     * (code)
3040
     * var map = new OpenLayers.Map( 'map', {
3041
     *     // other map options
3042
     *     restrictedExtent : OpenLayers.Bounds(-20037508, -20037508, 20037508, 20037508)
3043
     * } );
3044
     *
3045
     * var veLayer = new OpenLayers.Layer.VirtualEarth (
3046
     *     "Virtual Earth Layer"
3047
     * );
3048
     *
3049
     * map.addLayer( veLayer );
3050
     * (end)
3051
     *
3052
     * Parameters:
3053
     * name - {String}
3054
     * options - {Object}
3055
     */
3056
    initialize: function(name, options) {
3057
        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
3058
        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
3059
                                                                    arguments);
3060
        if(this.sphericalMercator) {
3061
            OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
3062
            this.initMercatorParameters();
3063
        }
3064
    },
3065

    
3066
    /**
3067
     * Method: loadMapObject
3068
     */
3069
    loadMapObject:function() {
3070

    
3071
        // create div and set to same size as map
3072
        var veDiv = OpenLayers.Util.createDiv(this.name);
3073
        var sz = this.map.getSize();
3074
        veDiv.style.width = sz.w + "px";
3075
        veDiv.style.height = sz.h + "px";
3076
        this.div.appendChild(veDiv);
3077

    
3078
        try { // crash prevention
3079
            this.mapObject = new VEMap(this.name);
3080
        } catch (e) { }
3081

    
3082
        if (this.mapObject != null) {
3083
            try { // this is to catch a Mozilla bug without falling apart
3084

    
3085
                // The fourth argument is whether the map is 'fixed' -- not
3086
                // draggable. See:
3087
                // http://blogs.msdn.com/virtualearth/archive/2007/09/28/locking-a-virtual-earth-map.aspx
3088
                //
3089
                this.mapObject.LoadMap(null, null, this.type, true);
3090
                this.mapObject.AttachEvent("onmousedown", OpenLayers.Function.True);
3091

    
3092
            } catch (e) { }
3093
            this.mapObject.HideDashboard();
3094
            if(typeof this.mapObject.SetAnimationEnabled == "function") {
3095
                this.mapObject.SetAnimationEnabled(this.animationEnabled);
3096
            }
3097
        }
3098

    
3099
        //can we do smooth panning? this is an unpublished method, so we need
3100
        // to be careful
3101
        if ( !this.mapObject ||
3102
             !this.mapObject.vemapcontrol ||
3103
             !this.mapObject.vemapcontrol.PanMap ||
3104
             (typeof this.mapObject.vemapcontrol.PanMap != "function")) {
3105

    
3106
            this.dragPanMapObject = null;
3107
        }
3108

    
3109
    },
3110

    
3111
    /**
3112
     * Method: onMapResize
3113
     */
3114
    onMapResize: function() {
3115
        this.mapObject.Resize(this.map.size.w, this.map.size.h);
3116
    },
3117

    
3118
    /**
3119
     * APIMethod: getWarningHTML
3120
     *
3121
     * Returns:
3122
     * {String} String with information on why layer is broken, how to get
3123
     *          it working.
3124
     */
3125
    getWarningHTML:function() {
3126
        return OpenLayers.i18n(
3127
            "getLayerWarning", {'layerType':'VE', 'layerLib':'VirtualEarth'}
3128
        );
3129
    },
3130

    
3131

    
3132

    
3133
    /************************************
3134
     *                                  *
3135
     *   MapObject Interface Controls   *
3136
     *                                  *
3137
     ************************************/
3138

    
3139

    
3140
  // Get&Set Center, Zoom
3141

    
3142
    /**
3143
     * APIMethod: setMapObjectCenter
3144
     * Set the mapObject to the specified center and zoom
3145
     *
3146
     * Parameters:
3147
     * center - {Object} MapObject LonLat format
3148
     * zoom - {int} MapObject zoom format
3149
     */
3150
    setMapObjectCenter: function(center, zoom) {
3151
        this.mapObject.SetCenterAndZoom(center, zoom);
3152
    },
3153

    
3154
    /**
3155
     * APIMethod: getMapObjectCenter
3156
     *
3157
     * Returns:
3158
     * {Object} The mapObject's current center in Map Object format
3159
     */
3160
    getMapObjectCenter: function() {
3161
        return this.mapObject.GetCenter();
3162
    },
3163

    
3164
    /**
3165
     * APIMethod: dragPanMapObject
3166
     *
3167
     * Parameters:
3168
     * dX - {Integer}
3169
     * dY - {Integer}
3170
     */
3171
    dragPanMapObject: function(dX, dY) {
3172
        this.mapObject.vemapcontrol.PanMap(dX, -dY);
3173
    },
3174

    
3175
    /**
3176
     * APIMethod: getMapObjectZoom
3177
     *
3178
     * Returns:
3179
     * {Integer} The mapObject's current zoom, in Map Object format
3180
     */
3181
    getMapObjectZoom: function() {
3182
        return this.mapObject.GetZoomLevel();
3183
    },
3184

    
3185

    
3186
  // LonLat - Pixel Translation
3187

    
3188
    /**
3189
     * APIMethod: getMapObjectLonLatFromMapObjectPixel
3190
     *
3191
     * Parameters:
3192
     * moPixel - {Object} MapObject Pixel format
3193
     *
3194
     * Returns:
3195
     * {Object} MapObject LonLat translated from MapObject Pixel
3196
     */
3197
    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
3198
        //the conditional here is to test if we are running the v6 of VE
3199
        return (typeof VEPixel != 'undefined')
3200
            ? this.mapObject.PixelToLatLong(moPixel)
3201
            : this.mapObject.PixelToLatLong(moPixel.x, moPixel.y);
3202
    },
3203

    
3204
    /**
3205
     * APIMethod: getMapObjectPixelFromMapObjectLonLat
3206
     *
3207
     * Parameters:
3208
     * moLonLat - {Object} MapObject LonLat format
3209
     *
3210
     * Returns:
3211
     * {Object} MapObject Pixel transtlated from MapObject LonLat
3212
     */
3213
    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
3214
        return this.mapObject.LatLongToPixel(moLonLat);
3215
    },
3216

    
3217

    
3218
    /************************************
3219
     *                                  *
3220
     *       MapObject Primitives       *
3221
     *                                  *
3222
     ************************************/
3223

    
3224

    
3225
  // LonLat
3226

    
3227
    /**
3228
     * APIMethod: getLongitudeFromMapObjectLonLat
3229
     *
3230
     * Parameters:
3231
     * moLonLat - {Object} MapObject LonLat format
3232
     *
3233
     * Returns:
3234
     * {Float} Longitude of the given MapObject LonLat
3235
     */
3236
    getLongitudeFromMapObjectLonLat: function(moLonLat) {
3237
        return this.sphericalMercator ?
3238
            this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lon :
3239
            moLonLat.Longitude;
3240
    },
3241

    
3242
    /**
3243
     * APIMethod: getLatitudeFromMapObjectLonLat
3244
     *
3245
     * Parameters:
3246
     * moLonLat - {Object} MapObject LonLat format
3247
     *
3248
     * Returns:
3249
     * {Float} Latitude of the given MapObject LonLat
3250
     */
3251
    getLatitudeFromMapObjectLonLat: function(moLonLat) {
3252
        return this.sphericalMercator ?
3253
            this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lat :
3254
            moLonLat.Latitude;
3255
    },
3256

    
3257
    /**
3258
     * APIMethod: getMapObjectLonLatFromLonLat
3259
     *
3260
     * Parameters:
3261
     * lon - {Float}
3262
     * lat - {Float}
3263
     *
3264
     * Returns:
3265
     * {Object} MapObject LonLat built from lon and lat params
3266
     */
3267
    getMapObjectLonLatFromLonLat: function(lon, lat) {
3268
        var veLatLong;
3269
        if(this.sphericalMercator) {
3270
            var lonlat = this.inverseMercator(lon, lat);
3271
            veLatLong = new VELatLong(lonlat.lat, lonlat.lon);
3272
        } else {
3273
            veLatLong = new VELatLong(lat, lon);
3274
        }
3275
        return veLatLong;
3276
    },
3277

    
3278
  // Pixel
3279

    
3280
    /**
3281
     * APIMethod: getXFromMapObjectPixel
3282
     *
3283
     * Parameters:
3284
     * moPixel - {Object} MapObject Pixel format
3285
     *
3286
     * Returns:
3287
     * {Integer} X value of the MapObject Pixel
3288
     */
3289
    getXFromMapObjectPixel: function(moPixel) {
3290
        return moPixel.x;
3291
    },
3292

    
3293
    /**
3294
     * APIMethod: getYFromMapObjectPixel
3295
     *
3296
     * Parameters:
3297
     * moPixel - {Object} MapObject Pixel format
3298
     *
3299
     * Returns:
3300
     * {Integer} Y value of the MapObject Pixel
3301
     */
3302
    getYFromMapObjectPixel: function(moPixel) {
3303
        return moPixel.y;
3304
    },
3305

    
3306
    /**
3307
     * APIMethod: getMapObjectPixelFromXY
3308
     *
3309
     * Parameters:
3310
     * x - {Integer}
3311
     * y - {Integer}
3312
     *
3313
     * Returns:
3314
     * {Object} MapObject Pixel from x and y parameters
3315
     */
3316
    getMapObjectPixelFromXY: function(x, y) {
3317
        //the conditional here is to test if we are running the v6 of VE
3318
        return (typeof VEPixel != 'undefined') ? new VEPixel(x, y)
3319
                         : new Msn.VE.Pixel(x, y);
3320
    },
3321

    
3322
    CLASS_NAME: "OpenLayers.Layer.VirtualEarth"
3323
});
3324

    
3325
/*
3326
 * Copyright 2007, Google Inc.
3327
 *
3328
 * Redistribution and use in source and binary forms, with or without
3329
 * modification, are permitted provided that the following conditions are met:
3330
 *
3331
 *  1. Redistributions of source code must retain the above copyright notice,
3332
 *     this list of conditions and the following disclaimer.
3333
 *  2. Redistributions in binary form must reproduce the above copyright notice,
3334
 *     this list of conditions and the following disclaimer in the documentation
3335
 *     and/or other materials provided with the distribution.
3336
 *  3. Neither the name of Google Inc. nor the names of its contributors may be
3337
 *     used to endorse or promote products derived from this software without
3338
 *     specific prior written permission.
3339
 *
3340
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
3341
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
3342
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
3343
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3344
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
3345
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
3346
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
3347
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3348
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
3349
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3350
 *
3351
 * Sets up google.gears.*, which is *the only* supported way to access Gears.
3352
 *
3353
 * Circumvent this file at your own risk!
3354
 *
3355
 * In the future, Gears may automatically define google.gears.* without this
3356
 * file. Gears may use these objects to transparently fix bugs and compatibility
3357
 * issues. Applications that use the code below will continue to work seamlessly
3358
 * when that happens.
3359
 */
3360

    
3361
(function() {
3362
  // We are already defined. Hooray!
3363
  if (window.google && google.gears) {
3364
    return;
3365
  }
3366

    
3367
  var factory = null;
3368

    
3369
  // Firefox
3370
  if (typeof GearsFactory != 'undefined') {
3371
    factory = new GearsFactory();
3372
  } else {
3373
    // IE
3374
    try {
3375
      factory = new ActiveXObject('Gears.Factory');
3376
      // privateSetGlobalObject is only required and supported on WinCE.
3377
      if (factory.getBuildInfo().indexOf('ie_mobile') != -1) {
3378
        factory.privateSetGlobalObject(this);
3379
      }
3380
    } catch (e) {
3381
      // Safari
3382
      if ((typeof navigator.mimeTypes != 'undefined')
3383
           && navigator.mimeTypes["application/x-googlegears"]) {
3384
        factory = document.createElement("object");
3385
        factory.style.display = "none";
3386
        factory.width = 0;
3387
        factory.height = 0;
3388
        factory.type = "application/x-googlegears";
3389
        document.documentElement.appendChild(factory);
3390
      }
3391
    }
3392
  }
3393

    
3394
  // *Do not* define any objects if Gears is not installed. This mimics the
3395
  // behavior of Gears defining the objects in the future.
3396
  if (!factory) {
3397
    return;
3398
  }
3399

    
3400
  // Now set up the objects, being careful not to overwrite anything.
3401
  //
3402
  // Note: In Internet Explorer for Windows Mobile, you can't add properties to
3403
  // the window object. However, global objects are automatically added as
3404
  // properties of the window object in all browsers.
3405
  if (!window.google) {
3406
    google = {};
3407
  }
3408

    
3409
  if (!google.gears) {
3410
    google.gears = {factory: factory};
3411
  }
3412
})();
3413

    
3414
/**
3415
 * Class: OpenLayers.Protocol.SQL
3416
 * Abstract SQL protocol class.  Not to be instantiated directly.  Use
3417
 *     one of the SQL protocol subclasses instead.
3418
 *
3419
 * Inherits from:
3420
 *  - <OpenLayers.Protocol>
3421
 */
3422
OpenLayers.Protocol.SQL = OpenLayers.Class(OpenLayers.Protocol, {
3423

    
3424
    /**
3425
     * APIProperty: databaseName
3426
     * {String}
3427
     */
3428
    databaseName: 'ol',
3429

    
3430
    /**
3431
     * APIProperty: tableName
3432
     * Name of the database table into which Features should be saved.
3433
     */
3434
    tableName: "ol_vector_features",
3435

    
3436
    /**
3437
     * Property: postReadFiltering
3438
     * {Boolean} Whether the filter (if there's one) must be applied after
3439
     *      the features have been read from the database; for example the
3440
     *      BBOX strategy passes the read method a BBOX spatial filter, if
3441
     *      postReadFiltering is true every feature read from the database
3442
     *      will go through the BBOX spatial filter, which can be costly;
3443
     *      defaults to true.
3444
     */
3445
    postReadFiltering: true,
3446

    
3447
    /**
3448
     * Constructor: OpenLayers.Protocol.SQL
3449
     */
3450
    initialize: function(options) {
3451
        OpenLayers.Protocol.prototype.initialize.apply(this, [options]);
3452
    },
3453

    
3454
    /**
3455
     * APIMethod: destroy
3456
     * Clean up the protocol.
3457
     */
3458
    destroy: function() {
3459
        OpenLayers.Protocol.prototype.destroy.apply(this);
3460
    },
3461

    
3462
    /**
3463
     * APIMethod: supported
3464
     * This should be overridden by specific subclasses
3465
     *
3466
     * Returns:
3467
     * {Boolean} Whether or not the browser supports the SQL backend
3468
     */
3469
    supported: function() {
3470
        return false;
3471
    },
3472

    
3473
    /**
3474
     * Method: evaluateFilter
3475
     * If postReadFiltering is true evaluate the filter against the feature
3476
     * and return the result of the evaluation, otherwise return true.
3477
     *
3478
     * Parameters:
3479
     * {<OpenLayers.Feature.Vector>} The feature.
3480
     * {<OpenLayers.Filter>} The filter.
3481
     *
3482
     * Returns:
3483
     * {Boolean} true if postReadFiltering if false, the result of the
3484
     * filter evaluation otherwise.
3485
     */
3486
    evaluateFilter: function(feature, filter) {
3487
        return filter && this.postReadFiltering ?
3488
            filter.evaluate(feature) : true;
3489
    },
3490

    
3491
    CLASS_NAME: "OpenLayers.Protocol.SQL"
3492
});
3493

    
3494
/**
3495
 * Class: OpenLayers.Protocol.SQL.Gears
3496
 * This Protocol stores feature in the browser via the Gears Database module
3497
 * <http://code.google.com/apis/gears/api_database.html>.
3498
 *
3499
 * The main advantage is that all the read, create, update and delete operations
3500
 * can be done offline.
3501
 *
3502
 * Inherits from:
3503
 *  - <OpenLayers.Protocol.SQL>
3504
 */
3505
OpenLayers.Protocol.SQL.Gears = OpenLayers.Class(OpenLayers.Protocol.SQL, {
3506

    
3507
    /**
3508
     * Property: FID_PREFIX
3509
     * {String}
3510
     */
3511
    FID_PREFIX: '__gears_fid__',
3512

    
3513
    /**
3514
     * Property: NULL_GEOMETRY
3515
     * {String}
3516
     */
3517
    NULL_GEOMETRY: '__gears_null_geometry__',
3518

    
3519
    /**
3520
     * Property: NULL_FEATURE_STATE
3521
     * {String}
3522
     */
3523
    NULL_FEATURE_STATE: '__gears_null_feature_state__',
3524

    
3525
    /**
3526
     * Property: jsonParser
3527
     * {<OpenLayers.Format.JSON>}
3528
     */
3529
    jsonParser: null,
3530

    
3531
    /**
3532
     * Property: wktParser
3533
     * {<OpenLayers.Format.WKT>}
3534
     */
3535
    wktParser: null,
3536

    
3537
    /**
3538
     * Property: fidRegExp
3539
     * {RegExp} Regular expression to know whether a feature was
3540
     *      created in offline mode.
3541
     */
3542
    fidRegExp: null,
3543

    
3544
    /**
3545
     * Property: saveFeatureState
3546
     * {Boolean} Whether to save the feature state (<OpenLayers.State>)
3547
     *      into the database, defaults to true.
3548
     */
3549
    saveFeatureState: true,
3550

    
3551
    /**
3552
     * Property: typeOfFid
3553
     * {String} The type of the feature identifier, either "number" or
3554
     *      "string", defaults to "string".
3555
     */
3556
    typeOfFid: "string",
3557

    
3558
    /**
3559
     * Property: db
3560
     * {GearsDatabase}
3561
     */
3562
    db: null,
3563

    
3564
    /**
3565
     * Constructor: OpenLayers.Protocol.SQL.Gears
3566
     */
3567
    initialize: function(options) {
3568
        if (!this.supported()) {
3569
            return;
3570
        }
3571
        OpenLayers.Protocol.SQL.prototype.initialize.apply(this, [options]);
3572
        this.jsonParser = new OpenLayers.Format.JSON();
3573
        this.wktParser = new OpenLayers.Format.WKT();
3574

    
3575
        this.fidRegExp = new RegExp('^' + this.FID_PREFIX);
3576
        this.initializeDatabase();
3577

    
3578

    
3579
    },
3580

    
3581
    /**
3582
     * Method: initializeDatabase
3583
     */
3584
    initializeDatabase: function() {
3585
        this.db = google.gears.factory.create('beta.database');
3586
        this.db.open(this.databaseName);
3587
        this.db.execute(
3588
            "CREATE TABLE IF NOT EXISTS " + this.tableName +
3589
            " (fid TEXT UNIQUE, geometry TEXT, properties TEXT," +
3590
            "  state TEXT)");
3591
   },
3592

    
3593
    /**
3594
     * APIMethod: destroy
3595
     * Clean up the protocol.
3596
     */
3597
    destroy: function() {
3598
        this.db.close();
3599
        this.db = null;
3600

    
3601
        this.jsonParser = null;
3602
        this.wktParser = null;
3603

    
3604
        OpenLayers.Protocol.SQL.prototype.destroy.apply(this);
3605
    },
3606

    
3607
    /**
3608
     * APIMethod: supported
3609
     * Determine whether a browser supports Gears
3610
     *
3611
     * Returns:
3612
     * {Boolean} The browser supports Gears
3613
     */
3614
    supported: function() {
3615
        return !!(window.google && google.gears);
3616
    },
3617

    
3618
    /**
3619
     * APIMethod: read
3620
     * Read all features from the database and return a
3621
     * <OpenLayers.Protocol.Response> instance. If the options parameter
3622
     * contains a callback attribute, the function is called with the response
3623
     * as a parameter.
3624
     *
3625
     * Parameters:
3626
     * options - {Object} Optional object for configuring the request; it
3627
     *      can have the {Boolean} property "noFeatureStateReset" which
3628
     *      specifies if the state of features read from the Gears
3629
     *      database must be reset to null, if "noFeatureStateReset"
3630
     *      is undefined or false then each feature's state is reset
3631
     *      to null, if "noFeatureStateReset" is true the feature state
3632
     *      is preserved.
3633
     *
3634
     * Returns:
3635
     * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
3636
     *      object.
3637
     */
3638
    read: function(options) {
3639
        OpenLayers.Protocol.prototype.read.apply(this, arguments);
3640
        options = OpenLayers.Util.applyDefaults(options, this.options);
3641

    
3642
        var feature, features = [];
3643
        var rs = this.db.execute("SELECT * FROM " + this.tableName);
3644
        while (rs.isValidRow()) {
3645
            feature = this.unfreezeFeature(rs);
3646
            if (this.evaluateFilter(feature, options.filter)) {
3647
                if (!options.noFeatureStateReset) {
3648
                    feature.state = null;
3649
                }
3650
                features.push(feature);
3651
            }
3652
            rs.next();
3653
        }
3654
        rs.close();
3655

    
3656
        var resp = new OpenLayers.Protocol.Response({
3657
            code: OpenLayers.Protocol.Response.SUCCESS,
3658
            requestType: "read",
3659
            features: features
3660
        });
3661

    
3662
        if (options && options.callback) {
3663
            options.callback.call(options.scope, resp);
3664
        }
3665

    
3666
        return resp;
3667
    },
3668

    
3669
    /**
3670
     * Method: unfreezeFeature
3671
     *
3672
     * Parameters:
3673
     * row - {ResultSet}
3674
     *
3675
     * Returns:
3676
     * {<OpenLayers.Feature.Vector>}
3677
     */
3678
    unfreezeFeature: function(row) {
3679
        var feature;
3680
        var wkt = row.fieldByName('geometry');
3681
        if (wkt == this.NULL_GEOMETRY) {
3682
            feature = new OpenLayers.Feature.Vector();
3683
        } else {
3684
            feature = this.wktParser.read(wkt);
3685
        }
3686

    
3687
        feature.attributes = this.jsonParser.read(
3688
            row.fieldByName('properties'));
3689

    
3690
        feature.fid = this.extractFidFromField(row.fieldByName('fid'));
3691

    
3692
        var state = row.fieldByName('state');
3693
        if (state == this.NULL_FEATURE_STATE) {
3694
            state = null;
3695
        }
3696
        feature.state = state;
3697

    
3698
        return feature;
3699
    },
3700

    
3701
    /**
3702
     * Method: extractFidFromField
3703
     *
3704
     * Parameters:
3705
     * field - {String}
3706
     *
3707
     * Returns
3708
     * {String} or {Number} The fid.
3709
     */
3710
    extractFidFromField: function(field) {
3711
        if (!field.match(this.fidRegExp) && this.typeOfFid == "number") {
3712
            field = parseFloat(field);
3713
        }
3714
        return field;
3715
    },
3716

    
3717
    /**
3718
     * APIMethod: create
3719
     * Create new features into the database.
3720
     *
3721
     * Parameters:
3722
     * features - {Array({<OpenLayers.Feature.Vector>})} or
3723
     *            {<OpenLayers.Feature.Vector>} The features to create in
3724
     *            the database.
3725
     * options - {Object} Optional object for configuring the request.
3726
     *
3727
     * Returns:
3728
     *  {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
3729
     *          object.
3730
     */
3731
    create: function(features, options) {
3732
        options = OpenLayers.Util.applyDefaults(options, this.options);
3733

    
3734
        var resp = this.createOrUpdate(features);
3735
        resp.requestType = "create";
3736

    
3737
        if (options && options.callback) {
3738
            options.callback.call(options.scope, resp);
3739
        }
3740

    
3741
        return resp;
3742
    },
3743

    
3744
    /**
3745
     * APIMethod: update
3746
     * Construct a request updating modified feature.
3747
     *
3748
     * Parameters:
3749
     * features - {Array({<OpenLayers.Feature.Vector>})} or
3750
     *            {<OpenLayers.Feature.Vector>} The features to update in
3751
     *            the database.
3752
     * options - {Object} Optional object for configuring the request.
3753
     *
3754
     * Returns:
3755
     *  {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
3756
     *          object.
3757
     */
3758
    update: function(features, options) {
3759
        options = OpenLayers.Util.applyDefaults(options, this.options);
3760

    
3761
        var resp = this.createOrUpdate(features);
3762
        resp.requestType = "update";
3763

    
3764
        if (options && options.callback) {
3765
            options.callback.call(options.scope, resp);
3766
        }
3767

    
3768
        return resp;
3769
    },
3770

    
3771
    /**
3772
     * Method: createOrUpdate
3773
     * Construct a request for updating or creating features in the
3774
     * database.
3775
     *
3776
     * Parameters:
3777
     * features - {Array({<OpenLayers.Feature.Vector>})} or
3778
     *      {<OpenLayers.Feature.Vector>} The feature to create or update
3779
     *      in the database.
3780
     *
3781
     * Returns:
3782
     *  {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
3783
     *          object.
3784
     */
3785
    createOrUpdate: function(features) {
3786
        if (!(OpenLayers.Util.isArray(features))) {
3787
            features = [features];
3788
        }
3789

    
3790
        var i, len = features.length, feature;
3791
        var insertedFeatures = new Array(len);
3792

    
3793
        for (i = 0; i < len; i++) {
3794
            feature = features[i];
3795
            var params = this.freezeFeature(feature);
3796
            this.db.execute(
3797
                "REPLACE INTO " + this.tableName +
3798
                " (fid, geometry, properties, state)" +
3799
                " VALUES (?, ?, ?, ?)",
3800
                params);
3801

    
3802
            var clone = feature.clone();
3803
            clone.fid = this.extractFidFromField(params[0]);
3804
            insertedFeatures[i] = clone;
3805
        }
3806

    
3807
        return new OpenLayers.Protocol.Response({
3808
            code: OpenLayers.Protocol.Response.SUCCESS,
3809
            features: insertedFeatures,
3810
            reqFeatures: features
3811
        });
3812
    },
3813

    
3814
    /**
3815
     * Method: freezeFeature
3816
     *
3817
     * Parameters:
3818
     * feature - {<OpenLayers.Feature.Vector>}
3819
     * state - {String} The feature state to store in the database.
3820
     *
3821
     * Returns:
3822
     * {Array}
3823
     */
3824
    freezeFeature: function(feature) {
3825
        // 2 notes:
3826
        // - fid might not be a string
3827
        // - getFeatureStateForFreeze needs the feature fid to it's stored
3828
        //   in the feature here
3829
        feature.fid = feature.fid != null ?
3830
            "" + feature.fid : OpenLayers.Util.createUniqueID(this.FID_PREFIX);
3831

    
3832
        var geometry = feature.geometry != null ?
3833
            feature.geometry.toString() : this.NULL_GEOMETRY;
3834

    
3835
        var properties = this.jsonParser.write(feature.attributes);
3836

    
3837
        var state = this.getFeatureStateForFreeze(feature);
3838

    
3839
        return [feature.fid, geometry, properties, state];
3840
    },
3841

    
3842
    /**
3843
     * Method: getFeatureStateForFreeze
3844
     * Get the state of the feature to store into the database.
3845
     *
3846
     * Parameters:
3847
     * feature - {<OpenLayers.Feature.Vector>} The feature.
3848
     *
3849
     * Returns
3850
     * {String} The state
3851
     */
3852
    getFeatureStateForFreeze: function(feature) {
3853
        var state;
3854
        if (!this.saveFeatureState) {
3855
            state = this.NULL_FEATURE_STATE;
3856
        } else if (this.createdOffline(feature)) {
3857
            // if the feature was created in offline mode, its
3858
            // state must remain INSERT
3859
            state = OpenLayers.State.INSERT;
3860
        } else {
3861
            state = feature.state;
3862
        }
3863
        return state;
3864
    },
3865

    
3866
    /**
3867
     * APIMethod: delete
3868
     * Delete features from the database.
3869
     *
3870
     * Parameters:
3871
     * features - {Array({<OpenLayers.Feature.Vector>})} or
3872
     *            {<OpenLayers.Feature.Vector>}
3873
     * options - {Object} Optional object for configuring the request.
3874
     *       This object is modified and should not be reused.
3875
     *
3876
     * Returns:
3877
     *  {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response>
3878
     *          object.
3879
     */
3880
    "delete": function(features, options) {
3881
        if (!(OpenLayers.Util.isArray(features))) {
3882
            features = [features];
3883
        }
3884

    
3885
        options = OpenLayers.Util.applyDefaults(options, this.options);
3886

    
3887
        var i, len, feature;
3888
        for (i = 0, len = features.length; i < len; i++) {
3889
            feature = features[i];
3890

    
3891
            // if saveFeatureState is set to true and if the feature wasn't created
3892
            // in offline mode we don't delete it in the database but just update
3893
            // it state column
3894
            if (this.saveFeatureState && !this.createdOffline(feature)) {
3895
                var toDelete = feature.clone();
3896
                toDelete.fid = feature.fid;
3897
                if (toDelete.geometry) {
3898
                    toDelete.geometry.destroy();
3899
                    toDelete.geometry = null;
3900
                }
3901
                toDelete.state = feature.state;
3902
                this.createOrUpdate(toDelete);
3903
            } else {
3904
                this.db.execute(
3905
                    "DELETE FROM " + this.tableName +
3906
                    " WHERE fid = ?", [feature.fid]);
3907
            }
3908
        }
3909

    
3910
        var resp = new OpenLayers.Protocol.Response({
3911
            code: OpenLayers.Protocol.Response.SUCCESS,
3912
            requestType: "delete",
3913
            reqFeatures: features
3914
        });
3915

    
3916
        if (options && options.callback) {
3917
            options.callback.call(options.scope, resp);
3918
        }
3919

    
3920
        return resp;
3921
    },
3922

    
3923
    /**
3924
     * Method: createdOffline
3925
     * Returns true if the feature had a feature id when it was created in
3926
     *      the Gears database, false otherwise; this is determined by
3927
     *      checking the form of the feature's fid value.
3928
     *
3929
     * Parameters:
3930
     * feature - {<OpenLayers.Feature.Vector>}
3931
     *
3932
     * Returns:
3933
     * {Boolean}
3934
     */
3935
    createdOffline: function(feature) {
3936
        return (typeof feature.fid == "string" &&
3937
                !!(feature.fid.match(this.fidRegExp)));
3938
    },
3939

    
3940
    /**
3941
     * APIMethod: commit
3942
     * Go over the features and for each take action
3943
     * based on the feature state. Possible actions are create,
3944
     * update and delete.
3945
     *
3946
     * Parameters:
3947
     * features - {Array({<OpenLayers.Feature.Vector>})}
3948
     * options - {Object} Object whose possible keys are "create", "update",
3949
     *      "delete", "callback" and "scope", the values referenced by the
3950
     *      first three are objects as passed to the "create", "update", and
3951
     *      "delete" methods, the value referenced by the "callback" key is
3952
     *      a function which is called when the commit operation is complete
3953
     *      using the scope referenced by the "scope" key.
3954
     *
3955
     * Returns:
3956
     * {Array({<OpenLayers.Protocol.Response>})} An array of
3957
     *       <OpenLayers.Protocol.Response> objects, one per request made
3958
     *       to the database.
3959
     */
3960
    commit: function(features, options) {
3961
        var opt, resp = [], nRequests = 0, nResponses = 0;
3962

    
3963
        function callback(resp) {
3964
            if (++nResponses < nRequests) {
3965
                resp.last = false;
3966
            }
3967
            this.callUserCallback(options, resp);
3968
        }
3969

    
3970
        var feature, toCreate = [], toUpdate = [], toDelete = [];
3971
        for (var i = features.length - 1; i >= 0; i--) {
3972
            feature = features[i];
3973
            switch (feature.state) {
3974
            case OpenLayers.State.INSERT:
3975
                toCreate.push(feature);
3976
                break;
3977
            case OpenLayers.State.UPDATE:
3978
                toUpdate.push(feature);
3979
                break;
3980
            case OpenLayers.State.DELETE:
3981
                toDelete.push(feature);
3982
                break;
3983
            }
3984
        }
3985
        if (toCreate.length > 0) {
3986
            nRequests++;
3987
            opt = OpenLayers.Util.applyDefaults(
3988
                {"callback": callback, "scope": this},
3989
                options.create
3990
            );
3991
            resp.push(this.create(toCreate, opt));
3992
        }
3993
        if (toUpdate.length > 0) {
3994
            nRequests++;
3995
            opt = OpenLayers.Util.applyDefaults(
3996
                {"callback": callback, "scope": this},
3997
                options.update
3998
            );
3999
            resp.push(this.update(toUpdate, opt));
4000
        }
4001
        if (toDelete.length > 0) {
4002
            nRequests++;
4003
            opt = OpenLayers.Util.applyDefaults(
4004
                {"callback": callback, "scope": this},
4005
                options["delete"]
4006
            );
4007
            resp.push(this["delete"](toDelete, opt));
4008
        }
4009

    
4010
        return resp;
4011
    },
4012

    
4013
    /**
4014
     * Method: clear
4015
     * Removes all rows of the table.
4016
     */
4017
    clear: function() {
4018
        this.db.execute("DELETE FROM " + this.tableName);
4019
    },
4020

    
4021
    /**
4022
     * Method: callUserCallback
4023
     * This method is called from within commit each time a request is made
4024
     * to the database, it is responsible for calling the user-supplied
4025
     * callbacks.
4026
     *
4027
     * Parameters:
4028
     * options - {Object} The map of options passed to the commit call.
4029
     * resp - {<OpenLayers.Protocol.Response>}
4030
     */
4031
    callUserCallback: function(options, resp) {
4032
        var opt = options[resp.requestType];
4033
        if (opt && opt.callback) {
4034
            opt.callback.call(opt.scope, resp);
4035
        }
4036
        if (resp.last && options.callback) {
4037
            options.callback.call(options.scope);
4038
        }
4039
    },
4040

    
4041
    CLASS_NAME: "OpenLayers.Protocol.SQL.Gears"
4042
});
4043

    
4044
/**
4045
 * Class: OpenLayers.Layer.Yahoo
4046
 *
4047
 * Inherits from:
4048
 *  - <OpenLayers.Layer.EventPane>
4049
 *  - <OpenLayers.Layer.FixedZoomLevels>
4050
 */
4051
OpenLayers.Layer.Yahoo = OpenLayers.Class(
4052
  OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {
4053

    
4054
    /**
4055
     * Constant: MIN_ZOOM_LEVEL
4056
     * {Integer} 0
4057
     */
4058
    MIN_ZOOM_LEVEL: 0,
4059

    
4060
    /**
4061
     * Constant: MAX_ZOOM_LEVEL
4062
     * {Integer} 17
4063
     */
4064
    MAX_ZOOM_LEVEL: 17,
4065

    
4066
    /**
4067
     * Constant: RESOLUTIONS
4068
     * {Array(Float)} Hardcode these resolutions so that they are more closely
4069
     *                tied with the standard wms projection
4070
     */
4071
    RESOLUTIONS: [
4072
        1.40625,
4073
        0.703125,
4074
        0.3515625,
4075
        0.17578125,
4076
        0.087890625,
4077
        0.0439453125,
4078
        0.02197265625,
4079
        0.010986328125,
4080
        0.0054931640625,
4081
        0.00274658203125,
4082
        0.001373291015625,
4083
        0.0006866455078125,
4084
        0.00034332275390625,
4085
        0.000171661376953125,
4086
        0.0000858306884765625,
4087
        0.00004291534423828125,
4088
        0.00002145767211914062,
4089
        0.00001072883605957031
4090
    ],
4091

    
4092
    /**
4093
     * APIProperty: type
4094
     * {YahooMapType}
4095
     */
4096
    type: null,
4097

    
4098
    /**
4099
     * APIProperty: wrapDateLine
4100
     * {Boolean} Allow user to pan forever east/west.  Default is true.
4101
     *     Setting this to false only restricts panning if
4102
     *     <sphericalMercator> is true.
4103
     */
4104
    wrapDateLine: true,
4105

    
4106
    /**
4107
     * APIProperty: sphericalMercator
4108
     * {Boolean} Should the map act as a mercator-projected map? This will
4109
     * cause all interactions with the map to be in the actual map projection,
4110
     * which allows support for vector drawing, overlaying other maps, etc.
4111
     */
4112
    sphericalMercator: false,
4113

    
4114
    /**
4115
     * Constructor: OpenLayers.Layer.Yahoo
4116
     *
4117
     * Parameters:
4118
     * name - {String}
4119
     * options - {Object}
4120
     */
4121
    initialize: function(name, options) {
4122
        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
4123
        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this,
4124
                                                                    arguments);
4125
        if(this.sphericalMercator) {
4126
            OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
4127
            this.initMercatorParameters();
4128
        }
4129
    },
4130

    
4131
    /**
4132
     * Method: loadMapObject
4133
     */
4134
    loadMapObject:function() {
4135
        try { //do not crash!
4136
            var size = this.getMapObjectSizeFromOLSize(this.map.getSize());
4137
            this.mapObject = new YMap(this.div, this.type, size);
4138
            this.mapObject.disableKeyControls();
4139
            this.mapObject.disableDragMap();
4140

    
4141
            //can we do smooth panning? (moveByXY is not an API function)
4142
            if ( !this.mapObject.moveByXY ||
4143
                 (typeof this.mapObject.moveByXY != "function" ) ) {
4144

    
4145
                this.dragPanMapObject = null;
4146
            }
4147
        } catch(e) {}
4148
    },
4149

    
4150
    /**
4151
     * Method: onMapResize
4152
     *
4153
     */
4154
    onMapResize: function() {
4155
        try {
4156
            var size = this.getMapObjectSizeFromOLSize(this.map.getSize());
4157
            this.mapObject.resizeTo(size);
4158
        } catch(e) {}
4159
    },
4160

    
4161

    
4162
    /**
4163
     * APIMethod: setMap
4164
     * Overridden from EventPane because we need to remove this yahoo event
4165
     *     pane which prohibits our drag and drop, and we can only do this
4166
     *     once the map has been loaded and centered.
4167
     *
4168
     * Parameters:
4169
     * map - {<OpenLayers.Map>}
4170
     */
4171
    setMap: function(map) {
4172
        OpenLayers.Layer.EventPane.prototype.setMap.apply(this, arguments);
4173

    
4174
        this.map.events.register("moveend", this, this.fixYahooEventPane);
4175
    },
4176

    
4177
    /**
4178
     * Method: fixYahooEventPane
4179
     * The map has been centered, so the mysterious yahoo eventpane has been
4180
     *     added. we remove it so that it doesnt mess with *our* event pane.
4181
     */
4182
    fixYahooEventPane: function() {
4183
        var yahooEventPane = OpenLayers.Util.getElement("ygddfdiv");
4184
        if (yahooEventPane != null) {
4185
            if (yahooEventPane.parentNode != null) {
4186
                yahooEventPane.parentNode.removeChild(yahooEventPane);
4187
            }
4188
            this.map.events.unregister("moveend", this,
4189
                                       this.fixYahooEventPane);
4190
        }
4191
    },
4192

    
4193
    /**
4194
     * APIMethod: getWarningHTML
4195
     *
4196
     * Returns:
4197
     * {String} String with information on why layer is broken, how to get
4198
     *          it working.
4199
     */
4200
    getWarningHTML:function() {
4201
        return OpenLayers.i18n(
4202
            "getLayerWarning", {'layerType':'Yahoo', 'layerLib':'Yahoo'}
4203
        );
4204
    },
4205

    
4206
  /********************************************************/
4207
  /*                                                      */
4208
  /*             Translation Functions                    */
4209
  /*                                                      */
4210
  /*    The following functions translate GMaps and OL    */
4211
  /*     formats for Pixel, LonLat, Bounds, and Zoom      */
4212
  /*                                                      */
4213
  /********************************************************/
4214

    
4215

    
4216
  //
4217
  // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom
4218
  //
4219

    
4220
    /**
4221
     * APIMethod: getOLZoomFromMapObjectZoom
4222
     *
4223
     * Parameters:
4224
     * gZoom - {Integer}
4225
     *
4226
     * Returns:
4227
     * {Integer} An OpenLayers Zoom level, translated from the passed in gZoom
4228
     *           Returns null if null value is passed in.
4229
     */
4230
    getOLZoomFromMapObjectZoom: function(moZoom) {
4231
        var zoom = null;
4232
        if (moZoom != null) {
4233
            zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getOLZoomFromMapObjectZoom.apply(this, [moZoom]);
4234
            zoom = 18 - zoom;
4235
        }
4236
        return zoom;
4237
    },
4238

    
4239
    /**
4240
     * APIMethod: getMapObjectZoomFromOLZoom
4241
     *
4242
     * Parameters:
4243
     * olZoom - {Integer}
4244
     *
4245
     * Returns:
4246
     * {Integer} A MapObject level, translated from the passed in olZoom
4247
     *           Returns null if null value is passed in
4248
     */
4249
    getMapObjectZoomFromOLZoom: function(olZoom) {
4250
        var zoom = null;
4251
        if (olZoom != null) {
4252
            zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getMapObjectZoomFromOLZoom.apply(this, [olZoom]);
4253
            zoom = 18 - zoom;
4254
        }
4255
        return zoom;
4256
    },
4257

    
4258
    /************************************
4259
     *                                  *
4260
     *   MapObject Interface Controls   *
4261
     *                                  *
4262
     ************************************/
4263

    
4264

    
4265
  // Get&Set Center, Zoom
4266

    
4267
    /**
4268
     * APIMethod: setMapObjectCenter
4269
     * Set the mapObject to the specified center and zoom
4270
     *
4271
     * Parameters:
4272
     * center - {Object} MapObject LonLat format
4273
     * zoom - {int} MapObject zoom format
4274
     */
4275
    setMapObjectCenter: function(center, zoom) {
4276
        this.mapObject.drawZoomAndCenter(center, zoom);
4277
    },
4278

    
4279
    /**
4280
     * APIMethod: getMapObjectCenter
4281
     *
4282
     * Returns:
4283
     * {Object} The mapObject's current center in Map Object format
4284
     */
4285
    getMapObjectCenter: function() {
4286
        return this.mapObject.getCenterLatLon();
4287
    },
4288

    
4289
    /**
4290
     * APIMethod: dragPanMapObject
4291
     *
4292
     * Parameters:
4293
     * dX - {Integer}
4294
     * dY - {Integer}
4295
     */
4296
    dragPanMapObject: function(dX, dY) {
4297
        this.mapObject.moveByXY({
4298
            'x': -dX,
4299
            'y': dY
4300
        });
4301
    },
4302

    
4303
    /**
4304
     * APIMethod: getMapObjectZoom
4305
     *
4306
     * Returns:
4307
     * {Integer} The mapObject's current zoom, in Map Object format
4308
     */
4309
    getMapObjectZoom: function() {
4310
        return this.mapObject.getZoomLevel();
4311
    },
4312

    
4313

    
4314
  // LonLat - Pixel Translation
4315

    
4316
    /**
4317
     * APIMethod: getMapObjectLonLatFromMapObjectPixel
4318
     *
4319
     * Parameters:
4320
     * moPixel - {Object} MapObject Pixel format
4321
     *
4322
     * Returns:
4323
     * {Object} MapObject LonLat translated from MapObject Pixel
4324
     */
4325
    getMapObjectLonLatFromMapObjectPixel: function(moPixel) {
4326
        return this.mapObject.convertXYLatLon(moPixel);
4327
    },
4328

    
4329
    /**
4330
     * APIMethod: getMapObjectPixelFromMapObjectLonLat
4331
     *
4332
     * Parameters:
4333
     * moLonLat - {Object} MapObject LonLat format
4334
     *
4335
     * Returns:
4336
     * {Object} MapObject Pixel transtlated from MapObject LonLat
4337
     */
4338
    getMapObjectPixelFromMapObjectLonLat: function(moLonLat) {
4339
        return this.mapObject.convertLatLonXY(moLonLat);
4340
    },
4341

    
4342

    
4343
    /************************************
4344
     *                                  *
4345
     *       MapObject Primitives       *
4346
     *                                  *
4347
     ************************************/
4348

    
4349

    
4350
  // LonLat
4351

    
4352
    /**
4353
     * APIMethod: getLongitudeFromMapObjectLonLat
4354
     *
4355
     * Parameters:
4356
     * moLonLat - {Object} MapObject LonLat format
4357
     *
4358
     * Returns:
4359
     * {Float} Longitude of the given MapObject LonLat
4360
     */
4361
    getLongitudeFromMapObjectLonLat: function(moLonLat) {
4362
        return this.sphericalMercator ?
4363
            this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lon :
4364
            moLonLat.Lon;
4365
    },
4366

    
4367
    /**
4368
     * APIMethod: getLatitudeFromMapObjectLonLat
4369
     *
4370
     * Parameters:
4371
     * moLonLat - {Object} MapObject LonLat format
4372
     *
4373
     * Returns:
4374
     * {Float} Latitude of the given MapObject LonLat
4375
     */
4376
    getLatitudeFromMapObjectLonLat: function(moLonLat) {
4377
        return this.sphericalMercator ?
4378
            this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lat :
4379
            moLonLat.Lat;
4380
    },
4381

    
4382
    /**
4383
     * APIMethod: getMapObjectLonLatFromLonLat
4384
     *
4385
     * Parameters:
4386
     * lon - {Float}
4387
     * lat - {Float}
4388
     *
4389
     * Returns:
4390
     * {Object} MapObject LonLat built from lon and lat params
4391
     */
4392
    getMapObjectLonLatFromLonLat: function(lon, lat) {
4393
        var yLatLong;
4394
        if(this.sphericalMercator) {
4395
            var lonlat = this.inverseMercator(lon, lat);
4396
            yLatLong = new YGeoPoint(lonlat.lat, lonlat.lon);
4397
        } else {
4398
            yLatLong = new YGeoPoint(lat, lon);
4399
        }
4400
        return yLatLong;
4401
    },
4402

    
4403
  // Pixel
4404

    
4405
    /**
4406
     * APIMethod: getXFromMapObjectPixel
4407
     *
4408
     * Parameters:
4409
     * moPixel - {Object} MapObject Pixel format
4410
     *
4411
     * Returns:
4412
     * {Integer} X value of the MapObject Pixel
4413
     */
4414
    getXFromMapObjectPixel: function(moPixel) {
4415
        return moPixel.x;
4416
    },
4417

    
4418
    /**
4419
     * APIMethod: getYFromMapObjectPixel
4420
     *
4421
     * Parameters:
4422
     * moPixel - {Object} MapObject Pixel format
4423
     *
4424
     * Returns:
4425
     * {Integer} Y value of the MapObject Pixel
4426
     */
4427
    getYFromMapObjectPixel: function(moPixel) {
4428
        return moPixel.y;
4429
    },
4430

    
4431
    /**
4432
     * APIMethod: getMapObjectPixelFromXY
4433
     *
4434
     * Parameters:
4435
     * x - {Integer}
4436
     * y - {Integer}
4437
     *
4438
     * Returns:
4439
     * {Object} MapObject Pixel from x and y parameters
4440
     */
4441
    getMapObjectPixelFromXY: function(x, y) {
4442
        return new YCoordPoint(x, y);
4443
    },
4444

    
4445
  // Size
4446

    
4447
    /**
4448
     * APIMethod: getMapObjectSizeFromOLSize
4449
     *
4450
     * Parameters:
4451
     * olSize - {<OpenLayers.Size>}
4452
     *
4453
     * Returns:
4454
     * {Object} MapObject Size from olSize parameter
4455
     */
4456
    getMapObjectSizeFromOLSize: function(olSize) {
4457
        return new YSize(olSize.w, olSize.h);
4458
    },
4459

    
4460
    CLASS_NAME: "OpenLayers.Layer.Yahoo"
4461
});
4462

    
4463
/**
4464
 * Class: OpenLayers.Layer.GML
4465
 * Create a vector layer by parsing a GML file. The GML file is
4466
 *     passed in as a parameter.
4467
 * *Deprecated*.  To be removed in 3.0.  Instead use OpenLayers.Layer.Vector
4468
 *     with Protocol.HTTP and Strategy.Fixed. Provide the protocol with a
4469
 *     format parameter to get the parser you want for your data.
4470
 *
4471
 * Inherits from:
4472
 *  - <OpenLayers.Layer.Vector>
4473
 */
4474
OpenLayers.Layer.GML = OpenLayers.Class(OpenLayers.Layer.Vector, {
4475

    
4476
    /**
4477
      * Property: loaded
4478
      * {Boolean} Flag for whether the GML data has been loaded yet.
4479
      */
4480
    loaded: false,
4481

    
4482
    /**
4483
      * APIProperty: format
4484
      * {<OpenLayers.Format>} The format you want the data to be parsed with.
4485
      */
4486
    format: null,
4487

    
4488
    /**
4489
     * APIProperty: formatOptions
4490
     * {Object} Hash of options which should be passed to the format when it is
4491
     * created. Must be passed in the constructor.
4492
     */
4493
    formatOptions: null,
4494

    
4495
    /**
4496
     * Constructor: OpenLayers.Layer.GML
4497
     * Load and parse a single file on the web, according to the format
4498
     * provided via the 'format' option, defaulting to GML.
4499
     *
4500
     * Parameters:
4501
     * name - {String}
4502
     * url - {String} URL of a GML file.
4503
     * options - {Object} Hashtable of extra options to tag onto the layer.
4504
     */
4505
     initialize: function(name, url, options) {
4506
        var newArguments = [];
4507
        newArguments.push(name, options);
4508
        OpenLayers.Layer.Vector.prototype.initialize.apply(this, newArguments);
4509
        this.url = url;
4510
    },
4511

    
4512
    /**
4513
     * APIMethod: setVisibility
4514
     * Set the visibility flag for the layer and hide/show&redraw accordingly.
4515
     * Fire event unless otherwise specified
4516
     * GML will be loaded if the layer is being made visible for the first
4517
     * time.
4518
     *
4519
     * Parameters:
4520
     * visible - {Boolean} Whether or not to display the layer
4521
     *                          (if in range)
4522
     * noEvent - {Boolean}
4523
     */
4524
    setVisibility: function(visibility, noEvent) {
4525
        OpenLayers.Layer.Vector.prototype.setVisibility.apply(this, arguments);
4526
        if(this.visibility && !this.loaded){
4527
            // Load the GML
4528
            this.loadGML();
4529
        }
4530
    },
4531

    
4532
    /**
4533
     * Method: moveTo
4534
     * If layer is visible and GML has not been loaded, load GML, then load GML
4535
     * and call OpenLayers.Layer.Vector.moveTo() to redraw at the new location.
4536
     *
4537
     * Parameters:
4538
     * bounds - {Object}
4539
     * zoomChanged - {Object}
4540
     * minor - {Object}
4541
     */
4542
    moveTo:function(bounds, zoomChanged, minor) {
4543
        OpenLayers.Layer.Vector.prototype.moveTo.apply(this, arguments);
4544
        // Wait until initialisation is complete before loading GML
4545
        // otherwise we can get a race condition where the root HTML DOM is
4546
        // loaded after the GML is paited.
4547
        // See http://trac.openlayers.org/ticket/404
4548
        if(this.visibility && !this.loaded){
4549
            this.loadGML();
4550
        }
4551
    },
4552

    
4553
    /**
4554
     * Method: loadGML
4555
     */
4556
    loadGML: function() {
4557
        if (!this.loaded) {
4558
            this.events.triggerEvent("loadstart");
4559
            OpenLayers.Request.GET({
4560
                url: this.url,
4561
                success: this.requestSuccess,
4562
                failure: this.requestFailure,
4563
                scope: this
4564
            });
4565
            this.loaded = true;
4566
        }
4567
    },
4568

    
4569
    /**
4570
     * Method: setUrl
4571
     * Change the URL and reload the GML
4572
     *
4573
     * Parameters:
4574
     * url - {String} URL of a GML file.
4575
     */
4576
    setUrl:function(url) {
4577
        this.url = url;
4578
        this.destroyFeatures();
4579
        this.loaded = false;
4580
        this.loadGML();
4581
    },
4582

    
4583
    /**
4584
     * Method: requestSuccess
4585
     * Process GML after it has been loaded.
4586
     * Called by initialize() and loadUrl() after the GML has been loaded.
4587
     *
4588
     * Parameters:
4589
     * request - {String}
4590
     */
4591
    requestSuccess:function(request) {
4592
        var doc = request.responseXML;
4593

    
4594
        if (!doc || !doc.documentElement) {
4595
            doc = request.responseText;
4596
        }
4597

    
4598
        var options = {};
4599

    
4600
        OpenLayers.Util.extend(options, this.formatOptions);
4601
        if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
4602
            options.externalProjection = this.projection;
4603
            options.internalProjection = this.map.getProjectionObject();
4604
        }
4605

    
4606
        var gml = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
4607
        this.addFeatures(gml.read(doc));
4608
        this.events.triggerEvent("loadend");
4609
    },
4610

    
4611
    /**
4612
     * Method: requestFailure
4613
     * Process a failed loading of GML.
4614
     * Called by initialize() and loadUrl() if there was a problem loading GML.
4615
     *
4616
     * Parameters:
4617
     * request - {String}
4618
     */
4619
    requestFailure: function(request) {
4620
        OpenLayers.Console.userError('Error in loading GML file ' +  this.url);
4621
        this.events.triggerEvent("loadend");
4622
    },
4623

    
4624
    CLASS_NAME: "OpenLayers.Layer.GML"
4625
});
4626

    
4627
/**
4628
 * Class: OpenLayers.Geometry.Rectangle
4629
 * This class is *not supported*, and probably isn't what you're looking for.
4630
 *     Instead, most users probably want something like:
4631
 *     (code)
4632
 *     var poly = new OpenLayers.Bounds(0,0,10,10).toGeometry();
4633
 *     (end)
4634
 *     This will create a rectangular Polygon geometry. 
4635
 * 
4636
 * Inherits:
4637
 *  - <OpenLayers.Geometry>
4638
 */
4639

    
4640
OpenLayers.Geometry.Rectangle = OpenLayers.Class(OpenLayers.Geometry, {
4641

    
4642
    /** 
4643
     * Property: x
4644
     * {Float}
4645
     */
4646
    x: null,
4647

    
4648
    /** 
4649
     * Property: y
4650
     * {Float}
4651
     */
4652
    y: null,
4653

    
4654
    /** 
4655
     * Property: width
4656
     * {Float}
4657
     */
4658
    width: null,
4659

    
4660
    /** 
4661
     * Property: height
4662
     * {Float}
4663
     */
4664
    height: null,
4665

    
4666
    /**
4667
     * Constructor: OpenLayers.Geometry.Rectangle
4668
     * 
4669
     * Parameters:
4670
     * points - {Array(<OpenLayers.Geometry.Point>)}
4671
     */
4672
    initialize: function(x, y, width, height) {
4673
        OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
4674
        
4675
        this.x = x;
4676
        this.y = y;
4677

    
4678
        this.width = width;
4679
        this.height = height;
4680
    },
4681
    
4682
    /**
4683
     * Method: calculateBounds
4684
     * Recalculate the bounds for the geometry.
4685
     */
4686
    calculateBounds: function() {
4687
        this.bounds = new OpenLayers.Bounds(this.x, this.y,
4688
                                            this.x + this.width, 
4689
                                            this.y + this.height);
4690
    },
4691
    
4692
    
4693
    /**
4694
     * APIMethod: getLength
4695
     * 
4696
     * Returns:
4697
     * {Float} The length of the geometry
4698
     */
4699
    getLength: function() {
4700
        var length = (2 * this.width) + (2 * this.height);
4701
        return length;
4702
    },
4703

    
4704
    /**
4705
     * APIMethod: getArea
4706
     * 
4707
     * Returns:
4708
     * {Float} The area of the geometry
4709
     */
4710
    getArea: function() {
4711
        var area = this.width * this.height;
4712
        return area;
4713
    },    
4714

    
4715
    CLASS_NAME: "OpenLayers.Geometry.Rectangle"
4716
});
4717

    
4718
/**
4719
 * Class: OpenLayers.Renderer.NG
4720
 *
4721
 * Inherits from:
4722
 *  - <OpenLayers.Renderer.Elements>
4723
 */
4724
OpenLayers.Renderer.NG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
4725

    
4726
    /**
4727
     * Constant: labelNodeType
4728
     * {String} The node type for text label containers. To be defined by
4729
     * subclasses.
4730
     */
4731
    labelNodeType: null,
4732

    
4733
    /**
4734
     * Constructor: OpenLayers.Renderer.NG
4735
     *
4736
     * Parameters:
4737
     * containerID - {String}
4738
     * options - {Object} options for this renderer. Supported options are:
4739
     *     * yOrdering - {Boolean} Whether to use y-ordering
4740
     *     * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored
4741
     *         if yOrdering is set to true.
4742
     */
4743

    
4744
    /**
4745
     * Method: updateDimensions
4746
     * To be extended by subclasses - here we set positioning related styles
4747
     * on HTML elements, subclasses have to do the same for renderer specific
4748
     * elements (e.g. viewBox, width and height of the rendererRoot)
4749
     *
4750
     * Parameters:
4751
     * zoomChanged - {Boolean} Has the zoom changed? If so, subclasses may have
4752
     *     to update feature styles/dimensions.
4753
     */
4754
    updateDimensions: function(zoomChanged) {
4755
        var mapExtent = this.map.getExtent();
4756
        var renderExtent = mapExtent.scale(3);
4757
        this.setExtent(renderExtent, true);
4758
        var res = this.getResolution();
4759
        var div = this.rendererRoot.parentNode;
4760
        var layerLeft = parseFloat(div.parentNode.style.left);
4761
        var layerTop = parseFloat(div.parentNode.style.top);
4762
        div.style.left = ((renderExtent.left - mapExtent.left) / res - layerLeft) + "px";
4763
        div.style.top = ((mapExtent.top - renderExtent.top) / res - layerTop) + "px";
4764
    },
4765

    
4766
    /**
4767
     * Method: resize
4768
     */
4769
    setSize: function() {
4770
        this.map.getExtent() && this.updateDimensions();
4771
    },
4772

    
4773
    /**
4774
     * Method: drawFeature
4775
     * Draw the feature.  The optional style argument can be used
4776
     * to override the feature's own style.  This method should only
4777
     * be called from layer.drawFeature().
4778
     *
4779
     * Parameters:
4780
     * feature - {<OpenLayers.Feature.Vector>}
4781
     * style - {<Object>}
4782
     *
4783
     * Returns:
4784
     * {Boolean} true if the feature has been drawn completely, false if not,
4785
     *     undefined if the feature had no geometry
4786
     */
4787
    drawFeature: function(feature, style) {
4788
        if(style == null) {
4789
            style = feature.style;
4790
        }
4791
        if (feature.geometry) {
4792
            var rendered = this.drawGeometry(feature.geometry, style, feature.id);
4793
            if(rendered !== false && style.label) {
4794
                var location = feature.geometry.getCentroid();
4795
                this.drawText(feature.id, style, location);
4796
            } else {
4797
                this.removeText(feature.id);
4798
            }
4799
            return rendered;
4800
        }
4801
    },
4802

    
4803
    /**
4804
     * Method: drawText
4805
     * Function for drawing text labels.
4806
     * This method is only called by the renderer itself.
4807
     *
4808
     * Parameters:
4809
     * featureId - {String|DOMElement}
4810
     * style - {Object}
4811
     * location - {<OpenLayers.Geometry.Point>}, will be modified inline
4812
     *
4813
     * Returns:
4814
     * {DOMElement} container holding the text label (to be populated by
4815
     * subclasses)
4816
     */
4817
    drawText: function(featureId, style, location) {
4818
        var label;
4819
        if (typeof featureId !== "string") {
4820
            label = featureId;
4821
        } else {
4822
            label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, this.labelNodeType);
4823
            label._featureId = featureId;
4824
        }
4825
        label._style = style;
4826
        label._x = location.x;
4827
        label._y = location.y;
4828
        if(style.labelXOffset || style.labelYOffset) {
4829
            var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;
4830
            var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;
4831
            var res = this.getResolution();
4832
            location.move(xOffset*res, yOffset*res);
4833
        }
4834

    
4835
        if(label.parentNode !== this.textRoot) {
4836
            this.textRoot.appendChild(label);
4837
        }
4838

    
4839
        return label;
4840
    },
4841

    
4842
    CLASS_NAME: "OpenLayers.Renderer.NG"
4843
});
4844

    
4845
// Monkey-patching Layer.Vector for Renderer.NG support
4846
(function() {
4847
    var moveTo = OpenLayers.Layer.Vector.prototype.moveTo;
4848
    OpenLayers.Layer.Vector.prototype.moveTo = function(bounds, zoomChanged, dragging) {
4849
        if (OpenLayers.Renderer.NG && this.renderer instanceof OpenLayers.Renderer.NG) {
4850
            OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
4851
            dragging || this.renderer.updateDimensions(zoomChanged);
4852
            if (!this.drawn) {
4853
                this.drawn = true;
4854
                var feature;
4855
                for(var i=0, len=this.features.length; i<len; i++) {
4856
                    this.renderer.locked = (i !== (len - 1));
4857
                    feature = this.features[i];
4858
                    this.drawFeature(feature);
4859
                }
4860
            }
4861
        } else {
4862
            moveTo.apply(this, arguments);
4863
        }
4864
    }
4865
    var redraw = OpenLayers.Layer.Vector.prototype.redraw;
4866
    OpenLayers.Layer.Vector.prototype.redraw = function() {
4867
        if (OpenLayers.Renderer.NG && this.renderer instanceof OpenLayers.Renderer.NG) {
4868
            this.drawn = false;
4869
        }
4870
        redraw.apply(this, arguments);
4871
    }
4872
})();
4873

    
4874
/**
4875
 * Class: OpenLayers.Renderer.SVG2
4876
 *
4877
 * Inherits from:
4878
 *  - <OpenLayers.Renderer.NG>
4879
 */
4880
OpenLayers.Renderer.SVG2 = OpenLayers.Class(OpenLayers.Renderer.NG, {
4881

    
4882
    /**
4883
     * Property: xmlns
4884
     * {String}
4885
     */
4886
    xmlns: "http://www.w3.org/2000/svg",
4887

    
4888
    /**
4889
     * Property: xlinkns
4890
     * {String}
4891
     */
4892
    xlinkns: "http://www.w3.org/1999/xlink",
4893

    
4894
    /**
4895
     * Property: symbolMetrics
4896
     * {Object} Cache for symbol metrics according to their svg coordinate
4897
     *     space. This is an object keyed by the symbol's id, and values are
4898
     *     an object with size, x and y properties.
4899
     */
4900
    symbolMetrics: null,
4901

    
4902
    /**
4903
     * Constant: labelNodeType
4904
     * {String} The node type for text label containers.
4905
     */
4906
    labelNodeType: "g",
4907

    
4908
    /**
4909
     * Constructor: OpenLayers.Renderer.SVG2
4910
     *
4911
     * Parameters:
4912
     * containerID - {String}
4913
     */
4914
    initialize: function(containerID) {
4915
        if (!this.supported()) {
4916
            return;
4917
        }
4918
        OpenLayers.Renderer.Elements.prototype.initialize.apply(this,
4919
                                                                arguments);
4920

    
4921
        this.symbolMetrics = {};
4922
    },
4923

    
4924
    /**
4925
     * APIMethod: supported
4926
     *
4927
     * Returns:
4928
     * {Boolean} Whether or not the browser supports the SVG renderer
4929
     */
4930
    supported: function() {
4931
        var svgFeature = "http://www.w3.org/TR/SVG11/feature#";
4932
        return (document.implementation &&
4933
           (document.implementation.hasFeature("org.w3c.svg", "1.0") ||
4934
            document.implementation.hasFeature(svgFeature + "SVG", "1.1") ||
4935
            document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1") ));
4936
    },
4937

    
4938
    /**
4939
     * Method: updateDimensions
4940
     *
4941
     * Parameters:
4942
     * zoomChanged - {Boolean}
4943
     */
4944
    updateDimensions: function(zoomChanged) {
4945
        OpenLayers.Renderer.NG.prototype.updateDimensions.apply(this, arguments);
4946

    
4947
        var res = this.getResolution();
4948

    
4949
        var width = this.extent.getWidth();
4950
        var height = this.extent.getHeight();
4951

    
4952
        var extentString = [
4953
            this.extent.left,
4954
            -this.extent.top,
4955
            width,
4956
            height
4957
        ].join(" ");
4958
        this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
4959
        this.rendererRoot.setAttributeNS(null, "width", width / res);
4960
        this.rendererRoot.setAttributeNS(null, "height", height / res);
4961

    
4962
        if (zoomChanged === true) {
4963
            // update styles for the new resolution
4964
            var i, len;
4965
            var nodes = this.vectorRoot.childNodes;
4966
            for (i=0, len=nodes.length; i<len; ++i) {
4967
                this.setStyle(nodes[i]);
4968
            }
4969
            var textNodes = this.textRoot.childNodes;
4970
            var label;
4971
            for (i=0, len=textNodes.length; i<len; ++i) {
4972
                label = textNodes[i];
4973
                this.drawText(label, label._style,
4974
                    new OpenLayers.Geometry.Point(label._x, label._y)
4975
                );
4976
            }
4977
        }
4978
    },
4979

    
4980
    /**
4981
     * Method: getNodeType
4982
     *
4983
     * Parameters:
4984
     * geometry - {<OpenLayers.Geometry>}
4985
     * style - {Object}
4986
     *
4987
     * Returns:
4988
     * {String} The corresponding node type for the specified geometry
4989
     */
4990
    getNodeType: function(geometry, style) {
4991
        var nodeType = null;
4992
        switch (geometry.CLASS_NAME) {
4993
            case "OpenLayers.Geometry.Point":
4994
                if (style.externalGraphic) {
4995
                    nodeType = "image";
4996
                } else if (this.isComplexSymbol(style.graphicName)) {
4997
                    nodeType = "svg";
4998
                } else {
4999
                    nodeType = "circle";
5000
                }
5001
                break;
5002
            case "OpenLayers.Geometry.Rectangle":
5003
                nodeType = "rect";
5004
                break;
5005
            case "OpenLayers.Geometry.LineString":
5006
                nodeType = "polyline";
5007
                break;
5008
            case "OpenLayers.Geometry.LinearRing":
5009
                nodeType = "polygon";
5010
                break;
5011
            case "OpenLayers.Geometry.Polygon":
5012
            case "OpenLayers.Geometry.Curve":
5013
                nodeType = "path";
5014
                break;
5015
            default:
5016
                break;
5017
        }
5018
        return nodeType;
5019
    },
5020

    
5021
    /**
5022
     * Method: setStyle
5023
     * Use to set all the style attributes to a SVG node.
5024
     *
5025
     * Takes care to adjust stroke width and point radius to be
5026
     * resolution-relative
5027
     *
5028
     * Parameters:
5029
     * node - {SVGDomElement} An SVG element to decorate
5030
     * style - {Object}
5031
     * options - {Object} Currently supported options include
5032
     *                              'isFilled' {Boolean} and
5033
     *                              'isStroked' {Boolean}
5034
     */
5035
    setStyle: function(node, style, options) {
5036
        style = style  || node._style;
5037
        options = options || node._options;
5038
        var resolution = this.getResolution();
5039
        var r = node._radius;
5040
        var widthFactor = resolution;
5041
        if (node._geometryClass == "OpenLayers.Geometry.Point" && r) {
5042
            node.style.visibility = "";
5043
            if (style.graphic === false) {
5044
                node.style.visibility = "hidden";
5045
            } else if (style.externalGraphic) {
5046

    
5047
                if (style.graphicTitle) {
5048
                    node.setAttributeNS(null, "title", style.graphicTitle);
5049
                    //Standards-conformant SVG
5050
                    // Prevent duplicate nodes. See issue https://github.com/openlayers/openlayers/issues/92
5051
                    var titleNode = node.getElementsByTagName("title");
5052
                    if (titleNode.length > 0) {
5053
                        titleNode[0].firstChild.textContent = style.graphicTitle;
5054
                    } else {
5055
                        var label = this.nodeFactory(null, "title");
5056
                        label.textContent = style.graphicTitle;
5057
                        node.appendChild(label);
5058
                    }
5059
                }
5060
                if (style.graphicWidth && style.graphicHeight) {
5061
                    node.setAttributeNS(null, "preserveAspectRatio", "none");
5062
                }
5063
                var width = style.graphicWidth || style.graphicHeight;
5064
                var height = style.graphicHeight || style.graphicWidth;
5065
                width = width ? width : style.pointRadius*2;
5066
                height = height ? height : style.pointRadius*2;
5067
                width *= resolution;
5068
                height *= resolution;
5069

    
5070
                var xOffset = (style.graphicXOffset != undefined) ?
5071
                    style.graphicXOffset * resolution : -(0.5 * width);
5072
                var yOffset = (style.graphicYOffset != undefined) ?
5073
                    style.graphicYOffset * resolution : -(0.5 * height);
5074

    
5075
                var opacity = style.graphicOpacity || style.fillOpacity;
5076

    
5077
                node.setAttributeNS(null, "x", node._x + xOffset);
5078
                node.setAttributeNS(null, "y", node._y + yOffset);
5079
                node.setAttributeNS(null, "width", width);
5080
                node.setAttributeNS(null, "height", height);
5081
                node.setAttributeNS(this.xlinkns, "href", style.externalGraphic);
5082
                node.setAttributeNS(null, "style", "opacity: "+opacity);
5083
                node.onclick = OpenLayers.Renderer.SVG2.preventDefault;
5084
            } else if (this.isComplexSymbol(style.graphicName)) {
5085
                // the symbol viewBox is three times as large as the symbol
5086
                var offset = style.pointRadius * 3 * resolution;
5087
                var size = offset * 2;
5088
                var src = this.importSymbol(style.graphicName);
5089
                widthFactor = this.symbolMetrics[src.id].size * 3 / size * resolution;
5090

    
5091
                // remove the node from the dom before we modify it. This
5092
                // prevents various rendering issues in Safari and FF
5093
                var parent = node.parentNode;
5094
                var nextSibling = node.nextSibling;
5095
                if(parent) {
5096
                    parent.removeChild(node);
5097
                }
5098

    
5099
                // The more appropriate way to implement this would be use/defs,
5100
                // but due to various issues in several browsers, it is safer to
5101
                // copy the symbols instead of referencing them.
5102
                // See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985
5103
                // and this email thread
5104
                // http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html
5105
                node.firstChild && node.removeChild(node.firstChild);
5106
                node.appendChild(src.firstChild.cloneNode(true));
5107
                node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox"));
5108

    
5109
                node.setAttributeNS(null, "width", size);
5110
                node.setAttributeNS(null, "height", size);
5111
                node.setAttributeNS(null, "x", node._x - offset);
5112
                node.setAttributeNS(null, "y", node._y - offset);
5113

    
5114
                // now that the node has all its new properties, insert it
5115
                // back into the dom where it was
5116
                if(nextSibling) {
5117
                    parent.insertBefore(node, nextSibling);
5118
                } else if(parent) {
5119
                    parent.appendChild(node);
5120
                }
5121
            } else {
5122
                node.setAttributeNS(null, "r", style.pointRadius * resolution);
5123
            }
5124

    
5125
            var rotation = style.rotation;
5126
            if (rotation !== undefined || node._rotation !== undefined) {
5127
                node._rotation = rotation;
5128
                rotation |= 0;
5129
                if (node.nodeName !== "svg") {
5130
                    node.setAttributeNS(null, "transform",
5131
                        ["rotate(", rotation, node._x, node._y, ")"].join(" ")
5132
                    );
5133
                } else {
5134
                    var metrics = this.symbolMetrics[src.id];
5135
                    node.firstChild.setAttributeNS(null, "transform",
5136
                        ["rotate(", rotation, metrics.x, metrics.y, ")"].join(" ")
5137
                    );
5138
                }
5139
            }
5140
        }
5141

    
5142
        if (options.isFilled) {
5143
            node.setAttributeNS(null, "fill", style.fillColor);
5144
            node.setAttributeNS(null, "fill-opacity", style.fillOpacity);
5145
        } else {
5146
            node.setAttributeNS(null, "fill", "none");
5147
        }
5148

    
5149
        if (options.isStroked) {
5150
            node.setAttributeNS(null, "stroke", style.strokeColor);
5151
            node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity);
5152
            node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor);
5153
            node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap || "round");
5154
            // Hard-coded linejoin for now, to make it look the same as in VML.
5155
            // There is no strokeLinejoin property yet for symbolizers.
5156
            node.setAttributeNS(null, "stroke-linejoin", "round");
5157
            style.strokeDashstyle && node.setAttributeNS(null,
5158
                "stroke-dasharray", this.dashStyle(style, widthFactor));
5159
        } else {
5160
            node.setAttributeNS(null, "stroke", "none");
5161
        }
5162

    
5163
        if (style.pointerEvents) {
5164
            node.setAttributeNS(null, "pointer-events", style.pointerEvents);
5165
        }
5166

    
5167
        if (style.cursor != null) {
5168
            node.setAttributeNS(null, "cursor", style.cursor);
5169
        }
5170

    
5171
        return node;
5172
    },
5173

    
5174
    /**
5175
     * Method: dashStyle
5176
     *
5177
     * Parameters:
5178
     * style - {Object}
5179
     * widthFactor - {Number}
5180
     *
5181
     * Returns:
5182
     * {String} A SVG compliant 'stroke-dasharray' value
5183
     */
5184
    dashStyle: function(style, widthFactor) {
5185
        var w = style.strokeWidth * widthFactor;
5186
        var str = style.strokeDashstyle;
5187
        switch (str) {
5188
            case 'solid':
5189
                return 'none';
5190
            case 'dot':
5191
                return [widthFactor, 4 * w].join();
5192
            case 'dash':
5193
                return [4 * w, 4 * w].join();
5194
            case 'dashdot':
5195
                return [4 * w, 4 * w, widthFactor, 4 * w].join();
5196
            case 'longdash':
5197
                return [8 * w, 4 * w].join();
5198
            case 'longdashdot':
5199
                return [8 * w, 4 * w, widthFactor, 4 * w].join();
5200
            default:
5201
                var parts = OpenLayers.String.trim(str).split(/\s+/g);
5202
                for (var i=0, ii=parts.length; i<ii; i++) {
5203
                    parts[i] = parts[i] * widthFactor;
5204
                }
5205
                return parts.join();
5206
        }
5207
    },
5208

    
5209
    /**
5210
     * Method: createNode
5211
     *
5212
     * Parameters:
5213
     * type - {String} Kind of node to draw
5214
     * id - {String} Id for node
5215
     *
5216
     * Returns:
5217
     * {DOMElement} A new node of the given type and id
5218
     */
5219
    createNode: function(type, id) {
5220
        var node = document.createElementNS(this.xmlns, type);
5221
        if (id) {
5222
            node.setAttributeNS(null, "id", id);
5223
        }
5224
        return node;
5225
    },
5226

    
5227
    /**
5228
     * Method: nodeTypeCompare
5229
     *
5230
     * Parameters:
5231
     * node - {SVGDomElement} An SVG element
5232
     * type - {String} Kind of node
5233
     *
5234
     * Returns:
5235
     * {Boolean} Whether or not the specified node is of the specified type
5236
     */
5237
    nodeTypeCompare: function(node, type) {
5238
        return (type == node.nodeName);
5239
    },
5240

    
5241
    /**
5242
     * Method: createRenderRoot
5243
     *
5244
     * Returns:
5245
     * {DOMElement} The specific render engine's root element
5246
     */
5247
    createRenderRoot: function() {
5248
        return this.nodeFactory(this.container.id + "_svgRoot", "svg");
5249
    },
5250

    
5251
    /**
5252
     * Method: createRoot
5253
     *
5254
     * Parameters:
5255
     * suffix - {String} suffix to append to the id
5256
     *
5257
     * Returns:
5258
     * {DOMElement}
5259
     */
5260
    createRoot: function(suffix) {
5261
        return this.nodeFactory(this.container.id + suffix, "g");
5262
    },
5263

    
5264
    /**
5265
     * Method: createDefs
5266
     *
5267
     * Returns:
5268
     * {DOMElement} The element to which we'll add the symbol definitions
5269
     */
5270
    createDefs: function() {
5271
        var defs = this.nodeFactory(this.container.id + "_defs", "defs");
5272
        this.rendererRoot.appendChild(defs);
5273
        return defs;
5274
    },
5275

    
5276
    /**************************************
5277
     *                                    *
5278
     *     GEOMETRY DRAWING FUNCTIONS     *
5279
     *                                    *
5280
     **************************************/
5281

    
5282
    /**
5283
     * Method: drawPoint
5284
     * This method is only called by the renderer itself.
5285
     *
5286
     * Parameters:
5287
     * node - {DOMElement}
5288
     * geometry - {<OpenLayers.Geometry>}
5289
     *
5290
     * Returns:
5291
     * {DOMElement} or false if the renderer could not draw the point
5292
     */
5293
    drawPoint: function(node, geometry) {
5294
        return this.drawCircle(node, geometry, 1);
5295
    },
5296

    
5297
    /**
5298
     * Method: drawCircle
5299
     * This method is only called by the renderer itself.
5300
     *
5301
     * Parameters:
5302
     * node - {DOMElement}
5303
     * geometry - {<OpenLayers.Geometry>}
5304
     * radius - {Float}
5305
     *
5306
     * Returns:
5307
     * {DOMElement} or false if the renderer could not draw the circle
5308
     */
5309
    drawCircle: function(node, geometry, radius) {
5310
        var x = geometry.x;
5311
        var y = -geometry.y;
5312
        node.setAttributeNS(null, "cx", x);
5313
        node.setAttributeNS(null, "cy", y);
5314
        node._x = x;
5315
        node._y = y;
5316
        node._radius = radius;
5317
        return node;
5318
    },
5319

    
5320
    /**
5321
     * Method: drawLineString
5322
     * This method is only called by the renderer itself.
5323
     *
5324
     * Parameters:
5325
     * node - {DOMElement}
5326
     * geometry - {<OpenLayers.Geometry>}
5327
     *
5328
     * Returns:
5329
     * {DOMElement} or null if the renderer could not draw all components of
5330
     *     the linestring, or false if nothing could be drawn
5331
     */
5332
    drawLineString: function(node, geometry) {
5333
        var path = this.getComponentsString(geometry.components);
5334
        node.setAttributeNS(null, "points", path);
5335
        return node;
5336
    },
5337

    
5338
    /**
5339
     * Method: drawLinearRing
5340
     * This method is only called by the renderer itself.
5341
     *
5342
     * Parameters:
5343
     * node - {DOMElement}
5344
     * geometry - {<OpenLayers.Geometry>}
5345
     *
5346
     * Returns:
5347
     * {DOMElement} or null if the renderer could not draw all components
5348
     *     of the linear ring, or false if nothing could be drawn
5349
     */
5350
    drawLinearRing: function(node, geometry) {
5351
        var path = this.getComponentsString(geometry.components);
5352
        node.setAttributeNS(null, "points", path);
5353
        return node;
5354
    },
5355

    
5356
    /**
5357
     * Method: drawPolygon
5358
     * This method is only called by the renderer itself.
5359
     *
5360
     * Parameters:
5361
     * node - {DOMElement}
5362
     * geometry - {<OpenLayers.Geometry>}
5363
     *
5364
     * Returns:
5365
     * {DOMElement} or null if the renderer could not draw all components
5366
     *     of the polygon, or false if nothing could be drawn
5367
     */
5368
    drawPolygon: function(node, geometry) {
5369
        var d = [];
5370
        var draw = true;
5371
        var complete = true;
5372
        var linearRingResult, path;
5373
        for (var j=0, len=geometry.components.length; j<len; j++) {
5374
            d.push("M");
5375
            path = this.getComponentsString(
5376
                geometry.components[j].components, " ");
5377
            d.push(path);
5378
        }
5379
        d.push("z");
5380
        node.setAttributeNS(null, "d", d.join(" "));
5381
        node.setAttributeNS(null, "fill-rule", "evenodd");
5382
        return node;
5383
    },
5384

    
5385
    /**
5386
     * Method: drawRectangle
5387
     * This method is only called by the renderer itself.
5388
     *
5389
     * Parameters:
5390
     * node - {DOMElement}
5391
     * geometry - {<OpenLayers.Geometry>}
5392
     *
5393
     * Returns:
5394
     * {DOMElement} or false if the renderer could not draw the rectangle
5395
     */
5396
    drawRectangle: function(node, geometry) {
5397
        node.setAttributeNS(null, "x", geometry.x);
5398
        node.setAttributeNS(null, "y", -geometry.y);
5399
        node.setAttributeNS(null, "width", geometry.width);
5400
        node.setAttributeNS(null, "height", geometry.height);
5401
        return node;
5402
    },
5403

    
5404
    /**
5405
     * Method: drawText
5406
     * Function for drawing text labels.
5407
     * This method is only called by the renderer itself.
5408
     *
5409
     * Parameters:
5410
     * featureId - {String|DOMElement}
5411
     * style - {Object}
5412
     * location - {<OpenLayers.Geometry.Point>}, will be modified inline
5413
     *
5414
     * Returns:
5415
     * {DOMElement} container holding the text label
5416
     */
5417
    drawText: function(featureId, style, location) {
5418
        var g = OpenLayers.Renderer.NG.prototype.drawText.apply(this, arguments);
5419
        var text = g.firstChild ||
5420
            this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_text", "text");
5421

    
5422
        var res = this.getResolution();
5423
        text.setAttributeNS(null, "x", location.x / res);
5424
        text.setAttributeNS(null, "y", - location.y / res);
5425
        g.setAttributeNS(null, "transform", "scale(" + res + ")");
5426

    
5427
        if (style.fontColor) {
5428
            text.setAttributeNS(null, "fill", style.fontColor);
5429
        }
5430
        if (style.fontOpacity) {
5431
            text.setAttributeNS(null, "opacity", style.fontOpacity);
5432
        }
5433
        if (style.fontFamily) {
5434
            text.setAttributeNS(null, "font-family", style.fontFamily);
5435
        }
5436
        if (style.fontSize) {
5437
            text.setAttributeNS(null, "font-size", style.fontSize);
5438
        }
5439
        if (style.fontWeight) {
5440
            text.setAttributeNS(null, "font-weight", style.fontWeight);
5441
        }
5442
        if (style.fontStyle) {
5443
            text.setAttributeNS(null, "font-style", style.fontStyle);
5444
        }
5445
        if (style.labelSelect === true) {
5446
            text.setAttributeNS(null, "pointer-events", "visible");
5447
            text._featureId = featureId;
5448
        } else {
5449
            text.setAttributeNS(null, "pointer-events", "none");
5450
        }
5451
        var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign;
5452
        text.setAttributeNS(null, "text-anchor",
5453
            OpenLayers.Renderer.SVG2.LABEL_ALIGN[align[0]] || "middle");
5454

    
5455
        if (OpenLayers.IS_GECKO === true) {
5456
            text.setAttributeNS(null, "dominant-baseline",
5457
                OpenLayers.Renderer.SVG2.LABEL_ALIGN[align[1]] || "central");
5458
        }
5459

    
5460
        var labelRows = style.label.split('\n');
5461
        var numRows = labelRows.length;
5462
        while (text.childNodes.length > numRows) {
5463
            text.removeChild(text.lastChild);
5464
        }
5465
        for (var i = 0; i < numRows; i++) {
5466
            var tspan = text.childNodes[i] ||
5467
                this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan_" + i, "tspan");
5468
            if (style.labelSelect === true) {
5469
                tspan._featureId = featureId;
5470
            }
5471
            if (OpenLayers.IS_GECKO === false) {
5472
                tspan.setAttributeNS(null, "baseline-shift",
5473
                    OpenLayers.Renderer.SVG2.LABEL_VSHIFT[align[1]] || "-35%");
5474
            }
5475
            tspan.setAttribute("x", location.x / res);
5476
            if (i == 0) {
5477
                var vfactor = OpenLayers.Renderer.SVG2.LABEL_VFACTOR[align[1]];
5478
                if (vfactor == null) {
5479
                    vfactor = -.5;
5480
                }
5481
                tspan.setAttribute("dy", (vfactor*(numRows-1)) + "em");
5482
            } else {
5483
                tspan.setAttribute("dy", "1em");
5484
            }
5485
            tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i];
5486
            if (!tspan.parentNode) {
5487
                text.appendChild(tspan);
5488
            }
5489
        }
5490

    
5491
        if (!text.parentNode) {
5492
            g.appendChild(text);
5493
        }
5494

    
5495
        return g;
5496
    },
5497

    
5498
    /**
5499
     * Method: getComponentString
5500
     *
5501
     * Parameters:
5502
     * components - {Array(<OpenLayers.Geometry.Point>)} Array of points
5503
     * separator - {String} character between coordinate pairs. Defaults to ","
5504
     *
5505
     * Returns:
5506
     * {Object} hash with properties "path" (the string created from the
5507
     *     components and "complete" (false if the renderer was unable to
5508
     *     draw all components)
5509
     */
5510
    getComponentsString: function(components, separator) {
5511
        var len = components.length;
5512
        var strings = new Array(len);
5513
        for (var i=0; i<len; i++) {
5514
            strings[i] = this.getShortString(components[i]);
5515
        }
5516

    
5517
        return strings.join(separator || ",");
5518
    },
5519

    
5520
    /**
5521
     * Method: getShortString
5522
     *
5523
     * Parameters:
5524
     * point - {<OpenLayers.Geometry.Point>}
5525
     *
5526
     * Returns:
5527
     * {String} or false if point is outside the valid range
5528
     */
5529
    getShortString: function(point) {
5530
        return point.x + "," + (-point.y);
5531
    },
5532

    
5533
    /**
5534
     * Method: importSymbol
5535
     * add a new symbol definition from the rendererer's symbol hash
5536
     *
5537
     * Parameters:
5538
     * graphicName - {String} name of the symbol to import
5539
     *
5540
     * Returns:
5541
     * {DOMElement} - the imported symbol
5542
     */
5543
    importSymbol: function (graphicName)  {
5544
        if (!this.defs) {
5545
            // create svg defs tag
5546
            this.defs = this.createDefs();
5547
        }
5548
        var id = this.container.id + "-" + graphicName;
5549

    
5550
        // check if symbol already exists in the defs
5551
        var existing = document.getElementById(id);
5552
        if (existing != null) {
5553
            return existing;
5554
        }
5555

    
5556
        var symbol = OpenLayers.Renderer.symbol[graphicName];
5557
        if (!symbol) {
5558
            throw new Error(graphicName + ' is not a valid symbol name');
5559
        }
5560

    
5561
        var symbolNode = this.nodeFactory(id, "symbol");
5562
        var node = this.nodeFactory(null, "polygon");
5563
        symbolNode.appendChild(node);
5564
        var symbolExtent = new OpenLayers.Bounds(
5565
                                    Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
5566

    
5567
        var points = [];
5568
        var x,y;
5569
        for (var i=0, len=symbol.length; i<len; i=i+2) {
5570
            x = symbol[i];
5571
            y = symbol[i+1];
5572
            symbolExtent.left = Math.min(symbolExtent.left, x);
5573
            symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
5574
            symbolExtent.right = Math.max(symbolExtent.right, x);
5575
            symbolExtent.top = Math.max(symbolExtent.top, y);
5576
            points.push(x, ",", y);
5577
        }
5578

    
5579
        node.setAttributeNS(null, "points", points.join(" "));
5580

    
5581
        var width = symbolExtent.getWidth();
5582
        var height = symbolExtent.getHeight();
5583
        // create a viewBox three times as large as the symbol itself,
5584
        // to allow for strokeWidth being displayed correctly at the corners.
5585
        var viewBox = [symbolExtent.left - width,
5586
                        symbolExtent.bottom - height, width * 3, height * 3];
5587
        symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
5588
        this.symbolMetrics[id] = {
5589
            size: Math.max(width, height),
5590
            x: symbolExtent.getCenterLonLat().lon,
5591
            y: symbolExtent.getCenterLonLat().lat
5592
        };
5593

    
5594
        this.defs.appendChild(symbolNode);
5595
        return symbolNode;
5596
    },
5597

    
5598
    /**
5599
     * Method: getFeatureIdFromEvent
5600
     *
5601
     * Parameters:
5602
     * evt - {Object} An <OpenLayers.Event> object
5603
     *
5604
     * Returns:
5605
     * {String} A feature id or undefined.
5606
     */
5607
    getFeatureIdFromEvent: function(evt) {
5608
        var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);
5609
        if(!featureId) {
5610
            var target = evt.target;
5611
            featureId = target.parentNode && target != this.rendererRoot ?
5612
                target.parentNode._featureId : undefined;
5613
        }
5614
        return featureId;
5615
    },
5616

    
5617
    CLASS_NAME: "OpenLayers.Renderer.SVG2"
5618
});
5619

    
5620
/**
5621
 * Constant: OpenLayers.Renderer.SVG2.LABEL_ALIGN
5622
 * {Object}
5623
 */
5624
OpenLayers.Renderer.SVG2.LABEL_ALIGN = {
5625
    "l": "start",
5626
    "r": "end",
5627
    "b": "bottom",
5628
    "t": "hanging"
5629
};
5630

    
5631
/**
5632
 * Constant: OpenLayers.Renderer.SVG2.LABEL_VSHIFT
5633
 * {Object}
5634
 */
5635
OpenLayers.Renderer.SVG2.LABEL_VSHIFT = {
5636
    // according to
5637
    // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html
5638
    // a baseline-shift of -70% shifts the text exactly from the
5639
    // bottom to the top of the baseline, so -35% moves the text to
5640
    // the center of the baseline.
5641
    "t": "-70%",
5642
    "b": "0"
5643
};
5644

    
5645
/**
5646
 * Constant: OpenLayers.Renderer.SVG2.LABEL_VFACTOR
5647
 * {Object}
5648
 */
5649
OpenLayers.Renderer.SVG2.LABEL_VFACTOR = {
5650
    "t": 0,
5651
    "b": -1
5652
};
5653

    
5654
/**
5655
 * Function: OpenLayers.Renderer.SVG2.preventDefault
5656
 * Used to prevent default events (especially opening images in a new tab on
5657
 * ctrl-click) from being executed for externalGraphic and graphicName symbols
5658
 */
5659
OpenLayers.Renderer.SVG2.preventDefault = function(e) {
5660
    e.preventDefault && e.preventDefault();
5661
};
5662

    
5663
/**
5664
 * Class: OpenLayers.Popup.AnchoredBubble
5665
 * This class is *deprecated*. Use {<OpenLayers.Popup.Anchored>} and
5666
 * round corners using CSS3's border-radius property.
5667
 *
5668
 * Inherits from:
5669
 *  - <OpenLayers.Popup.Anchored>
5670
 */
5671
OpenLayers.Popup.AnchoredBubble = OpenLayers.Class(OpenLayers.Popup.Anchored, {
5672

    
5673
    /**
5674
     * Property: rounded
5675
     * {Boolean} Has the popup been rounded yet?
5676
     */
5677
    rounded: false,
5678

    
5679
    /**
5680
     * Constructor: OpenLayers.Popup.AnchoredBubble
5681
     *
5682
     * Parameters:
5683
     * id - {String}
5684
     * lonlat - {<OpenLayers.LonLat>}
5685
     * contentSize - {<OpenLayers.Size>}
5686
     * contentHTML - {String}
5687
     * anchor - {Object} Object to which we'll anchor the popup. Must expose
5688
     *     a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>)
5689
     *     (Note that this is generally an <OpenLayers.Icon>).
5690
     * closeBox - {Boolean}
5691
     * closeBoxCallback - {Function} Function to be called on closeBox click.
5692
     */
5693
    initialize:function(id, lonlat, contentSize, contentHTML, anchor, closeBox,
5694
                        closeBoxCallback) {
5695

    
5696
        this.padding = new OpenLayers.Bounds(
5697
            0, OpenLayers.Popup.AnchoredBubble.CORNER_SIZE,
5698
            0, OpenLayers.Popup.AnchoredBubble.CORNER_SIZE
5699
        );
5700
        OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
5701
    },
5702

    
5703
    /**
5704
     * Method: draw
5705
     *
5706
     * Parameters:
5707
     * px - {<OpenLayers.Pixel>}
5708
     *
5709
     * Returns:
5710
     * {DOMElement} Reference to a div that contains the drawn popup.
5711
     */
5712
    draw: function(px) {
5713

    
5714
        OpenLayers.Popup.Anchored.prototype.draw.apply(this, arguments);
5715

    
5716
        this.setContentHTML();
5717

    
5718
        //set the popup color and opacity
5719
        this.setBackgroundColor();
5720
        this.setOpacity();
5721

    
5722
        return this.div;
5723
    },
5724

    
5725
    /**
5726
     * Method: updateRelativePosition
5727
     * The popup has been moved to a new relative location, in which case
5728
     *     we will want to re-do the rico corners.
5729
     */
5730
    updateRelativePosition: function() {
5731
        this.setRicoCorners();
5732
    },
5733

    
5734
    /**
5735
     * APIMethod: setSize
5736
     *
5737
     * Parameters:
5738
     * contentSize - {<OpenLayers.Size>} the new size for the popup's
5739
     *     contents div (in pixels).
5740
     */
5741
    setSize:function(contentSize) {
5742
        OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
5743

    
5744
        this.setRicoCorners();
5745
    },
5746

    
5747
    /**
5748
     * APIMethod: setBackgroundColor
5749
     *
5750
     * Parameters:
5751
     * color - {String}
5752
     */
5753
    setBackgroundColor:function(color) {
5754
        if (color != undefined) {
5755
            this.backgroundColor = color;
5756
        }
5757

    
5758
        if (this.div != null) {
5759
            if (this.contentDiv != null) {
5760
                this.div.style.background = "transparent";
5761
                OpenLayers.Rico.Corner.changeColor(this.groupDiv,
5762
                                                   this.backgroundColor);
5763
            }
5764
        }
5765
    },
5766

    
5767
    /**
5768
     * APIMethod: setOpacity
5769
     *
5770
     * Parameters:
5771
     * opacity - {float}
5772
     */
5773
    setOpacity:function(opacity) {
5774
        OpenLayers.Popup.Anchored.prototype.setOpacity.call(this, opacity);
5775

    
5776
        if (this.div != null) {
5777
            if (this.groupDiv != null) {
5778
                OpenLayers.Rico.Corner.changeOpacity(this.groupDiv,
5779
                                                     this.opacity);
5780
            }
5781
        }
5782
    },
5783

    
5784
    /**
5785
     * Method: setBorder
5786
     * Always sets border to 0. Bubble Popups can not have a border.
5787
     *
5788
     * Parameters:
5789
     * border - {Integer}
5790
     */
5791
    setBorder:function(border) {
5792
        this.border = 0;
5793
    },
5794

    
5795
    /**
5796
     * Method: setRicoCorners
5797
     * Update RICO corners according to the popup's current relative postion.
5798
     */
5799
    setRicoCorners:function() {
5800

    
5801
        var corners = this.getCornersToRound(this.relativePosition);
5802
        var options = {corners: corners,
5803
                         color: this.backgroundColor,
5804
                       bgColor: "transparent",
5805
                         blend: false};
5806

    
5807
        if (!this.rounded) {
5808
            OpenLayers.Rico.Corner.round(this.div, options);
5809
            this.rounded = true;
5810
        } else {
5811
            OpenLayers.Rico.Corner.reRound(this.groupDiv, options);
5812
            //set the popup color and opacity
5813
            this.setBackgroundColor();
5814
            this.setOpacity();
5815
        }
5816
    },
5817

    
5818
    /**
5819
     * Method: getCornersToRound
5820
     *
5821
     * Returns:
5822
     * {String} The proper corners string ("tr tl bl br") for rico to round.
5823
     */
5824
    getCornersToRound:function() {
5825

    
5826
        var corners = ['tl', 'tr', 'bl', 'br'];
5827

    
5828
        //we want to round all the corners _except_ the opposite one.
5829
        var corner = OpenLayers.Bounds.oppositeQuadrant(this.relativePosition);
5830
        OpenLayers.Util.removeItem(corners, corner);
5831

    
5832
        return corners.join(" ");
5833
    },
5834

    
5835
    CLASS_NAME: "OpenLayers.Popup.AnchoredBubble"
5836
});
5837

    
5838
/**
5839
 * Constant: CORNER_SIZE
5840
 * {Integer} 5. Border space for the RICO corners.
5841
 */
5842
OpenLayers.Popup.AnchoredBubble.CORNER_SIZE = 5;
(2-2/2)