-
Notifications
You must be signed in to change notification settings - Fork 0
/
googlemaps.php
366 lines (307 loc) · 13.7 KB
/
googlemaps.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
<?php
/* Copyright 2015, 2016 Francis Meyvis*/
/**
* Googlemaps a Grav plugin
*
* This plugin inserts googlemap object(s) into the resulting HTML document
* using Google's google map API service.
*
* Borrows inspiration from the Grav Toc plugin.
*
* Licensed under MIT, see LICENSE.
*
* @package Googlemaps
* @version 1.0.0
* @link <https://github.com/aptly-io/grav-plugin-googlemaps>
* @author Francis Meyvis <https://aptly.io/contact>
* @copyright 2015, 2016 Francis Meyvis
* @license MIT <http://opensource.org/licenses/MIT>
*/
// use following namespace to avoids bin/gpm fails
namespace Grav\Plugin;
use Grav\Common\Plugin;
use RocketTheme\Toolbox\Event\Event;
class GooglemapsPlugin extends Plugin
{
/// marker options
private static $markerFieldNames = [
'location', // the marker's location
'title', // the marker's title appearing when hovering over
'zIndex', // the marker's order among other markers
'timeout', // the marker's dropdown timeout
'info', // the info for the infowindow when clicking the marker
'icon', // the picture URL for replacing the standard marker's look
'link' // the target when clicking the marker
];
/// additional options to override Google's default google map object appearance and behaviour
private static $optionalMapOptions = [
'backgroundColor', // a background colour, pretty when zooming out
'disableDefaultUI', // false disables the default UI controls
'disableDoubleClickZoom', // false disables zoom/center on double click
'draggable', // false disables dragging the map (associated customization options draggableCursor and draggingCursor are not supported)
'draggableCursor', // the name or url of the cursor to display when mousing over a draggable map
'draggingCursor', // the name or url of the cursor to display when the map is being dragged
'keyboardShortcuts', // false prevents map control from the keyboard, enabled by default
'mapTypeControl', // false disables the Map type control
'mapTypeControlOptions', // map type look and feel e.g. {style: 2, position: 11} for DROPDOWN_MENU at bottom center
'maxZoom', // maximal zooming
'minZoom', // mininmal zooming
'scrollwheel', // false prevents zooming by scrollwheel
'streetViewControl', // false disables the street view peg man (avoid for maps without street road overlay)
'streetViewControlOptions', // the streetview look and feel e.g. { position: 11}
'zoomControl', // false disables the zoom control
'zoomControlOptions' // the zoom control look and feel e.g. { position: 11}
];
/// Manage our asset cache because Grav fails to do this properly
protected $assetData;
protected $assetId;
/// Return a list of subscribed events
public static function getSubscribedEvents()
{
return [
'onPluginsInitialized' => ['onPluginsInitialized', 0]
];
}
/// Initialize the plug-in
public function onPluginsInitialized()
{
if ($this->isAdmin()) {
// For speed-up when the admin plugin is active
$this->active = false;
} else {
if ($this->config->get('plugins.googlemaps.enabled')) {
// if the plugin is active globally, subscribe to additional events
$this->enable([
'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0],
// 1 so it has a higher priority than the asset plugin
'onPageInitialized' => ['onPageInitialized', 1]
]);
}
}
}
/// Register the enabled plugin's template PATH
public function onTwigTemplatePaths()
{
$this->grav['twig']->twig_paths[] = __DIR__ . '/templates';
}
/// Get googlemaps' asset from the cache for this page
public function onPageInitialized()
{
// merge global with page specific googlemaps' yaml settings
$defaults = (array) $this->config->get('plugins.googlemaps');
$page = $this->grav['page'];
if (isset($page->header()->googlemaps)) {
$this->config->set('plugins.googlemaps', array_merge($defaults, $page->header()->googlemaps));
}
// subscribe to additional events
$this->enable([
'onPageContentProcessed' => ['onPageContentProcessed', 0],
'onOutputGenerated' => ['onOutputGenerated', 0]
]);
// if the plugin is active on this page
if ($this->config->get('plugins.googlemaps.enabled', false)) {
// get this page's cached assets (if any)
$cache = $this->grav['cache'];
$this->assetId = md5('googlemaps' . $page->path() . $this->grav['language']->getLanguage() . $cache->getKey());
$this->assetData = $cache->fetch($this->assetId);
// set this page's assets from the cache (if any)
// note: if enabled flag for the page itself is false then this introduces
// unnecessary assets (because cache cannot be deleted ...
$this->setAssetsFromData();
$this->assetData = null; // avoid saving again in onOutputGenerated()
}
}
/// Replace place markers [GOOGLEMAPS:<tagid>] with the googlemaps.html.twig to render a google maps
public function onPageContentProcessed(Event $event)
{
$page = $event['page'];
$config = $this->mergeConfig($page);
// get current rendered content
$content = $page->getRawContent();
// replace marker(s) with Google map object(s)
$page->setRawContent($this->processMarkdownContent($content, $config));
// set this page's assets that where dynamically generated
$this->setAssetsFromData();
}
/// Save dynamically generated assets
public function onOutputGenerated()
{
if (isset($this->assetData)) {
$cache = $this->grav['cache'];
if (0 < count($this->assetData) || !method_exists($cache, "delete")) {
$cache->save($this->assetId, $this->assetData);
} else {
$cache->delete($this->assetId);
}
}
}
/// Add all the assets (from cache or dynamically generated) for this page
private function setAssetsFromData()
{
if (is_array($this->assetData)) {
$assets = $this->grav['assets'];
foreach ($this->assetData as $item) {
$assetType = $item['type'];
if ($assetType == 'css') {
$assets->addCss($item['data']);
} elseif ($assetType == 'js') {
$assets->addJs($item['data'], $item['prio'], true, null, $item['where']);
} elseif ($assetType == 'inlinejs') {
$assets->addInlineJs($item['data'], $item['prio'], $item['where']);
}
}
}
}
private function addAssetData($data, $type, $prio, $where)
{
$this->assetData[] = [
'data' => $data,
'type' => $type,
'prio' => $prio,
'where' => $where
];
}
/// Setup the necessary css/js assets
private function addAssets($config)
{
if ($config->get('built_in_css', false)) {
$this->addAssetData('plugin://googlemaps/assets/css/googlemaps.css', 'css', null, null);
}
// need Google's library from the following URL
$googleMapLibUri = 'https://maps.googleapis.com/maps/api/js?v=3';
$language = $this->grav['language']->getLanguage();
if ($language) {
$googleMapLibUri .= '&language=' . $language;
}
$apiKey = $config->get('apiKey', false);
if (!is_bool($apiKey)) {
$googleMapLibUri .= '&key=' . $apiKey; // appends a Google's provided key if any
}
$this->addAssetData($googleMapLibUri, 'js', 3, 'bottom');
// Use normal or minified glue depending on debugging being active
$googleMapGlueUri = ($this->config->get('system.debugger.enabled', false))
? 'plugin://googlemaps/assets/js/googlemaps.js'
: $googleMapGlueUri = 'plugin://googlemaps/assets/js/googlemaps.min.js';
$this->addAssetData($googleMapGlueUri, 'js', 2, 'bottom');
}
/// Setup one marker based on the page's YAML information
private function createMarker($configMarker)
{
$marker = [];
foreach (GooglemapsPlugin::$markerFieldNames as $field) {
if (isset($configMarker[$field])) {
$marker[$field] = $configMarker[$field];
}
}
return $marker;
}
/// Setup a markers array to initialize a specific googlemap HTML object
private function createMarkers($tagid, $config)
{
$markers = [];
if ($config->get($tagid . ".markers")) {
foreach ($config->value($tagid . ".markers") as $marker) {
$markers[] = $this->createMarker($marker);
}
}
return $markers;
}
/// Setup the map's options based on the page's YAML information
private function createMapOptions($tagid, $config)
{
// mandatory options for creating a google map object
$mapOptions = [
// default center: oudegem :-)
'center' => $config->value($tagid . ".center", "51.010009, 4.061270"),
'zoom' => $config->value($tagid . ".zoom", 12),
// an array of possible values: HYBRID, ROADMAP, SATELLITE, TERRAIN
'mapTypeId' => "google.maps.MapTypeId." . $config->value($tagid . ".type", "ROADMAP")
];
// only take those options if specified in the header configuration
foreach (GooglemapsPlugin::$optionalMapOptions as $field) {
$idx = $tagid . "." . $field;
if (isset($config[$idx])) {
$mapOptions[$field] = $config[$idx];
}
}
return $mapOptions;
}
/// Setup the twig variables to initialize a specific googlemap HTML object
private function createGooglemapVars($tagid, $config)
{
// main options for creating a google map object
$mapOptions = $this->createMapOptions($tagid, $config);
// options for populating the map with KML, markers, info windows etc.
$displayOptions = [
'kmlStatus' => $config->value($tagid . ".kmlStatus", "false")
];
if ($config->get($tagid . ".kmlUrl")) {
$displayOptions['kmlUrl'] = $config->get($tagid . ".kmlUrl");
}
$displayOptions['markers'] = $this->createMarkers($tagid, $config);
$vars['googlemaps'] = [
'tagid' => $tagid, // identifies each googlemap object
'mapOptions' => $mapOptions,
'displayOptions' => $displayOptions,
'controlStyle' => $config->value($tagid . ".controlStyle", "")
];
return $vars;
}
/// Replace all found markers with HTML and JS
private function replaceMarkers($config, $regex, $matches, $content)
{
$twig = $this->grav['twig'];
$replacements = [];
foreach ($matches as $match) {
$tagid = strtolower($match['tagid'][0]);
$vars = $this->createGooglemapVars($tagid, $config);
$googlemapsCall = $twig->processTemplate('partials/googlemapsCall' . TEMPLATE_EXT, $vars);
$this->addAssetData($googlemapsCall, 'inlinejs', 1, 'bottom');
$googlemapsVars = $twig->processTemplate('partials/googlemaps' . TEMPLATE_EXT, $vars);
$replacements[] = $googlemapsVars; // Save rendered Googlemap for later replacement
}
return \preg_replace_callback(
$regex,
function ($matches) use ($replacements) {
static $idx = 0;
return $replacements[$idx++];
},
$content
);
}
/// Simply discard all found markers
private function discardMarkers($regex, $matches, $content)
{
return \preg_replace_callback(
$regex,
function ($matches) {
return "";
},
$content
);
}
/// Replace each marker, markers are found based on reg. ex.
private function processMarkdownContent($content, $config)
{
// Find all occurrences of GOOGLEMAP in content
// ~ marks the start and end of the pattern, i is an option for caseless matching
// The pattern to match is
// - [GOOGLEMAPS:
// - some identification (called tagid in the documentation and source)
static $regex = '~\[(GOOGLEMAPS)\:(?P<tagid>[^\:\]]*)\]~i';
$matches = false;
if (\preg_match_all($regex, $content, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
if ($config->get('enabled', true)) {
// Found markers to replace; add necessary css and javascript to the document
$this->addAssets($config);
// Replace individual markers with html
$content = $this->replaceMarkers($config, $regex, $matches, $content);
} else {
// note this causes havoc if another modular contains a googlemap
$this->assetData = []; // Discard individual markers
$content = $this->discardMarkers($regex, $matches, $content);
}
}
return $content;
}
}