%PDF- %PDF-
Direktori : /www/varak.net/wiki.varak.net/extensions/MobileFrontend/includes/transforms/ |
Current File : /www/varak.net/wiki.varak.net/extensions/MobileFrontend/includes/transforms/LazyImageTransform.php |
<?php namespace MobileFrontend\Transforms; use DOMElement; use DOMDocument; class LazyImageTransform implements IMobileTransform { /** * Do not lazy load images smaller than this size (in pixels) * @var int */ const SMALL_IMAGE_DIMENSION_THRESHOLD_IN_PX = 50; /** * Do not lazy load images smaller than this size (in relative to x-height of the current font) * @var int */ const SMALL_IMAGE_DIMENSION_THRESHOLD_IN_EX = 10; /** * Whether to skip the loading of small images * @property $skipSmall */ protected $skipSmall; /** * @param bool $skipSmallImages whether small images should be excluded from lazy loading */ public function __construct( $skipSmallImages = false ) { $this->skipSmall = $skipSmallImages; } /** * Insert a table of content placeholder into the element * which will be progressively enhanced via JS * @param DOMElement $node to be transformed */ public function apply( DOMElement $node ) { $this->doRewriteImagesForLazyLoading( $node, $node->ownerDocument ); } /** * @see MobileFormatter#getImageDimensions * * @param DOMElement $img * @param string $dimension Either "width" or "height" * @return string|null */ private function getImageDimension( DOMElement $img, $dimension ) { $style = $img->getAttribute( 'style' ); $numMatches = preg_match( "/.*?{$dimension} *\: *([^;]*)/", $style, $matches ); if ( !$numMatches && !$img->hasAttribute( $dimension ) ) { return null; } return $numMatches ? trim( $matches[1] ) : $img->getAttribute( $dimension ) . 'px'; } /** * Determine the user perceived width and height of an image element based on `style`, `width`, * and `height` attributes. * * As in the browser, the `style` attribute takes precedence over the `width` and `height` * attributes. If the image has no `style`, `width` or `height` attributes, then the image is * dimensionless. * * @param DOMElement $img <img> element * @return array with width and height parameters if dimensions are found */ public function getImageDimensions( DOMElement $img ) { $result = []; foreach ( [ 'width', 'height' ] as $dimensionName ) { $dimension = $this->getImageDimension( $img, $dimensionName ); if ( $dimension ) { $result[$dimensionName] = $dimension; } } return $result; } /** * Is image dimension small enough to not lazy load it * * @param string $dimension in css format, supports only px|ex units * @return bool */ public function isDimensionSmallerThanThreshold( $dimension ) { $matches = null; if ( preg_match( '/(\d+)(\.\d+)?(px|ex)/', $dimension, $matches ) === 0 ) { return false; } $size = $matches[1]; $unit = array_pop( $matches ); switch ( strtolower( $unit ) ) { case 'px': return $size <= self::SMALL_IMAGE_DIMENSION_THRESHOLD_IN_PX; case 'ex': return $size <= self::SMALL_IMAGE_DIMENSION_THRESHOLD_IN_EX; default: return false; } } /** * @param array $dimensions * @return bool */ private function skipLazyLoadingForSmallDimensions( array $dimensions ) { if ( array_key_exists( 'width', $dimensions ) && $this->isDimensionSmallerThanThreshold( $dimensions['width'] ) ) { return true; }; if ( array_key_exists( 'height', $dimensions ) && $this->isDimensionSmallerThanThreshold( $dimensions['height'] ) ) { return true; } return false; } /** * Enables images to be loaded asynchronously * * @param DOMElement|DOMDocument $el Element or document to rewrite images in. * @param DOMDocument $doc Document to create elements in */ private function doRewriteImagesForLazyLoading( $el, DOMDocument $doc ) { $lazyLoadSkipSmallImages = $this->skipSmall; foreach ( $el->getElementsByTagName( 'img' ) as $img ) { $parent = $img->parentNode; $dimensions = $this->getImageDimensions( $img ); $dimensionsStyle = ( isset( $dimensions['width'] ) ? "width: {$dimensions['width']};" : '' ) . ( isset( $dimensions['height'] ) ? "height: {$dimensions['height']};" : '' ); if ( $lazyLoadSkipSmallImages && $this->skipLazyLoadingForSmallDimensions( $dimensions ) ) { continue; } // HTML only clients $noscript = $doc->createElement( 'noscript' ); // To be loaded image placeholder $imgPlaceholder = $doc->createElement( 'span' ); $imgPlaceholder->setAttribute( 'class', 'lazy-image-placeholder' ); $imgPlaceholder->setAttribute( 'style', $dimensionsStyle ); foreach ( [ 'src', 'alt', 'width', 'height', 'srcset', 'class' ] as $attr ) { if ( $img->hasAttribute( $attr ) ) { $imgPlaceholder->setAttribute( "data-$attr", $img->getAttribute( $attr ) ); } } // Assume data saving and remove srcset attribute from the non-js experience $img->removeAttribute( 'srcset' ); // T145222: Add a non-breaking space inside placeholders to ensure that they do not report // themselves as invisible when inline. $imgPlaceholder->appendChild( $doc->createEntityReference( 'nbsp' ) ); // Set the placeholder where the original image was $parent->replaceChild( $imgPlaceholder, $img ); // Add the original image to the HTML only markup $noscript->appendChild( $img ); // Insert the HTML only markup before the placeholder $parent->insertBefore( $noscript, $imgPlaceholder ); } } }