/* global google */ CKEDITOR.dialog.add( 'googlemaps', function( editor ) { 'use strict'; var numbering = function( id ) { return id + CKEDITOR.tools.getNextNumber(); }, GMapPreview = numbering( 'GMapPreview' ), mapDiv, COLORS = editor.config.googleMaps_overlayColors || [ '#880000', '#008800', '#000088','#888800', '#880088', '#008888', '#000000', '#888888' ] , colorIndex_ = -1 , MarkerColors = editor.config.googleMaps_markerColors || [ 'green', 'purple', 'yellow', 'blue', 'orange', 'red' ] , iconColorIndex_ = -1 , custom_Icons = editor.config.googleMaps_Icons, markers = [], lines = [], areas = [], texts = [], circles = [], activeMarker = null, KmlOverlay = null, Mode = '', map = null, drawingManager, geocoder, overlayMapper, allMapTypes, oParsedMap, oFakeImage, theDialog, weatherLayer, currentPath = 'Default', trafficLayer, transitLayer, bicycleLayer, lang = editor.lang.googlemaps, internalUpdate = true; // true until the map is ready: no sync between dialog and map. A Mutex afterwards if (typeof custom_Icons == 'undefined') { editor.config.googleMaps_Icons = {}; custom_Icons = editor.config.googleMaps_Icons; } // FIX problems due to the CKEditor reset stylesheet var node = CKEDITOR.document.getHead().append( 'style' ); node.setAttribute( 'type', 'text/css' ); var content = ''; content += '#' + GMapPreview + ' * {white-space:normal; cursor:inherit; text-align:inherit;}'; content += '#' + GMapPreview + ' .Input_Text {cursor:text; border-style:inset; border-width:2px; margin-left:1em;}'; content += '#' + GMapPreview + ' .Input_Button {border-style:outset; border-width:2px; margin:0 1em;}'; content += '#' + GMapPreview + ' .Gmaps_Buttons {clear:both; text-align:center; margin-top:4px;}'; content += '#' + GMapPreview + ' .Gmaps_Options td {clear:both}'; content += '#' + GMapPreview + ' a {cursor:pointer}'; // fix issues with Bootstrap content += '#' + GMapPreview + ' img {max-width: none;}'; content += '.GMapsButton {cursor:pointer; background:url("' + editor.plugins.googlemaps.path + 'images/sprite.png") no-repeat top left; width: 24px; height: 24px;}'; content += '.GMapsButtonActive {outline:1px solid #316AC5; background-color:#C1D2EE;}'; content += '.GMapsButton:hover {outline:1px solid #316AC5;}'; content += '.pac-container {z-index:12000;}'; if (CKEDITOR.env.ie && CKEDITOR.env.version >= 9) { // The CKEditor reset of width and size causes a bug in the rendering of lines and areas as they aren't drawn with the correct size // http://code.google.com/p/gmaps-api-issues/issues/detail?id=4343 content += '#' + GMapPreview + ' canvas { width:256px; height:256px; }'; } if (CKEDITOR.env.ie && CKEDITOR.env.version < 11) node.$.styleSheet.cssText = content; else node.$.innerHTML = content; // Define the overlay, derived from google.maps.OverlayView function Label(opt_options) { // Initialization this.setValues(opt_options); var marker = opt_options.marker; if (marker) { this.setValues({ map:marker.getMap() }); this.bindTo('position', marker); this.bindTo('title', marker); } // Label specific var span = this.span_ = document.createElement('span'); span.style.cssText = 'white-space:nowrap; border:1px solid #999; padding:2px; background-color:white'; if (opt_options.className) span.className = opt_options.className; var div = this.div_ = document.createElement('div'); div.appendChild(span); div.style.cssText = 'position: absolute; display: none'; } function InitializeLabels() { Label.prototype = new google.maps.OverlayView; // Implement onAdd Label.prototype.onAdd = function() { var pane = this.getPanes().overlayLayer; pane.appendChild(this.div_); // Ensures the label is redrawn if the title or position is changed. var me = this; this.listeners_ = [ google.maps.event.addListener(this, 'position_changed', function() { me.draw(); }), google.maps.event.addListener(this, 'title_changed', function() { me.draw(); }) ]; }; // Implement onRemove Label.prototype.onRemove = function() { this.div_.parentNode.removeChild(this.div_); // Label is removed from the map, stop updating its position/text. for (var i = 0, I = this.listeners_.length; i < I; ++i) { google.maps.event.removeListener(this.listeners_[i]); } }; // Implement draw Label.prototype.draw = function() { var projection = this.getProjection(), position = projection.fromLatLngToDivPixel(this.get('position')), div = this.div_, title = this.get('title'); div.style.left = position.x + 'px'; div.style.top = position.y + 'px'; div.style.display = 'block'; if (title) this.span_.innerHTML = title.toString(); }; } function initLoader() { if (window.google) { if (window.google.maps && google.maps.drawing && google.maps.geometry && google.maps.weather) { initializeMaps(); return; } if ( window.google.load ) { loadMaps(); return; } } window['CKE_googleMaps_callback'] = function() { initializeMaps(); }; // The Google AJAX loader seems to require an API key. // So we can't use client location var script = document.createElement('script'); script.src = 'https://maps.googleapis.com/maps/api/js?libraries=drawing,geometry,places,weather&callback=CKE_googleMaps_callback'; if (oParsedMap.key) script.src += '&key=' + oParsedMap.key; script.type = 'text/javascript'; document.getElementsByTagName('head')[0].appendChild(script); } function loadMaps() { var key = ''; if (oParsedMap.key) key = '&key=' + oParsedMap.key; window.google.load('maps', '3', { callback:initializeMaps, other_params:'libraries=drawing,geometry,places,weather' + key }); } function initializeMaps() { window['CKE_googleMaps_callback'] = null; InitializeLabels(); mapDiv = document.getElementById( GMapPreview ); UpdateDimensions(); allMapTypes = [ google.maps.MapTypeId.ROADMAP, google.maps.MapTypeId.SATELLITE, google.maps.MapTypeId.HYBRID, google.maps.MapTypeId.TERRAIN ]; internalUpdate = true; var myLatlng = new google.maps.LatLng( oParsedMap.centerLat, oParsedMap.centerLon ); var myOptions = { zoom: parseInt(oParsedMap.zoom, 10), center: myLatlng, mapTypeId: allMapTypes[ oParsedMap.mapType ], heading : oParsedMap.heading, tilt: oParsedMap.tilt }; map = new google.maps.Map(mapDiv, myOptions); if (oParsedMap.panorama) { var pan = oParsedMap.panorama; var panorama = map.getStreetView(); panorama.setVisible(true); panorama.setPov( { heading: pan.heading, pitch: pan.pitch, zoom: pan.zoom }); panorama.setPosition( new google.maps.LatLng(pan.lat, pan.lng) ); } // Creates a drawing manager attached to the map that allows the user to draw // markers, lines, and shapes. var polyOptions = { editable: true }; drawingManager = new google.maps.drawing.DrawingManager({ drawingControl:false, polylineOptions: polyOptions, rectangleOptions: polyOptions, circleOptions: polyOptions, polygonOptions: polyOptions, map: map }); google.maps.event.addListener(drawingManager, 'overlaycomplete', function(e) { if (e.type == google.maps.drawing.OverlayType.POLYLINE) { var polyline = e.overlay; polyline.OverlayType = 'polyline'; lines.push(polyline); } if (e.type == google.maps.drawing.OverlayType.POLYGON) { var polygon = e.overlay; polygon.OverlayType = 'polygon'; areas.push(polygon); } FinishEditOverlay(); }); setMapTypeControl(theDialog.getContentElement('Options', 'cmbMapTypes') ); setZoomControl(theDialog.getContentElement('Options', 'cmbZoomControl') ); setScaleControl(theDialog.getContentElement('Options', 'chkScale') ); setOverviewControl(theDialog.getContentElement('Options', 'chkOverviewMap') ); setOverviewControlOptions( oParsedMap.overviewMapControlOpened ); setWeather(theDialog.getContentElement('Options', 'chkWeather') ); setPath(theDialog.getContentElement('Options','cmbPathType')); // Load the data var i, markerPoints, point, linesData, line, polyline, areasData, area, circlesData, circle, polygon, textsData, points, aLinePoints, j, values; markers = []; lines = []; areas = []; texts = []; circles = []; activeMarker = null; KmlOverlay = null; Mode = ''; markerPoints = oParsedMap.markerPoints; for (i = 0; i < markerPoints.length; i++) { var marker = markerPoints[i]; point = new google.maps.LatLng(parseFloat(marker.lat), parseFloat(marker.lon)); AddMarkerAtPoint(point, marker.text, marker.color, marker.title, marker.maxWidth, false, marker.open); } textsData = oParsedMap.textsData; for (i = 0; i < textsData.length; i++) { point = new google.maps.LatLng(parseFloat(textsData[i].lat), parseFloat(textsData[i].lon)); AddTextAtPoint(point, textsData[i].title, textsData[i].className, false); } linesData = oParsedMap.linesData; for (i = 0; i < linesData.length; i++) { line = linesData[i]; if (line.points) aLinePoints = google.maps.geometry.encoding.decodePath(line.points); else { // parsed from static: points = line.PointsData.split('|'); aLinePoints = []; for (j = 1; j < points.length; j++) { values = points[j].split(','); aLinePoints.push(new google.maps.LatLng(parseFloat(values[0]), parseFloat(values[1])) ); } } polyline = new google.maps.Polyline( { map:map, path: aLinePoints, strokeColor: line.color, strokeOpacity: line.opacity, strokeWeight: line.weight, clickable:false } ); polyline.OverlayType = 'polyline'; lines.push(polyline); } areasData = oParsedMap.areasData; for (i = 0; i < areasData.length; i++) { area = areasData[i]; line = area.polylines[0]; polygon = new google.maps.Polygon( { map:map, paths: google.maps.geometry.encoding.decodePath(line.points), strokeColor: line.color, strokeOpacity: line.opacity, strokeWeight: line.weight, fillColor:area.color, fillOpacity:area.opacity, clickable:false } ); polygon.OverlayType = 'polygon'; areas.push(polygon); } circlesData = oParsedMap.circlesData; for (i = 0; i < circlesData.length; i++) { var circleData = circlesData[i]; point = new google.maps.LatLng(parseFloat(circleData.lat), parseFloat(circleData.lon)); circle = new google.maps.Circle( { map:map, center: point, radius: parseInt(circleData.radius, 10), strokeColor: circleData.color, strokeOpacity: circleData.opacity, strokeWeight: circleData.weight, fillColor:circleData.fillColor, fillOpacity:circleData.fillOpacity, clickable:false } ); circle.OverlayType = 'circle'; circles.push(circle); } onKmlChange(); google.maps.event.addListener(map, 'click', function(point) { if (Mode == 'EditLine') { FinishEditOverlay(); return; } if (Mode == 'AddMarker') AddMarkerAtPoint( point.latLng, editor.config.googleMaps_MarkerText || lang.defaultMarkerText, getIconColor(), lang.defaultTitle, 200, true ); if (Mode == 'AddText') AddTextAtPoint( point.latLng, lang.defaultTitle, 'MarkerTitle', true ); if (Mode == 'AddCircle') { var radius = 250 * Math.pow(2, 15) / Math.pow(2, map.getZoom() ); var circleOptions = drawingManager.get('circleOptions'); circleOptions.strokeColor = getColor( google.maps.drawing.OverlayType.CIRCLE ); circleOptions.strokeOpacity = 0.7; circleOptions.strokeWeight = 2; circleOptions.fillColor = circleOptions.strokeColor; circleOptions.fillOpacity = 0.2; circleOptions.center = point.latLng; circleOptions.radius = radius; circleOptions.map = map; var circle = new google.maps.Circle( circleOptions ); circle.OverlayType = 'circle'; circles.push( circle ); FinishEditOverlay(); } }); google.maps.event.addListener(map,'projection_changed', function() { overlayMapper = new google.maps.OverlayView(); overlayMapper.draw = function() {}; overlayMapper.setMap(map); }); var input = theDialog.getContentElement('Info', 'searchDirection').getInputElement().$; var autocomplete = new google.maps.places.Autocomplete(input); autocomplete.bindTo('bounds', map); google.maps.event.addListener(autocomplete, 'place_changed', function() { searchAddress(); }); internalUpdate = false; if (CKEDITOR.env.ie7Compat) fixIE7display(); if (CKEDITOR.env.ie6Compat) fixIE6display(); } // IE in quirks mode puts the map behind the controls function fixIE6display() { var w = document.getElementById(GMapPreview); w.style.position = 'relative'; w.style.top = '80px'; } // In IE7 or IE8 with compatibility mode, the content just dissapears on initial load, as well as // while hovering the dialog buttons. function fixIE7display() { var w = document.getElementById(GMapPreview); w.style.position = ''; w.parentNode.style.position = 'relative'; window.setTimeout( function() { w.style.position = 'relative'; w.parentNode.style.position = '';}, 0); } function UpdateDimensions() { if (mapDiv) { mapDiv.style.width = theDialog.getValueOf( 'Info', 'txtWidth') + 'px'; mapDiv.style.height = theDialog.getValueOf( 'Info', 'txtHeight') + 'px'; } if (map) google.maps.event.trigger(map, 'resize'); } function setMapTypeControl(obj) { var style; switch (obj.getValue()) { case 'None': style = 'None'; break; case 'Default': style = google.maps.MapTypeControlStyle.DEFAULT; break; case 'Full': style = google.maps.MapTypeControlStyle.HORIZONTAL_BAR; break; case 'Menu': style = google.maps.MapTypeControlStyle.DROPDOWN_MENU; break; } if (style == 'None') map.setOptions({ mapTypeControl:false }); else map.setOptions({ mapTypeControl:true, mapTypeControlOptions : { style: style } }); } function setZoomControl(obj) { var style; switch (obj.getValue()) { case 'None': style = 'None'; break; case 'Default': style = google.maps.ZoomControlStyle.DEFAULT; break; case 'Full': style = google.maps.ZoomControlStyle.ZOOM_PAN; break; case 'Small': style = google.maps.ZoomControlStyle.SMALL; break; } if (style == 'None') map.setOptions({ zoomControl:false, panControl:false, streetViewControl:false, rotateControl:false }); else map.setOptions({ zoomControl:true, panControl:true, streetViewControl:true, rotateControl:true, zoomControlOptions : { style: style } }); } function setWeather(obj) { if ( !weatherLayer ) { var wopts = {}; if (oParsedMap.weatherUsaUnits) { wopts = { temperatureUnits: google.maps.weather.TemperatureUnit.FAHRENHEIT, windSpeedUnits: google.maps.weather.WindSpeedUnit.MILES_PER_HOUR }; } weatherLayer = new google.maps.weather.WeatherLayer(wopts); } weatherLayer.setMap( obj.getValue() ? map : null ); } function setPath(obj) { switch (currentPath) { case 'Default': break; case 'Traffic': trafficLayer.setMap( null ); break; case 'Transit': transitLayer.setMap( null ); break; case 'Bicycle': bicycleLayer.setMap( null ); break; } currentPath = obj.getValue(); switch (currentPath) { case 'Default': break; case 'Traffic': if ( !trafficLayer ) trafficLayer = new google.maps.TrafficLayer(); trafficLayer.setMap( map ); break; case 'Transit': if ( !transitLayer ) transitLayer = new google.maps.TransitLayer(); transitLayer.setMap( map ); break; case 'Bicycle': if ( !bicycleLayer ) bicycleLayer = new google.maps.BicyclingLayer(); bicycleLayer.setMap( map ); break; } } function setScaleControl(obj) { map.setOptions({ scaleControl: obj.getValue() }); } function setOverviewControl(obj) { map.setOptions({ overviewMapControl: obj.getValue() }); } function setOverviewControlOptions( opened ) { map.setOptions({ overviewMapControlOptions: { opened: opened } }); } function getMapTypeIndex() { return CKEDITOR.tools.indexOf( allMapTypes, map.getMapTypeId() ); } function searchAddress() { var address = theDialog.getValueOf('Info', 'searchDirection'); if (!geocoder) geocoder = new google.maps.Geocoder(); geocoder.geocode( { 'address': address }, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { map.setCenter( results[0].geometry.location ); AddMarkerAtPoint( results[0].geometry.location, address, getIconColor(), lang.defaultTitle, 200, false ); } else alert('Geocode was not successful for the following reason: ' + status); }); } function getColor( type ) { if ( editor.config.googleMaps_newOverlayColor ) { if (typeof editor.config.googleMaps_newOverlayColor == 'string') return editor.config.googleMaps_newOverlayColor; // else it is a function return editor.config.googleMaps_newOverlayColor( type ); } colorIndex_ += 1; return COLORS[colorIndex_ % COLORS.length]; } function getIconColor() { if ( editor.config.googleMaps_newMarkerColor ) { if (typeof editor.config.googleMaps_newMarkerColor == 'string') return editor.config.googleMaps_newMarkerColor; // else it is a function return editor.config.googleMaps_newMarkerColor(); } iconColorIndex_ += 1; return MarkerColors[iconColorIndex_ % MarkerColors.length]; } function getIcon( color ) { var data = custom_Icons && custom_Icons[ color ]; if (!data) return 'https://maps.gstatic.com/mapfiles/ms/micons/' + color + '-dot.png'; return data.marker.image; } function getTextIcon() { return editor.plugins.googlemaps.path + 'images/TextIcon.png'; } // Change mode to enable addition of new elements. function setMode( newMode ) { var currentMode = Mode; FinishEditOverlay(); // toggle if ( currentMode == newMode ) { Mode = ''; return; } Mode = newMode; switch (Mode) { case 'AddMarker': changeImage( 'btnAddNewMarker', true ); // Change cursor type setMapCursor('crosshair'); break; case 'AddLine': changeImage( 'btnAddNewLine', true ); var polylineOptions = drawingManager.get('polylineOptions'); polylineOptions.strokeColor = getColor( google.maps.drawing.OverlayType.POLYLINE ); polylineOptions.strokeOpacity = 0.8; polylineOptions.strokeWeight = 4; drawingManager.set('polylineOptions', polylineOptions); drawingManager.setDrawingMode( google.maps.drawing.OverlayType.POLYLINE ); break; case 'AddArea': changeImage( 'btnAddNewArea', true ); var polygonOptions = drawingManager.get('polygonOptions'); polygonOptions.strokeColor = getColor( google.maps.drawing.OverlayType.POLYGON ); polygonOptions.strokeOpacity = 0.7; polygonOptions.strokeWeight = 2; polygonOptions.fillColor = polygonOptions.strokeColor; polygonOptions.fillOpacity = 0.2; drawingManager.set('polygonOptions', polygonOptions); drawingManager.setDrawingMode( google.maps.drawing.OverlayType.POLYGON ); break; case 'AddCircle': changeImage( 'btnAddNewCircle', true ); // Change cursor type setMapCursor('crosshair'); break; case 'AddText': changeImage( 'btnAddNewText', true ); // Change cursor type setMapCursor('crosshair'); break; } } var selectedShape; function clearSelection() { if (selectedShape) { selectedShape.setEditable(false); selectedShape = null; } } // polylines and polygons. Check the OverlayType property. function EnableOverlayEditing(overlay) { overlay.setOptions( { clickable: true } ); google.maps.event.addListener(overlay, 'mouseover', function() { if ( Mode === '' && overlay != selectedShape ) { setMode('EditLine'); selectedShape = overlay; overlay.setEditable(true); } }); google.maps.event.addListener(overlay, 'click', function( latlng ) { if (Mode.substr(0,3) == 'Add') { google.maps.event.trigger(map, 'click', latlng); return; } activeMarker = overlay; editor.openNestedDialog( 'googlemapsLine', function(dialog) { var obj = activeMarker; var data = { strokeColor: obj.strokeColor, strokeOpacity: obj.strokeOpacity.toFixed(1), strokeWeight: obj.strokeWeight }; if (activeMarker.OverlayType == 'polygon' || activeMarker.OverlayType == 'circle') { data.fillColor = obj.fillColor; data.fillOpacity = obj.fillOpacity.toFixed(1); } dialog.setValues( data ); dialog.onRemoveOverlay = DeleteCurrentOverlay; }, function(dialog) { var data = dialog.getValues(); activeMarker.setOptions( data ); } ); }); // Remove vertex with right click on the polygon if (overlay.OverlayType == 'polygon' || overlay.OverlayType == 'polyline') { google.maps.event.addListener(overlay ,'rightclick', function deleteNode( ev ) { if (ev.vertex != null) { // if only 2 points left, remove the line. var min = (overlay.OverlayType == 'polyline') ? 2 : 3; if (overlay.getPath().getLength() == min) { activeMarker = overlay; DeleteCurrentOverlay(); return; } overlay.getPath().removeAt(ev.vertex); } }); } } function DisableOverlayEditing(overlay) { overlay.setOptions( { clickable: false } ); google.maps.event.clearListeners(overlay, 'click'); google.maps.event.clearListeners(overlay, 'rightclick'); google.maps.event.clearListeners(overlay, 'mouseover'); } function setMapCursor(cursor) { map.setOptions({ draggableCursor:cursor }); } function AddMarkerAtPoint( point, text, color, title, maxWidth, interactive, open ) { var marker = new google.maps.Marker( { position:point, map:map, title: title, icon: getIcon(color), draggable: true }) , label; marker.text = text; marker.color = color; marker.maxWidth = maxWidth; if ( title !== '' ) { label = new Label({ marker:marker, className: 'MarkerTitle' }); marker.label = label; } google.maps.event.addListener(marker, 'click', function() { EditMarker(this); }); markers.push( marker ); FinishEditOverlay(); if (interactive) EditMarker( marker ); else { marker.setOptions( { draggable:false } ); if (open) EditMarker( marker ); } } function OpenInfoWindow( obj, text ) { if (!obj._infoWindow) { obj._infoWindow = new google.maps.InfoWindow({ maxWidth:obj.maxWidth }); } obj._infoWindow.setContent( text ); obj._infoWindow.open(map, obj); } function EditMarker( obj ) { if (!obj.getDraggable()) { if (obj.text) OpenInfoWindow(obj, '
' + obj.text + '
'); return; } // We are really editing. activeMarker = obj; Mode = 'EditMarker'; editor.openNestedDialog( 'googlemapsMarker', prepareMarkerTextsDialog, readMarkerTextsDialog ); } function prepareMarkerTextsDialog( dialog ) { dialog.setValues( activeMarker ); dialog.onRemoveMarker = DeleteCurrentMarker; } function readMarkerTextsDialog( dialog ) { var data = dialog.getValues(); activeMarker.text = data.text; activeMarker.maxWidth = data.maxWidth; var title = data.title, label = activeMarker.label; activeMarker.setTitle( title ); activeMarker.color = data.color; activeMarker.setIcon( getIcon(data.color) ); if ( title !== '' ) { if ( !label ) { label = new Label({ marker:activeMarker, className: 'MarkerTitle' }); activeMarker.label = label; } } else { if ( label ) { label.setMap( null ); activeMarker.label = null; } } } function DeleteCurrentMarker() { // Remove it from the global array for ( var j = 0; j < markers.length; j++ ) { if ( markers[j] == activeMarker) { markers.splice(j, 1); break; } } if ( activeMarker.label ) { activeMarker.label.setMap( null ); activeMarker.label = null; } // Remove it from the map activeMarker.setMap( null ); // this will reset activeMarker FinishedEditing(); } function DeleteCurrentOverlay() { var group; switch (activeMarker.OverlayType) { case 'polyline': group = lines; break; case 'polygon': group = areas; break; case 'circle': group = circles; break; } // Remove it from the global array for ( var j = 0; j < group.length; j++ ) { if ( group[j] == activeMarker) { group.splice(j, 1); break; } } DisableOverlayEditing(activeMarker); // Remove it from the map activeMarker.setMap( null ); // this will reset activeMarker FinishedEditing(); } function FinishedEditing() { Mode = ''; activeMarker = null; } function AddTextAtPoint( point, text, className, interactive ) { var marker = new google.maps.Marker( { position:point, map:map, title: text, icon: getTextIcon(), draggable: true }) , label; marker.className = className; if ( text !== '' ) { label = new Label({ marker:marker, className: className }); marker.label = label; } google.maps.event.addListener(marker, 'click', function() { EditText(this); }); texts.push( marker ); FinishEditOverlay(); if (interactive) EditText( marker ); else marker.setVisible(false); } function EditText(obj) { if (!obj.getDraggable()) return; // We are really editing. activeMarker = obj; Mode = 'EditText'; editor.openNestedDialog( 'googlemapsText', function(dialog) { dialog.setValues( { title:obj.get('title') });}, updateTextMarker ); } function updateTextMarker( dialog ) { var data = dialog.getValues(); var title = data.title, label = activeMarker.label; activeMarker.setTitle( title ); if ( title !== '' ) { if ( !label ) { label = new Label( { marker:activeMarker, className: 'MarkerTitle' } ); activeMarker.label = label; } } else { DeleteCurrentText(); } FinishedEditing(); } function DeleteCurrentText() { // Remove it from the global array for ( var j = 0; j < texts.length; j++ ) { if ( texts[j] == activeMarker) { texts.splice(j, 1); break; } } if ( activeMarker.label ) { activeMarker.label.setMap( null ); activeMarker.label = null; } // Remove it from the map activeMarker.setMap( null ); } function changeImage(id, active) { theDialog.getContentElement('Elements', id).getElement()[ active ? 'addClass' : 'removeClass']( 'GMapsButtonActive' ); } function FinishEditOverlay() { // Switch back to non-drawing mode after drawing a shape. if (drawingManager) drawingManager.setDrawingMode(null); clearSelection(); if (Mode === '') return; var line, area, circle; switch (Mode) { case 'AddLine': changeImage( 'btnAddNewLine', false); line = lines[lines.length - 1]; if (line) { line.setEditable(false); EnableOverlayEditing(line); } Mode = ''; break; case 'AddArea': changeImage( 'btnAddNewArea', false); area = areas[areas.length - 1]; if (area) { area.setEditable(false); EnableOverlayEditing(area); } Mode = ''; break; case 'AddMarker': changeImage( 'btnAddNewMarker', false); // Change cursor type setMapCursor(''); break; case 'AddCircle': changeImage( 'btnAddNewCircle', false); circle = circles[circles.length - 1]; if (circle) { circle.setEditable(false); EnableOverlayEditing(circle); } Mode = ''; // Change cursor type setMapCursor(''); break; case 'AddText': changeImage( 'btnAddNewText', false); // Change cursor type setMapCursor(''); break; case 'EditMarker': FinishedEditing(); break; case 'EditText': FinishedEditing(); break; case 'EditLine': FinishedEditing(); break; default: break; } } function DisableEditing() { var i; for (i = 0; i < markers.length; i++) markers[i].setOptions({ draggable:false }); for (i = 0; i < texts.length; i++) texts[i].setVisible(false); for (i = 0; i < lines.length; i++) DisableOverlayEditing( lines[i] ); for (i = 0; i < areas.length; i++) DisableOverlayEditing( areas[i] ); for (i = 0; i < circles.length; i++) DisableOverlayEditing( circles[i] ); } function EnableEditing() { var i; for (i = 0; i < markers.length; i++) markers[i].setOptions({ draggable:true }); for (i = 0; i < texts.length; i++) texts[i].setVisible(true); for (i = 0; i < lines.length; i++) EnableOverlayEditing( lines[i] ); for (i = 0; i < areas.length; i++) EnableOverlayEditing( areas[i] ); for (i = 0; i < circles.length; i++) EnableOverlayEditing( circles[i] ); } function EncodeLineData(polyline, forArea) { var o = { color : polyline.strokeColor, opacity : polyline.strokeOpacity, weight : polyline.strokeWeight }, path = polyline.getPath(), length = path.getLength(); if (length < 2) return null; // Static areas by default are created with just the "minimum" points, and they are properly closed on a live map // but they appear with the last segment missing on the static image, so let's copy the first point in this case if ( forArea ) { var first = path.getAt(0), last = path.getAt(length - 1); if (!first.equals(last)) path.push(first); } o.points = google.maps.geometry.encoding.encodePath(path); return o; } function resolveUrl(url) { // Resolve the url so it becomes a full URL including the host var img = document.createElement('IMG'); img.src = url; url = img.src; img = null; return url; } function onKmlChange(fileUrl) { var oKmlUrl = theDialog.getContentElement('Elements', 'txtKMLUrl'); if (!oKmlUrl) return; var url = (fileUrl && typeof(fileUrl) == 'string') ? fileUrl : oKmlUrl.getValue(); if (oKmlUrl.lastUrl == url) return; oKmlUrl.lastUrl = url; if (KmlOverlay) { if (KmlOverlay.url == url) return; KmlOverlay.setMap( null ); KmlOverlay = null; } if ( !url ) return; var match = /:\/\/(.*?)\//.exec(url), newUrl; if (!match) { url = resolveUrl(url); newUrl = true; match = /:\/\/(.*?)\//.exec(url); } if (match[1].indexOf('.') == -1) { // The function is called while CKFinder is open, so we have to delay it window.setTimeout( function() { alert('Error: You must provide a public server in the url of KML files.'); }, 500); oKmlUrl.lastUrl = ''; return false; } KmlOverlay = new google.maps.KmlLayer(url, { map:map }); if (newUrl) { oKmlUrl.lastUrl = url; oKmlUrl.setValue( url ); theDialog.showPage('Elements'); var div = theDialog.getContentElement('Elements', 'KMLContainer').getElement(); div.show(); } } function RefreshSize() { var contents = theDialog.parts.contents, Wrapper = document.getElementById('Wrapper' + GMapPreview); Wrapper.style.width = contents.$.style.width; Wrapper.style.height = (parseInt(contents.$.style.height, 10) - 90) + 'px'; } return { title : lang.title, minWidth : 500, minHeight : 460, onLoad : function() { theDialog = this; // Act on tab switching theDialog.on('selectPage', function(e) { if (CKEDITOR.env.ie7Compat) fixIE7display(); if (e.data.page == 'Elements') { EnableEditing(); } else { FinishEditOverlay(); DisableEditing(); } }); theDialog.on('resize', RefreshSize); // Adjust the contents so they take only the top of the dialog and add a bottom div // that will show the map in all the tabs var contents = this.parts.contents; var children = contents.getChildren(); for (var i = children.count() - 1; i >= 0; i--) { var div = children.getItem(i); div.$.style.height = '88px'; } contents.appendHtml('
'); // It flickers, but at least the map doesn't go away // I can't find any other solution for the moment. if (CKEDITOR.env.ie7Compat) { this.parts.footer.on( 'mousemove', fixIE7display); this.parts.footer.on( 'mouseleave', function() { window.setTimeout(fixIE7display, 0);} ); } }, onShow : function() { Mode = ''; internalUpdate = true; oFakeImage = null; oParsedMap = null; var handler = editor.plugins.googleMapsHandler; // Try to detect a map var fakeImage = this.getSelectedElement(); if ( fakeImage ) { oFakeImage = fakeImage.$; var mapNumber = oFakeImage.getAttribute( 'mapnumber' ); if ( mapNumber ) { oParsedMap = handler.getMap( mapNumber ); oParsedMap && oParsedMap.updateDimensions( oFakeImage ); } if (!oParsedMap) { if (!handler.isStaticImage( oFakeImage )) oFakeImage = null; else { oParsedMap = handler.createNew(); oParsedMap.parseStaticMap2( oFakeImage ); // this way it will be recreated, // first we select the first div that we usually create and select. // then on OK the structure will be recreated overwriting the existing one if (oFakeImage.parentNode.nodeName == 'DIV' && !oFakeImage.previousSibling && !oFakeImage.nextSibling) { oFakeImage = oFakeImage.parentNode; if (editor.config.googleMaps_WrapperClass && oFakeImage.parentNode.nodeName == 'DIV' && oFakeImage.parentNode.className == editor.config.googleMaps_WrapperClass) oFakeImage = oFakeImage.parentNode; editor.getSelection().selectElement( new CKEDITOR.dom.element( oFakeImage ) ); } oFakeImage = null; } } } if ( !oParsedMap ) { oParsedMap = handler.createNew(); // Try W3C Geolocation if we are creating a new map and no default has been set if (navigator.geolocation && !editor.config.googleMaps_CenterLat) { // Request location only once. Next time use the localstorage if (localStorage.mapsCenter) { var point = JSON.parse(localStorage.mapsCenter); oParsedMap.centerLat = point.lat; oParsedMap.centerLon = point.lng; } else { navigator.geolocation.getCurrentPosition( function(position) { oParsedMap.centerLat = position.coords.latitude.toFixed(5); oParsedMap.centerLon = position.coords.longitude.toFixed(5); localStorage.mapsCenter = JSON.stringify({ lat: oParsedMap.centerLat, lng: oParsedMap.centerLon }); if (!map) return; map.setCenter( new google.maps.LatLng(oParsedMap.centerLat, oParsedMap.centerLon) ); }); } } } theDialog.setValueOf( 'Info', 'txtWidth', oParsedMap.width); // theDialog.setValueOf( 'Info', 'chkResponsive', oParsedMap.responsive); theDialog.setValueOf( 'Info', 'txtHeight', oParsedMap.height); theDialog.setValueOf( 'Options', 'cmbGeneratedType', oParsedMap.generatedType); theDialog.setValueOf( 'Options', 'cmbZoomControl', oParsedMap.zoomControl); theDialog.setValueOf( 'Options', 'cmbMapTypes', oParsedMap.mapTypeControl); theDialog.setValueOf( 'Options', 'chkScale', oParsedMap.scaleControl); theDialog.setValueOf( 'Options', 'chkOverviewMap', oParsedMap.overviewMapControl); theDialog.setValueOf( 'Options', 'chkWeather', oParsedMap.weather); theDialog.setValueOf( 'Options', 'cmbPathType', oParsedMap.pathType); var oKmlUrl = theDialog.getContentElement('Elements', 'txtKMLUrl'); if (oKmlUrl) oKmlUrl.setValue( oParsedMap.kmlOverlay); // Init the map initLoader(); }, onOk : function() { oParsedMap.width = theDialog.getValueOf('Info', 'txtWidth'); // oParsedMap.responsive = theDialog.getValueOf('Info', 'chkResponsive'); oParsedMap.height = theDialog.getValueOf('Info', 'txtHeight'); oParsedMap.zoom = map.getZoom(); var point = map.getCenter(); oParsedMap.centerLat = point.lat().toFixed(5); oParsedMap.centerLon = point.lng().toFixed(5); oParsedMap.tilt = map.getTilt(); oParsedMap.heading = map.getHeading(); oParsedMap.mapType = getMapTypeIndex(); oParsedMap.generatedType = parseInt(theDialog.getValueOf('Options', 'cmbGeneratedType'), 10); oParsedMap.zoomControl = theDialog.getValueOf('Options', 'cmbZoomControl'); oParsedMap.mapTypeControl = theDialog.getValueOf('Options', 'cmbMapTypes'); oParsedMap.scaleControl = theDialog.getValueOf('Options', 'chkScale'); oParsedMap.overviewMapControl = theDialog.getValueOf('Options', 'chkOverviewMap'); oParsedMap.overviewMapControlOpened = map.overviewMapControlOptions.opened; oParsedMap.weather = theDialog.getValueOf('Options', 'chkWeather'); oParsedMap.pathType = theDialog.getValueOf('Options', 'cmbPathType'); var panorama = map.getStreetView(); if (panorama.getVisible()) { var oPan = {}; point = panorama.getPosition(); oPan.lat = point.lat().toFixed(7); oPan.lng = point.lng().toFixed(7); var pov = panorama.getPov(); oPan.heading = pov.heading; oPan.pitch = pov.pitch; oPan.zoom = pov.zoom; oParsedMap.panorama = oPan; } else oParsedMap.panorama = null; var markerPoints = [], textsData = [], linesData = [], line, areasData = [], area, circlesData = [], circle, i; for (i = 0; i < markers.length; i++) { var marker = markers[i]; point = marker.getPosition(); markerPoints.push({ lat: point.lat().toFixed(5), lon: point.lng().toFixed(5), text:marker.text, color:marker.color, title:marker.get('title'), maxWidth:marker.maxWidth, open:(marker._infoWindow && marker._infoWindow.map) }); } oParsedMap.markerPoints = markerPoints; for (i = 0; i < texts.length; i++) { point = texts[i].getPosition(); textsData.push({ lat: point.lat().toFixed(5), lon: point.lng().toFixed(5), title:texts[i].get('title'), className:texts[i].className }); } oParsedMap.textsData = textsData; for (i = 0; i < lines.length; i++) { line = EncodeLineData( lines[i], false ); if (line) linesData.push( line ); } oParsedMap.linesData = linesData; for (i = 0; i < areas.length; i++) { area = { polylines:[] }; // find polylines area.polylines.push( EncodeLineData( areas[i], true ) ); if (area.polylines[0]) { area.color = areas[i].fillColor; area.opacity = areas[i].fillOpacity; areasData.push( area ); } } oParsedMap.areasData = areasData; for (i = 0; i < circles.length; i++) { circle = circles[i]; circlesData.push( { color : circle.strokeColor, opacity : circle.strokeOpacity, weight : circle.strokeWeight, fillColor : circle.fillColor, fillOpacity : circle.fillOpacity, radius : circle.radius.toFixed(0), lat: circle.center.lat().toFixed(5), lon: circle.center.lng().toFixed(5) } ); } oParsedMap.circlesData = circlesData; var oKmlUrl = theDialog.getContentElement('Elements', 'txtKMLUrl'); if (oKmlUrl) oParsedMap.kmlOverlay = oKmlUrl.getValue(); if ( !oFakeImage ) oFakeImage = oParsedMap.createHtmlElement(); oParsedMap.updateHTMLElement( oFakeImage ); }, onHide : function() { var i, marker; // Destroy map. V3 doesn't have a "map.unload()" for (i = 0; i < markers.length; i++) { marker = markers[i]; marker.setMap( null ); if (marker.label) { marker.label.setMap(null); marker.label = null; } } for (i = 0; i < texts.length; i++) { marker = texts[i]; marker.setMap( null ); if (marker.label) { marker.label.setMap(null); marker.label = null; } } for (i = 0; i < lines.length; i++) { lines[i].setMap( null ); } for (i = 0; i < areas.length; i++) { areas[i].setMap( null ); } if (KmlOverlay) { KmlOverlay.setMap( null ); KmlOverlay = null; } google.maps.event.clearInstanceListeners( map ); map = null; internalUpdate = true; }, contents : [ { id : 'Info', label : lang.map, elements : [ { type : 'hbox', widths : [ '115px', '115px', '240px' ], children : [ { id : 'txtWidth', type : 'text', widths : [ '55px', '60px' ], width: '50px', labelLayout : 'horizontal', label : lang.width, onBlur : UpdateDimensions, required : true }, { id : 'txtHeight', type : 'text', widths : [ '55px', '60px' ], width: '50px', labelLayout : 'horizontal', label : lang.height, onBlur : UpdateDimensions, required : true }, /* { id : 'chkResponsive', type : 'checkbox', labelLayout : 'horizontal', label : lang.responsive, onChange : function(o) { if (internalUpdate) return; // nothing to do on preview } }, */ { // Padding at the right type : 'html', html : '
' } ] }, { type : 'hbox', widths : [ '340px', '100px', '' ], children : [ { id : 'searchDirection', type : 'text', label : lang.searchDirection, labelLayout : 'horizontal', onKeyup: function( evt ) { if ( evt.data.getKeystroke() == 13 ) { searchAddress(); evt.stop(); return false; } }, // extra code due to #7516 onKeydown: function( evt ) { if ( evt.data.getKeystroke() == 13 ) { evt.stop(); evt.data.preventDefault( true ); evt.data.stopPropagation(); return false; } } }, { id : 'btnSearch', type : 'button', align : 'center', label : lang.search, onClick : searchAddress } ] } ] }, { id : 'Options', label : lang.options, elements : [ { type : 'hbox', widths : [ '180px', '100px', '100px', '0' ], children : [ { id : 'cmbGeneratedType', type : 'select', labelLayout : 'horizontal', label : lang.loadMap, items : [ [ lang.onlyStatic, '1' ], [ lang.onClick, '2' ], [ lang.onLoad, '3' ], [ lang.byScript, '4' ] ] }, { id : 'chkScale', type : 'checkbox', labelLayout : 'horizontal', label : lang.scale, onChange : function() { if (internalUpdate) return; setScaleControl(this); } }, { id : 'chkOverviewMap', type : 'checkbox', labelLayout : 'horizontal', label : lang.overview, onChange : function() { if (internalUpdate) return; setOverviewControl(this); } }, { id : 'cmbMapTypes', hidden: true, type : 'select', labelLayout : 'horizontal', label : lang.mapTypes, onChange : function() { if (internalUpdate) return; internalUpdate = true; setMapTypeControl(this); internalUpdate = false; }, items : [ [ lang.none, 'None' ], [ lang.Default, 'Default' ], [ lang.mapTypesFull, 'Full' ], [ lang.mapTypesMenu, 'Menu' ] ] } ] }, { type : 'hbox', widths : [ '180px', '100px', '100px', '0' ], children : [ { id : 'cmbPathType', type : 'select', labelLayout : 'horizontal', label : lang.paths, onChange : function() { if (internalUpdate) return; internalUpdate = true; setPath(this); internalUpdate = false; }, items : [ [ lang.Default, 'Default' ], [ lang.traffic, 'Traffic' ], [ lang.transit, 'Transit' ], [ lang.bicycle, 'Bicycle' ] ] }, { id : 'chkWeather', type : 'checkbox', labelLayout : 'horizontal', label : lang.weather, onChange : function() { if (internalUpdate) return; setWeather(this); } }, { id : 'cmbZoomControl', hidden: true, type : 'select', labelLayout : 'horizontal', label : lang.zoomControl + ' ', items : [ [ lang.none, 'None' ], [ lang.Default, 'Default' ], [ lang.smallZoom, 'Small' ], [ lang.fullZoom, 'Full' ] ], onChange : function() { if (internalUpdate) return; internalUpdate = true; setZoomControl(this); internalUpdate = false; } }, { // Padding at the right type : 'html', html : '
' } ] } ] }, { id : 'Elements', label : lang.elements, elements : [ { type : 'hbox', widths : [ '26px', '26px', '26px', '26px', '26px', '338px' ], children : [ { type : 'html', id : 'btnAddNewMarker', onClick: function() { setMode('AddMarker'); }, html : '
' + '
' }, { type : 'html', id : 'btnAddNewLine', onClick: function() { setMode('AddLine'); }, html : '
' + '
' }, { type : 'html', id : 'btnAddNewArea', onClick: function() { setMode('AddArea'); }, html : '
' + '
' }, { type : 'html', id : 'btnAddNewCircle', onClick: function() { setMode('AddCircle'); }, html : '
' + '
' }, { type : 'html', id : 'btnAddNewText', onClick: function() { setMode('AddText'); }, html : '
' + '
' }, { type : 'html', id : 'btnKmlToggle', onClick: function() { // Toggle visibility var div = theDialog.getContentElement('Elements', 'KMLContainer').getElement(); if (div.$.style.display == 'none' ) div.show(); else div.hide(); }, html : '
' + '
' }, { //padding at the right type : 'html', html : '
 
' } ] }, { id : 'KMLContainer', type : 'hbox', widths : [ '250px' ], hidden : true, children : [ { id : 'txtKMLUrl', type : 'text', labelLayout : 'horizontal', label : lang.kmlUrl, onBlur: onKmlChange, onKeyup: function( evt ) { if ( evt.data.getKeystroke() == 13 ) { onKmlChange(); evt.stop(); return false; } }, // extra code due to #7516 onKeydown: function( evt ) { if ( evt.data.getKeystroke() == 13 ) { evt.stop(); evt.data.preventDefault( true ); evt.data.stopPropagation(); return false; } }, validate: function() { var url = this.getValue(); if ( !url ) return true; var match = /:\/\/(.*?)\//.exec(url); if (!match || match[1].indexOf('.') == -1) { alert('Error: You must provide a public server in the url of KML files.'); return false; } } }, { type : 'button', id : 'browse', hidden : true, filebrowser : { action : 'Browse', target: 'Elements:txtKMLUrl', url: editor.config.filebrowserKmlBrowseUrl || editor.config.filebrowserBrowseUrl, onSelect : onKmlChange }, label : editor.lang.common.browseServer } ] } ] } ] }; } );