<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>Google Maps JavaScript API v3 Example: Place Photos</title>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false&libraries=places"></script>
<script>
var markers = [];
function initialize() {
var map = new google.maps.Map(document.getElementById('map-canvas'), {
mapTypeId: google.maps.MapTypeId.ROADMAP,
maxZoom: 16,
styles: [
{
elementType: 'labels',
stylers: [ { visibility: 'on' } ]
},
{
stylers: [ { saturation: -100 }, { lightness: -20 } ]
}
]
});
var defaultBounds = new google.maps.LatLngBounds(
new google.maps.LatLng(-33.8902, 151.1759),
new google.maps.LatLng(-33.8474, 151.2631));
map.fitBounds(defaultBounds);
var input = document.getElementById('target');
var searchBox = new google.maps.places.SearchBox(input);
searchBox.setBounds(map.getBounds());
var modalWindow = new ModalWindow(map);
google.maps.event.addListener(searchBox, 'places_changed', function() {
var places = searchBox.getPlaces();
if (!places.length) {
return;
}
for (var i = 0, marker; marker = markers[i]; i++) {
marker.setMap(null);
}
markers = [];
var bounds = new google.maps.LatLngBounds();
for (var i = 0, place; place = places[i]; i++) {
if (place.photos) {
markers.push(new PhotoMarker(place, map, modalWindow));
} else {
markers.push(new google.maps.Marker({
position: place.geometry.location,
map: map,
icon: new google.maps.MarkerImage(
'https://maps.gstatic.com/intl/en_us/mapfiles/markers2/measle.png',
null, null, new google.maps.Point(3.5,3.5)),
clickable: false
}));
}
bounds.extend(place.geometry.location);
}
map.fitBounds(bounds);
});
google.maps.event.addListener(map, 'bounds_changed', function() {
searchBox.setBounds(map.getBounds());
});
}
/* Photo Marker */
function PhotoMarker(place, map, modalWindow) {
this.modalWindow_ = modalWindow;
this.place_ = place;
this.setMap(map);
}
PhotoMarker.prototype = new google.maps.OverlayView();
PhotoMarker.prototype.onAdd = function() {
this.img_ = document.createElement('img');
this.img_.className = 'photo-marker';
this.img_.title = this.place_.name;
this.img_.src = this.place_.photos[0].getUrl({
'maxWidth': 35,
'maxHeight': 35
});
this.getPanes().overlayImage.appendChild(this.img_);
var that = this;
google.maps.event.addDomListener(this.img_, 'click', function() {
that.modalWindow_.getDetails(that.place_);
});
};
PhotoMarker.prototype.draw = function() {
var that = this;
if (!this.img_ || (this.img_ && !this.img_.complete)) {
window.clearTimeout(this.imgLoader_);
this.imgLoader_ = window.setTimeout(function() {
that.draw();
}, 50);
return;
}
var proj = this.getProjection();
var pos = proj.fromLatLngToDivPixel(this.place_.geometry.location);
var w = this.img_.offsetWidth;
var h = this.img_.offsetHeight;
this.img_.style.left = Math.floor(pos.x - w / 2) + 'px';
this.img_.style.top = Math.floor(pos.y - h / 2) + 'px';
};
PhotoMarker.prototype.onRemove = function() {
this.img_.parentNode.removeChild(this.img_);
this.img_ = null;
};
/* Modal Window */
function ModalWindow(map) {
this.service_ = new google.maps.places.PlacesService(map);
this.createDOMElements_();
this.addEventListeners_();
}
ModalWindow.prototype.createDOMElements_ = function() {
this.modal_ = document.createElement('div');
this.modal_.id = 'modal';
document.body.appendChild(this.modal_);
var modalWindow = document.createElement('div');
modalWindow.id = 'window';
this.modal_.appendChild(modalWindow);
var close = document.createElement('img');
close.id = 'close';
close.className = 'close';
close.src = '../images/close.png';
close.alt = 'Close window';
modalWindow.appendChild(close);
this.info_ = document.createElement('div');
this.info_.id = 'info';
modalWindow.appendChild(this.info_);
this.photo_ = document.createElement('div');
this.photo_.id = 'photo';
modalWindow.appendChild(this.photo_);
this.photos_ = document.createElement('div');
this.photos_.id = 'photos';
modalWindow.appendChild(this.photos_);
this.attribution_ = document.createElement('div');
this.attribution_.id = 'attribution';
modalWindow.appendChild(this.attribution_);
var mask = document.createElement('div');
mask.id = 'mask';
this.modal_.appendChild(mask);
};
ModalWindow.prototype.addEventListeners_ = function() {
var that = this;
google.maps.event.addDomListener(this.modal_, 'click', function(e) {
if (e.target && (e.target.id == 'close' || e.target.id == 'mask')) {
that.hideWindow_();
}
});
google.maps.event.addDomListener(document, 'keyup', function(e) {
// Esc key
if (e.keyCode == 27) {
that.hideWindow_();
}
});
};
ModalWindow.prototype.getDetails = function(place) {
if (this.place_ && place.id == this.place_.id) {
this.showWindow_();
return;
}
var that = this;
this.service_.getDetails({'reference': place.reference},
function(details, status) {
if (status != google.maps.places.PlacesServiceStatus.OK) {
return;
}
that.place_ = place;
that.details_ = details;
that.createContent_();
that.showWindow_();
});
};
ModalWindow.prototype.createContent_ = function(place, status) {
this.createInfo_();
this.createPhoto_(this.place_.photos[0]);
this.createThumbnails_();
this.createAttribution_(this.place_.photos[0]);
};
ModalWindow.prototype.createPhoto_ = function(photo) {
this.photo_.innerHTML = '';
var img = document.createElement('img');
img.src = photo.getUrl({'maxWidth': 480, 'maxHeight': 246});
this.photo_.appendChild(img);
};
ModalWindow.prototype.createInfo_ = function() {
this.info_.innerHTML = '';
var name = document.createElement('h3');
name.innerHTML = this.place_.name;
this.info_.appendChild(name);
var address = document.createElement('p');
address.innerHTML = this.place_.formatted_address;
this.info_.appendChild(address);
};
ModalWindow.prototype.createThumbnails_ = function() {
this.photos_.innerHTML = '';
for (var i = 0; i < this.details_.photos.length; i++) {
var thumbnail = document.createElement('img');
thumbnail.id = i;
thumbnail.src = this.details_.photos[i].getUrl({'maxWidth': 88,
'maxHeight': 88});
this.photos_.appendChild(thumbnail);
}
this.photos_.firstChild.className = 'selected';
this.selected_ = this.photos_.firstChild;
var that = this;
google.maps.event.addDomListener(this.photos_, 'click', function(e) {
if (e.target && e.target.nodeName == 'IMG' &&
e.target.className != 'selected') {
that.changeSelected_(e.target);
}
});
};
ModalWindow.prototype.changeSelected_ = function(thumbnail) {
this.selected_.className = '';
thumbnail.className = 'selected';
this.selected_ = thumbnail;
this.createPhoto_(this.details_.photos[thumbnail.id]);
this.createAttribution_(this.details_.photos[thumbnail.id]);
};
ModalWindow.prototype.createAttribution_ = function(photo) {
this.attribution_.innerHTML = '';
if (photo.html_attributions.length) {
this.attribution_.innerHTML = photo.html_attributions[0];
var link = this.attribution_.getElementsByTagName('a');
if (link[0]) {
link[0].target = '_blank';
}
}
};
ModalWindow.prototype.showWindow_ = function() {
this.modal_.style.display = 'block';
};
ModalWindow.prototype.hideWindow_ = function() {
this.modal_.style.display = 'none';
};
google.maps.event.addDomListenerOnce(window, 'load', initialize);
</script>
<style>
html, body {
font-size: 14px;
font-family: Arial, Helvetica, sans-serif;
height: 100%;
margin: 0;
padding: 0;
}
input {
font-size: 16px;
font-family: Arial, Helvetica, sans-serif;
}
h3 {
margin: 0 0 5px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
p {
margin: 0 0 10px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.photo-marker {
display: block;
border-radius: 2px;
border-radius: 2px;
border-radius: 2px;
border: 2px solid #eee;
box-shadow: rgba(255, 255, 255, 0.4) 0 2px 4px;
box-shadow: rgba(255, 255, 255, 0.4) 0 2px 4px;
box-shadow: rgba(255, 255, 255, 0.4) 0 2px 4px;
cursor: pointer;
position: absolute;
z-index: 0;
}
.photo-marker:hover {
z-index: 1;
}
img.close {
cursor: pointer;
width: 38px;
height: 38px;
float: right;
margin-left: 10px;
}
#map-canvas {
height: 100%;
}
#target {
width: 345px;
outline: none;
border: 1px solid #fff;
position: absolute;
top: 5px;
left: 50%;
margin-left: -181px;
width: 350px;
z-index: 1000;
background-color: #fff;
padding: 5px;
border-radius: 2px;
border-radius: 2px;
border-radius: 2px;
box-shadow: rgba(255, 255, 255, 0.4) 0 2px 4px;
box-shadow: rgba(255, 255, 255, 0.4) 0 2px 4px;
box-shadow: rgba(255, 255, 255, 0.4) 0 2px 4px;
}
/* Modal Window styles */
#modal {
display: none;
}
#mask {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 1001;
background: #fff;
display: block;
opacity: 0.5;
}
#window {
width: 578px;
height: 298px;
position: fixed;
display: block;
z-index: 1002;
border: 1px solid #dcdcdc;
padding: 10px;
background: #fff;
border-radius: 2px;
border-radius: 2px;
border-radius: 2px;
box-shadow: rgba(0, 0, 0, 0.2) 0 0 40px;
box-shadow: rgba(0, 0, 0, 0.2) 0 0 40px;
box-shadow: rgba(0, 0, 0, 0.2) 0 0 40px;
left: 50%;
top: 50%;
margin-left: -300px;
margin-top: -160px;
}
#photo {
height: 246px;
width: 480px;
line-height: 243px;
text-align: center;
margin: 0 10px 0 0;
background: #000;
float: left;
}
#photo img {
display: inline-block;
vertical-align: middle;
}
#attribution {
user-select: none;
user-select: none;
user-select: none;
user-select: none;
line-height: 19px;
padding-right: 2px;
padding-left: 50px;
background-image:
linear-gradient(left,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.5) 50px);
background-image:
linear-gradient(left,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.5) 50px);
background-image:
linear-gradient(left,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.5) 50px);
background-image:
linear-gradient(left,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.5) 50px);
font-size: 10px;
color: #444;
margin-bottom: 10px;
position: absolute;
bottom: 0;
right: 108px;
}
#attribution a {
color: #444;
}
#left {
width:300px;
height:100%;
float:left;
}
#photos {
float: left;
width: 88px;
overflow: hidden;
overflow-y: auto;
height: 246px;
text-align: center;
}
#photos img {
opacity: 0.7;
margin: 0 0 10px 0;
cursor: pointer;
box-sizing: border-box;
box-sizing: border-box;
box-sizing: border-box;
vertical-align: middle;
}
#photos img:nth-last-child(1) {
margin: 0;
}
#photos img:hover {
opacity: 0.9;
}
#photos img.selected {
opacity: 1;
cursor: default;
}
</style>
<script>
$(function() {
$( "#photo" ).resizable();
$( "#photo" ).draggable({revert: 'invalid'});
$( "#left" ).droppable({
accept: '#photo',
drop: function( event, ui ) {
$( this )
.find( "p" )
.html( "Dropped!" );
}
});
});
</script>
</head>
<body>
<div id="left"></div>
<input id="target" type="text" placeholder="Search Box">
<div id="map-canvas"></div>
</body>
</html>
Output
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |