/* $Id: gmap.js,v 1.5 2008/10/29 17:39:41 bdragon Exp $ */ /** * Drupal to Google Maps API bridge. */ // GMap overseer singleton Drupal.gmap = new function() { var _handlers = {}; var _maps = {}; /** * Retrieve a map object for use by a non-widget. * Use this if you need to be able to fire events against a certain map * which you have the mapid for. * Be a good GMap citizen! Remember to send change()s after modifying variables! */ this.getMap = function(mapid) { return _maps[mapid]; }; this.unloadMap = function(mapid) { delete _maps[mapid]; }; this.addHandler = function(handler,callback) { if (!_handlers[handler]) { _handlers[handler] = []; } _handlers[handler].push(callback); }; this.globalChange = function(name,userdata) { for (var mapid in Drupal.settings.gmap) { _maps[mapid].change(name,-1,userdata); } }; this.setup = function() { var obj = this; var initcallback = function(mapid) { return (function() { _maps[mapid].change("bootstrap_options",-1); _maps[mapid].change("boot",-1); _maps[mapid].change("init",-1); // Send some changed events to fire up the rest of the initial settings.. _maps[mapid].change("maptypechange",-1); _maps[mapid].change("controltypechange",-1); _maps[mapid].change("alignchange",-1); // Set ready to put the event system into action. _maps[mapid].ready = true; _maps[mapid].change("ready",-1); }); }; if (Drupal.settings && Drupal.settings.gmap) { var mapid = obj.id.split('-'); var instanceid = mapid.pop(); mapid.shift(); mapid.join('-'); var control = instanceid.replace(/\d+$/, ''); // Lazy init the map object. if (!_maps[mapid]) { _maps[mapid] = new Drupal.gmap.map(Drupal.settings.gmap[mapid]); // Prepare the initialization callback. var callback = initcallback(mapid); setTimeout(callback, 0); } if (_handlers[control]) { for (var i=0; i<_handlers[control].length; i++) { _handlers[control][i].call(_maps[mapid], obj); } } else { // Element with wrong class? } } }; }(); Drupal.gmap.factory = {}; Drupal.gmap.map = function(v) { this.vars = v; this.map = undefined; this.ready = false; var _bindings = {}; /** * Register interest in a change. */ this.bind = function(name,callback) { if (!_bindings[name]) { _bindings[name] = []; } return _bindings[name].push(callback) - 1; }; /** * Change notification. * Interested parties can act on changes. */ this.change = function(name,id,userdata) { var c; if (_bindings[name]) { for (c = 0; c < _bindings[name].length; c++) { if (c != id) { _bindings[name][c](userdata); } } } if (name != 'all') { this.change('all',-1,name,userdata); } }; /** * Deferred change notification. * This will cause a change notification to be tacked on to the *end* of the event queue. */ this.deferChange = function(name,id,userdata) { var obj = this; // This will move the function call to the end of the event loop. setTimeout(function(){ obj.change(name,id,userdata); }, 0); }; }; //////////////////////////////////////// // Map widget // //////////////////////////////////////// Drupal.gmap.addHandler('gmap',function(elem) { var obj = this; var _ib = {}; // Respond to incoming zooms _ib.zoom = obj.bind("zoom",function(){ obj.map.setZoom(obj.vars.zoom); }); // Respond to incoming moves _ib.move = obj.bind("move", function(){ obj.map.panTo(new GLatLng(obj.vars.latitude,obj.vars.longitude)); }); // Respond to incoming map type changes _ib.mtc = obj.bind("maptypechange", function() { var i; for (i = 0; i < obj.opts.mapTypeNames.length; i++) { if (obj.opts.mapTypeNames[i] == obj.vars.maptype) { obj.map.setMapType(obj.opts.mapTypes[i]); break; } } }); // Respond to incoming width changes. _ib.width = obj.bind("widthchange",function(w){ obj.map.getContainer().style.width = w; obj.map.checkResize(); }); // Send out outgoing width changes. // N/A // Respond to incoming height changes. _ib.height = obj.bind("heightchange",function(h){ obj.map.getContainer().style.height = h; obj.map.checkResize(); }); // Send out outgoing height changes. // N/A // Respond to incoming control type changes. _ib.ctc = obj.bind("controltypechange",function() { if(obj.currentcontrol) { obj.map.removeControl(obj.currentcontrol); } if (obj.vars.controltype=='Micro') {obj.map.addControl(obj.currentcontrol = new GSmallZoomControl());} if (obj.vars.controltype=='Small') {obj.map.addControl(obj.currentcontrol = new GSmallMapControl());} if (obj.vars.controltype=='Large') {obj.map.addControl(obj.currentcontrol = new GLargeMapControl());} }); // Send out outgoing control type changes. // N/A obj.bind("bootstrap_options", function() { // Bootup options. var opts = {}; // Object literal GMapOptions obj.opts = opts; // Null out the enabled types. opts.mapTypes = []; opts.mapTypeNames = []; // Load google map types. if (obj.vars.baselayers['Map']) { opts.mapTypes.push(G_NORMAL_MAP); opts.mapTypeNames.push('Map'); } if (obj.vars.baselayers['Satellite']) { opts.mapTypes.push(G_SATELLITE_MAP); opts.mapTypeNames.push('Satellite'); } if (obj.vars.baselayers['Hybrid']) { opts.mapTypes.push(G_HYBRID_MAP); opts.mapTypeNames.push('Hybrid'); } if (obj.vars.baselayers['Physical']) { opts.mapTypes.push(G_PHYSICAL_MAP); opts.mapTypeNames.push('Physical'); } }); obj.bind("boot", function() { obj.map = new GMap2(elem, obj.opts); }); obj.bind("init",function() { var map = obj.map; // Map type control if (obj.vars.mtc == 'standard') { map.addControl(new GMapTypeControl()); } else if (obj.vars.mtc == 'hier') { map.addControl(new GHierarchicalMapTypeControl()); } else if (obj.vars.mtc == 'menu') { map.addControl(new GMenuMapTypeControl()); } if (obj.vars.behavior.overview) { map.addControl(new GOverviewMapControl()); } if (obj.vars.behavior.scale) { map.addControl(new GScaleControl()); } if (obj.vars.behavior.nodrag) { map.disableDragging(); } else if (!obj.vars.behavior.nokeyboard) { obj._kbdhandler = new GKeyboardHandler(map); } if (obj.vars.extent) { var c = obj.vars.extent; var extent = new GLatLngBounds(new GLatLng(c[0][0], c[0][1]), new GLatLng(c[1][0], c[1][1])); obj.vars.latitude = extent.getCenter().lat(); obj.vars.longitude = extent.getCenter().lng(); obj.vars.zoom = map.getBoundsZoomLevel(extent); } if (obj.vars.behavior.collapsehack) { // Modify collapsable fieldsets to make maps check dom state when the resize handle // is clicked. This may not necessarily be the correct thing to do in all themes, // hence it being a behavior. setTimeout(function(){ var r = function() { map.checkResize(); map.setCenter(new GLatLng(obj.vars.latitude,obj.vars.longitude), obj.vars.zoom); }; $(elem).parents('fieldset.collapsible').children('legend').children('a').click(r); // Would be nice, but doesn't work. //$(elem).parents('fieldset.collapsible').children('.fieldset-wrapper').scroll(r); },0); } map.setCenter(new GLatLng(obj.vars.latitude,obj.vars.longitude), obj.vars.zoom); if (!obj.vars.nocontzoom) { map.enableDoubleClickZoom(); map.enableContinuousZoom(); } if (!obj.vars.nomousezoom) { map.enableScrollWheelZoom(); } // Send out outgoing zooms GEvent.addListener(map, "zoomend", function(oldzoom,newzoom) { obj.vars.zoom = newzoom; obj.change("zoom", _ib.zoom); }); // Send out outgoing moves GEvent.addListener(map,"moveend",function() { var coord = map.getCenter(); obj.vars.latitude = coord.lat(); obj.vars.longitude = coord.lng(); obj.change("move", _ib.move); }); // Send out outgoing map type changes. GEvent.addListener(map,"maptypechanged",function() { // If the map isn't ready yet, ignore it. if (obj.ready) { var type = map.getCurrentMapType(); var i; for (i = 0; i < obj.opts.mapTypes.length; i++) { if (obj.opts.mapTypes[i] == type) { obj.vars.maptype = obj.opts.mapTypeNames[i]; } } obj.change("maptypechange", _ib.mtc); } }); }); }); //////////////////////////////////////// // Zoom widget // //////////////////////////////////////// Drupal.gmap.addHandler('zoom', function(elem) { var obj = this; // Respond to incoming zooms var binding = obj.bind("zoom", function(){ elem.value = obj.vars.zoom; }); // Send out outgoing zooms $(elem).change(function() { obj.vars.zoom = parseInt(elem.value, 10); obj.change("zoom", binding); }); }); //////////////////////////////////////// // Latitude widget // //////////////////////////////////////// Drupal.gmap.addHandler('latitude', function(elem) { var obj = this; // Respond to incoming movements. var binding = obj.bind("move", function(){ elem.value = '' + obj.vars.latitude; }); // Send out outgoing movements. $(elem).change(function() { obj.vars.latitude = Number(this.value); obj.change("move", binding); }); }); //////////////////////////////////////// // Longitude widget // //////////////////////////////////////// Drupal.gmap.addHandler('longitude', function(elem) { var obj = this; // Respond to incoming movements. var binding = obj.bind("move", function(){ elem.value = '' + obj.vars.longitude; }); // Send out outgoing movements. $(elem).change(function() { obj.vars.longitude = Number(this.value); obj.change("move", binding); }); }); //////////////////////////////////////// // Latlon widget // //////////////////////////////////////// Drupal.gmap.addHandler('latlon', function(elem) { var obj = this; // Respond to incoming movements. var binding = obj.bind("move", function(){ elem.value = '' + obj.vars.latitude + ',' + obj.vars.longitude; }); // Send out outgoing movements. $(elem).change(function() { var t = this.value.split(','); obj.vars.latitude = Number(t[0]); obj.vars.longitude = Number(t[1]); obj.change("move", binding); }); }); //////////////////////////////////////// // Maptype widget // //////////////////////////////////////// Drupal.gmap.addHandler('maptype', function(elem) { var obj = this; // Respond to incoming movements. var binding = obj.bind("maptypechange", function(){ elem.value = obj.vars.maptype; }); // Send out outgoing movements. $(elem).change(function() { obj.vars.maptype = elem.value; obj.change("maptypechange", binding); }); }); (function() { // BEGIN CLOSURE var re = /([0-9.]+)\s*(em|ex|px|in|cm|mm|pt|pc|%)/; var normalize = function(str) { var ar; if ((ar = re.exec(str.toLowerCase()))) { return ar[1] + ar[2]; } return null; }; //////////////////////////////////////// // Width widget // //////////////////////////////////////// Drupal.gmap.addHandler('width', function(elem) { var obj = this; // Respond to incoming width changes. var binding = obj.bind("widthchange", function(w){ elem.value = normalize(w); }); // Send out outgoing width changes. $(elem).change(function() { var n; if ((n = normalize(elem.value))) { elem.value = n; obj.change('widthchange', binding, n); } }); obj.bind('init',function(){ $(elem).change(); }); }); //////////////////////////////////////// // Height widget // //////////////////////////////////////// Drupal.gmap.addHandler('height', function(elem) { var obj = this; // Respond to incoming height changes. var binding = obj.bind("heightchange",function(h){ elem.value = normalize(h); }); // Send out outgoing height changes. $(elem).change(function() { var n; if ((n = normalize(elem.value))) { elem.value = n; obj.change('heightchange', binding, n); } }); obj.bind('init',function(){ $(elem).change(); }); }); })(); // END CLOSURE //////////////////////////////////////// // Control type widget // //////////////////////////////////////// Drupal.gmap.addHandler('controltype', function(elem) { var obj = this; // Respond to incoming height changes. var binding = obj.bind("controltypechange", function(){ elem.value = obj.vars.controltype; }); // Send out outgoing height changes. $(elem).change(function() { obj.vars.controltype = elem.value; obj.change("controltypechange", binding); }); }); // Map cleanup. if (Drupal.jsEnabled) { $(document).unload(GUnload); } Drupal.behaviors.GMap = function (context) { $('.gmap-control:not(.gmap-processed)', context).addClass('gmap-processed').each(Drupal.gmap.setup); };