22 public static $id = 0;
34 $parser->setHook(
'imagemap', [
'ImageMap',
'render' ] );
43 public static function render( $input, $params, $parser ) {
47 $lines = explode(
"\n", $input );
61 $descTypesCanonical =
'top-right, bottom-right, bottom-left, top-left, none';
63 $defaultLinkAttribs =
false;
80 $bits = explode(
'|',
$line, 2 );
81 if ( count( $bits ) == 1 ) {
85 list( $image, $options ) = $bits;
88 if ( !$imageTitle || !$imageTitle->inNamespace(
NS_FILE ) ) {
91 if (
wfIsBadImage( $imageTitle->getDBkey(), $parser->mTitle ) ) {
95 $parsedOptions = $parser->recursiveTagParse( $options );
96 $imageHTML = $parser->makeImage( $imageTitle, $parsedOptions );
97 $parser->replaceLinkHolders( $imageHTML );
98 $imageHTML = $parser->mStripState->unstripBoth( $imageHTML );
99 $imageHTML = Sanitizer::normalizeCharReferences( $imageHTML );
101 $domDoc =
new DOMDocument();
102 Wikimedia\suppressWarnings();
103 $ok = $domDoc->loadXML( $imageHTML );
104 Wikimedia\restoreWarnings();
108 $xpath =
new DOMXPath( $domDoc );
109 $imgs = $xpath->query(
'//img' );
110 if ( !$imgs->length ) {
113 $imageNode = $imgs->item( 0 );
114 $thumbWidth = $imageNode->getAttribute(
'width' );
115 $thumbHeight = $imageNode->getAttribute(
'height' );
118 if ( !$imageObj || !$imageObj->exists() ) {
124 $denominator = $imageObj->getWidth() + $imageObj->getHeight();
125 $numerator = $thumbWidth + $thumbHeight;
126 if ( $denominator <= 0 || $numerator <= 0 ) {
129 $scale = $numerator / $denominator;
134 $cmd = strtok(
$line,
" \t" );
135 if ( $cmd ==
'desc' ) {
136 $typesText =
wfMessage(
'imagemap_desc_types' )->inContentLanguage()->text();
137 if ( $descTypesCanonical != $typesText ) {
139 $typesText = $descTypesCanonical .
', ' . $typesText;
141 $types = array_map(
'trim', explode(
',', $typesText ) );
142 $type = trim( strtok(
'' ) );
143 $descType = array_search(
$type, $types );
144 if ( $descType > 4 ) {
146 $descType = $descType - 5;
149 if ( $descType ===
false || $descType < 0 ) {
150 return self::error(
'imagemap_invalid_desc', $typesText );
157 $link = trim( strstr(
$line,
'[' ) );
159 if ( preg_match(
'/^ \[\[ ([^|]*+) \| ([^\]]*+) \]\] \w* $ /x', $link, $m ) ) {
161 $alt = trim( $m[2] );
162 } elseif ( preg_match(
'/^ \[\[ ([^\]]*+) \]\] \w* $ /x', $link, $m ) ) {
164 if ( is_null(
$title ) ) {
165 return self::error(
'imagemap_invalid_title', $lineNum );
167 $alt =
$title->getFullText();
168 } elseif ( in_array( substr( $link, 1, strpos( $link,
'//' ) + 1 ),
$wgUrlProtocols )
169 || in_array( substr( $link, 1, strpos( $link,
':' ) ),
$wgUrlProtocols )
171 if ( preg_match(
'/^ \[ ([^\s]*+) \s ([^\]]*+) \] \w* $ /x', $link, $m ) ) {
173 $alt = trim( $m[2] );
175 } elseif ( preg_match(
'/^ \[ ([^\]]*+) \] \w* $ /x', $link, $m ) ) {
176 $title = $alt = trim( $m[1] );
180 return self::error(
'imagemap_no_link', $lineNum );
183 return self::error(
'imagemap_invalid_title', $lineNum );
186 $shapeSpec = substr(
$line, 0, -strlen( $link ) );
189 $shape = strtok( $shapeSpec,
" \t" );
196 if ( !is_array( $coords ) ) {
202 if ( !is_array( $coords ) ) {
208 $coord = strtok(
" \t" );
209 while ( $coord !==
false ) {
210 if ( !is_numeric( $coord ) || $coord > 1e9 ) {
211 return self::error(
'imagemap_invalid_coord', $lineNum );
214 $coord = strtok(
" \t" );
216 if ( !count( $coords ) ) {
217 return self::error(
'imagemap_missing_coord', $lineNum );
219 if ( count( $coords ) % 2 !== 0 ) {
220 return self::error(
'imagemap_poly_odd', $lineNum );
224 return self::error(
'imagemap_unrecognised_shape', $lineNum );
228 foreach ( $coords as $i => $c ) {
229 $coords[$i] = (int)round( $c * $scale );
235 $attribs[
'href'] =
$title;
236 $attribs[
'class'] =
'plainlinks';
238 $attribs[
'rel'] =
'nofollow';
240 } elseif (
$title->getFragment() !=
'' &&
$title->getPrefixedDBkey() ==
'' ) {
243 $attribs[
'href'] =
$title->getFragmentForURL();
245 $attribs[
'href'] =
$title->getLocalURL() .
$title->getFragmentForURL();
247 if ( $shape !=
'default' ) {
248 $attribs[
'shape'] = $shape;
251 $attribs[
'coords'] = implode(
',', $coords );
254 if ( $shape !=
'default' ) {
255 $attribs[
'alt'] = $alt;
257 $attribs[
'title'] = $alt;
259 if ( $shape ==
'default' ) {
260 $defaultLinkAttribs = $attribs;
271 if ( $first || !$imageNode ) {
275 if ( $mapHTML ==
'' ) {
286 $mapName =
'ImageMap_' . substr( md5( $mapHTML ), 0, 16 );
287 $mapHTML =
"<map name=\"$mapName\">\n$mapHTML</map>\n";
290 $imageNode->setAttribute(
'usemap',
"#$mapName" );
294 $anchor = $imageNode->parentNode;
295 $parent = $anchor->parentNode;
296 $div = $parent->insertBefore(
new DOMElement(
'div' ), $anchor );
297 $div->setAttribute(
'class',
'noresize' );
298 if ( $defaultLinkAttribs ) {
299 $defaultAnchor = $div->appendChild(
new DOMElement(
'a' ) );
300 foreach ( $defaultLinkAttribs as $name => $value ) {
301 $defaultAnchor->setAttribute( $name, $value );
303 $imageParent = $defaultAnchor;
310 if ( $mapHTML !=
'' ) {
311 $mapDoc =
new DOMDocument();
312 $mapDoc->loadXML( $mapHTML );
313 $mapNode = $domDoc->importNode( $mapDoc->documentElement,
true );
314 $div->appendChild( $mapNode );
317 $imageParent->appendChild( $imageNode->cloneNode(
true ) );
318 $parent->removeChild( $anchor );
321 $xpath =
new DOMXPath( $domDoc );
322 $magnify = $xpath->query(
'//div[@class="magnify"]' );
323 if ( !$magnify->length && $descType != self::NONE ) {
325 if ( $descType == self::TOP_LEFT || $descType == self::BOTTOM_LEFT ) {
328 $marginLeft = $thumbWidth - 20;
330 if ( $descType == self::TOP_LEFT || $descType == self::TOP_RIGHT ) {
331 $marginTop = -$thumbHeight;
337 $div->setAttribute(
'style',
"height: {$thumbHeight}px; width: {$thumbWidth}px; " );
338 $descWrapper = $div->appendChild(
new DOMElement(
'div' ) );
339 $descWrapper->setAttribute(
'style',
340 "margin-left: {$marginLeft}px; " .
341 "margin-top: {$marginTop}px; " .
345 $descAnchor = $descWrapper->appendChild(
new DOMElement(
'a' ) );
346 $descAnchor->setAttribute(
'href', $imageTitle->getLocalURL() );
347 $descAnchor->setAttribute(
349 wfMessage(
'imagemap_description' )->inContentLanguage()->text()
351 $descImg = $descAnchor->appendChild(
new DOMElement(
'img' ) );
352 $descImg->setAttribute(
354 wfMessage(
'imagemap_description' )->inContentLanguage()->text()
356 $url = $config->get(
'ExtensionAssetsPath' ) .
'/ImageMap/resources/desc-20.png';
357 $descImg->setAttribute(
359 OutputPage::transformResourcePath( $config, $url )
361 $descImg->setAttribute(
'style',
'border: none;' );
367 $output = preg_replace(
'/<\?xml[^?]*\?>/',
'', $domDoc->saveXML(
null, LIBXML_NOEMPTYTAG ) );
370 foreach ( $links as
$title ) {
375 $parser->mOutput->addImage(
$title->getDBkey() );
378 $parser->mOutput->addLink(
$title );
381 foreach ( $extLinks as
$title ) {
382 $parser->mOutput->addExternalLink(
$title );
396 for ( $i = 0; $i < $count; $i++ ) {
397 $coord = strtok(
" \t" );
398 if ( $coord ===
false ) {
399 return self::error(
'imagemap_missing_coord', $lineNum );
401 if ( !is_numeric( $coord ) || $coord > 1e9 || $coord < 0 ) {
402 return self::error(
'imagemap_invalid_coord', $lineNum );
404 $coords[$i] = $coord;
415 return '<p class="error">' .
wfMessage( $name,
$line )->parse() .
'</p>';