]> git.mxchange.org Git - friendica.git/blob - view/theme/diabook/js/jquery.mapquery.core.js
diabook-themes: state of the boxes at right_aside are now stored in db, instead of...
[friendica.git] / view / theme / diabook / js / jquery.mapquery.core.js
1 /* Copyright (c) 2011 by MapQuery Contributors (see AUTHORS for
2  * full list of contributors). Published under the MIT license.
3  * See https://github.com/mapquery/mapquery/blob/master/LICENSE for the
4  * full text of the license. */
5 (function ($) {
6 /**
7 # jquery.mapquery.core.js
8 The main MapQuery file. It contains the MapQuery constructor, the MapQuery.Map
9 constructor and the MapQuery.Layer constructor.
10
11
12 ### *$('selector')*.`mapQuery([options])`
13 _version added 0.1_
14 ####**Description**: initialise MapQuery and associate it with
15 the matched element
16
17 **options**  an object of key-value pairs with options for the map. Possible
18 pairs are:
19
20  * **layers** (array of MapQuery.Layer *or* MapQuery.Layer): Either an array
21  * or a single layer that should be added to the map
22  * **center** ({position: [x,y], zoom: z(int), box: [llx,lly,urx,ury]}):
23  * Initially go to a certain location. At least one layer (in the `layers`
24  * option) needs to be specified.
25
26 > Returns: $('selector') (jQuery object)
27
28
29 We can initialise MapQuery without any options, or for instance pass in a layer
30 object. The mapQuery function returns a jQuery object, to access the mapQuery object retrieve
31 the 'mapQuery' data object.
32
33      var map = $('#map').mapQuery(); //create an empty map
34      var map = $('#map').mapQuery({layers:[{type:'osm'}]); //create a map with osm
35
36      var mq = map.data('mapQuery'); //get the MapQuery object
37  */
38 $.MapQuery = $.MapQuery || {};
39
40 /**
41
42 ---
43
44 #MapQuery.Map
45
46 The MapQuery.Map object. It is automatically constructed from the options
47 given in the `mapQuery([options])` constructor. The Map object is refered
48 to as _map_ in the documentation.
49  */
50 $.MapQuery.Map = function(element, options) {
51     var self = this;
52     //If there are a maxExtent and a projection other than Spherical Mercator
53     //automagically set maxResolution if it is not set
54     //TODO smo 20110614: put maxExtent and maxResolution setting in the
55     //proper option building routine
56     if(options){
57     if(!options.maxResolution&&options.maxExtent&&options.projection){
58         options.maxResolution = (options.maxExtent[2]-options.maxExtent[0])/256;
59     }}
60     this.options = $.extend({}, new $.fn.mapQuery.defaults.map(), options);
61
62     this.element = element;
63     // TODO vmx 20110609: do proper options building
64     // TODO SMO 20110616: make sure that all projection strings are uppercase
65     // smo 20110620: you need the exact map options in the overviewmap widget
66     // as such we need to preserve them
67     this.olMapOptions = $.extend({}, this.options);
68     delete this.olMapOptions.layers;
69     delete this.olMapOptions.maxExtent;
70     delete this.olMapOptions.zoomToMaxExtent;
71     //TODO SMO20110630 the maxExtent is in mapprojection, decide whether or
72     //not we need to change it to displayProjection
73     this.maxExtent = this.options.maxExtent;
74     this.olMapOptions.maxExtent = new OpenLayers.Bounds(
75     this.maxExtent[0],this.maxExtent[1],this.maxExtent[2],this.maxExtent[3]);
76
77
78     OpenLayers.IMAGE_RELOAD_ATTEMPTS = 3;
79     OpenLayers.Util.onImageLoadErrorColor = "transparent";
80
81     // create the OpenLayers Map
82     this.olMap = new OpenLayers.Map(this.element[0], this.olMapOptions);
83
84     //OpenLayers doesn't want to return a maxExtent when there is no baselayer
85     //set (eg on an empty map, so we create a fake baselayer
86     this.olMap.addLayer(new OpenLayers.Layer('fake', {baseLayer: true}));
87
88     // Keep IDs of vector layer for select feature control
89     this.vectorLayers = [];
90     this.selectFeatureControl = null;
91     // Counts up to create unique IDs
92     this.idCounter = 0;
93
94     element.data('mapQuery', this);
95     this.layersList = {};
96
97     // To bind and trigger jQuery events
98     this.events = $({});
99     // create triggers for all OpenLayers map events
100     var events = {};
101     $.each(this.olMap.EVENT_TYPES, function(i, evt) {
102         events[evt] = function() {
103             self.events.trigger(evt, arguments);
104         };
105     });
106     this.olMap.events.on(events);
107
108     // Add layers to the map
109     if (this.options.layers!==undefined) {
110         this.layers(this.options.layers);
111         // You can only go to some location if there were layers added
112         if (this.options.center!==undefined) {
113             this.center(this.options.center);
114         }
115     }
116
117     // zoom to the maxExtent of the map if no precise location was specified
118     if (this.options.zoomToMaxExtent && this.options.center===undefined) {
119         this.olMap.zoomToMaxExtent();
120     }
121 };
122
123 $.MapQuery.Map.prototype = {
124  /**
125 ###*map*.`layers([options])`
126 _version added 0.1_
127 ####**Description**: get/set the layers of the map
128
129 **options** an object of key-value pairs with options to create one or
130 more layers
131
132 >Returns: [layer] (array of MapQuery.Layer)
133
134
135 The `.layers()` method allows us to attach layers to a mapQuery object. It takes
136 an options object with layer options. To add multiple layers, create an array of
137 layer options objects. If an options object is given, it will return the
138 resulting layer(s). We can also use it to retrieve all layers currently attached
139 to the map.
140
141
142      var osm = map.layers({type:'osm'}); //add an osm layer to the map
143      var layers = map.layers(); //get all layers of the map
144
145      */
146     layers: function(options) {
147         //var o = $.extend({}, options);
148         var self = this;
149         switch(arguments.length) {
150         case 0:
151             return this._allLayers();
152         case 1:
153             if (!$.isArray(options)) {
154                 return this._addLayer(options);
155             }
156             else {
157                 return $.map(options, function(layer) {
158                     return self._addLayer(layer);
159                 });
160             }
161             break;
162         default:
163             throw('wrong argument number');
164         }
165     },
166     // Returns all layers as an array, sorted by there order in the map. First
167     // element in the array is the topmost layer
168     _allLayers: function() {
169         var layers = [];
170         $.each(this.layersList, function(id, layer) {
171             var item = [layer.position(), layer];
172             layers.push(item);
173         });
174         var sorted = layers.sort( function compare(a, b) {
175             return a[0] - b[0];
176         });
177         var result = $.map(sorted, function(item) {
178             return item[1];
179         });
180         return result.reverse();
181     },
182     _addLayer: function(options) {
183         var id = this._createId();
184         var layer = new $.MapQuery.Layer(this, id, options);
185         this.layersList[id] = layer;
186         if (layer.isVector) {
187             this.vectorLayers.push(id);
188         }
189         this._updateSelectFeatureControl(this.vectorLayers);
190         this.events.trigger('mqAddLayer',layer);
191         return layer;
192     },
193     // Creates a new unique ID for a layer
194     _createId: function() {
195         return 'mapquery' + this.idCounter++;
196     },
197     _removeLayer: function(id) {
198         // remove id from vectorlayer if it is there list
199         this.vectorLayers = $.grep(this.vectorLayers, function(elem) {
200             return elem != id;
201         });
202         this._updateSelectFeatureControl(this.vectorLayers);
203         this.events.trigger('mqRemoveLayer',id);
204         delete this.layersList[id];
205         // XXX vmx: shouldn't the layer be destroyed() properly?
206         return this;
207     },
208 /**
209  ###*map*.`center([options])`
210 _version added 0.1_
211 ####**Description**: get/set the extent, zoom and position of the map
212
213 **position** the position as [x,y] in displayProjection (default EPSG:4326)
214 to center the map at
215 **zoom** the zoomlevel as integer to zoom the map to
216 **box** an array with the lower left x, lower left y, upper right x,
217 upper right y to zoom the map to,
218 this will take precedent when conflicting with any of the above values
219 **projection** the projection the coordinates are in, default is
220 the displayProjection
221
222 >Returns: {position: [x,y], zoom: z(int), box: [llx,lly,urx,ury]}
223
224
225 The `.center()` method allows us to move to map to a specific zoom level,
226 specific position or a specific extent. We can specify the projection of the
227 coordinates to override the displayProjection. For instance you want to show
228 the coordinates in 4326, but you have a dataset in EPSG:28992
229 (dutch projection). We can also retrieve the current zoomlevel, position and
230 extent from the map. The coordinates are returned in displayProjection.
231
232
233      var center = map.center(); //get the current zoom, position and extent
234      map.center({zoom:4}); //zoom to zoomlevel 4
235      map.center({position:[5,52]}); //pan to point 5,52
236      map.center(box:[-180,-90,180,90]); //zoom to the box -180,-900,180,90
237      //pan to point 125000,485000 in dutch projection
238      map.center({position:[125000,485000],projection:'EPSG:28992'});
239  */
240     center: function (options) {
241         var position;
242         var mapProjection;
243         // Determine source projection
244         var sourceProjection = null;
245         var zoom;
246         var box;
247         if(options && options.projection) {
248             sourceProjection = options.projection.CLASS_NAME ===
249             'OpenLayers.Projection' ? options.projection :
250             new OpenLayers.Projection(options.projection);
251         } else {
252             var displayProjection = this.olMap.displayProjection;
253             if(!displayProjection) {
254                 // source == target
255                 sourceProjection = new OpenLayers.Projection('EPSG:4326');
256             } else {
257                 sourceProjection = displayProjection.CLASS_NAME ===
258             'OpenLayers.Projection' ? displayProjection :
259             new OpenLayers.Projection(displayProjection);
260             }
261         }
262
263         // Get the current position
264         if (arguments.length===0) {
265             position = this.olMap.getCenter();
266             zoom = this.olMap.getZoom();
267             box = this.olMap.getExtent();
268             mapProjection = this.olMap.getProjectionObject();
269
270
271             if (!mapProjection.equals(sourceProjection)) {
272                 position.transform(mapProjection, sourceProjection);
273             }
274             box.transform(mapProjection,sourceProjection);
275             box = box!==null ? box.toArray() : [];
276             return {
277                 position: [position.lon, position.lat],
278                 zoom: this.olMap.getZoom(),
279                 box: box
280             };
281         }
282
283         // Zoom to the extent of the box
284         if (options.box!==undefined) {
285             mapProjection = this.olMap.getProjectionObject();
286             box = new OpenLayers.Bounds(
287         options.box[0], options.box[1],options.box[2], options.box[3]);
288             if (!mapProjection.equals(sourceProjection)) {
289                 box.transform(sourceProjection,mapProjection);
290             }
291             this.olMap.zoomToExtent(box);
292
293         }
294         // Only zoom is given
295         else if (options.position===undefined) {
296             this.olMap.zoomTo(options.zoom);
297         }
298         // Position is given, zoom maybe as well
299         else {
300             position = new OpenLayers.LonLat(options.position[0],
301                                              options.position[1]);
302             mapProjection = this.olMap.getProjectionObject();
303             if (!mapProjection.equals(sourceProjection)) {
304                 position.transform(sourceProjection, mapProjection);
305             }
306             // options.zoom might be undefined, so we are good to
307             // pass it on
308             this.olMap.setCenter(position, options.zoom);
309         }
310     },
311     _updateSelectFeatureControl: function(layerIds) {
312         var vectorLayers = [];
313         var layersList = this.layersList;
314         if (this.selectFeatureControl!==null) {
315             this.selectFeatureControl.deactivate();
316             this.selectFeatureControl.destroy();
317         }
318         $.each(layerIds, function() {
319             vectorLayers.push(layersList[this].olLayer);
320         });
321         this.selectFeatureControl = new OpenLayers.Control.SelectFeature(
322             vectorLayers);
323         this.olMap.addControl(this.selectFeatureControl);
324         this.selectFeatureControl.activate();
325     },
326     bind: function() {
327         this.events.bind.apply(this.events, arguments);
328     },
329     one: function() {
330         this.events.one.apply(this.events, arguments);
331     },
332     destroy: function() {
333         this.olMap.destroy();
334         this.element.removeData('mapQuery');
335     }
336 };
337 /**
338
339 ---
340
341 #MapQuery.Layer
342
343 The MapQuery.Layer object. It is constructed with layer options object in the
344 map.`layers([options])` function or by passing a `layer:{options}` object in
345 the `mapQuery()` constructor. The Layer object is refered to as _layer_ in the
346 documentation.
347  */
348 $.MapQuery.Layer = function(map, id, options) {
349
350     var self = this;
351     // apply default options that are not specific to a layer
352
353     this.id = id;
354     this.label = options.label || this.id;
355     // a reference to the map object is needed as it stores e.g. the list
356     // of all layers (and we need to keep track of it, if we delete a
357     // layer)
358     this.map = map;
359
360     // true if this layer is a vector layer
361     this.isVector = false;
362
363     // to bind and trigger jQuery events
364     this.events = $({});
365
366     // create the actual layer based on the options
367     // Returns layer and final options for the layer (for later re-use,
368     // e.g. zoomToMaxExtent).
369     var res = $.MapQuery.Layer.types[options.type.toLowerCase()].call(
370         this, options);
371     this.olLayer = res.layer;
372     this.options = res.options;
373
374     // create triggers for all OpenLayers layer events
375     var events = {};
376     $.each(this.olLayer.EVENT_TYPES, function(i, evt) {
377         events[evt] = function() {
378             self.events.trigger(evt, arguments);
379             self.map.events.trigger(evt, arguments);
380         };
381     });
382     this.olLayer.events.on(events);
383
384     this.map.olMap.addLayer(this.olLayer);
385 };
386
387 $.MapQuery.Layer.prototype = {
388 /**
389 ###*layer*.`down([delta])`
390 _version added 0.1_
391 ####**Description**: move the layer down in the layer stack of the map
392
393 **delta** the amount of layers the layer has to move down in the layer
394 stack (default 1)
395
396 >Returns layer (MapQuery.Layer)
397
398
399 The `.down()` method is a shortcut method for `.position(pos)` which makes
400 it easier to move a layer down in the layerstack relative to its current
401 position. It takes an integer and will try to move the layer down the number of
402 places given. If delta is bigger than the current position in the stack, it
403 will put the layer at the bottom.
404
405
406      layer.down();  //move layer 1 place down
407      layer.down(3); //move layer 3 places down
408
409  */
410     down: function(delta) {
411         delta = delta || 1;
412         var pos = this.position();
413         pos = pos - delta;
414         if (pos<0) {pos = 0;}
415         this.position(pos);
416         return this;
417     },
418     // NOTE vmx: this would be pretty cool, but it's not easily possible
419     // you could use $.each($.geojq.layer())) instead, this is for pure
420     // convenience.
421     each: function () {},
422 /**
423 ###*layer*.`remove()`
424 _version added 0.1_
425 ####**Description**: remove the layer from the map
426
427 >Returns: id (string)
428
429
430 The `.remove()` method allows us to remove a layer from the map.
431 It returns an id to allow widgets to remove their references to the
432 destroyed layer.
433
434      var id = layer.remove(); //remove this layer
435
436
437  */
438     remove: function() {
439         this.map.olMap.removeLayer(this.olLayer);
440         // remove references to this layer that are stored in the
441         // map object
442         return this.map._removeLayer(this.id);
443     },
444 /**
445 ###*layer*.`position([position])`
446 _version added 0.1_
447 ####**Description**: get/set the `position` of the layer in the layer
448 stack of the map
449
450 **position** an integer setting the new position of the layer in the layer stack
451
452 >Returns: position (integer)
453
454
455 The `.position()` method allows us to change the position of the layer in the
456 layer stack. It will take into account the hidden baselayer that is used by
457 OpenLayers. The lowest layer is position 0. If no position is given, it will
458 return the current postion.
459
460
461      var pos =  layer.position(); //get position of layer in the layer stack
462      layer.position(2); //put layer on position 2 in the layer stack
463
464  */
465     position: function(pos) {
466         if (pos===undefined) {
467             return this.map.olMap.getLayerIndex(this.olLayer)-1;
468         }
469         else {
470             return this.map.olMap.setLayerIndex(this.olLayer, pos+1);
471         }
472     },
473 /**
474 ###*layer*.`up([delta])`
475 _version added 0.1_
476 ####**Description**: move the layer up in the layer stack of the map
477
478 **delta** the amount of layers the layer has to move up in the layer
479 stack (default 1)
480
481 >Returns: layer (MapQuery.Layer)
482
483
484 The `.up()` method is a shortcut method for `.position(pos)` which makes
485 it easier to move a layer up in the layerstack relative to its current
486 position. It takes an integer and will move the layer up the number of places
487 given.
488
489
490
491      layer.up();  //move layer 1 place up
492      layer.up(3); //move layer 3 places up
493 */
494     up: function(delta) {
495         delta = delta || 1;
496         var pos = this.position();
497         pos = pos + delta;
498         this.position(pos);
499         return this;
500     },
501 /**
502 ###*layer*.`visible([visible])`
503 _version added 0.1_
504 ####**Description**: get/set the `visible` state of the layer
505
506 **visible** a boolean setting the visibiliyu of the layer
507
508 >Returns: visible (boolean)
509
510
511 The `.visible()` method allows us to change the visibility of the layer.
512 If no visible is given, it will return the current visibility.
513
514
515      var vis =  layer.visible(); //get the visibility of layer
516      layer.visible(true); //set visibility of layer to true
517
518  */
519     visible: function(vis) {
520         if (vis===undefined) {
521             return this.olLayer.getVisibility();
522         }
523         else {
524             this.olLayer.setVisibility(vis);
525             return this;
526         }
527     },
528 /**
529 ###*layer*.`opacity([opacity])`
530 _version added 0.1_
531 ####**Description**: get/set the `opacity` of the layer
532
533 **position** a float [0-1] setting the opacity of the layer
534
535 >Returns: opacity (float)
536
537
538 The `.opacity()` method allows us to change the opacity of the layer.
539 If no opacity is given, it will return the current opacity.
540
541
542      var opac =  layer.opacity(); //get opacity of layer
543      layer.opacity(0.7); //set opacity of layer to 0.7
544
545  */
546     opacity: function(opac) {
547          if (opac===undefined) {
548             // this.olLayer.opacity can be null if never
549         // set so return the visibility
550             var value = this.olLayer.opacity ?
551             this.olLayer.opacity : this.olLayer.getVisibility();
552             return value;
553         }
554         else {
555             this.olLayer.setOpacity(opac);
556             return this;
557         }
558     },
559     // every event gets the layer passed in
560     bind: function() {
561         this.events.bind.apply(this.events, arguments);
562     },
563     one: function() {
564         this.events.one.apply(this.events, arguments);
565     }
566 };
567
568 $.fn.mapQuery = function(options) {
569     return this.each(function() {
570         var instance = $.data(this, 'mapQuery');
571         if (!instance) {
572             $.data(this, 'mapQuery', new $.MapQuery.Map($(this), options));
573         }
574     });
575 };
576
577 $.extend($.MapQuery.Layer, {
578     types: {
579 /**
580 ###*layer* `{type:bing}`
581 _version added 0.1_
582 ####**Description**: create a Bing maps layer
583
584 **view** a string ['road','hybrid','satellite'] to define which Bing maps
585 layer to use (default road)   
586 **key** Bing Maps API key for your application. Get you own at
587 http://bingmapsportal.com/ 
588 **label** string with the name of the layer
589
590
591       layers:[{
592             type:'bing',      //create a bing maps layer
593             view:'satellite', //use the bing satellite layer
594             key:'ArAGGPJ16xm0RX' //the Bing maps API key
595             }]
596
597 */
598         bing: function(options) {
599             var o = $.extend(true, {}, $.fn.mapQuery.defaults.layer.all,
600                 $.fn.mapQuery.defaults.layer.bing,
601                 options);
602             var view = o.view;
603             switch(view){
604                 case 'road':
605                     view = 'Road'; break;
606                 case 'hybrid':
607                     view = 'AerialWithLabels'; break;
608                 case 'satellite':
609                     view = 'Aerial'; break;
610             }
611             return {
612                 layer: new OpenLayers.Layer.Bing({type:view,key:o.key}),
613                 options: o
614             };
615         },
616         //Not sure this one is worth pursuing works with ecwp:// & jpip:// urls
617         //See ../lib/NCSOpenLayersECWP.js
618         ecwp: function(options) {
619             var o = $.extend(true, {}, $.fn.mapQuery.defaults.layer.all,
620                     $.fn.mapQuery.defaults.layer.raster,
621                     options);
622             return {
623                 layer: new OpenLayers.Layer.ECWP(o.label, o.url, o),
624                 options: o
625             };
626         },
627 /**
628 ###*layer* `{type:google}`
629 _version added 0.1_
630 ####**Description**: create a Google maps layer
631
632 **view** a string ['road','hybrid','satellite'] to define which Google maps
633 layer to use (default road)
634 **label** string with the name of the layer
635
636
637 *Note* you need to include the google maps v3 API in your application by adding
638 `<script src="http://maps.google.com/maps/api/js?v=3.5&amp;sensor=false"type="text/javascript"></script>`
639
640
641       layers:[{
642             type:'google',      //create a google maps layer
643             view:'hybrid' //use the google hybridlayer
644             }]
645
646 */
647         google: function(options) {
648             var o = $.extend(true, {}, $.fn.mapQuery.defaults.layer.all,
649                     $.fn.mapQuery.defaults.layer.google,
650                     options);
651             var view = o.view;
652             switch(view){
653                 case 'road':
654                     view = google.maps.MapTypeId.ROADMAP; break;
655                 case 'terrain':
656                     view = google.maps.MapTypeId.TERRAIN; break;
657                 case 'hybrid':
658                     view = google.maps.MapTypeId.HYBRID; break;
659                 case 'satellite':
660                     view = google.maps.MapTypeId.SATELLITE; break;
661             }
662             return {
663                 layer: new OpenLayers.Layer.Google({type:view}),
664                 options: o
665             };
666         },
667 /**
668 ###*layer* `{type:vector}`
669 _version added 0.1_
670 ####**Description**: create a vector layer
671
672 **label** string with the name of the layer
673
674
675       layers:[{
676             type:'vector'     //create a vector layer
677             }]
678
679 */
680         vector: function(options) {
681             var o = $.extend(true, {}, $.fn.mapQuery.defaults.layer.all,
682                     $.fn.mapQuery.defaults.layer.vector,
683                     options);
684             this.isVector = true;
685             return {
686                 layer: new OpenLayers.Layer.Vector(o.label),
687                 options: o
688             };
689         },
690 /**
691 ###*layer* `{type:json}`
692 _version added 0.1_
693 ####**Description**: create a JSON layer
694
695 **url** a string pointing to the location of the JSON data
696 **strategies** a string ['bbox','cluster','filter','fixed','paging','refresh','save']
697 stating which update strategy should be used (default fixed)
698 (see also http://dev.openlayers.org/apidocs/files/OpenLayers/Strategy-js.html)
699 **projection** a string with the projection of the JSON data (default EPSG:4326)
700 **styleMap** {object} the style to be used to render the JSON data    
701 **label** string with the name of the layer
702
703
704       layers:[{
705             type: 'JSON',
706             url: 'data/reservate.json',
707             label: 'reservate'
708             }]
709
710 */
711         json: function(options) {
712             var o = $.extend(true, {}, $.fn.mapQuery.defaults.layer.all,
713                     $.fn.mapQuery.defaults.layer.vector,
714                     options);
715             this.isVector = true;
716             var strategies = [];
717             for (var i in o.strategies) {
718                 if(o.strategies.hasOwnProperty(i)) {
719                     switch(o.strategies[i].toLowerCase()) {
720                     case 'bbox':
721                         strategies.push(new OpenLayers.Strategy.BBOX());
722                    break;
723                     case 'cluster':
724                         strategies.push(new OpenLayers.Strategy.Cluster());
725                    break;
726                     case 'filter':
727                         strategies.push(new OpenLayers.Strategy.Filter());
728                    break;
729                     case 'fixed':
730                         strategies.push(new OpenLayers.Strategy.Fixed());
731                    break;
732                     case 'paging':
733                         strategies.push(new OpenLayers.Strategy.Paging());
734                    break;
735                     case 'refresh':
736                         strategies.push(new OpenLayers.Strategy.Refresh());
737                    break;
738                     case 'save':
739                         strategies.push(new OpenLayers.Strategy.Save());
740                    break;
741                     }
742                 }
743             }
744             var protocol;
745             // only use JSONP if we use http(s)
746             if (o.url.match(/^https?:\/\//)!==null &&
747                 !$.MapQuery.util.sameOrigin(o.url)) {
748                 protocol = 'Script';
749             }
750             else {
751                 protocol = 'HTTP';
752             }
753
754             var params = {
755                 protocol: new OpenLayers.Protocol[protocol]({
756                     url: o.url,
757                     format: new OpenLayers.Format.GeoJSON()
758                 }),
759                 strategies: strategies,
760                 projection: o.projection || 'EPSG:4326',
761                 styleMap: o.styleMap
762             };
763             return {
764                 layer: new OpenLayers.Layer.Vector(o.label, params),
765                 options: o
766             };
767         },
768 /**
769 ###*layer* `{type:osm}`
770 _version added 0.1_
771 ####**Description**: create an OpenStreetMap layer
772
773  
774 **label** string with the name of the layer   
775 **url** A single URL (string) or an array of URLs to OSM-like server like 
776 Cloudmade   
777 **attribution** A string to put some attribution on the map
778
779       layers:[{
780         type: 'osm',
781         url: [
782           'http://a.tile.cloudmade.com/<yourapikey>/999/256/${z}/${x}/${y}.png',
783           'http://b.tile.cloudmade.com/<yourapikey>/999/256/${z}/${x}/${y}.png',
784           'http://c.tile.cloudmade.com/<yourapikey>/999/256/${z}/${x}/${y}.png'
785         ],
786         attribution: "Data &copy; 2009 <a href='http://openstreetmap.org/'>
787           OpenStreetMap</a>. Rendering &copy; 2009 
788           <a href='http://cloudmade.com'>CloudMade</a>."
789         }]
790
791 */
792         osm: function(options) {
793             var o = $.extend(true, {}, $.fn.mapQuery.defaults.layer.all,
794                 $.fn.mapQuery.defaults.layer.osm,
795                 options);
796             var label = options.label || undefined;
797             var url = options.url || undefined;
798             return {
799                 layer: new OpenLayers.Layer.OSM(label, url, o),
800                 options: o
801             };
802         },
803 /**
804 ###*layer* `{type:wms}`
805 _version added 0.1_
806 ####**Description**: create a WMS layer
807
808 **url** a string pointing to the location of the WMS service
809 **layers** a string with the name of the WMS layer(s)
810 **format** a string with format of the WMS image (default image/jpeg)
811 **transparent** a boolean for requesting images with transparency
812 **label** string with the name of the layer
813
814
815       layers:[{
816             type:'wms',
817             url:'http://vmap0.tiles.osgeo.org/wms/vmap0',
818             layers:'basic'
819             }]
820
821 */
822         wms: function(options) {
823             var o = $.extend(true, {}, $.fn.mapQuery.defaults.layer.all,
824                     $.fn.mapQuery.defaults.layer.raster,
825                     options);
826             var params = {
827                 layers: o.layers,
828                 transparent: o.transparent,
829                 format: o.format
830             };
831             return {
832                 layer: new OpenLayers.Layer.WMS(o.label, o.url, params, o),
833                 options: o
834             };
835         },
836 //TODO complete this documentation
837 /**
838 ###*layer* `{type:wmts}`
839 _version added 0.1_
840 ####**Description**: create a WMTS (tiling) layer
841
842 **url** a string pointing to the location of the WMTS service
843 **layer** a string with the name of the WMTS layer
844 **matrixSet** a string with one of the advertised matrix set identifiers
845 **style** a string with one of the advertised layer styles    
846 **label** string with the name of the layer
847
848
849       layers:[{
850             type:'wmts'
851             }]
852
853 */
854         wmts: function(options) {
855             var o = $.extend(true, {}, $.fn.mapQuery.defaults.layer.all,
856                     $.fn.mapQuery.defaults.layer.wmts);
857             //smo 20110614 the maxExtent is set here with OpenLayers.Bounds
858             if (options.sphericalMercator===true) {
859                 $.extend(true, o, {
860                     maxExtent: new OpenLayers.Bounds(
861                         -128 * 156543.0339, -128 * 156543.0339,
862                         128 * 156543.0339, 128 * 156543.0339),
863                     maxResolution: 156543.0339,
864                     numZoomLevels: 19,
865                     projection: 'EPSG:900913',
866                     units: 'm'
867                 });
868             }
869             $.extend(true, o, options);
870             // use by default all options that were passed in for the final
871             // openlayers layer consrtuctor
872             var params = $.extend(true, {}, o);
873
874             // remove trailing slash
875             if (params.url.charAt(params.url.length-1)==='/') {
876                 params.url = params.url.slice(0, params.url.length-1);
877             }
878             // if no options that influence the URL where set, extract them
879             // from the given URL
880             if (o.layer===undefined && o.matrixSet===undefined &&
881                     o.style===undefined) {
882                 var url = $.MapQuery.util.parseUri(params.url);
883                 var urlParts = url.path.split('/');
884                 var wmtsPath = urlParts.slice(urlParts.length-3);
885                 params.url = url.protocol ? url.protocol + '//' : '';
886                 params.url += url.authority +
887                     // remove WMTS version (1.0.0) as well
888                     urlParts.slice(0, urlParts.length-4).join('/');
889                 params.layer = wmtsPath[0];
890                 params.style = wmtsPath[1];
891                 params.matrixSet = wmtsPath[2];
892             }
893             return {
894                 layer: new OpenLayers.Layer.WMTS(params),
895                 options: o
896             };
897         }
898     }
899 });
900
901 // default options for the map and layers
902 $.fn.mapQuery.defaults = {
903     // The controls for the map are per instance, therefore it need to
904     // be an function that can be initiated per instance
905     map: function() {
906         return {
907             // Remove quirky moveTo behavior, probably not a good idea in the
908             // long run
909             allOverlays: true,
910             controls: [
911                 // Since OL2.11 the Navigation control includes touch navigation as well
912                 new OpenLayers.Control.Navigation({
913                     documentDrag: true,
914                     dragPanOptions: {
915                         interval: 1,
916                         enableKinetic: true
917                     }
918                 }),
919                 new OpenLayers.Control.ArgParser(),
920                 new OpenLayers.Control.Attribution(),
921                 new OpenLayers.Control.KeyboardDefaults()
922             ],
923             format: 'image/png',
924             maxExtent: [-128*156543.0339,
925                 -128*156543.0339,
926                 128*156543.0339,
927                 128*156543.0339],
928             maxResolution: 156543.0339,
929             numZoomLevels: 19,
930             projection: 'EPSG:900913',
931             displayProjection: 'EPSG:4326',
932             zoomToMaxExtent: true,
933             units: 'm'
934         };
935     },
936     layer: {
937         all: {
938             isBaseLayer: false,
939         //in general it is kinda pointless to load tiles outside a maxextent
940             displayOutsideMaxExtent: false
941         },
942         bing: {
943             transitionEffect: 'resize',
944             view: 'road',
945             sphericalMercator: true
946         },
947         google: {
948             transitionEffect: 'resize',
949             view: 'road',
950             sphericalMercator: true
951         },
952         osm: {
953             transitionEffect: 'resize',
954             sphericalMercator: true
955         },
956         raster: {
957             // options for raster layers
958             transparent: true
959         },
960         vector: {
961             // options for vector layers
962             strategies: ['fixed']
963         },
964         wmts: {
965             format: 'image/jpeg',
966             requestEncoding: 'REST',
967             sphericalMercator: false
968         }
969     }
970 };
971
972 // Some utility functions
973
974 $.MapQuery.util = {};
975 // http://blog.stevenlevithan.com/archives/parseuri (2010-12-18)
976 // parseUri 1.2.2
977 // (c) Steven Levithan <stevenlevithan.com>
978 // MIT License
979 // Edited to include the colon in the protocol, just like it is
980 // with window.location.protocol
981 $.MapQuery.util.parseUri = function (str) {
982     var o = $.MapQuery.util.parseUri.options,
983         m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
984         uri = {},
985         i = 14;
986
987     while (i--) {uri[o.key[i]] = m[i] || "";}
988
989     uri[o.q.name] = {};
990     uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
991         if ($1) {uri[o.q.name][$1] = $2;}
992     });
993
994     return uri;
995 };
996 $.MapQuery.util.parseUri.options = {
997     strictMode: false,
998     key: ["source", "protocol", "authority", "userInfo", "user",
999             "password", "host", "port", "relative", "path", "directory",
1000             "file", "query", "anchor"],
1001     q: {
1002         name: "queryKey",
1003         parser: /(?:^|&)([^&=]*)=?([^&]*)/g
1004     },
1005     parser: {
1006         strict: /^(?:([^:\/?#]+:))?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
1007         loose:  /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+:))?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
1008         }
1009 };
1010 // Checks whether a URL conforms to the same origin policy or not
1011 $.MapQuery.util.sameOrigin = function(url) {
1012     var parsed = $.MapQuery.util.parseUri(url);
1013     parsed.protocol = parsed.protocol || 'file:';
1014     parsed.port = parsed.port || "80";
1015
1016     var current = {
1017         domain: document.domain,
1018         port: window.location.port,
1019         protocol: window.location.protocol
1020     };
1021     current.port = current.port || "80";
1022
1023     return parsed.protocol===current.protocol &&
1024         parsed.port===current.port &&
1025         // the current domain is a suffix of the parsed domain
1026         parsed.host.match(current.domain + '$')!==null;
1027 };
1028 })(jQuery);