1<?php 2/** 3 * Image Map 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Tom N Harris <tnharris@whoopdedo.org> 7 */ 8 9// must be run within Dokuwiki 10if (!defined('DOKU_INC')) die(); 11 12if (!defined('DOKU_LF')) define('DOKU_LF', "\n"); 13if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC.'lib/plugins/'); 14 15require_once(DOKU_PLUGIN.'syntax.php'); 16 17class syntax_plugin_imagemap extends DokuWiki_Syntax_Plugin { 18 19 function getType(){ return 'container'; } 20 function getSort(){ return 316; } 21 function getPType(){ return 'block';} 22 function getAllowedTypes() { 23 return array('formatting','substition','disabled','protected','container','paragraphs'); 24 } 25 26 function connectTo($mode) { 27 $this->Lexer->addEntryPattern('\{\{map>[^\}]+\}\}', $mode, 'plugin_imagemap'); 28 } 29 function postConnect() { 30 $this->Lexer->addExitPattern('\{\{<map\}\}', 'plugin_imagemap'); 31 } 32 33 function handle($match, $state, $pos, &$handler){ 34 global $conf; 35 global $ID; 36 $args = array($state); 37 switch ($state) { 38 case DOKU_LEXER_ENTER: 39 $img = Doku_Handler_Parse_Media(substr($match, 6, -2)); 40 if ($img['title']) { 41 $mapname = str_replace(':','',cleanID($img['title'])); 42 $mapname = ltrim($mapname, '0123456789._-'); 43 } 44 if (empty($mapname)) { 45 if ($img['type'] == 'internalmedia') { 46 $src = $img['src']; 47 resolve_mediaid(getNS($ID),$src, $exists); 48 $nssep = ($conf['useslash']) ? '[:;/]' : '[:;]'; 49 $mapname = preg_replace('!.*'.$nssep.'!','',$src); 50 } else { 51 $src = parse_url($img['src']); 52 $mapname = str_replace(':','',cleanID($src['host'].$src['path'].$src['query'])); 53 $mapname = ltrim($mapname, '0123456789._-'); 54 } 55 if (empty($mapname)) { 56 $mapname = 'imagemap'.$pos; 57 } 58 } 59 $args = array($state, $img['type'], $img['src'], $img['title'], $mapname, 60 $img['align'], $img['width'], $img['height'], 61 $img['cache']); 62 63 $ReWriter =& new ImageMap_Handler($mapname, $handler->CallWriter); 64 $handler->CallWriter =& $ReWriter; 65 break; 66 case DOKU_LEXER_EXIT: 67 $handler->CallWriter->process(); 68 $ReWriter =& $handler->CallWriter; 69 $handler->CallWriter =& $ReWriter->CallWriter; 70 break; 71 case DOKU_LEXER_MATCHED: 72 break; 73 case DOKU_LEXER_UNMATCHED: 74 $args[] = $match; 75 break; 76 } 77 return $args; 78 } 79 80 function render($format, &$renderer, $data) { 81 global $conf; 82 global $ID; 83 static $has_content=false; 84 $state = $data[0]; 85 if (substr($format,0,5) == 'xhtml') { 86 switch ($state) { 87 case DOKU_LEXER_ENTER: 88 list($state,$type,$src,$title,$name,$align,$width,$height,$cache) = $data; 89 if ($type=='internalmedia') { 90 resolve_mediaid(getNS($ID),$src, $exists); 91 } 92 $renderer->doc .= '<p>'.DOKU_LF; 93 $src = ml($src,array('w'=>$width,'h'=>$height,'cache'=>$cache)); 94 $renderer->doc .= ' <img src="'.$src.'" class="media'.$align.' imap" usemap="#'.$name.'"'; 95 if($align == 'right' || $align == 'left') 96 $renderer->doc .= ' align="'.$align.'"'; 97 if (!is_null($title)) { 98 $title = $renderer->_xmlEntities($title); 99 $renderer->doc .= ' title="'.$title.'"'; 100 $renderer->doc .= ' alt="'.$title.'"'; 101 } else { 102 $renderer->doc .= ' alt=""'; 103 } 104 if (!is_null($width)) 105 $renderer->doc .= ' width="'.$renderer->_xmlEntities($width).'"'; 106 if (!is_null($height)) 107 $renderer->doc .= ' height="'.$renderer->_xmlEntities($height).'"'; 108 $renderer->doc .= ' />'.DOKU_LF; 109 $renderer->doc .= '</p>'.DOKU_LF; 110 $renderer->doc .= '<map name="'.$name.'">'.DOKU_LF; 111 $has_content = false; 112 break; 113 case DOKU_LEXER_MATCHED: 114 if ($data[1]=='area') { 115 @list($state,$match,$shape,$coords,$type,$title,$url,$extra) = $data; 116 $target = ''; 117 switch ($type) { 118 case 'internallink': 119 if ($url === '') $url = $ID; 120 $default = $renderer->_simpleTitle($url); 121 resolve_pageid(getNS($ID),$url,$exists); 122 $title = $renderer->_getLinkTitle($title, $default, $isImg, $url); 123 list($url,$hash) = explode('#',$url,2); 124 if (!empty($hash)) $hash = $renderer->_headerToLink($hash); 125 $url = wl($url); 126 if ($hash) $url .= '#'.$hash; 127 $target = $conf['target']['wiki']; 128 break; 129 case 'locallink': 130 $title = $renderer->_getLinkTitle($title, $url, $isImg); 131 $url = $renderer->_headerToLink($url); 132 $url = '#'.$url; 133 break; 134 case 'externallink': 135 $title = $renderer->_getLinkTitle($title, $url, $isImg); 136 // url might be an attack vector, only allow registered protocols 137 if(is_null($this->schemes)) $this->schemes = getSchemes(); 138 list($scheme) = explode('://',$url); 139 $scheme = strtolower($scheme); 140 if(!in_array($scheme,$this->schemes)) $url = ''; 141 $target = $conf['target']['extern']; 142 break; 143 case 'interwikilink': 144 $title = $renderer->_getLinkTitle($title, $url, $isImg); 145 $url = $renderer->_resolveInterWiki($extra,$url); 146 if (strpos($url,DOKU_URL) === 0) 147 $target = $conf['target']['wiki']; 148 else 149 $target = $conf['target']['interwiki']; 150 break; 151 case 'emaillink': 152 $url = $renderer->_xmlEntities($url); 153 $url = obfuscate($url); 154 $title = $renderer->_getLinkTitle($title, $url, $isImg); 155 if ($conf['mailguard'] == 'visible') 156 $url = rawurlencode($url); 157 $url = 'mailto:'.$url; 158 break; 159 case 'windowssharelink': 160 $title = $renderer->_getLinkTitle($title, $url, $isImg); 161 $url = str_replace('\\','/',$url); 162 $url = 'file:///'.$url; 163 $target = $conf['target']['windows']; 164 break; 165 case 'externalmedia': 166 list($url,$hash) = explode('#',$url,2); 167 $title = $renderer->_media($url, $title, null,null,null,null, false); 168 $url = ml($url, array('cache'=>$extra[0])); 169 if ($hash) $url .= '#'.$hash; 170 $target = $conf['target']['extern']; 171 break; 172 case 'internalmedia': 173 list($url,$hash) = explode('#',$url,2); 174 resolve_mediaid(getNS($ID), $url, $exists); 175 $title = $renderer->_media($url, $title, null,null,null,null, false); 176 $url = ml($url, array('id'=>$ID,'cache'=>$extra[0]), ($extra[1]=='direct')); 177 if ($hash) $url .= '#'.$hash; 178 break; 179 } 180 if($url){ 181 $renderer->doc .= '<area href="'.$url.'"'; 182 if (!empty($target)) 183 $renderer->doc .= ' target="'.$target.'"'; 184 $renderer->doc .= ' title="'.$title.'" alt="'.$title.'"'; 185 $renderer->doc .= ' shape="'.$shape.'" coords="'.$coords.'" />'; 186 } 187 } elseif ($data[1]=='divstart') { 188 $renderer->doc .= DOKU_LF.'<div class="imapcontent">'.DOKU_LF; 189 $has_content = true; 190 } elseif ($data[1]=='divend') { 191 $renderer->doc .= DOKU_LF;//.'</div>'.DOKU_LF; 192 } 193 break; 194 case DOKU_LEXER_EXIT: 195 if ($has_content) $renderer->doc .= '</div>'.DOKU_LF; 196 $renderer->doc .= '</map>'; 197 break; 198 case DOKU_LEXER_UNMATCHED: 199 $renderer->doc .= $renderer->_xmlEntities($data[1]); 200 break; 201 } 202 return true; 203 } 204 elseif ($format == 'metadata') { 205 switch ($state) { 206 case DOKU_LEXER_ENTER: 207 list($state,$type,$src,$title,$name) = $data; 208 if ($type=='internalmedia') { 209 resolve_mediaid(getNS($ID),$src, $exists); 210 $renderer->meta['relation']['media'][$src] = $exists; 211 } 212 if (is_null($title)) 213 $title = $name; 214 if ($renderer->capture && $title) 215 $renderer->doc .= '['.$title.']'; 216 break; 217 case DOKU_LEXER_EXIT: 218 break; 219 case DOKU_LEXER_UNMATCHED: 220 if ($renderer->capture) 221 $renderer->doc .= $data[1]; 222 break; 223 } 224 return true; 225 } 226 return false; 227 } 228 229} 230 231class ImageMap_Handler { 232 233 var $CallWriter; 234 235 var $calls = array(); 236 var $areas = array(); 237 var $mapname; 238 239 function ImageMap_Handler($name, &$CallWriter) { 240 $this->CallWriter =& $CallWriter; 241 $this->mapname = $name; 242 } 243 244 function writeCall($call) { 245 $this->calls[] = $call; 246 } 247 248 function writeCalls($calls) { 249 $this->calls = array_merge($this->calls, $calls); 250 } 251 252 function finalise() { 253 $last_call = end($this->calls); 254 $this->process(); 255 $this->_addPluginCall(array(DOKU_LEXER_EXIT), $last_call[2]); 256 $this->CallWriter->finalise(); 257 } 258 259 function process() { 260 $last_call = end($this->calls); 261 $first_call = array_shift($this->calls); 262 263 $this->CallWriter->writeCall($first_call); 264 $this->_processLinks($first_call[2]); 265 266 if (!empty($this->calls)) { 267 $this->_addPluginCall(array(DOKU_LEXER_MATCHED,'divstart'), $first_call[2]); 268 //Force a new paragraph 269 $this->CallWriter->writeCall(array('eol',array(),$this->calls[0][2])); 270 $this->CallWriter->writeCalls($this->calls); 271 $this->_addPluginCall(array(DOKU_LEXER_MATCHED,'divend'), $last_call[2]); 272 } 273 } 274 275 function _addPluginCall($args, $pos) { 276 $this->CallWriter->writeCall(array('plugin', 277 array('imagemap', $args, $args[0]), 278 $pos)); 279 } 280 281 function _addArea($pos, $type, $title, $url, $extra=null) { 282 if (preg_match('/^(.*)@([^@]+)$/u', $title, $match)) { 283 $coords = explode(',',$match[2]); 284 if (count($coords) == 3) { 285 $shape = 'circle'; 286 } elseif (count($coords) == 4) { 287 $shape = 'rect'; 288 } elseif (count($coords) >= 6) { 289 $shape = 'poly'; 290 } else { 291 return $title; 292 } 293 $coords = array_map('trim', $coords); 294 $title = trim($match[1]); 295 $this->_addPluginCall(array(DOKU_LEXER_MATCHED, 'area', $shape, join(',',$coords), 296 $type, $title, $url, $extra), $pos); 297 } 298 return $title; 299 } 300 301 function _processLinks($pos) { 302 for ($n=0;$n<count($this->calls);$n++) { 303 $data =& $this->calls[$n][1]; 304 $type = $this->calls[$n][0]; 305 switch ($type) { 306 case 'internallink': 307 case 'locallink': 308 case 'externallink': 309 case 'emaillink': 310 case 'windowssharelink': 311 if (is_array($data[1])) { 312 $title = $data[1]['title']; 313 } else { 314 $title = $data[1]; 315 } 316 $title = $this->_addArea($pos, $type, $title, $data[0]); 317 if (is_array($data[1])) { 318 $data[1]['title'] = $title; 319 } else { 320 $data[1] = $title; 321 } 322 break; 323 case 'interwikilink': 324 if (is_array($data[1])) { 325 $title = $data[1]['title']; 326 } else { 327 $title = $data[1]; 328 } 329 $title = $this->_addArea($pos, $type, $title, $data[3], $data[2]); 330 if (is_array($data[1])) { 331 $data[1]['title'] = $title; 332 } else { 333 $data[1] = $title; 334 } 335 break; 336 case 'externalmedia': 337 case 'internalmedia': 338 if ($data[6] !== 'nolink') { 339 if (is_array($data[1])) { 340 $title = $data[1]['title']; 341 } else { 342 $title = $data[1]; 343 } 344 $title = $this->_addArea($pos, $type, $title, $data[0], array($data[5],$data[6])); 345 if (is_array($data[1])) { 346 $data[1]['title'] = $title; 347 } else { 348 $data[1] = $title; 349 } 350 } 351 break; 352 } 353 } 354 } 355 356} 357 358//Setup VIM: ex: et ts=2 : 359