/* 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, '