22 public static $id = 0;
34 $parser->setHook(
'imagemap', [
'ImageMap',
'render' ] );
61 $descTypesCanonical =
'top-right, bottom-right, bottom-left, top-left, none';
63 $defaultLinkAttribs =
false;
80 $bits = explode(
'|',
$line, 2 );
81 if (
count( $bits ) == 1 ) {
88 if ( !$imageTitle || !$imageTitle->inNamespace(
NS_FILE ) ) {
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 );
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();
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 || $coord < 0 ) {
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 );
240 } elseif (
$title->getFragment() !=
'' &&
$title->getPrefixedDBkey() ==
'' ) {
247 if ( $shape !=
'default' ) {
251 $attribs[
'coords'] = implode(
',', $coords );
254 if ( $shape !=
'default' ) {
259 if ( $shape ==
'default' ) {
271 if ( $first || !$imageNode ) {
275 if ( $mapHTML ==
'' ) {
284 $mapName =
"ImageMap_" . ++self::$id .
'_' . mt_rand( 0, 0x7fffffff );
285 $mapHTML =
"<map name=\"$mapName\">\n$mapHTML</map>\n";
288 $imageNode->setAttribute(
'usemap',
"#$mapName" );
292 $anchor = $imageNode->parentNode;
294 $div =
$parent->insertBefore(
new DOMElement(
'div' ), $anchor );
295 $div->setAttribute(
'class',
'noresize' );
296 if ( $defaultLinkAttribs ) {
297 $defaultAnchor = $div->appendChild(
new DOMElement(
'a' ) );
301 $imageParent = $defaultAnchor;
308 if ( $mapHTML !=
'' ) {
309 $mapDoc =
new DOMDocument();
310 $mapDoc->loadXML( $mapHTML );
311 $mapNode = $domDoc->importNode( $mapDoc->documentElement,
true );
312 $div->appendChild( $mapNode );
315 $imageParent->appendChild( $imageNode->cloneNode(
true ) );
316 $parent->removeChild( $anchor );
319 $xpath =
new DOMXPath( $domDoc );
320 $magnify = $xpath->query(
'//div[@class="magnify"]' );
321 if ( !$magnify->length && $descType != self::NONE ) {
323 if ( $descType == self::TOP_LEFT || $descType == self::BOTTOM_LEFT ) {
326 $marginLeft = $thumbWidth - 20;
328 if ( $descType == self::TOP_LEFT || $descType == self::TOP_RIGHT ) {
329 $marginTop = -$thumbHeight;
335 $div->setAttribute(
'style',
"height: {$thumbHeight}px; width: {$thumbWidth}px; " );
336 $descWrapper = $div->appendChild(
new DOMElement(
'div' ) );
337 $descWrapper->setAttribute(
'style',
338 "margin-left: {$marginLeft}px; " .
339 "margin-top: {$marginTop}px; " .
343 $descAnchor = $descWrapper->appendChild(
new DOMElement(
'a' ) );
344 $descAnchor->setAttribute(
'href', $imageTitle->getLocalURL() );
345 $descAnchor->setAttribute(
347 wfMessage(
'imagemap_description' )->inContentLanguage()->
text()
349 $descImg = $descAnchor->appendChild(
new DOMElement(
'img' ) );
350 $descImg->setAttribute(
352 wfMessage(
'imagemap_description' )->inContentLanguage()->
text()
354 $url = $config->get(
'ExtensionAssetsPath' ) .
'/ImageMap/resources/desc-20.png';
355 $descImg->setAttribute(
357 OutputPage::transformResourcePath( $config, $url )
359 $descImg->setAttribute(
'style',
'border: none;' );
365 $output = preg_replace(
'/<\?xml[^?]*\?>/',
'', $domDoc->saveXML(
null, LIBXML_NOEMPTYTAG ) );
394 for ( $i = 0; $i < $count; $i++ ) {
395 $coord = strtok(
" \t" );
396 if ( $coord ===
false ) {
397 return self::error(
'imagemap_missing_coord', $lineNum );
399 if ( !is_numeric( $coord ) || $coord > 1e9 || $coord < 0 ) {
400 return self::error(
'imagemap_invalid_coord', $lineNum );
402 $coords[$i] = $coord;