// -----------------------------------------------------------------------------
// Based on
// github.com/angular-ui/angular-ui/tree/fe66f1890fc9f62bffe661bf2fa756a05d5d0b81
// 05 Sept 2012
// -----------------------------------------------------------------------------
(function () {
var app = angular.module('ui.directives');
// Setup map events from a google map object to trigger on a given element,
// then just use ui-event to catch events from an element
function bindMapEvents(scope, eventsStr, googleObject, element) {
angular.forEach(eventsStr.split(' '), function (eventName) {
//Prefix all googlemap events with 'map-', so eg 'click'
//for the googlemap doesn't interfere with a normal 'click' event
var $event = { type: 'map-' + eventName };
google.maps.event.addListener(googleObject, eventName, function (evt) {
element.trigger(angular.extend({}, $event, evt));
// We create an $apply if it isn't happening--needs better support.
// Don't want to use timeout because tons of these events fire at once,
// and we only need one $apply
if (!scope.$$phase) scope.$apply();
});
});
}
app.directive('uiMap',
['ui.config', '$parse', function (uiConfig, $parse) {
var mapEvents = 'bounds_changed center_changed click dblclick ' +
'drag dragend dragstart heading_changed idle maptypeid_changed ' +
'mousemove mouseout mouseover projection_changed resize rightclick ' +
'tilesloaded tilt_changed zoom_changed';
var options = uiConfig.map || {};
return {
restrict: 'A',
//doesn't work as E for unknown reason
link: function (scope, elm, attrs) {
var opts = angular.extend({}, options, scope.$eval(attrs.uiOptions));
var map = new google.maps.Map(elm[0], opts);
var model = $parse(attrs.uiMap);
//Set scope variable for the map
model.assign(scope, map);
bindMapEvents(scope, mapEvents, map, elm);
}
};
}]);
app.directive('uiMapInfoWindow',
['ui.config', '$parse', '$compile', function (uiConfig, $parse, $compile) {
var infoWindowEvents = 'closeclick content_change domready ' +
'position_changed zindex_changed';
var options = uiConfig.mapInfoWindow || {};
return {
link: function (scope, elm, attrs) {
var opts = angular.extend({}, options, scope.$eval(attrs.uiOptions));
opts.content = elm[0];
var model = $parse(attrs.uiMapInfoWindow);
var infoWindow = model(scope);
if (!infoWindow) {
infoWindow = new google.maps.InfoWindow(opts);
model.assign(scope, infoWindow);
}
bindMapEvents(scope, infoWindowEvents, infoWindow, elm);
// Don't need infowindow's template on the DOM anymore--now stored in
// google maps. So we just replace the old element with an empty div.
//!\\ DO NOT remove it altogether because that can mess up angular
elm.replaceWith('<div></div>');
//Decorate infoWindow.open to $compile contents before opening
var _open = infoWindow.open;
infoWindow.open = function open(a1, a2, a3, a4, a5, a6) {
$compile(elm.contents())(scope);
_open.call(infoWindow, a1, a2, a3, a4, a5, a6);
};
}
};
}]);
// Map overlay directives all work the same. Take map marker for example
// <ui-map-marker="myMarker"> will $watch 'myMarker' and each time it changes,
// it will hook up myMarker's events to the directive dom element. Then
// ui-event will be able to catch all of myMarker's events. Super simple.
function mapOverlayDirective(directiveName, events) {
app.directive(directiveName, [function () {
return {
restrict: 'A',
link: function (scope, elm, attrs) {
scope.$watch(attrs[directiveName], function (newObject) {
bindMapEvents(scope, events, newObject, elm);
});
}
};
}]);
}
mapOverlayDirective('uiMapMarker',
'animation_changed click clickable_changed cursor_changed ' +
'dblclick drag dragend draggable_changed dragstart flat_changed icon_changed ' +
'mousedown mouseout mouseover mouseup position_changed rightclick ' +
'shadow_changed shape_changed title_changed visible_changed zindex_changed');
mapOverlayDirective('uiMapPolyline',
'click dblclick mousedown mousemove mouseout mouseover mouseup rightclick');
mapOverlayDirective('uiMapPolygon',
'click dblclick mousedown mousemove mouseout mouseover mouseup rightclick');
mapOverlayDirective('uiMapRectangle',
'bounds_changed click dblclick mousedown mousemove mouseout mouseover ' +
'mouseup rightclick');
mapOverlayDirective('uiMapCircle',
'center_changed click dblclick mousedown mousemove ' +
'mouseout mouseover mouseup radius_changed rightclick');
mapOverlayDirective('uiMapGroundOverlay',
'click dblclick');
// -----------------------------------------------------------------------------
// Replaced native Angular-UI uiMapMarker directive to allows markers to be
// added to the map as soon as it has been drawn.
// https://github.com/angular-ui/angular-ui/issues/173#issuecomment-8765849
// -----------------------------------------------------------------------------
/*
app.directive('uiMapMarker', ['$parse', function($parse) {
var events = 'animation_changed click clickable_changed cursor_changed ' +
'dblclick drag dragend draggable_changed dragstart flat_changed ' +
'icon_changed mousedown mouseout mouseover mouseup position_changed ' +
'rightclick shadow_changed shape_changed title_changed visible_changed ' +
'zindex_changed';
return {
restrict: 'A',
link: function (scope, elm, attrs) {
console.log(
'attrs.icAttachToMap: ', attrs.icAttachToMap,
'\nattrs.icAttachToMap: ', attrs.icAttachToMap,
'\nattrs.icLat: ', attrs.icLat,
'\nattrs.icLng: ', attrs.icLng
);
var mapName = attrs.icAttachToMap;
var lat = parseFloat(attrs.icLat);
var lng = parseFloat(attrs.icLng);
f = scope.$watch(mapName, function(map) {
if (map) {
var marker = new google.maps.Marker({
map: map,
position: new google.maps.LatLng(icLat, icLng)
});
var model = $parse(attrs.uiMapMarker);
model.assign(scope, marker);
bindMapEvents(scope, events, marker, elm);
f();
}
});
}
};
}]);
*/
})();
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" ng-app="gMap">
<head>
<title></title>
<script src="//code.jquery.com/jquery-1.9.0.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.js"></script>
<script src="//maps.googleapis.com/maps/api/js?sensor=false"></script>
<script src="infobox.js"></script>
<link href="style.css" rel="stylesheet" />
<script>
//document.write('<base href="' + document.location + '" />');
</script>
<script src="app.js"></script>
<script src="controllers.js"></script>
<script src="angular_ui.js"></script>
<!-- <script src="aui-event.js"></script>
<script src="aui-if.js"></script>
<script src="aui-map.js"></script> -->
<script src="//angular-ui.github.com/angular-ui/build/angular-ui.js"></script>
</head>
<body>
<div ng-view></div>
<div
id="map_container"
ng-controller="GoogleMaps">
<div ui-if="mapReady">
<div
id="map_canvas"
ui-map="myMap"
ui-options="mapOptions"
></div>
<div
ng-repeat="marker in mapMarkers"
ui-map-marker="mapMarkers[$index]"
ui-event="{'map-click':'openMarkerInfo(marker)'}"
></div>
<div
ui-map-info-window="myInfoWindow"
ng-include="'infobox.html'"
></div>
</div>
</div>
</body>
</html>
function Main() {}
function GoogleMaps( $scope , $http )
{
var mapDefaults = {
center: new google.maps.LatLng(25,-90),
zoom: 4,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
$scope.mapReady = false;
$scope.mapOptions = {};
$scope.mapMarkers = [];
$scope.locations = [];
$http.get('map.json').then(function mapData(response) {
console.log(
'response.status: ', response.status,
'\nresponse.data: ', response.data,
'\nresponse.config: ', response.config
);
var map_data = response.data;
var user_defaults = map_data.user.defaults;
console.log(
'map_data: ', map_data,
'\nuser_defaults: ', user_defaults
);
$scope.mapOptions = {
"center": (typeof user_defaults.center !== 'undefined') ?
new google.maps.LatLng(user_defaults.center[0],user_defaults.center[1])
: mapDefaults.center,
"zoom": (typeof user_defaults.zoom !== 'undefined') ?
parseInt(user_defaults.zoom,10)
: mapDefaults.zoom,
"mapTypeId": mapDefaults.mapTypeId
};
$scope.locations = map_data.locations;
$scope.mapReady = true;
//console.log('$scope.mapOptions (after): ', $scope.mapOptions);
});
$scope.$watch('myMap', function(map){
if (map) {
console.log('$scope.locations', $scope.locations, '\n$scope.myMap: ', $scope.myMap);
for ( var i = 0, count = $scope.locations.length; i < count; i++ ) {
var latLng = new google.maps.LatLng( $scope.locations[i][0] , $scope.locations[i][0] );
$scope.mapMarkers.push(new google.maps.Marker({
map: $scope.myMap,
position: latLng
}));
}//for
}//fi
});
/*
$scope.initMarkers = function(mapMarkers) {
alert('initMarkers');
console.log('mapMarkers', mapMarkers, '\n$scope.myMap: ', $scope.myMap);
for ( var i = 0, count = mapMarkers.length; i < count; i++ ) {
var latLng = new google.maps.LatLng( mapMarkers[i][0] , mapMarkers[i][0] );
$scope.mapMarkers.push(new google.maps.Marker({
map: $scope.myMap,
position: latLng
}));
}
};
*/
$scope.addMarker = function($event) {
$scope.mapMarkers.push(new google.maps.Marker({
map: $scope.myMap,
position: $event.latLng
}));
};
$scope.openMarkerInfo = function(marker) {
$scope.currentMarker = marker;
$scope.currentMarkerLat = marker.getPosition().lat();
$scope.currentMarkerLng = marker.getPosition().lng();
$scope.myInfoWindow.open($scope.myMap, marker);
};
$scope.setMarkerPosition = function(marker, lat, lng) {
marker.setPosition(new google.maps.LatLng(lat, lng));
};
}//GoogleMaps{}
{
"region": "vancouver",
"locations": [
[49.285415,-123.114982],
[49.287,-123.117]
],
"user": {
"defaults": {
"center": [49.285415,-123.114982],
"zoom":15
}
}
}
'use strict';
/* App Module */
angular.module( 'gMap' , ['ui'])
.config([
'$routeProvider',
function($routeProvider) {
$routeProvider.
when(
'/',{
templateUrl: 'main.html',
controller: Main
}).
otherwise({redirectTo: '/'});
}//fn()
])//config()
.value('ui.config', {
map: {
center: new google.maps.LatLng(25,-90),
zoom: 4,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
});
html,
body,
#map_container,
#map_container > div,
#map_canvas {
height:100%;
width:100%;
}
.infobox_container {
background-color:white;
}
.infobox_container,
.infobox_container aside {
padding:10px;
}
.infobox_container aside {
background-color:#eee;
height:100%;
left:0px;
overflow:auto;
position:absolute;
top:0px;
z-index:-1;
}
<div class="infobox_content">
<aside
class="users"
ng-include src="'users.html'"
ic-slide={"direction":"left","condition":"aside_users"}
></aside>
<i
class="icon icon-user"
ng-click="aside_users=!aside_users"
ng-init="aside_users=false"
>users</i>
<table>
<tbody>
<tr>
<td>aside_users:</td> <td>{{aside_users}}</td>
</tr>
</tbody>
</table>
</div>
USERS!
/**
* @name InfoBox
* @version 1.1.12 [December 11, 2012]
* @author Gary Little (inspired by proof-of-concept code from Pamela Fox of Google)
* @copyright Copyright 2010 Gary Little [gary at luxcentral.com]
* @fileoverview InfoBox extends the Google Maps JavaScript API V3 <tt>OverlayView</tt> class.
* <p>
* An InfoBox behaves like a <tt>google.maps.InfoWindow</tt>, but it supports several
* additional properties for advanced styling. An InfoBox can also be used as a map label.
* <p>
* An InfoBox also fires the same events as a <tt>google.maps.InfoWindow</tt>.
*/
/*!
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*jslint browser:true */
/*global google */
/**
* @name InfoBoxOptions
* @class This class represents the optional parameter passed to the {@link InfoBox} constructor.
* @property {string|Node} content The content of the InfoBox (plain text or an HTML DOM node).
* @property {boolean} [disableAutoPan=false] Disable auto-pan on <tt>open</tt>.
* @property {number} maxWidth The maximum width (in pixels) of the InfoBox. Set to 0 if no maximum.
* @property {Size} pixelOffset The offset (in pixels) from the top left corner of the InfoBox
* (or the bottom left corner if the <code>alignBottom</code> property is <code>true</code>)
* to the map pixel corresponding to <tt>position</tt>.
* @property {LatLng} position The geographic location at which to display the InfoBox.
* @property {number} zIndex The CSS z-index style value for the InfoBox.
* Note: This value overrides a zIndex setting specified in the <tt>boxStyle</tt> property.
* @property {string} [boxClass="infoBox"] The name of the CSS class defining the styles for the InfoBox container.
* @property {Object} [boxStyle] An object literal whose properties define specific CSS
* style values to be applied to the InfoBox. Style values defined here override those that may
* be defined in the <code>boxClass</code> style sheet. If this property is changed after the
* InfoBox has been created, all previously set styles (except those defined in the style sheet)
* are removed from the InfoBox before the new style values are applied.
* @property {string} closeBoxMargin The CSS margin style value for the close box.
* The default is "2px" (a 2-pixel margin on all sides).
* @property {string} closeBoxURL The URL of the image representing the close box.
* Note: The default is the URL for Google's standard close box.
* Set this property to "" if no close box is required.
* @property {Size} infoBoxClearance Minimum offset (in pixels) from the InfoBox to the
* map edge after an auto-pan.
* @property {boolean} [isHidden=false] Hide the InfoBox on <tt>open</tt>.
* [Deprecated in favor of the <tt>visible</tt> property.]
* @property {boolean} [visible=true] Show the InfoBox on <tt>open</tt>.
* @property {boolean} alignBottom Align the bottom left corner of the InfoBox to the <code>position</code>
* location (default is <tt>false</tt> which means that the top left corner of the InfoBox is aligned).
* @property {string} pane The pane where the InfoBox is to appear (default is "floatPane").
* Set the pane to "mapPane" if the InfoBox is being used as a map label.
* Valid pane names are the property names for the <tt>google.maps.MapPanes</tt> object.
* @property {boolean} enableEventPropagation Propagate mousedown, mousemove, mouseover, mouseout,
* mouseup, click, dblclick, touchstart, touchend, touchmove, and contextmenu events in the InfoBox
* (default is <tt>false</tt> to mimic the behavior of a <tt>google.maps.InfoWindow</tt>). Set
* this property to <tt>true</tt> if the InfoBox is being used as a map label.
*/
/**
* Creates an InfoBox with the options specified in {@link InfoBoxOptions}.
* Call <tt>InfoBox.open</tt> to add the box to the map.
* @constructor
* @param {InfoBoxOptions} [opt_opts]
*/
function InfoBox(opt_opts) {
opt_opts = opt_opts || {};
google.maps.OverlayView.apply(this, arguments);
// Standard options (in common with google.maps.InfoWindow):
//
this.content_ = opt_opts.content || "";
this.disableAutoPan_ = opt_opts.disableAutoPan || false;
this.maxWidth_ = opt_opts.maxWidth || 0;
this.pixelOffset_ = opt_opts.pixelOffset || new google.maps.Size(0, 0);
this.position_ = opt_opts.position || new google.maps.LatLng(0, 0);
this.zIndex_ = opt_opts.zIndex || null;
// Additional options (unique to InfoBox):
//
this.boxClass_ = opt_opts.boxClass || "infoBox";
this.boxStyle_ = opt_opts.boxStyle || {};
this.closeBoxMargin_ = opt_opts.closeBoxMargin || "2px";
this.closeBoxURL_ = opt_opts.closeBoxURL || "http://www.google.com/intl/en_us/mapfiles/close.gif";
if (opt_opts.closeBoxURL === "") {
this.closeBoxURL_ = "";
}
this.infoBoxClearance_ = opt_opts.infoBoxClearance || new google.maps.Size(1, 1);
if (typeof opt_opts.visible === "undefined") {
if (typeof opt_opts.isHidden === "undefined") {
opt_opts.visible = true;
} else {
opt_opts.visible = !opt_opts.isHidden;
}
}
this.isHidden_ = !opt_opts.visible;
this.alignBottom_ = opt_opts.alignBottom || false;
this.pane_ = opt_opts.pane || "floatPane";
this.enableEventPropagation_ = opt_opts.enableEventPropagation || false;
this.div_ = null;
this.closeListener_ = null;
this.moveListener_ = null;
this.contextListener_ = null;
this.eventListeners_ = null;
this.fixedWidthSet_ = null;
}
/* InfoBox extends OverlayView in the Google Maps API v3.
*/
InfoBox.prototype = new google.maps.OverlayView();
/**
* Creates the DIV representing the InfoBox.
* @private
*/
InfoBox.prototype.createInfoBoxDiv_ = function () {
var i;
var events;
var bw;
var me = this;
// This handler prevents an event in the InfoBox from being passed on to the map.
//
var cancelHandler = function (e) {
e.cancelBubble = true;
if (e.stopPropagation) {
e.stopPropagation();
}
};
// This handler ignores the current event in the InfoBox and conditionally prevents
// the event from being passed on to the map. It is used for the contextmenu event.
//
var ignoreHandler = function (e) {
e.returnValue = false;
if (e.preventDefault) {
e.preventDefault();
}
if (!me.enableEventPropagation_) {
cancelHandler(e);
}
};
if (!this.div_) {
this.div_ = document.createElement("div");
this.setBoxStyle_();
if (typeof this.content_.nodeType === "undefined") {
this.div_.innerHTML = this.getCloseBoxImg_() + this.content_;
} else {
this.div_.innerHTML = this.getCloseBoxImg_();
this.div_.appendChild(this.content_);
}
// Add the InfoBox DIV to the DOM
this.getPanes()[this.pane_].appendChild(this.div_);
this.addClickHandler_();
if (this.div_.style.width) {
this.fixedWidthSet_ = true;
} else {
if (this.maxWidth_ !== 0 && this.div_.offsetWidth > this.maxWidth_) {
this.div_.style.width = this.maxWidth_;
this.div_.style.overflow = "auto";
this.fixedWidthSet_ = true;
} else { // The following code is needed to overcome problems with MSIE
bw = this.getBoxWidths_();
this.div_.style.width = (this.div_.offsetWidth - bw.left - bw.right) + "px";
this.fixedWidthSet_ = false;
}
}
this.panBox_(this.disableAutoPan_);
if (!this.enableEventPropagation_) {
this.eventListeners_ = [];
// Cancel event propagation.
//
// Note: mousemove not included (to resolve Issue 152)
events = ["mousedown", "mouseover", "mouseout", "mouseup",
"click", "dblclick", "touchstart", "touchend", "touchmove"];
for (i = 0; i < events.length; i++) {
this.eventListeners_.push(google.maps.event.addDomListener(this.div_, events[i], cancelHandler));
}
// Workaround for Google bug that causes the cursor to change to a pointer
// when the mouse moves over a marker underneath InfoBox.
this.eventListeners_.push(google.maps.event.addDomListener(this.div_, "mouseover", function (e) {
this.style.cursor = "default";
}));
}
this.contextListener_ = google.maps.event.addDomListener(this.div_, "contextmenu", ignoreHandler);
/**
* This event is fired when the DIV containing the InfoBox's content is attached to the DOM.
* @name InfoBox#domready
* @event
*/
google.maps.event.trigger(this, "domready");
}
};
/**
* Returns the HTML <IMG> tag for the close box.
* @private
*/
InfoBox.prototype.getCloseBoxImg_ = function () {
var img = "";
if (this.closeBoxURL_ !== "") {
img = "<img";
img += " src='" + this.closeBoxURL_ + "'";
img += " align=right"; // Do this because Opera chokes on style='float: right;'
img += " style='";
img += " position: relative;"; // Required by MSIE
img += " cursor: pointer;";
img += " margin: " + this.closeBoxMargin_ + ";";
img += " z-index: 2;";
img += "'>";
}
return img;
};
/**
* Adds the click handler to the InfoBox close box.
* @private
*/
InfoBox.prototype.addClickHandler_ = function () {
var closeBox;
if (this.closeBoxURL_ !== "") {
closeBox = this.div_.firstChild;
this.closeListener_ = google.maps.event.addDomListener(closeBox, "click", this.getCloseClickHandler_());
} else {
this.closeListener_ = null;
}
};
/**
* Returns the function to call when the user clicks the close box of an InfoBox.
* @private
*/
InfoBox.prototype.getCloseClickHandler_ = function () {
var me = this;
return function (e) {
// 1.0.3 fix: Always prevent propagation of a close box click to the map:
e.cancelBubble = true;
if (e.stopPropagation) {
e.stopPropagation();
}
/**
* This event is fired when the InfoBox's close box is clicked.
* @name InfoBox#closeclick
* @event
*/
google.maps.event.trigger(me, "closeclick");
me.close();
};
};
/**
* Pans the map so that the InfoBox appears entirely within the map's visible area.
* @private
*/
InfoBox.prototype.panBox_ = function (disablePan) {
var map;
var bounds;
var xOffset = 0, yOffset = 0;
if (!disablePan) {
map = this.getMap();
if (map instanceof google.maps.Map) { // Only pan if attached to map, not panorama
if (!map.getBounds().contains(this.position_)) {
// Marker not in visible area of map, so set center
// of map to the marker position first.
map.setCenter(this.position_);
}
bounds = map.getBounds();
var mapDiv = map.getDiv();
var mapWidth = mapDiv.offsetWidth;
var mapHeight = mapDiv.offsetHeight;
var iwOffsetX = this.pixelOffset_.width;
var iwOffsetY = this.pixelOffset_.height;
var iwWidth = this.div_.offsetWidth;
var iwHeight = this.div_.offsetHeight;
var padX = this.infoBoxClearance_.width;
var padY = this.infoBoxClearance_.height;
var pixPosition = this.getProjection().fromLatLngToContainerPixel(this.position_);
if (pixPosition.x < (-iwOffsetX + padX)) {
xOffset = pixPosition.x + iwOffsetX - padX;
} else if ((pixPosition.x + iwWidth + iwOffsetX + padX) > mapWidth) {
xOffset = pixPosition.x + iwWidth + iwOffsetX + padX - mapWidth;
}
if (this.alignBottom_) {
if (pixPosition.y < (-iwOffsetY + padY + iwHeight)) {
yOffset = pixPosition.y + iwOffsetY - padY - iwHeight;
} else if ((pixPosition.y + iwOffsetY + padY) > mapHeight) {
yOffset = pixPosition.y + iwOffsetY + padY - mapHeight;
}
} else {
if (pixPosition.y < (-iwOffsetY + padY)) {
yOffset = pixPosition.y + iwOffsetY - padY;
} else if ((pixPosition.y + iwHeight + iwOffsetY + padY) > mapHeight) {
yOffset = pixPosition.y + iwHeight + iwOffsetY + padY - mapHeight;
}
}
if (!(xOffset === 0 && yOffset === 0)) {
// Move the map to the shifted center.
//
var c = map.getCenter();
map.panBy(xOffset, yOffset);
}
}
}
};
/**
* Sets the style of the InfoBox by setting the style sheet and applying
* other specific styles requested.
* @private
*/
InfoBox.prototype.setBoxStyle_ = function () {
var i, boxStyle;
if (this.div_) {
// Apply style values from the style sheet defined in the boxClass parameter:
this.div_.className = this.boxClass_;
// Clear existing inline style values:
this.div_.style.cssText = "";
// Apply style values defined in the boxStyle parameter:
boxStyle = this.boxStyle_;
for (i in boxStyle) {
if (boxStyle.hasOwnProperty(i)) {
this.div_.style[i] = boxStyle[i];
}
}
// Fix up opacity style for benefit of MSIE:
//
if (typeof this.div_.style.opacity !== "undefined" && this.div_.style.opacity !== "") {
this.div_.style.filter = "alpha(opacity=" + (this.div_.style.opacity * 100) + ")";
}
// Apply required styles:
//
this.div_.style.position = "absolute";
this.div_.style.visibility = 'hidden';
if (this.zIndex_ !== null) {
this.div_.style.zIndex = this.zIndex_;
}
}
};
/**
* Get the widths of the borders of the InfoBox.
* @private
* @return {Object} widths object (top, bottom left, right)
*/
InfoBox.prototype.getBoxWidths_ = function () {
var computedStyle;
var bw = {top: 0, bottom: 0, left: 0, right: 0};
var box = this.div_;
if (document.defaultView && document.defaultView.getComputedStyle) {
computedStyle = box.ownerDocument.defaultView.getComputedStyle(box, "");
if (computedStyle) {
// The computed styles are always in pixel units (good!)
bw.top = parseInt(computedStyle.borderTopWidth, 10) || 0;
bw.bottom = parseInt(computedStyle.borderBottomWidth, 10) || 0;
bw.left = parseInt(computedStyle.borderLeftWidth, 10) || 0;
bw.right = parseInt(computedStyle.borderRightWidth, 10) || 0;
}
} else if (document.documentElement.currentStyle) { // MSIE
if (box.currentStyle) {
// The current styles may not be in pixel units, but assume they are (bad!)
bw.top = parseInt(box.currentStyle.borderTopWidth, 10) || 0;
bw.bottom = parseInt(box.currentStyle.borderBottomWidth, 10) || 0;
bw.left = parseInt(box.currentStyle.borderLeftWidth, 10) || 0;
bw.right = parseInt(box.currentStyle.borderRightWidth, 10) || 0;
}
}
return bw;
};
/**
* Invoked when <tt>close</tt> is called. Do not call it directly.
*/
InfoBox.prototype.onRemove = function () {
if (this.div_) {
this.div_.parentNode.removeChild(this.div_);
this.div_ = null;
}
};
/**
* Draws the InfoBox based on the current map projection and zoom level.
*/
InfoBox.prototype.draw = function () {
this.createInfoBoxDiv_();
var pixPosition = this.getProjection().fromLatLngToDivPixel(this.position_);
this.div_.style.left = (pixPosition.x + this.pixelOffset_.width) + "px";
if (this.alignBottom_) {
this.div_.style.bottom = -(pixPosition.y + this.pixelOffset_.height) + "px";
} else {
this.div_.style.top = (pixPosition.y + this.pixelOffset_.height) + "px";
}
if (this.isHidden_) {
this.div_.style.visibility = 'hidden';
} else {
this.div_.style.visibility = "visible";
}
};
/**
* Sets the options for the InfoBox. Note that changes to the <tt>maxWidth</tt>,
* <tt>closeBoxMargin</tt>, <tt>closeBoxURL</tt>, and <tt>enableEventPropagation</tt>
* properties have no affect until the current InfoBox is <tt>close</tt>d and a new one
* is <tt>open</tt>ed.
* @param {InfoBoxOptions} opt_opts
*/
InfoBox.prototype.setOptions = function (opt_opts) {
if (typeof opt_opts.boxClass !== "undefined") { // Must be first
this.boxClass_ = opt_opts.boxClass;
this.setBoxStyle_();
}
if (typeof opt_opts.boxStyle !== "undefined") { // Must be second
this.boxStyle_ = opt_opts.boxStyle;
this.setBoxStyle_();
}
if (typeof opt_opts.content !== "undefined") {
this.setContent(opt_opts.content);
}
if (typeof opt_opts.disableAutoPan !== "undefined") {
this.disableAutoPan_ = opt_opts.disableAutoPan;
}
if (typeof opt_opts.maxWidth !== "undefined") {
this.maxWidth_ = opt_opts.maxWidth;
}
if (typeof opt_opts.pixelOffset !== "undefined") {
this.pixelOffset_ = opt_opts.pixelOffset;
}
if (typeof opt_opts.alignBottom !== "undefined") {
this.alignBottom_ = opt_opts.alignBottom;
}
if (typeof opt_opts.position !== "undefined") {
this.setPosition(opt_opts.position);
}
if (typeof opt_opts.zIndex !== "undefined") {
this.setZIndex(opt_opts.zIndex);
}
if (typeof opt_opts.closeBoxMargin !== "undefined") {
this.closeBoxMargin_ = opt_opts.closeBoxMargin;
}
if (typeof opt_opts.closeBoxURL !== "undefined") {
this.closeBoxURL_ = opt_opts.closeBoxURL;
}
if (typeof opt_opts.infoBoxClearance !== "undefined") {
this.infoBoxClearance_ = opt_opts.infoBoxClearance;
}
if (typeof opt_opts.isHidden !== "undefined") {
this.isHidden_ = opt_opts.isHidden;
}
if (typeof opt_opts.visible !== "undefined") {
this.isHidden_ = !opt_opts.visible;
}
if (typeof opt_opts.enableEventPropagation !== "undefined") {
this.enableEventPropagation_ = opt_opts.enableEventPropagation;
}
if (this.div_) {
this.draw();
}
};
/**
* Sets the content of the InfoBox.
* The content can be plain text or an HTML DOM node.
* @param {string|Node} content
*/
InfoBox.prototype.setContent = function (content) {
this.content_ = content;
if (this.div_) {
if (this.closeListener_) {
google.maps.event.removeListener(this.closeListener_);
this.closeListener_ = null;
}
// Odd code required to make things work with MSIE.
//
if (!this.fixedWidthSet_) {
this.div_.style.width = "";
}
if (typeof content.nodeType === "undefined") {
this.div_.innerHTML = this.getCloseBoxImg_() + content;
} else {
this.div_.innerHTML = this.getCloseBoxImg_();
this.div_.appendChild(content);
}
// Perverse code required to make things work with MSIE.
// (Ensures the close box does, in fact, float to the right.)
//
if (!this.fixedWidthSet_) {
this.div_.style.width = this.div_.offsetWidth + "px";
if (typeof content.nodeType === "undefined") {
this.div_.innerHTML = this.getCloseBoxImg_() + content;
} else {
this.div_.innerHTML = this.getCloseBoxImg_();
this.div_.appendChild(content);
}
}
this.addClickHandler_();
}
/**
* This event is fired when the content of the InfoBox changes.
* @name InfoBox#content_changed
* @event
*/
google.maps.event.trigger(this, "content_changed");
};
/**
* Sets the geographic location of the InfoBox.
* @param {LatLng} latlng
*/
InfoBox.prototype.setPosition = function (latlng) {
this.position_ = latlng;
if (this.div_) {
this.draw();
}
/**
* This event is fired when the position of the InfoBox changes.
* @name InfoBox#position_changed
* @event
*/
google.maps.event.trigger(this, "position_changed");
};
/**
* Sets the zIndex style for the InfoBox.
* @param {number} index
*/
InfoBox.prototype.setZIndex = function (index) {
this.zIndex_ = index;
if (this.div_) {
this.div_.style.zIndex = index;
}
/**
* This event is fired when the zIndex of the InfoBox changes.
* @name InfoBox#zindex_changed
* @event
*/
google.maps.event.trigger(this, "zindex_changed");
};
/**
* Sets the visibility of the InfoBox.
* @param {boolean} isVisible
*/
InfoBox.prototype.setVisible = function (isVisible) {
this.isHidden_ = !isVisible;
if (this.div_) {
this.div_.style.visibility = (this.isHidden_ ? "hidden" : "visible");
}
};
/**
* Returns the content of the InfoBox.
* @returns {string}
*/
InfoBox.prototype.getContent = function () {
return this.content_;
};
/**
* Returns the geographic location of the InfoBox.
* @returns {LatLng}
*/
InfoBox.prototype.getPosition = function () {
return this.position_;
};
/**
* Returns the zIndex for the InfoBox.
* @returns {number}
*/
InfoBox.prototype.getZIndex = function () {
return this.zIndex_;
};
/**
* Returns a flag indicating whether the InfoBox is visible.
* @returns {boolean}
*/
InfoBox.prototype.getVisible = function () {
var isVisible;
if ((typeof this.getMap() === "undefined") || (this.getMap() === null)) {
isVisible = false;
} else {
isVisible = !this.isHidden_;
}
return isVisible;
};
/**
* Shows the InfoBox. [Deprecated; use <tt>setVisible</tt> instead.]
*/
InfoBox.prototype.show = function () {
this.isHidden_ = false;
if (this.div_) {
this.div_.style.visibility = "visible";
}
};
/**
* Hides the InfoBox. [Deprecated; use <tt>setVisible</tt> instead.]
*/
InfoBox.prototype.hide = function () {
this.isHidden_ = true;
if (this.div_) {
this.div_.style.visibility = "hidden";
}
};
/**
* Adds the InfoBox to the specified map or Street View panorama. If <tt>anchor</tt>
* (usually a <tt>google.maps.Marker</tt>) is specified, the position
* of the InfoBox is set to the position of the <tt>anchor</tt>. If the
* anchor is dragged to a new location, the InfoBox moves as well.
* @param {Map|StreetViewPanorama} map
* @param {MVCObject} [anchor]
*/
InfoBox.prototype.open = function (map, anchor) {
var me = this;
if (anchor) {
this.position_ = anchor.getPosition();
this.moveListener_ = google.maps.event.addListener(anchor, "position_changed", function () {
me.setPosition(this.getPosition());
});
}
this.setMap(map);
if (this.div_) {
this.panBox_();
}
};
/**
* Removes the InfoBox from the map.
*/
InfoBox.prototype.close = function () {
var i;
if (this.closeListener_) {
google.maps.event.removeListener(this.closeListener_);
this.closeListener_ = null;
}
if (this.eventListeners_) {
for (i = 0; i < this.eventListeners_.length; i++) {
google.maps.event.removeListener(this.eventListeners_[i]);
}
this.eventListeners_ = null;
}
if (this.moveListener_) {
google.maps.event.removeListener(this.moveListener_);
this.moveListener_ = null;
}
if (this.contextListener_) {
google.maps.event.removeListener(this.contextListener_);
this.contextListener_ = null;
}
this.setMap(null);
};
angular.module('ui.config', []).value('ui.config', {});
angular.module('ui.filters', ['ui.config']);
angular.module('ui.directives', ['ui.config']);
angular.module('ui', ['ui.filters', 'ui.directives', 'ui.config']);
<div
id="map_canvas"
ui-map="myMap"
ui-options="mapOptions"
></div>
<div
ng-repeat="marker in markers"
ui-map-marker="markers[$index]"
ui-event="{'map-click':'openMarkerInfo(marker)'}"
></div>
<div
ui-map-info-window="myInfoWindow"
ng-include="'infobox.html'"
></div>
/*
* Defines the ui-if tag. This removes/adds an element from the dom depending on a condition
* Originally created by @tigbro, for the @jquery-mobile-angular-adapter
* https://github.com/tigbro/jquery-mobile-angular-adapter
*/
angular.module('ui.directives').directive('uiIf', [function () {
return {
transclude: 'element',
priority: 1000,
terminal: true,
restrict: 'A',
compile: function (element, attr, transclude) {
return function (scope, element, attr) {
var childElement;
var childScope;
scope.$watch(attr['uiIf'], function (newValue) {
if (childElement) {
childElement.remove();
childElement = undefined;
}
if (childScope) {
childScope.$destroy();
childScope = undefined;
}
if (newValue) {
childScope = scope.$new();
transclude(childScope, function (clone) {
childElement = clone;
element.after(clone);
});
}
});
};
}
};
}]);
/**
* General-purpose Event binding. Bind any event not natively supported by Angular
* Pass an object with keynames for events to ui-event
* Allows $event object and $params object to be passed
*
* @example <input ui-event="{ focus : 'counter++', blur : 'someCallback()' }">
* @example <input ui-event="{ myCustomEvent : 'myEventHandler($event, $params)'}">
*
* @param ui-event {string|object literal} The event to bind to as a string or a hash of events with their callbacks
*/
angular.module('ui.directives').directive('uiEvent', ['$parse',
function ($parse) {
return function (scope, elm, attrs) {
var events = scope.$eval(attrs.uiEvent);
angular.forEach(events, function (uiEvent, eventName) {
var fn = $parse(uiEvent);
elm.bind(eventName, function (evt) {
var params = Array.prototype.slice.call(arguments);
//Take out first paramater (event object);
params = params.splice(1);
scope.$apply(function () {
fn(scope, {$event: evt, $params: params});
});
});
});
};
}]);