1/*jslint white: false */ 2/*jslint forin: true */ 3/*global OpenLayers Drupal $ document jQuery window */ 4 5/** 6 * @file 7 * This file holds the main javascript API for OpenLayers. It is 8 * responsable for loading and displaying the map. 9 * 10 * @ingroup openlayers 11 */ 12 13/** 14 * This is a workaround for a bug involving IE and VML support. 15 * See the Drupal Book page describing this problem: 16 * http://drupal.org/node/613002 17 */ 18 19document.namespaces; 20 21(function($) { 22 23Drupal.settings.openlayers = {}; 24Drupal.settings.openlayers.maps = {}; 25 26/** 27 * Minimal OpenLayers map bootstrap. 28 * All additional operations occur in additional Drupal behaviors. 29 */ 30Drupal.behaviors.openlayers = { 31 'attach': function(context, settings) { 32 Drupal.openlayers.loadProjections(context, settings); 33 34 if (typeof(Drupal.settings.openlayers) === 'object' && 35 Drupal.settings.openlayers.maps && 36 !$(context).data('openlayers')) { 37 $('.openlayers-map').once('openlayers-map', function() { 38 // By setting the stop_render variable to TRUE, this will 39 // halt the render process. If set, one could remove this setting 40 // then call Drupal.attachBehaviors again to get it started 41 var map_id = $(this).attr('id'); 42 if (Drupal.settings.openlayers.maps[map_id] && Drupal.settings.openlayers.maps[map_id].stop_render != true) { 43 var map = Drupal.settings.openlayers.maps[map_id]; 44 45 if(map.default_layer===null){ 46 Drupal.openlayers.console.error("Map configuration is invalid as it lacks a base layer."); 47 } 48 49 // Use try..catch for error handling. 50 try { 51 // Set OpenLayers language based on document language, 52 // rather than browser language 53 OpenLayers.Lang.setCode($('html').attr('lang')); 54 55 var options = {}; 56 // This is necessary because the input JSON cannot contain objects 57 options.projection = new OpenLayers.Projection(map.projection); 58 options.displayProjection = new OpenLayers.Projection(map.displayProjection); 59 60 // Restrict map to its extent (usually projection extent). 61 options.maxExtent = OpenLayers.Bounds.fromArray(map.maxExtent); 62 63 options.maxResolution = 'auto'; 64 options.controls = []; 65 66 // Change image, CSS, and proxy paths if specified 67 if (map.image_path) { 68 OpenLayers.ImgPath = Drupal.openlayers.relatePath(map.image_path, 69 Drupal.settings.basePath); 70 } 71 if (map.css_path) { 72 options.theme = Drupal.openlayers.relatePath(map.css_path, 73 Drupal.settings.basePath); 74 } 75 if (map.proxy_host) { 76 OpenLayers.ProxyHost = Drupal.openlayers.relatePath(map.proxy_host, 77 Drupal.settings.basePath); 78 } 79 80 // Initialize openlayers map 81 var openlayers = new OpenLayers.Map(map_id, options); 82 83 // Run the layer addition first 84 Drupal.openlayers.addLayers(map, openlayers); 85 86 // Ensure redraw as maps stays blank until first zoom otherwise (observed with EPSG:2056) 87 openlayers.moveTo(openlayers.getCenter(), openlayers.getZoom(), { 88 forceZoomChange: true 89 }); 90 91 // Attach data to map DOM object 92 $(this).data('openlayers', {'map': map, 'openlayers': openlayers}); 93 94 // Finally, attach behaviors 95 Drupal.attachBehaviors(this); 96 } 97 catch (e) { 98 var errorMessage = e.name + ': ' + e.message; 99 if (typeof console != 'undefined') { 100 Drupal.openlayers.console.log(errorMessage); 101 } 102 else { 103 $(this).text('Error during map rendering: ' + errorMessage); 104 } 105 } 106 } 107 }); 108 } 109 } 110}; 111 112/** 113 * Collection of helper methods. 114 */ 115Drupal.openlayers = { 116 // Determine path based on format. 117 'relatePath': function(path, basePath) { 118 // Check for a full URL or an absolute path. 119 if (path.indexOf('://') >= 0 || path.indexOf('/') == 0) { 120 return path; 121 } 122 else { 123 return basePath + path; 124 } 125 }, 126 /* 127 * Redraw Vectors. 128 * This is necessary because various version of IE cannot draw vectors on 129 * $(document).ready() 130 */ 131 'redrawVectors': function() { 132 $(window).load( 133 function() { 134 var map; 135 for (map in Drupal.settings.openlayers.maps) {
136 $.each($('#' + map).data('openlayers') 137 .openlayers.getLayersByClass('OpenLayers.Layer.Vector'), 138 function(i, layer) { 139 layer.redraw(); 140 } 141 ); 142 } 143 } 144 ); 145 }, 146 /** 147 * Add layers to the map 148 * 149 * @param map Drupal settings object for the map. 150 * @param openlayers OpenLayers Map Object. 151 */ 152 'addLayers': function(map, openlayers) { 153 154 var sorted = []; 155 for (var name in map.layers) { 156 sorted.push({'name': name, 'weight': map.layers[name].weight, 'isBaseLayer': map.layers[name].isBaseLayer }); 157 } 158 159 sorted.sort(function(a, b) { 160 var x = parseInt(a.weight, 10), y = parseInt(b.weight, 10); 161 return ((a.isBaseLayer && x < y) ? -1 : ((b.isBaseLayer || x > y) ? 1 : 0)); 162 }); 163 164 for (var i = 0; i < sorted.length; ++i) { 165 var layer, 166 name = sorted[i].name, 167 options = map.layers[name]; 168 169 // Add reference to our layer ID 170 options.drupalID = name; 171 // Ensure that the layer handler is available 172 if (options.layer_handler !== undefined && 173 Drupal.openlayers.layer[options.layer_handler] !== undefined) { 174 layer = Drupal.openlayers.layer[options.layer_handler](map.layers[name].title, map, options); 175 176 layer.visibility = !!(!map.layer_activated || map.layer_activated[name]); 177 178 if (layer.isBaseLayer === false) { 179 layer.displayInLayerSwitcher = (!map.layer_switcher || map.layer_switcher[name]); 180 } 181 182 openlayers.addLayer(layer); 183 } 184 } 185 186 openlayers.setBaseLayer(openlayers.getLayersBy('drupalID', map.default_layer)[0]); 187 188 // Set the restricted extent if wanted. 189 // Prevents the map from being panned outside of a specfic bounding box. 190 if (typeof map.center.restrict !== 'undefined' && map.center.restrict.restrictextent) { 191 var desiredRestriction = OpenLayers.Bounds.fromString( 192 map.center.restrict.restrictedExtent 193 ).transform(new OpenLayers.Projection('EPSG:3857'), openlayers.projection); 194 195 if(desiredRestriction.left>=openlayers.maxExtent.left && desiredRestriction.right<=openlayers.maxExtent.right 196 && desiredRestriction.top<=openlayers.maxExtent.top && desiredRestriction.bottom>=openlayers.maxExtent.bottom){ 197 198 openlayers.restrictedExtent = desiredRestriction; 199 } else { 200 // Given the map to set the restricted extent is not dependent on the map projection 201 // it does allow to set an extent outwith the valid bound of the map projection. As a 202 // result no valid data could be requested and thus the wrong extent needs to be ignored. 203 Drupal.openlayers.console.error("Restricted bounds ignored as not within projection bounds."); 204 } 205 } 206 207 // Zoom & center 208 if (map.center.initial) { 209 var center = OpenLayers.LonLat.fromString(map.center.initial.centerpoint).transform( 210 new OpenLayers.Projection('EPSG:4326'), 211 new OpenLayers.Projection(map.projection)); 212 var zoom = parseInt(map.center.initial.zoom, 10); 213 openlayers.setCenter(center, zoom, false, false); 214 } 215 }, 216 /** 217 * Abstraction of OpenLayer's feature adding syntax to work with Drupal output. 218 * Ideally this should be rolled into the PHP code, because we don't want to manually 219 * parse WKT 220 */ 221 'addFeatures': function(map, layer, features) { 222 var newFeatures = []; 223 224 // Go through features 225 for (var key in features) { 226 var feature = features[key]; 227 var newFeatureObject = this.objectFromFeature(feature); 228 229 // If we have successfully extracted geometry add additional 230 // properties and queue it for addition to the layer 231 if (newFeatureObject) { 232 var newFeatureSet = []; 233 234 // Check to see if it is a new feature, or an array of new features. 235 if (typeof(newFeatureObject[0]) === 'undefined') { 236 newFeatureSet[0] = newFeatureObject; 237 } 238 else { 239 newFeatureSet = newFeatureObject; 240 } 241 242 // Go through new features 243 for (var i = 0; i < newFeatureSet.length; i++) { 244 var newFeature = newFeatureSet[i]; 245 246 // Transform the geometry if the 'projection' property is different from the map projection 247 if (feature.projection) { 248 if (feature.projection !== map.projection) { 249 var featureProjection = new OpenLayers.Projection(feature.projection); 250 var mapProjection = new OpenLayers.Projection(map.projection); 251 newFeature.geometry.transform(featureProjection, mapProjection); 252 } 253 } 254 255 // Add attribute data 256 if (feature.attributes) { 257 // Attributes belong to features, not single component geometries 258 // of them. But we're creating a geometry for each component for 259 // better performance and clustering support. Let's call these 260 // "pseudofeatures". 261 // 262 // In order to identify the real feature each geometry belongs to 263 // we then add a 'fid' parameter to the "pseudofeature". 264 // NOTE: 'drupalFID' is only unique within a single layer. 265 newFeature.attributes = feature.attributes; 266 newFeature.data = feature.attributes; 267 newFeature.drupalFID = key; 268 } 269 270 // Add style information 271 if (feature.style) { 272 newFeature.style = jQuery.extend({}, 273 OpenLayers.Feature.Vector.style['default'], 274 feature.style); 275 } 276 277 // Push new features 278 newFeatures.push(newFeature); 279 } 280 } 281 } 282 283 // Add new features if there are any 284 if (newFeatures.length !== 0) { 285 layer.addFeatures(newFeatures); 286 } 287 }, 288 289 'getStyleMap': function(map, layername) { 290 if (map.styles) { 291 var stylesAdded = {}; 292 293 // Grab and map base styles. 294 for (var style in map.styles) { 295 stylesAdded[style] = new OpenLayers.Style(map.styles[style]); 296 } 297 298 // Implement layer-specific styles. First default, then select. 299 if (map.layer_styles !== undefined && map.layer_styles[layername]) { 300 var style = map.layer_styles[layername]; 301 stylesAdded['default'] = new OpenLayers.Style(map.styles[style]); 302 } 303 if (map.layer_styles_select !== undefined && map.layer_styles_select[layern
303ame]) { 304 var style = map.layer_styles_select[layername]; 305 stylesAdded['select'] = new OpenLayers.Style(map.styles[style]); 306 } 307 if (map.layer_styles_temporary !== undefined && map.layer_styles_temporary[layername]) { 308 var style = map.layer_styles_temporary[layername]; 309 stylesAdded['temporary'] = new OpenLayers.Style(map.styles[style]); 310 } 311 312 return new OpenLayers.StyleMap(stylesAdded); 313 } 314 else { 315 return new OpenLayers.StyleMap({ 316 'default': new OpenLayers.Style({ 317 pointRadius: 5, 318 fillColor: '#ffcc66', 319 strokeColor: '#ff9933', 320 strokeWidth: 4, 321 fillOpacity: 0.5 322 }), 323 'select': new OpenLayers.Style({ 324 fillColor: '#66ccff', 325 strokeColor: '#3399ff' 326 }) 327 }); 328 } 329 }, 330 331 'objectFromFeature': function(feature) { 332 var wktFormat = new OpenLayers.Format.WKT(); 333 // Extract geometry either from wkt property or lon/lat properties 334 if (feature.wkt) { 335 return wktFormat.read(feature.wkt); 336 } 337 else if (feature.lon) { 338 return wktFormat.read('POINT(' + feature.lon + ' ' + feature.lat + ')'); 339 } 340 }, 341 342 /** 343 * Add Behavior. 344 * 345 * This is a wrapper around adding behaviors for OpenLayers. 346 * a module does not have to use this, but it helps cut 347 * down on code. 348 * 349 * @param id 350 * The identifier of the behavior that is attached to 351 * the map. 352 * @param attach 353 * The callback function for the attach part of the 354 * Drupal behavior. 355 * @param detach 356 * The callback function for the detach part of the 357 * Drupal behavior. 358 */ 359 'addBehavior': function(id, attach, detach) { 360 // Add as a Drupal behavior. Add a prefix, just to be safe. 361 Drupal.behaviors['openlayers_auto_' + id] = { 362 attach: function (context, settings) { 363 var data = $(context).data('openlayers'); 364 365 // Ensure that there is a map and that the appropriate 366 // behavior exists. Need "data &&" to avoid js crash 367 // when data is empty 368 var localBehavior = data && data.map.behaviors[id]; 369 370 // Ensure scope in the attach callback 371 var that = this; 372 if (localBehavior) { 373 $(context).once('openlayers-' + id, function () { 374 attach.apply(that, [data, data.map.behaviors[id], context, settings]); 375 }); 376 } 377 }, 378 // Maybe we need a little more handling here. 379 detach: detach 380 }; 381 }, 382 383 /** 384 * Add Control. 385 * 386 * This is a wrapper around adding controls to maps. It 387 * is not needed but saves some code. 388 */ 389 'addControl': function(openlayers, controlName, options) { 390 var control = new OpenLayers.Control[controlName](options); 391 openlayers.addControl(control); 392 control.activate(); 393 return control; 394 }, 395 396 /** 397 * Instructs the Proj4js module to make projection definitions available 398 * to Proj4js and thus OpenLayers, too. 399 * 400 * Triggering the initialization of Proj4js this way guarantees projection 401 * definitions are available. 402 */ 403 'loadProjections': function(context, settings){ 404 // Proj4js is not necessarily present as we only load it when OpenLayers 405 // can't handle the projections is use without its help. 406 if(Drupal.behaviors.proj4js){ 407 Drupal.behaviors.proj4js.attach(context, settings); 408 } 409 }, 410 411 /** 412 * Logging implementation that logs using the browser's logging API. 413 * Falls back to doing nothing in case no such API is available. Simulates 414 * the presece of Firebug's console API in Drupal.openlayers.console. 415 */ 416 'console': (function(){ 417 var api = {}; 418 var logger; 419 if(typeof(console)==="object" && typeof(console.log)==="function"){ 420 logger = function(){ 421 // Use console.log as fallback for missing parts of API if present. 422 console.log.apply(console, arguments); 423 } 424 } else { 425 logger = function (){ 426 // Ignore call as no logging facility is available. 427 }; 428 } 429 jQuery(["log", "debug", "info", "warn", "exception", "assert", "dir", 430 "dirxml", "trace", "group", "groupEnd", "groupCollapsed", "profile", 431 "profileEnd", "count", "clear", "time", "timeEnd", "timeStamp", "table", 432 "error"]).each(function(index, functionName){ 433 if(typeof(console)!=="object" || typeof(console[functionName])!=="function"){ 434 // Use fallback as browser does not provide implementation. 435 api[functionName] = logger; 436 } else { 437 api[functionName] = function(){ 438 // Use browsers implementation. 439 console[functionName].apply(console, arguments); 440 }; 441 } 442 }); 443 return api; 444 })() 445}; 446 447Drupal.openlayers.layer = {}; 448})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.