1<?php 2/** 3 * DokuWiki Plugin Googlemaps3 4 * 5 * @license GPL 3 (https://www.gnu.org/licenses/gpl-3.0.html) 6 * @author Bernard Condrau <bernard@condrau.com> 7 * @version 2021-05-12, for Google Maps v3 API and DokuWiki Hogfather 8 * @see https://www.dokuwiki.org/plugin:googlemaps3 9 * @see https://www.dokuwiki.org/plugin:googlemaps 10 * 11 * Complete rewrite of Christopher Smith's Google Maps Plugin from 2008 with additional functionality 12 * syntax.php plugin syntax definition 13 */ 14// Must be run within Dokuwiki 15if(!defined('DOKU_INC')) die(); 16 17/** 18 * Syntax for Google Maps v3 19 */ 20class syntax_plugin_googlemaps3 extends DokuWiki_Syntax_Plugin { 21 22 private $mapID = 0; 23 private $markerID = 0; 24 25 private $defaultMapOptions = array( 26 'mapID' => 0, // used to allow css override 27 'type' => 'roadmap', // roadmap, hybrid, satellite, terrain 28 'width' => '', // default style in css file 29 'height' => '', // default style in css file 30 'lat' => 12.57076, // lat+lng are mandatory 31 'lng' => 99.96260, // lat+lng are mandatory 32 'address' => '', 33 'zoom' => 0, // zoom is mandatory 34 'language' => '', // google maps defaults to language set in browser 35 'region' => '', // google maps defaults region bias to US 36 'disableDefaultUI' => 0, // google maps UI defaults 37 'zoomControl' => 1, 38 'mapTypeControl' => 1, 39 'scaleControl' => 1, 40 'streetViewControl' => 1, 41 'rotateControl' => 1, 42 'fullscreenControl' => 1, 43 'kml' => 'off', 44 ); 45 private $defaultMarkerOptions = array( 46 'markerID' => 0, // used to allow css override 47 'lat' => 0, 48 'lng' => 0, 49 'title' => '', 50 'icon' => '', 51 'info' => '', 52 'dir' => '', 53 'img' => '', 54 'width' => '', 55 ); 56 /** 57 * Syntax Type 58 * 59 * Needs to return one of the mode types defined in $PARSER_MODES in parser.php 60 * 61 * @return string 62 */ 63 public function getType() { 64 return 'substition'; 65 } 66 67 /** 68 * Paragraph Handling 69 * 70 * @return string 71 */ 72 public function getPType() { 73 return 'block'; 74 } 75 76 /** 77 * Sort for applying this mode 78 * 79 * @return int 80 */ 81 public function getSort() { 82 return 900; 83 } 84 85 /** 86 * @param string $mode 87 */ 88 function connectTo($mode) { 89 $this->Lexer->addSpecialPattern('<googlemaps3 ?[^>\n]*>.*?</googlemaps3>',$mode,'plugin_googlemaps3'); 90 } 91 92 /** 93 * Handler to prepare matched data for the rendering process 94 * 95 * @param string $match The text matched by the patterns 96 * @param int $state The lexer state for the match 97 * @param int $pos The character position of the matched text 98 * @param Doku_Handler $handler The Doku_Handler object 99 * @return bool|array Return an array with all data you want to use in render, false don't add an instruction 100 */ 101 function handle($match, $state, $pos, Doku_Handler $handler){ 102 103 static $initialised = false; 104 105 list($mapOptions, $markerOptions) = explode('>',substr($match,12,-14),2); 106 107 $map = $this->getMapOptions($mapOptions); 108 $markers = $this->getMarkers($markerOptions); 109 110 // determine width and height (inline styles) for the map image 111 if ($map['width'] || $map['height']) { 112 $style = $map['width'] ? 'width: '.(is_numeric($map['width']) ? $map['width'].'px' : $map['width']).";" : ""; 113 $style .= $map['height'] ? 'height: '.(is_numeric($map['height']) ? $map['height'].'px' : $map['height']).";" : ""; 114// $style = $map['width'] ? 'width: '.$map['width'].";" : ""; 115// $style .= $map['height'] ? 'height: '.$map['height'].";" : ""; 116 $style = "style='$style'"; 117 } else { 118 $style = ''; 119 } 120 unset($map['width'],$map['height']); 121 122 // determine region and language 123 $lang = ($map['region'] ? '®ion='.$map['region'] : ($this->getConf('region') ? '®ion='.$this->getConf('region') : '')); 124 $lang .= ($map['language'] ? '&language='.$map['language'] : ($this->getConf('language') ? '&language='.$this->getConf('language') : '')); 125 unset($map['region'],$map['language']); 126 127 // create a javascript parameter string for the map 128 $jsOptions = ''; 129 foreach ($map as $key => $val) { 130 $jsOptions .= is_numeric($val) ? "$key : $val," : (is_bool($val) ? "$key : ".(int)$val."," : "$key : '".hsc($val)."',"); 131 } 132 133 // create a javascript serialisation of the markers data 134 $jsMarker = ''; 135 if (!empty($markers)) { 136 foreach ($markers as $marker) { 137 $jsMarker .= "{markerID:".$marker['markerID'].", lat:".$marker['lat'].", lng:".$marker['lng']. 138 ($marker['title'] ? ", title:'".$marker['title']."'" : ""). 139 ($marker['icon'] ? ", icon:'".$marker['icon']."'" : ""). 140 ($marker['info'] ? ", info:'".$marker['info']."'" : ""). 141 ($marker['dir'] ? ", dir:'".$marker['dir']."'" : ""). 142 ($marker['img'] ? ", img:'".$marker['img']."'" : ""). 143 ($marker['width'] ? ", width:'".$marker['width']."'" : ""). 144 "},"; 145 } 146 $jsMarker = "marker : [ ".$jsMarker." ]"; 147 } 148 149 if ($initialised) { 150 $jsData = ''; 151 } else { 152 $initialised = true; 153 $jsData = 'var googlemaps3 = new Array();'; 154 } 155 $jsData .= "googlemaps3[googlemaps3.length] = {".$jsOptions.$jsMarker." };"; 156 return array($map['mapID'], $style, $lang, $jsData); 157 } 158 159 /** 160 * Renders the map in the wiki page 161 * 162 * @param string $mode output format being rendered 163 * @param Doku_Renderer $renderer the current renderer object 164 * @param array $data data created by handler() 165 * @return boolean rendered correctly? (however, returned value is not used at the moment) 166 */ 167 function render($mode, Doku_Renderer $renderer, $data) { 168 169 static $initialised = false; 170 171 if ($mode == 'xhtml') { 172 list($mapID, $style, $lang, $jsData) = $data; 173 174 // include script only once 175 if (!$initialised) { 176 $initialised = true; 177 $renderer->doc .= "<script src='https://maps.googleapis.com/maps/api/js?key=".$this->getConf('key').$lang."&callback=initMap' defer></script>"; 178 } 179 $renderer->doc .= "<script>$jsData</script>"; 180 $renderer->doc .= "<div id='googlemaps3map".$mapID."' class='googlemaps3'".($style ? ' '.$style : '')."></div>"; 181 return true; 182 } 183 return false; 184 } 185 186 /** 187 * extract map options 188 * 189 * @param string $pattern string of map options 190 * @return array associative array of map options 191 */ 192 private function getMapOptions($pattern) { 193 194 $options = array(); 195 preg_match_all('/(\w*)="(.*?)"/us', $pattern, $options, PREG_SET_ORDER); 196 197 // parse match for instructions 198 $map = $this->defaultMapOptions; 199 $map['mapID'] = ++$this->mapID; 200 foreach($options as $option) { 201 list($match, $key, $val) = $option; 202 if (isset($map[$key])) if ($key=='kml') $map[$key] = $val; else $map[$key] = strtolower($val); 203 if (isset($map[$key])) { 204 if ($val=='true') $val = 1; elseif ($val=='false') $val = 0; 205 $map[$key] = $val; 206 } 207 } 208 return $map; 209 } 210 211 /** 212 * extract markers information 213 * 214 * @param string $pattern multi-line string of markers 215 * @return array multi-dimensional associative array of markers 216 */ 217 private function getMarkers($pattern) { 218 219 $dlm = $this->getConf('delim'); 220 $markers = array(); 221 preg_match_all('/.+/', $pattern, $lines, PREG_PATTERN_ORDER); // get all markers 222 foreach ($lines[0] AS $line) { 223 preg_match_all('/(?<=\\'.$dlm.'|^|\n)(.*?)(?=\\'.$dlm.'|$|\n)/u', $line, $matches,PREG_PATTERN_ORDER); // get marker options 224 $markers[] = array_combine(array_keys($this->defaultMarkerOptions), array_merge(array(0), $matches[0], array_fill(0, count($this->defaultMarkerOptions)-count($matches[0])-1,''))); 225 } 226 227 // trim leading "delimiter" if any and get default if option empty 228 foreach ($markers as $mark => $marker) { 229 foreach ($marker as $option => $value) { 230 if ($value) $markers[$mark][$option] = ltrim($markers[$mark][$option], $dlm); 231 if (!$value) $markers[$mark][$option] = $this->defaultMarkerOptions[$option]; 232 } 233 } 234 foreach ($markers as $mark => $marker) { 235 $markers[$mark]['markerID'] = ++$this->markerID; 236 if ($markers[$mark]['lat'] == 'address') { 237 $markers[$mark]['lat'] = "'".$markers[$mark]['lat']."'"; 238 $markers[$mark]['lng'] = "'".$markers[$mark]['lng']."'"; 239 } else { 240 $markers[$mark]['lat'] = is_numeric($marker['lat']) ? floatval($marker['lat']) : 0; 241 $markers[$mark]['lng'] = is_numeric($marker['lng']) ? floatval($marker['lng']) : 0; 242 } 243 $markers[$mark]['icon'] = ($marker['icon'] && strpos($marker['icon'], '.') ? $this->getConf('path').$marker['icon'] : $marker['icon']); 244 $markers[$mark]['info'] = str_replace("\n","", p_render("xhtml", p_get_instructions($markers[$mark]['info']), $info)); 245 } 246 return $markers; 247 } 248} 249