Skip to content

Instantly share code, notes, and snippets.

@lkacenja
Created June 8, 2017 20:48
Show Gist options
  • Save lkacenja/e27d9390ca2e74647289b7922547ded0 to your computer and use it in GitHub Desktop.
Save lkacenja/e27d9390ca2e74647289b7922547ded0 to your computer and use it in GitHub Desktop.
Js powering GOK trail map.
(function ($, Drupal, window, document, undefined) {
// Establish namespaces
Drupal.trails = Drupal.trails || {};
Drupal.trails.Views = Drupal.trails.Views || {};
// Establish global pubsub
Backbone.pubSub = Backbone.pubSub || _.extend({}, Backbone.Events);
// Map config specific vars
var styles = [
{
featureType: "poi",
stylers: [
{ visibility: "off" }
]
}
],
defaults = {
zoom: 7,
center: new google.maps.LatLng(38.717697, -98.292612),
styles: styles
};
// Cheap template
function infoBoxTemplate(item) {
var output = item.node_title;
if (item.field_trails_in_network_node_title) {
output += ' in ' + item.field_trails_in_network_node_title;
}
output += '<div class="map-triangle"><div class="left-triangle"><div></div></div><div class="right-triangle"><div></div></div></div>';
return output;
}
function infoBoxMediaTemplate(item) {
var output = "<a href='" + item.link + "'>";
output += "<img src='" + item.large + "'/></a>";
return output;
}
// VIEWS
Drupal.trails.Views.app = Backbone.View.extend({
initialize : function(element) {
_.bindAll(this, 'renderInfoBox');
// Some high level vars
this.markers = [];
this.mediaMarkers = [];
this.media = {};
this.activeData = {};
this.clicked = false;
// Check for settings
if (Drupal.settings.trailMap) {
if (Drupal.settings.trailMap.args) {
this.args = Drupal.settings.trailMap.args;
}
if (Drupal.settings.trailMap.activeTrail) {
this.activeTrail = Drupal.settings.trailMap.activeTrail;
}
if (Drupal.settings.trailMap.center) {
this.center = Drupal.settings.trailMap.center;
}
}
// Set element
this.setElement(element);
// Catch event from not-front-fitler-block.js
Backbone.pubSub.on('notFrontFilter:change', this.updateView, this);
// Subscribe to state change.
Backbone.pubSub.on('app:changeState', this.handleStateChange, this);
// Create a preloader and store it.
var preloader = new Drupal.trails.Views.Preloader;
this.preloader = preloader.render();
// No Results
var noResults = new Drupal.trails.Views.NoResults;
this.noResults = noResults.render();
// Create a new Google map instance and store it.
this.map = new google.maps.Map(this.el, defaults);
google.maps.event.addListener(this.map, 'zoom_changed', this.handleEvent(this, this.handleZoom));
google.maps.event.addListener(this.map, 'click', this.handleEvent(this, this.handleOffClick));
this.handleEvent(this, this.handleZoom).call(this.map);
// View-wide info box one per map.
this.infoBox = new InfoBox();
google.maps.event.addListener(this.infoBox, 'closeclick', this.handleEvent(this, this.handleOffClick));
// Trigger a single fetch with no filters for initial load.
Backbone.pubSub.trigger('notFrontFilter:change');
},
updateView : function(data) {
Backbone.pubSub.trigger('app:changeState', 'loading');
if (this.args) {
data = data || {};
data['arguments'] = this.args;
}
$.ajax({
'url' : Drupal.settings.basePath + 'gdc/load/view/trails_landing_page/page/computed/node_trails_computed__trails_computed_trail',
'success' : this.handleResponse,
'type' : 'POST',
'data' : {'ajaxPost' : data},
'context' : this,
'cache': false
});
},
handleResponse : function(data) {
var x, m, point, marker, feature, center;
// Clear the map
this.clear();
data = JSON.parse(data);
if (!data || (data && data.length == 0)) {
$(this.el).append(this.noResults);
}
else {
this.noResults.detach();
}
this.currentData = data;
for (x in data) {
// Markers
if (!data[x].start && console && console.log) {
console.log("Corrupt Dataset Skipped: ");
console.log(data[x]);
continue;
}
marker = new google.maps.Marker({
position: new google.maps.LatLng(data[x].start.coordinates[1], data[x].start.coordinates[0]),
map: this.map,
id: x
});
if (data[x].dont_show_trail == "dont_show") {
marker.setVisible(false);
}
marker.addListener('mouseover', this.handleEvent(this, this.handleMouseOver));
marker.addListener('mouseout', this.handleEvent(this, this.handleMouseOut));
marker.addListener('click', this.handleEvent(this, this.handleClick));
this.markers.push(marker);
// Features (i.e. tracts are added via data layer)
if (data[x].geom) {
feature = {};
feature.type = 'Feature';
feature.id = x;
feature.geometry = data[x].geom;
feature.properties = feature.properties || {};
if (this.activeTrail && this.activeTrail == data[x].nid) {
feature.properties.color = '#519D14';
}
else {
feature.properties.color = '#000000';
}
this.map.data.addGeoJson(feature);
}
if (data[x].media && data[x].media.length > 0) {
for (m in data[x].media) {
marker = new google.maps.Marker({
position: new google.maps.LatLng(data[x].media[m].coordinates[1], data[x].media[m].coordinates[0]),
map: this.map,
id: data[x].media[m].link,
icon: data[x].media[m].tiny
});
this.media[data[x].media[m].link] = data[x].media[m];
marker.addListener('click', this.handleEvent(this, this.handleMediaClick));
this.mediaMarkers.push(marker);
}
}
}
if (this.center) {
this.map.setZoom(12);
center = new google.maps.LatLng(this.center[1], this.center[0]);
this.map.panTo(center);
}
this.defaultDataStyles();
this.handleEvent(this, this.handleZoom).call(this.map);
// View-wide info box one per map.
this.map.data.addListener('mouseover', this.handleEvent(this, this.handleMouseOver));
this.map.data.addListener('mouseout', this.handleEvent(this, this.handleMouseOut));
this.map.data.addListener('click', this.handleEvent(this, this.handleClick));
Backbone.pubSub.trigger('app:changeState', 'ready');
},
clear: function() {
var x,
dataLayer = this.map.data;
for (x in this.markers) {
if (this.markers[x]) {
this.markers[x].setMap(null);
//this.markers[x] = null;
}
}
for (x in this.mediaMarkers) {
this.mediaMarkers[x].setMap(null);
}
this.markers = [];
this.mediaMarkers = [];
dataLayer.forEach(
function(feature) {
dataLayer.remove(feature);
}
);
},
defaultDataStyles: function() {
var dataLayer = this.map.data;
dataLayer.map.data.forEach(
function(feature) {
var color = feature.getProperty('color');
dataLayer.overrideStyle(feature, {strokeColor: color, fillColor: color});
}
);
},
renderInfoBox: function(item, marker, template) {
var height, $content, content = template.call(this, item);
this.infoBox.close();
$content = $(content);
height = parseInt($content.height(), 10);
this.infoBox.setOptions({pixelOffset: new google.maps.Size(-100, -(height + 105))});
this.infoBox.setContent(content);
this.infoBox.open(this.map, marker);
},
deactivateFeature: function(id) {
var color, feature = this.map.data.getFeatureById(id);
if (feature) {
color = feature.getProperty('color');
this.map.data.overrideStyle(feature, {strokeColor: color, fillColor: color});
}
},
// Sort of a kludge to handle our need of both contexts:
// Google Maps event context and this view.
// ctx is always this view and this is Google Maps.
handleEvent: function(ctx, callback) {
return function(e) {
callback.call(this, e, ctx);
}
},
handleMouseOver: function(e, ctx) {
var id, feature;
if (typeof(e.feature) != 'undefined') {
feature = e.feature;
id = feature.getId();
}
else if (this.id) {
id = this.id;
feature = ctx.map.data.getFeatureById(id);
}
ctx.map.data.overrideStyle(feature, {strokeColor: '#FFDE00'});
},
handleMouseOut: function(e, ctx) {
var id, feature;
if (typeof(e.feature) != 'undefined') {
feature = e.feature;
id = feature.getId();
}
else if (this.id) {
id = this.id;
feature = ctx.map.data.getFeatureById(id);
}
if (id !== ctx.clicked) {
ctx.deactivateFeature(id);
}
},
handleClick: function(e, ctx) {
var id;
if (ctx.clicked) {
ctx.deactivateFeature(ctx.clicked);
}
if (typeof(e.feature) != 'undefined') {
id = e.feature.getId();
}
else if (this.id) {
id = this.id;
}
if (id && ctx.currentData[id]) {
ctx.clicked = id;
ctx.renderInfoBox(ctx.currentData[id], ctx.markers[id], infoBoxTemplate);
}
},
handleOffClick: function(e, ctx) {
ctx.clicked = null;
ctx.infoBox.close();
ctx.defaultDataStyles();
},
handleMediaClick: function (e, ctx) {
var id, media;
if (this.id) {
id = this.id;
media = ctx.media[id];
ctx.renderInfoBox(media, this, infoBoxMediaTemplate);
}
},
handleZoom: function(e, ctx) {
if (this.zoom >= 9) {
ctx.map.data.setStyle({visible: true});
}
else {
ctx.map.data.setStyle({visible: false});
}
},
handleStateChange: function(state) {
if (state == 'ready') {
this.preloader.detach();
$(this.el).css('opacity', 1);
}
if (state == 'loading') {
$(this.el).after(this.preloader);
$(this.el).css('opacity', .5);
}
}
});
// Preloader
Drupal.trails.Views.Preloader = Backbone.View.extend({
initialize : function() {
_.bindAll(this, 'render');
},
render : function() {
return this.template();
},
template : function() {
$img = $('<img/>');
$img.attr({'src' : '/sites/all/modules/custom/goc_dynamic_components/js/preloader.gif', id : 'preloader'});
$img.css({position : 'absolute', top : 254, left : 318});
$wrapper = $('<div/>').attr('id', 'add-preloader');
$wrapper.css({'position' : 'absolute', 'top': 0, 'left': 0, 'width' : 680, 'height' : 552, 'z-index' : 100});
$wrapper.append($img);
return $wrapper;
}
});
Drupal.trails.Views.NoResults = Backbone.View.extend({
initialize : function() {
_.bindAll(this, 'render');
},
render : function() {
return this.template();
},
template : function() {
var msg = "Your search returned no results. Please try again.",
$div = $('<div/>');
$div.addClass('no-results').text(msg);
return $div;
}
});
Drupal.behaviors.trails = {
attach : function() {
$('#trail-map').once(
'trails',
function() {
// Fire everything off.
var App = new Drupal.trails.Views.app($(this));
}
);
}
};
})(jQuery, Drupal, this, this.document);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment