From 653acae812fe237e5c73d68a1bca501be4d09eaf Mon Sep 17 00:00:00 2001 From: Lorenz Ulrich Date: Thu, 18 Mar 2021 23:04:40 +0100 Subject: [PATCH 1/3] [TASK] Update for TYPO3 10 LTS, cleanup extension --- Classes/DataProcessing/GalleryProcessor.php | 511 ------------------ .../ResponsiveGalleryProcessor.php | 5 +- Classes/Service/SrcSetService.php | 14 +- .../ViewHelpers/ResponsiveImageViewHelper.php | 14 +- Classes/ViewHelpers/SrcSetViewHelper.php | 8 +- Configuration/Services.yaml | 14 + Configuration/Styling/setup.typoscript | 60 -- Configuration/TSconfig/Page/All.typoscript | 4 +- Resources/Private/Language/de.locallang.xlf | 2 +- Resources/Private/TypoScript/setup.typoscript | 9 +- composer.json | 28 +- ext_emconf.php | 44 +- ext_localconf.php | 12 +- 13 files changed, 80 insertions(+), 645 deletions(-) delete mode 100644 Classes/DataProcessing/GalleryProcessor.php create mode 100644 Configuration/Services.yaml delete mode 100644 Configuration/Styling/setup.typoscript diff --git a/Classes/DataProcessing/GalleryProcessor.php b/Classes/DataProcessing/GalleryProcessor.php deleted file mode 100644 index 0b54e0c..0000000 --- a/Classes/DataProcessing/GalleryProcessor.php +++ /dev/null @@ -1,511 +0,0 @@ - [ - 'center' => [0, 8], - 'right' => [1, 9, 17, 25], - 'left' => [2, 10, 18, 26] - ], - 'vertical' => [ - 'above' => [0, 1, 2], - 'intext' => [17, 18, 25, 26], - 'below' => [8, 9, 10] - ] - ]; - - /** - * Storage for processed data - * - * @var array - */ - protected $galleryData = [ - 'position' => [ - 'horizontal' => '', - 'vertical' => '', - 'noWrap' => false - ], - 'width' => 0, - 'count' => [ - 'files' => 0, - 'columns' => 0, - 'rows' => 0, - ], - 'columnSpacing' => 0, - 'border' => [ - 'enabled' => false, - 'width' => 0, - 'padding' => 0, - ], - 'rows' => [] - ]; - - /** - * @var int - */ - protected $numberOfColumns; - - /** - * @var int - */ - protected $mediaOrientation; - - /** - * @var int - */ - protected $maxGalleryWidth; - - /** - * @var int - */ - protected $maxGalleryWidthInText; - - /** - * @var int - */ - protected $equalMediaHeight; - - /** - * @var int - */ - protected $equalMediaWidth; - - /** - * @var int - */ - protected $columnSpacing; - - /** - * @var bool - */ - protected $borderEnabled; - - /** - * @var int - */ - protected $borderWidth; - - /** - * @var int - */ - protected $borderPadding; - - /** - * @var string - */ - protected $cropVariant = 'default'; - - /** - * The (filtered) media files to be used in the gallery - * - * @var FileInterface[] - */ - protected $fileObjects = []; - - /** - * The calculated dimensions for each media element - * - * @var array - */ - protected $mediaDimensions = []; - - /** - * Process data for a gallery, for instance the CType "textmedia" - * - * @param ContentObjectRenderer $cObj The content object renderer, which contains data of the content element - * @param array $contentObjectConfiguration The configuration of Content Object - * @param array $processorConfiguration The configuration of this processor - * @param array $processedData Key/value store of processed data (e.g. to be passed to a Fluid View) - * @return array the processed data as key/value store - * @throws ContentRenderingException - */ - public function process( - ContentObjectRenderer $cObj, - array $contentObjectConfiguration, - array $processorConfiguration, - array $processedData - ) { - if (isset($processorConfiguration['if.']) && !$cObj->checkIf($processorConfiguration['if.'])) { - return $processedData; - } - - $this->contentObjectRenderer = $cObj; - $this->processorConfiguration = $processorConfiguration; - - $filesProcessedDataKey = (string)$cObj->stdWrapValue( - 'filesProcessedDataKey', - $processorConfiguration, - 'files' - ); - if (isset($processedData[$filesProcessedDataKey]) && is_array($processedData[$filesProcessedDataKey])) { - $this->fileObjects = $processedData[$filesProcessedDataKey]; - $this->galleryData['count']['files'] = count($this->fileObjects); - } else { - throw new ContentRenderingException('No files found for key ' . $filesProcessedDataKey . ' in $processedData.', 1436809789); - } - - $this->numberOfColumns = (int)$this->getConfigurationValue('numberOfColumns', 'imagecols'); - $this->mediaOrientation = (int)$this->getConfigurationValue('mediaOrientation', 'imageorient'); - $this->maxGalleryWidth = (int)$this->getConfigurationValue('maxGalleryWidth') ?: 600; - $this->maxGalleryWidthInText = (int)$this->getConfigurationValue('maxGalleryWidthInText') ?: 300; - $this->equalMediaHeight = (int)$this->getConfigurationValue('equalMediaHeight', 'imageheight'); - $this->equalMediaWidth = (int)$this->getConfigurationValue('equalMediaWidth', 'imagewidth'); - $this->columnSpacing = (int)$this->getConfigurationValue('columnSpacing'); - $this->borderEnabled = (bool)$this->getConfigurationValue('borderEnabled', 'imageborder'); - $this->borderWidth = (int)$this->getConfigurationValue('borderWidth'); - $this->borderPadding = (int)$this->getConfigurationValue('borderPadding'); - $this->cropVariant = $this->getConfigurationValue('cropVariant') ?: 'default'; - - $this->determineGalleryPosition(); - $this->determineMaximumGalleryWidth(); - - $this->calculateRowsAndColumns(); - $this->calculateMediaWidthsAndHeights(); - - $this->prepareGalleryData(); - - $targetFieldName = (string)$cObj->stdWrapValue( - 'as', - $processorConfiguration, - 'gallery' - ); - - $processedData[$targetFieldName] = $this->galleryData; - - return $processedData; - } - - /** - * Get configuration value from processorConfiguration - * with when $dataArrayKey fallback to value from cObj->data array - * - * @param string $key - * @param string|null $dataArrayKey - * @return string - */ - protected function getConfigurationValue($key, $dataArrayKey = null) - { - $defaultValue = ''; - if ($dataArrayKey && isset($this->contentObjectRenderer->data[$dataArrayKey])) { - $defaultValue = $this->contentObjectRenderer->data[$dataArrayKey]; - } - return $this->contentObjectRenderer->stdWrapValue( - $key, - $this->processorConfiguration, - $defaultValue - ); - } - - /** - * Define the gallery position - * - * Gallery has a horizontal and a vertical position towards the text - * and a possible wrapping of the text around the gallery. - */ - protected function determineGalleryPosition() - { - foreach ($this->availableGalleryPositions as $positionDirectionKey => $positionDirectionValue) { - foreach ($positionDirectionValue as $positionKey => $positionArray) { - if (in_array($this->mediaOrientation, $positionArray, true)) { - $this->galleryData['position'][$positionDirectionKey] = $positionKey; - } - } - } - - if ($this->mediaOrientation === 25 || $this->mediaOrientation === 26) { - $this->galleryData['position']['noWrap'] = true; - } - } - - /** - * Get the gallery width based on vertical position - */ - protected function determineMaximumGalleryWidth() - { - if ($this->galleryData['position']['vertical'] === 'intext') { - $this->galleryData['width'] = $this->maxGalleryWidthInText; - } else { - $this->galleryData['width'] = $this->maxGalleryWidth; - } - } - - /** - * Calculate the amount of rows and columns - */ - protected function calculateRowsAndColumns() - { - - // If no columns defined, set it to 1 - $columns = max((int)$this->numberOfColumns, 1); - - // When more columns than media elements, set the columns to the amount of media elements - if ($columns > $this->galleryData['count']['files']) { - $columns = $this->galleryData['count']['files']; - } - - if ($columns === 0) { - $columns = 1; - } - - // Calculate the rows from the amount of files and the columns - $rows = ceil($this->galleryData['count']['files'] / $columns); - - $this->galleryData['count']['columns'] = $columns; - $this->galleryData['count']['rows'] = (int)$rows; - } - - /** - * Calculate the width/height of the media elements - * - * Based on the width of the gallery, defined equal width or height by a user, the spacing between columns and - * the use of a border, defined by user, where the border width and padding are taken into account - * - * File objects MUST already be filtered. They need a height and width to be shown in the gallery - */ - protected function calculateMediaWidthsAndHeights() - { - $columnSpacingTotal = ($this->galleryData['count']['columns'] - 1) * $this->columnSpacing; - - $galleryWidthMinusBorderAndSpacing = max($this->galleryData['width'] - $columnSpacingTotal, 1); - - if ($this->borderEnabled) { - $borderPaddingTotal = ($this->galleryData['count']['columns'] * 2) * $this->borderPadding; - $borderWidthTotal = ($this->galleryData['count']['columns'] * 2) * $this->borderWidth; - $galleryWidthMinusBorderAndSpacing = $galleryWidthMinusBorderAndSpacing - $borderPaddingTotal - $borderWidthTotal; - } - - // User entered a predefined height - if ($this->equalMediaHeight) { - $mediaScalingCorrection = 1; - $maximumRowWidth = 0; - - // Calculate the scaling correction when the total of media elements is wider than the gallery width - for ($row = 1; $row <= $this->galleryData['count']['rows']; $row++) { - $totalRowWidth = 0; - for ($column = 1; $column <= $this->galleryData['count']['columns']; $column++) { - $fileKey = (($row - 1) * $this->galleryData['count']['columns']) + $column - 1; - if ($fileKey > $this->galleryData['count']['files'] - 1) { - break 2; - } - $currentMediaScaling = $this->equalMediaHeight / max($this->getCroppedDimensionalProperty($this->fileObjects[$fileKey], 'height'), 1); - $totalRowWidth += $this->getCroppedDimensionalProperty($this->fileObjects[$fileKey], 'width') * $currentMediaScaling; - } - $maximumRowWidth = max($totalRowWidth, $maximumRowWidth); - $mediaInRowScaling = $totalRowWidth / $galleryWidthMinusBorderAndSpacing; - $mediaScalingCorrection = max($mediaInRowScaling, $mediaScalingCorrection); - } - - // Set the corrected dimensions for each media element - foreach ($this->fileObjects as $key => $fileObject) { - $mediaHeight = floor($this->equalMediaHeight / $mediaScalingCorrection); - $mediaWidth = floor( - $this->getCroppedDimensionalProperty($fileObject, 'width') * ($mediaHeight / max($this->getCroppedDimensionalProperty($fileObject, 'height'), 1)) - ); - $this->mediaDimensions[$key] = [ - 'width' => $mediaWidth, - 'height' => $mediaHeight - ]; - } - - // Recalculate gallery width - $this->galleryData['width'] = floor($maximumRowWidth / $mediaScalingCorrection); - - // User entered a predefined width - } elseif ($this->equalMediaWidth) { - $mediaScalingCorrection = 1; - - // Calculate the scaling correction when the total of media elements is wider than the gallery width - $totalRowWidth = $this->galleryData['count']['columns'] * $this->equalMediaWidth; - $mediaInRowScaling = $totalRowWidth / $galleryWidthMinusBorderAndSpacing; - $mediaScalingCorrection = max($mediaInRowScaling, $mediaScalingCorrection); - - // Set the corrected dimensions for each media element - foreach ($this->fileObjects as $key => $fileObject) { - $mediaWidth = floor($this->equalMediaWidth / $mediaScalingCorrection); - $mediaHeight = floor( - $this->getCroppedDimensionalProperty($fileObject, 'height') * ($mediaWidth / max($this->getCroppedDimensionalProperty($fileObject, 'width'), 1)) - ); - $this->mediaDimensions[$key] = [ - 'width' => $mediaWidth, - 'height' => $mediaHeight - ]; - } - - // Recalculate gallery width - $this->galleryData['width'] = floor($totalRowWidth / $mediaScalingCorrection); - - // Automatic setting of width and height - } else { - $maxMediaWidth = (int)($galleryWidthMinusBorderAndSpacing / $this->galleryData['count']['columns']); - foreach ($this->fileObjects as $key => $fileObject) { - $mediaWidth = min($maxMediaWidth, $this->getCroppedDimensionalProperty($fileObject, 'width')); - $mediaHeight = floor( - $this->getCroppedDimensionalProperty($fileObject, 'height') * ($mediaWidth / max($this->getCroppedDimensionalProperty($fileObject, 'width'), 1)) - ); - $this->mediaDimensions[$key] = [ - 'width' => $mediaWidth, - 'height' => $mediaHeight - ]; - } - } - } - - /** - * When retrieving the height or width for a media file - * a possible cropping needs to be taken into account. - * - * @param FileInterface $fileObject - * @param string $dimensionalProperty 'width' or 'height' - * - * @return int - */ - protected function getCroppedDimensionalProperty(FileInterface $fileObject, $dimensionalProperty) - { - if (!$fileObject->hasProperty('crop') || empty($fileObject->getProperty('crop'))) { - return $fileObject->getProperty($dimensionalProperty); - } - - $croppingConfiguration = $fileObject->getProperty('crop'); - $cropVariantCollection = CropVariantCollection::create((string)$croppingConfiguration); - return (int)$cropVariantCollection->getCropArea($this->cropVariant)->makeAbsoluteBasedOnFile($fileObject)->asArray()[$dimensionalProperty]; - } - - /** - * Prepare the gallery data - * - * Make an array for rows, columns and configuration - */ - protected function prepareGalleryData() - { - for ($row = 1; $row <= $this->galleryData['count']['rows']; $row++) { - for ($column = 1; $column <= $this->galleryData['count']['columns']; $column++) { - $fileKey = (($row - 1) * $this->galleryData['count']['columns']) + $column - 1; - - $this->galleryData['rows'][$row]['columns'][$column] = [ - 'media' => $this->fileObjects[$fileKey] ?? null, - 'dimensions' => [ - 'width' => $this->mediaDimensions[$fileKey]['width'] ?? null, - 'height' => $this->mediaDimensions[$fileKey]['height'] ?? null - ] - ]; - } - } - - $this->galleryData['columnSpacing'] = $this->columnSpacing; - $this->galleryData['border']['enabled'] = $this->borderEnabled; - $this->galleryData['border']['width'] = $this->borderWidth; - $this->galleryData['border']['padding'] = $this->borderPadding; - } -} diff --git a/Classes/DataProcessing/ResponsiveGalleryProcessor.php b/Classes/DataProcessing/ResponsiveGalleryProcessor.php index 9ddd96e..daac449 100644 --- a/Classes/DataProcessing/ResponsiveGalleryProcessor.php +++ b/Classes/DataProcessing/ResponsiveGalleryProcessor.php @@ -2,11 +2,12 @@ namespace Visol\Viresponsiveimages\DataProcessing; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; +use TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException; use TYPO3\CMS\Frontend\DataProcessing\GalleryProcessor; /** - * passing imageorient to fluid where a custom viewhelper handles the output of sourcesets - * based on several settings and can be used for f.i. the CType "textmedia" + * Passing imageorient to fluid where a custom Viewhelper handles the output of source sets + * based on several settings and can be used for e.g. the CType "textmedia" */ class ResponsiveGalleryProcessor extends GalleryProcessor { diff --git a/Classes/Service/SrcSetService.php b/Classes/Service/SrcSetService.php index 99f8fd0..13f57f4 100644 --- a/Classes/Service/SrcSetService.php +++ b/Classes/Service/SrcSetService.php @@ -14,24 +14,24 @@ use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection; use TYPO3\CMS\Core\Resource\AbstractFile; use TYPO3\CMS\Core\Resource\FileInterface; +use TYPO3\CMS\Extbase\Service\ImageService; /** * Render the srcset attribute with responsive images. Accepts mostly the same parameters as the uri.image ViewHelper of the Neos.Media package: * asset, maximumWidth, maximumHeight, allowCropping, ratio. - * */ class SrcSetService { /** - * @var \TYPO3\CMS\Extbase\Service\ImageService + * @var ImageService */ protected $imageService; /** - * @param \TYPO3\CMS\Extbase\Service\ImageService $imageService + * @param ImageService $imageService */ - public function injectImageService(\TYPO3\CMS\Extbase\Service\ImageService $imageService) + public function injectImageService(ImageService $imageService) { $this->imageService = $imageService; } @@ -43,10 +43,10 @@ public function injectImageService(\TYPO3\CMS\Extbase\Service\ImageService $imag * @param float $ratio * @param int $maximumWidth * @param int $maximumHeight - * @param boolean $allowCropping - * @param int $quality + * @param string $crop + * @param string $cropVariant * @param array $sizes - * @param RequestInterface $request + * @param boolean $absolute * @return string * @throws \Exception */ diff --git a/Classes/ViewHelpers/ResponsiveImageViewHelper.php b/Classes/ViewHelpers/ResponsiveImageViewHelper.php index 915412e..e1f5042 100644 --- a/Classes/ViewHelpers/ResponsiveImageViewHelper.php +++ b/Classes/ViewHelpers/ResponsiveImageViewHelper.php @@ -16,7 +16,8 @@ use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Extbase\Utility\DebuggerUtility; +use TYPO3\CMS\Fluid\ViewHelpers\ImageViewHelper; +use Visol\Viresponsiveimages\Service\SrcSetService; /** * Resizes a given image (if required) and renders the respective img tag @@ -62,7 +63,7 @@ * Could not get image resource for "NonExistingImage.png". * */ -class ResponsiveImageViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\ImageViewHelper +class ResponsiveImageViewHelper extends ImageViewHelper { /** @@ -70,21 +71,20 @@ class ResponsiveImageViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\ImageViewHe */ const RATIO_PATTERN = '/(\d+):(\d+)/'; - /** * @var string */ protected $tagName = 'img'; /** - * @var \Visol\Viresponsiveimages\Service\SrcSetService + * @var SrcSetService */ protected $srcSetService; /** - * @param \Visol\Viresponsiveimages\Service\SrcSetService $srcSetService + * @param SrcSetService $srcSetService */ - public function injectSrcSetService(\Visol\Viresponsiveimages\Service\SrcSetService $srcSetService) + public function injectSrcSetService(SrcSetService $srcSetService) { $this->srcSetService = $srcSetService; } @@ -159,7 +159,7 @@ public function render() } $sizes = GeneralUtility::intExplode(',', $sizesCsv, true); - $srcSetString = $this->srcSetService->getSrcSetAttribute($image, $ratio, $maximumWidth, $maximumHeight, $crop, $cropVariant, $sizes, null); + $srcSetString = $this->srcSetService->getSrcSetAttribute($image, $ratio, $maximumWidth, $maximumHeight, $crop, $cropVariant, $sizes); $classNames = ['lazyload']; if (isset($this->arguments['class'])) { $classNames[] = $this->arguments['class']; diff --git a/Classes/ViewHelpers/SrcSetViewHelper.php b/Classes/ViewHelpers/SrcSetViewHelper.php index ccf7961..b7d1e86 100644 --- a/Classes/ViewHelpers/SrcSetViewHelper.php +++ b/Classes/ViewHelpers/SrcSetViewHelper.php @@ -16,6 +16,8 @@ use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; +use TYPO3Fluid\Fluid\Core\ViewHelper\Exception; /** * Resizes a given image (if required) and renders the respective img tag @@ -61,7 +63,7 @@ * Could not get image resource for "NonExistingImage.png". * */ -class SrcSetViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper +class SrcSetViewHelper extends AbstractViewHelper { const RATIO_PATTERN = '/(\d):(\d)/'; @@ -122,7 +124,7 @@ public function initializeArguments() * * @see https://docs.typo3.org/typo3cms/TyposcriptReference/ContentObjects/Image/ * - * @throws \TYPO3\CMS\Fluid\Core\ViewHelper\Exception + * @throws Exception * @return string Rendered tag */ public function render() @@ -156,7 +158,7 @@ public function render() } $sizes = GeneralUtility::intExplode(',', $sizesCsv, true); - $srcSetString = $this->srcSetService->getSrcSetAttribute($image, $ratio, $maximumWidth, $maximumHeight, $crop, $cropVariant, $sizes, null); + $srcSetString = $this->srcSetService->getSrcSetAttribute($image, $ratio, $maximumWidth, $maximumHeight, $crop, $cropVariant, $sizes); } catch (ResourceDoesNotExistException $e) { // thrown if file does not exist } catch (\UnexpectedValueException $e) { diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml new file mode 100644 index 0000000..ec18901 --- /dev/null +++ b/Configuration/Services.yaml @@ -0,0 +1,14 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + Visol\Viresponsiveimages\: + resource: '../Classes/*' + + Visol\Viresponsiveimages\Service\SrcSetService: + public: true + + Visol\Viresponsiveimages\DataProcessing\ResponsiveGalleryProcessor: + public: true diff --git a/Configuration/Styling/setup.typoscript b/Configuration/Styling/setup.typoscript deleted file mode 100644 index d0828c4..0000000 --- a/Configuration/Styling/setup.typoscript +++ /dev/null @@ -1,60 +0,0 @@ -plugin.tx_viresponsiveimages._CSS_DEFAULT_STYLE ( - - figure.responsive { - display: block; - } - - figcaption.responsive { width: 100%; display: block } - - img.img-responsive, img.img-fluid { - width: 100%; - display: block; - } - - .csc-textpic-responsive .ce-column { width: 100% } - - .csc-textpic-responsive.csc-textpic-100 .ce-column { width: 100% } - .csc-textpic-responsive.csc-textpic-50 .ce-column { width: 50% } - .csc-textpic-responsive.csc-textpic-33 .ce-column { width: 33% } - .csc-textpic-responsive.csc-textpic-25 .ce-column { width: 25% } - - /* above/below text */ - - .csc-textpic-responsive.csc-textpic-100 img.img.img-fluid { width: 100% } - .csc-textpic-responsive.csc-textpic-50 img.img.img-fluid { width: 50% } - .csc-textpic-responsive.csc-textpic-33 img.img.img-fluid { width: 33% } - .csc-textpic-responsive.csc-textpic-25 img.img.img-fluid { width: 25% } - - /* in text */ - - .csc-textpic-responsive.csc-textpic-50.csc-textpic-intext-left .ce-gallery { width: 50%; float: left; margin-right: 15px; margin-bottom: 15px; } - .csc-textpic-responsive.csc-textpic-50.csc-textpic-intext-left .ce-column { width: 100% } - - .csc-textpic-responsive.csc-textpic-50.csc-textpic-intext-right .ce-gallery { width: 50%; float: right; margin-left: 15px; margin-bottom: 15px; } - .csc-textpic-responsive.csc-textpic-50.csc-textpic-intext-right .ce-column { width: 100% } - - .csc-textpic-responsive.csc-textpic-33.csc-textpic-intext-left .ce-gallery { width: 33%; float: left; margin-right: 15px; margin-bottom: 0; } - .csc-textpic-responsive.csc-textpic-33.csc-textpic-intext-left .ce-column { width: 100% } - - .csc-textpic-responsive.csc-textpic-33.csc-textpic-intext-right .ce-gallery { width: 33%; float: right; margin-left: 15px; margin-bottom: 0; } - .csc-textpic-responsive.csc-textpic-33.csc-textpic-intext-right .ce-column { width: 100% } - - .csc-textpic-responsive.csc-textpic-25.csc-textpic-intext-left .ce-gallery { width: 25%; float: left; margin-right: 15px; margin-bottom: 0; } - .csc-textpic-responsive.csc-textpic-25.csc-textpic-intext-left .ce-column { width: 100% } - - .csc-textpic-responsive.csc-textpic-25.csc-textpic-intext-right .ce-gallery { width: 25%; float: right; margin-left: 15px; margin-bottom: 0; } - .csc-textpic-responsive.csc-textpic-25.csc-textpic-intext-right .ce-column { width: 100% } - - .csc-textpic-responsive.csc-textpic-12.csc-textpic-intext-left .ce-gallery { width: 12%; float: left; margin-right: 15px; margin-bottom: 0; } - .csc-textpic-responsive.csc-textpic-12.csc-textpic-intext-left .ce-column { width: 100% } - - .csc-textpic-responsive.csc-textpic-12.csc-textpic-intext-right .ce-gallery { width: 12%; float: right; margin-left: 15px; margin-bottom: 0; } - .csc-textpic-responsive.csc-textpic-12.csc-textpic-intext-right .ce-column { width: 100% } - - /* besite text */ - - - - /* galleries */ - -) diff --git a/Configuration/TSconfig/Page/All.typoscript b/Configuration/TSconfig/Page/All.typoscript index 0b14048..efe2cc7 100644 --- a/Configuration/TSconfig/Page/All.typoscript +++ b/Configuration/TSconfig/Page/All.typoscript @@ -1,5 +1,5 @@ # # PageTS for viresponsiveimages # - - +@import 'EXT:viresponsiveimages/Configuration/TSconfig/Page/TCEFORM.typoscript' +@import 'EXT:viresponsiveimages/Configuration/TSconfig/Page/TCEMAIN.typoscript' diff --git a/Resources/Private/Language/de.locallang.xlf b/Resources/Private/Language/de.locallang.xlf index 4c57c8d..bd9c9b8 100644 --- a/Resources/Private/Language/de.locallang.xlf +++ b/Resources/Private/Language/de.locallang.xlf @@ -7,7 +7,7 @@ Standard-TSConfig für responsive Images aktivieren. Falls diese Option deaktiviert ist, muss die TSConfig manuell eingebunden werden. - Im text + Im Text Neben dem Text diff --git a/Resources/Private/TypoScript/setup.typoscript b/Resources/Private/TypoScript/setup.typoscript index 590a228..83a3f44 100644 --- a/Resources/Private/TypoScript/setup.typoscript +++ b/Resources/Private/TypoScript/setup.typoscript @@ -9,9 +9,6 @@ page.includeJSFooterlibs { lazysizes9core = EXT:viresponsiveimages/Resources/Private/Javascripts/lazysizes/lazysizes.min.js } - - - - - - +@import 'EXT:viresponsiveimages/Resources/Private/TypoScript/ContentElement/Image.typoscript' +@import 'EXT:viresponsiveimages/Resources/Private/TypoScript/ContentElement/Textpic.typoscript' +@import 'EXT:viresponsiveimages/Resources/Private/TypoScript/ContentElement/Textmedia.typoscript' diff --git a/composer.json b/composer.json index 798d1e8..fbf0ad7 100644 --- a/composer.json +++ b/composer.json @@ -1,15 +1,17 @@ { - "name": "visol/viresponsiveimages", - "type": "typo3-cms-extension", - "description": "Responsive images for fluid-styled-content", - "homepage": "https://github.com/visol/ext-viresponsiveimages", - "license": ["GPL-2.0+"], - "require": { - "typo3/cms-core": ">8.7.25" - }, - "autoload": { - "psr-4": { - "Visol\\Viresponsiveimages\\": "Classes/" - } - } + "name": "visol/viresponsiveimages", + "type": "typo3-cms-extension", + "description": "Responsive images for fluid_styled_content", + "homepage": "https://github.com/visol/ext-viresponsiveimages", + "license": [ + "GPL-2.0-or-later" + ], + "require": { + "typo3/cms-core": ">10.4.0" + }, + "autoload": { + "psr-4": { + "Visol\\Viresponsiveimages\\": "Classes/" + } + } } diff --git a/ext_emconf.php b/ext_emconf.php index 968f656..399df5d 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -1,32 +1,22 @@ 'Responsive Images for fluid-styled-content', - 'description' => 'Configurable backend and frontend implementation of the responsive image handling.', - 'category' => 'fe', - 'author' => 'Visol', - 'author_email' => 'info@visol.ch', - 'state' => 'beta', - 'internal' => '', - 'uploadfolder' => '0', - 'createDirs' => '', - 'clearCacheOnLoad' => 0, - 'version' => '0.9.15', - 'constraints' => [ - 'depends' => [ - 'typo3' => '8.7.25-9.5.99', +$EM_CONF['viresponsiveimages'] = [ + 'title' => 'Responsive Images for fluid_styled_content', + 'description' => 'Configurable backend and frontend implementation of the responsive image handling.', + 'category' => 'fe', + 'author' => 'visol digitale Dienstleistungen GmbH', + 'author_email' => 'support@visol.ch', + 'state' => 'stable', + 'internal' => '', + 'uploadfolder' => '0', + 'createDirs' => '', + 'clearCacheOnLoad' => 0, + 'version' => '1.0.0', + 'constraints' => [ + 'depends' => [ + 'typo3' => '10.4.0-10.4.99', ], - 'conflicts' => [], - 'suggests' => [], + 'conflicts' => [], + 'suggests' => [], ], ]; diff --git a/ext_localconf.php b/ext_localconf.php index 63a796b..50b204a 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -1,23 +1,23 @@ ' + "@import 'EXT:viresponsiveimages/Configuration/TSconfig/Page/All.typoscript'" ); /** * TypoScript Constants */ -\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTypoScriptConstants( - '' +\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPageTSConfig( + "@import 'EXT:viresponsiveimages/Resources/Private/TypoScript/constants.typoscript'" ); /** * TypoScript Setup */ -TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTypoScriptSetup( - '' +\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPageTSConfig( + "@import 'EXT:viresponsiveimages/Resources/Private/TypoScript/setup.typoscript'" ); From 8f044ee197a75fc7407c0b34228aab0c39bff139 Mon Sep 17 00:00:00 2001 From: Lorenz Ulrich Date: Thu, 18 Mar 2021 23:20:12 +0100 Subject: [PATCH 2/3] [TASK] Move TypoScript to Configuration --- .../ContentElement/Image.typoscript | 0 .../ContentElement/Textmedia.typoscript | 0 .../ContentElement/Textpic.typoscript | 0 .../TypoScript/constants.typoscript | 0 .../TypoScript/setup.typoscript | 6 +-- ext_emconf.php | 3 -- ext_localconf.php | 41 +++++++++++-------- 7 files changed, 26 insertions(+), 24 deletions(-) rename {Resources/Private => Configuration}/TypoScript/ContentElement/Image.typoscript (100%) rename {Resources/Private => Configuration}/TypoScript/ContentElement/Textmedia.typoscript (100%) rename {Resources/Private => Configuration}/TypoScript/ContentElement/Textpic.typoscript (100%) rename {Resources/Private => Configuration}/TypoScript/constants.typoscript (100%) rename {Resources/Private => Configuration}/TypoScript/setup.typoscript (67%) diff --git a/Resources/Private/TypoScript/ContentElement/Image.typoscript b/Configuration/TypoScript/ContentElement/Image.typoscript similarity index 100% rename from Resources/Private/TypoScript/ContentElement/Image.typoscript rename to Configuration/TypoScript/ContentElement/Image.typoscript diff --git a/Resources/Private/TypoScript/ContentElement/Textmedia.typoscript b/Configuration/TypoScript/ContentElement/Textmedia.typoscript similarity index 100% rename from Resources/Private/TypoScript/ContentElement/Textmedia.typoscript rename to Configuration/TypoScript/ContentElement/Textmedia.typoscript diff --git a/Resources/Private/TypoScript/ContentElement/Textpic.typoscript b/Configuration/TypoScript/ContentElement/Textpic.typoscript similarity index 100% rename from Resources/Private/TypoScript/ContentElement/Textpic.typoscript rename to Configuration/TypoScript/ContentElement/Textpic.typoscript diff --git a/Resources/Private/TypoScript/constants.typoscript b/Configuration/TypoScript/constants.typoscript similarity index 100% rename from Resources/Private/TypoScript/constants.typoscript rename to Configuration/TypoScript/constants.typoscript diff --git a/Resources/Private/TypoScript/setup.typoscript b/Configuration/TypoScript/setup.typoscript similarity index 67% rename from Resources/Private/TypoScript/setup.typoscript rename to Configuration/TypoScript/setup.typoscript index 83a3f44..0794036 100644 --- a/Resources/Private/TypoScript/setup.typoscript +++ b/Configuration/TypoScript/setup.typoscript @@ -9,6 +9,6 @@ page.includeJSFooterlibs { lazysizes9core = EXT:viresponsiveimages/Resources/Private/Javascripts/lazysizes/lazysizes.min.js } -@import 'EXT:viresponsiveimages/Resources/Private/TypoScript/ContentElement/Image.typoscript' -@import 'EXT:viresponsiveimages/Resources/Private/TypoScript/ContentElement/Textpic.typoscript' -@import 'EXT:viresponsiveimages/Resources/Private/TypoScript/ContentElement/Textmedia.typoscript' +@import 'EXT:viresponsiveimages/Configuration/TypoScript/ContentElement/Image.typoscript' +@import 'EXT:viresponsiveimages/Configuration/TypoScript/ContentElement/Textpic.typoscript' +@import 'EXT:viresponsiveimages/Configuration/TypoScript/ContentElement/Textmedia.typoscript' diff --git a/ext_emconf.php b/ext_emconf.php index 399df5d..cbfb11d 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -7,9 +7,6 @@ 'author' => 'visol digitale Dienstleistungen GmbH', 'author_email' => 'support@visol.ch', 'state' => 'stable', - 'internal' => '', - 'uploadfolder' => '0', - 'createDirs' => '', 'clearCacheOnLoad' => 0, 'version' => '1.0.0', 'constraints' => [ diff --git a/ext_localconf.php b/ext_localconf.php index 50b204a..3308d6c 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -1,23 +1,28 @@ Date: Fri, 19 Mar 2021 21:58:55 +0100 Subject: [PATCH 3/3] [TASK] Remove picturefill(.min).js --- Resources/Private/Javascripts/picturefill.js | 1544 ----------------- .../Private/Javascripts/picturefill.min.js | 5 - 2 files changed, 1549 deletions(-) delete mode 100644 Resources/Private/Javascripts/picturefill.js delete mode 100644 Resources/Private/Javascripts/picturefill.min.js diff --git a/Resources/Private/Javascripts/picturefill.js b/Resources/Private/Javascripts/picturefill.js deleted file mode 100644 index a60a3ca..0000000 --- a/Resources/Private/Javascripts/picturefill.js +++ /dev/null @@ -1,1544 +0,0 @@ -/*! picturefill - v3.0.2 - 2016-02-12 - * https://scottjehl.github.io/picturefill/ - * Copyright (c) 2016 https://github.com/scottjehl/picturefill/blob/master/Authors.txt; Licensed MIT - */ -/*! Gecko-Picture - v1.0 - * https://github.com/scottjehl/picturefill/tree/3.0/src/plugins/gecko-picture - * Firefox's early picture implementation (prior to FF41) is static and does - * not react to viewport changes. This tiny module fixes this. - */ -(function(window) { - /*jshint eqnull:true */ - var ua = navigator.userAgent; - - if ( window.HTMLPictureElement && ((/ecko/).test(ua) && ua.match(/rv\:(\d+)/) && RegExp.$1 < 45) ) { - addEventListener("resize", (function() { - var timer; - - var dummySrc = document.createElement("source"); - - var fixRespimg = function(img) { - var source, sizes; - var picture = img.parentNode; - - if (picture.nodeName.toUpperCase() === "PICTURE") { - source = dummySrc.cloneNode(); - - picture.insertBefore(source, picture.firstElementChild); - setTimeout(function() { - picture.removeChild(source); - }); - } else if (!img._pfLastSize || img.offsetWidth > img._pfLastSize) { - img._pfLastSize = img.offsetWidth; - sizes = img.sizes; - img.sizes += ",100vw"; - setTimeout(function() { - img.sizes = sizes; - }); - } - }; - - var findPictureImgs = function() { - var i; - var imgs = document.querySelectorAll("picture > img, img[srcset][sizes]"); - for (i = 0; i < imgs.length; i++) { - fixRespimg(imgs[i]); - } - }; - var onResize = function() { - clearTimeout(timer); - timer = setTimeout(findPictureImgs, 99); - }; - var mq = window.matchMedia && matchMedia("(orientation: landscape)"); - var init = function() { - onResize(); - - if (mq && mq.addListener) { - mq.addListener(onResize); - } - }; - - dummySrc.srcset = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; - - if (/^[c|i]|d$/.test(document.readyState || "")) { - init(); - } else { - document.addEventListener("DOMContentLoaded", init); - } - - return onResize; - })()); - } -})(window); - -/*! Picturefill - v3.0.2 - * http://scottjehl.github.io/picturefill - * Copyright (c) 2015 https://github.com/scottjehl/picturefill/blob/master/Authors.txt; - * License: MIT - */ - -(function( window, document, undefined ) { - // Enable strict mode - "use strict"; - - // HTML shim|v it for old IE (IE9 will still need the HTML video tag workaround) - document.createElement( "picture" ); - - var warn, eminpx, alwaysCheckWDescriptor, evalId; - // local object for method references and testing exposure - var pf = {}; - var isSupportTestReady = false; - var noop = function() {}; - var image = document.createElement( "img" ); - var getImgAttr = image.getAttribute; - var setImgAttr = image.setAttribute; - var removeImgAttr = image.removeAttribute; - var docElem = document.documentElement; - var types = {}; - var cfg = { - //resource selection: - algorithm: "" - }; - var srcAttr = "data-pfsrc"; - var srcsetAttr = srcAttr + "set"; - // ua sniffing is done for undetectable img loading features, - // to do some non crucial perf optimizations - var ua = navigator.userAgent; - var supportAbort = (/rident/).test(ua) || ((/ecko/).test(ua) && ua.match(/rv\:(\d+)/) && RegExp.$1 > 35 ); - var curSrcProp = "currentSrc"; - var regWDesc = /\s+\+?\d+(e\d+)?w/; - var regSize = /(\([^)]+\))?\s*(.+)/; - var setOptions = window.picturefillCFG; - /** - * Shortcut property for https://w3c.github.io/webappsec/specs/mixedcontent/#restricts-mixed-content ( for easy overriding in tests ) - */ - // baseStyle also used by getEmValue (i.e.: width: 1em is important) - var baseStyle = "position:absolute;left:0;visibility:hidden;display:block;padding:0;border:none;font-size:1em;width:1em;overflow:hidden;clip:rect(0px, 0px, 0px, 0px)"; - var fsCss = "font-size:100%!important;"; - var isVwDirty = true; - - var cssCache = {}; - var sizeLengthCache = {}; - var DPR = window.devicePixelRatio; - var units = { - px: 1, - "in": 96 - }; - var anchor = document.createElement( "a" ); - /** - * alreadyRun flag used for setOptions. is it true setOptions will reevaluate - * @type {boolean} - */ - var alreadyRun = false; - - // Reusable, non-"g" Regexes - - // (Don't use \s, to avoid matching non-breaking space.) - var regexLeadingSpaces = /^[ \t\n\r\u000c]+/, - regexLeadingCommasOrSpaces = /^[, \t\n\r\u000c]+/, - regexLeadingNotSpaces = /^[^ \t\n\r\u000c]+/, - regexTrailingCommas = /[,]+$/, - regexNonNegativeInteger = /^\d+$/, - - // ( Positive or negative or unsigned integers or decimals, without or without exponents. - // Must include at least one digit. - // According to spec tests any decimal point must be followed by a digit. - // No leading plus sign is allowed.) - // https://html.spec.whatwg.org/multipage/infrastructure.html#valid-floating-point-number - regexFloatingPoint = /^-?(?:[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/; - - var on = function(obj, evt, fn, capture) { - if ( obj.addEventListener ) { - obj.addEventListener(evt, fn, capture || false); - } else if ( obj.attachEvent ) { - obj.attachEvent( "on" + evt, fn); - } - }; - - /** - * simple memoize function: - */ - - var memoize = function(fn) { - var cache = {}; - return function(input) { - if ( !(input in cache) ) { - cache[ input ] = fn(input); - } - return cache[ input ]; - }; - }; - - // UTILITY FUNCTIONS - - // Manual is faster than RegEx - // http://jsperf.com/whitespace-character/5 - function isSpace(c) { - return (c === "\u0020" || // space - c === "\u0009" || // horizontal tab - c === "\u000A" || // new line - c === "\u000C" || // form feed - c === "\u000D"); // carriage return - } - - /** - * gets a mediaquery and returns a boolean or gets a css length and returns a number - * @param css mediaqueries or css length - * @returns {boolean|number} - * - * based on: https://gist.github.com/jonathantneal/db4f77009b155f083738 - */ - var evalCSS = (function() { - - var regLength = /^([\d\.]+)(em|vw|px)$/; - var replace = function() { - var args = arguments, index = 0, string = args[0]; - while (++index in args) { - string = string.replace(args[index], args[++index]); - } - return string; - }; - - var buildStr = memoize(function(css) { - - return "return " + replace((css || "").toLowerCase(), - // interpret `and` - /\band\b/g, "&&", - - // interpret `,` - /,/g, "||", - - // interpret `min-` as >= - /min-([a-z-\s]+):/g, "e.$1>=", - - // interpret `max-` as <= - /max-([a-z-\s]+):/g, "e.$1<=", - - //calc value - /calc([^)]+)/g, "($1)", - - // interpret css values - /(\d+[\.]*[\d]*)([a-z]+)/g, "($1 * e.$2)", - //make eval less evil - /^(?!(e.[a-z]|[0-9\.&=|><\+\-\*\(\)\/])).*/ig, "" - ) + ";"; - }); - - return function(css, length) { - var parsedLength; - if (!(css in cssCache)) { - cssCache[css] = false; - if (length && (parsedLength = css.match( regLength ))) { - cssCache[css] = parsedLength[ 1 ] * units[parsedLength[ 2 ]]; - } else { - /*jshint evil:true */ - try{ - cssCache[css] = new Function("e", buildStr(css))(units); - } catch(e) {} - /*jshint evil:false */ - } - } - return cssCache[css]; - }; - })(); - - var setResolution = function( candidate, sizesattr ) { - if ( candidate.w ) { // h = means height: || descriptor.type === 'h' do not handle yet... - candidate.cWidth = pf.calcListLength( sizesattr || "100vw" ); - candidate.res = candidate.w / candidate.cWidth ; - } else { - candidate.res = candidate.d; - } - return candidate; - }; - - /** - * - * @param opt - */ - var picturefill = function( opt ) { - - if (!isSupportTestReady) {return;} - - var elements, i, plen; - - var options = opt || {}; - - if ( options.elements && options.elements.nodeType === 1 ) { - if ( options.elements.nodeName.toUpperCase() === "IMG" ) { - options.elements = [ options.elements ]; - } else { - options.context = options.elements; - options.elements = null; - } - } - - elements = options.elements || pf.qsa( (options.context || document), ( options.reevaluate || options.reselect ) ? pf.sel : pf.selShort ); - - if ( (plen = elements.length) ) { - - pf.setupRun( options ); - alreadyRun = true; - - // Loop through all elements - for ( i = 0; i < plen; i++ ) { - pf.fillImg(elements[ i ], options); - } - - pf.teardownRun( options ); - } - }; - - /** - * outputs a warning for the developer - * @param {message} - * @type {Function} - */ - warn = ( window.console && console.warn ) ? - function( message ) { - console.warn( message ); - } : - noop - ; - - if ( !(curSrcProp in image) ) { - curSrcProp = "src"; - } - - // Add support for standard mime types. - types[ "image/jpeg" ] = true; - types[ "image/gif" ] = true; - types[ "image/png" ] = true; - - function detectTypeSupport( type, typeUri ) { - // based on Modernizr's lossless img-webp test - // note: asynchronous - var image = new window.Image(); - image.onerror = function() { - types[ type ] = false; - picturefill(); - }; - image.onload = function() { - types[ type ] = image.width === 1; - picturefill(); - }; - image.src = typeUri; - return "pending"; - } - - // test svg support - types[ "image/svg+xml" ] = document.implementation.hasFeature( "http://www.w3.org/TR/SVG11/feature#Image", "1.1" ); - - /** - * updates the internal vW property with the current viewport width in px - */ - function updateMetrics() { - - isVwDirty = false; - DPR = window.devicePixelRatio; - cssCache = {}; - sizeLengthCache = {}; - - pf.DPR = DPR || 1; - - units.width = Math.max(window.innerWidth || 0, docElem.clientWidth); - units.height = Math.max(window.innerHeight || 0, docElem.clientHeight); - - units.vw = units.width / 100; - units.vh = units.height / 100; - - evalId = [ units.height, units.width, DPR ].join("-"); - - units.em = pf.getEmValue(); - units.rem = units.em; - } - - function chooseLowRes( lowerValue, higherValue, dprValue, isCached ) { - var bonusFactor, tooMuch, bonus, meanDensity; - - //experimental - if (cfg.algorithm === "saveData" ){ - if ( lowerValue > 2.7 ) { - meanDensity = dprValue + 1; - } else { - tooMuch = higherValue - dprValue; - bonusFactor = Math.pow(lowerValue - 0.6, 1.5); - - bonus = tooMuch * bonusFactor; - - if (isCached) { - bonus += 0.1 * bonusFactor; - } - - meanDensity = lowerValue + bonus; - } - } else { - meanDensity = (dprValue > 1) ? - Math.sqrt(lowerValue * higherValue) : - lowerValue; - } - - return meanDensity > dprValue; - } - - function applyBestCandidate( img ) { - var srcSetCandidates; - var matchingSet = pf.getSet( img ); - var evaluated = false; - if ( matchingSet !== "pending" ) { - evaluated = evalId; - if ( matchingSet ) { - srcSetCandidates = pf.setRes( matchingSet ); - pf.applySetCandidate( srcSetCandidates, img ); - } - } - img[ pf.ns ].evaled = evaluated; - } - - function ascendingSort( a, b ) { - return a.res - b.res; - } - - function setSrcToCur( img, src, set ) { - var candidate; - if ( !set && src ) { - set = img[ pf.ns ].sets; - set = set && set[set.length - 1]; - } - - candidate = getCandidateForSrc(src, set); - - if ( candidate ) { - src = pf.makeUrl(src); - img[ pf.ns ].curSrc = src; - img[ pf.ns ].curCan = candidate; - - if ( !candidate.res ) { - setResolution( candidate, candidate.set.sizes ); - } - } - return candidate; - } - - function getCandidateForSrc( src, set ) { - var i, candidate, candidates; - if ( src && set ) { - candidates = pf.parseSet( set ); - src = pf.makeUrl(src); - for ( i = 0; i < candidates.length; i++ ) { - if ( src === pf.makeUrl(candidates[ i ].url) ) { - candidate = candidates[ i ]; - break; - } - } - } - return candidate; - } - - function getAllSourceElements( picture, candidates ) { - var i, len, source, srcset; - - // SPEC mismatch intended for size and perf: - // actually only source elements preceding the img should be used - // also note: don't use qsa here, because IE8 sometimes doesn't like source as the key part in a selector - var sources = picture.getElementsByTagName( "source" ); - - for ( i = 0, len = sources.length; i < len; i++ ) { - source = sources[ i ]; - source[ pf.ns ] = true; - srcset = source.getAttribute( "srcset" ); - - // if source does not have a srcset attribute, skip - if ( srcset ) { - candidates.push( { - srcset: srcset, - media: source.getAttribute( "media" ), - type: source.getAttribute( "type" ), - sizes: source.getAttribute( "sizes" ) - } ); - } - } - } - - /** - * Srcset Parser - * By Alex Bell | MIT License - * - * @returns Array [{url: _, d: _, w: _, h:_, set:_(????)}, ...] - * - * Based super duper closely on the reference algorithm at: - * https://html.spec.whatwg.org/multipage/embedded-content.html#parse-a-srcset-attribute - */ - - // 1. Let input be the value passed to this algorithm. - // (TO-DO : Explain what "set" argument is here. Maybe choose a more - // descriptive & more searchable name. Since passing the "set" in really has - // nothing to do with parsing proper, I would prefer this assignment eventually - // go in an external fn.) - function parseSrcset(input, set) { - - function collectCharacters(regEx) { - var chars, - match = regEx.exec(input.substring(pos)); - if (match) { - chars = match[ 0 ]; - pos += chars.length; - return chars; - } - } - - var inputLength = input.length, - url, - descriptors, - currentDescriptor, - state, - c, - - // 2. Let position be a pointer into input, initially pointing at the start - // of the string. - pos = 0, - - // 3. Let candidates be an initially empty source set. - candidates = []; - - /** - * Adds descriptor properties to a candidate, pushes to the candidates array - * @return undefined - */ - // (Declared outside of the while loop so that it's only created once. - // (This fn is defined before it is used, in order to pass JSHINT. - // Unfortunately this breaks the sequencing of the spec comments. :/ ) - function parseDescriptors() { - - // 9. Descriptor parser: Let error be no. - var pError = false, - - // 10. Let width be absent. - // 11. Let density be absent. - // 12. Let future-compat-h be absent. (We're implementing it now as h) - w, d, h, i, - candidate = {}, - desc, lastChar, value, intVal, floatVal; - - // 13. For each descriptor in descriptors, run the appropriate set of steps - // from the following list: - for (i = 0 ; i < descriptors.length; i++) { - desc = descriptors[ i ]; - - lastChar = desc[ desc.length - 1 ]; - value = desc.substring(0, desc.length - 1); - intVal = parseInt(value, 10); - floatVal = parseFloat(value); - - // If the descriptor consists of a valid non-negative integer followed by - // a U+0077 LATIN SMALL LETTER W character - if (regexNonNegativeInteger.test(value) && (lastChar === "w")) { - - // If width and density are not both absent, then let error be yes. - if (w || d) {pError = true;} - - // Apply the rules for parsing non-negative integers to the descriptor. - // If the result is zero, let error be yes. - // Otherwise, let width be the result. - if (intVal === 0) {pError = true;} else {w = intVal;} - - // If the descriptor consists of a valid floating-point number followed by - // a U+0078 LATIN SMALL LETTER X character - } else if (regexFloatingPoint.test(value) && (lastChar === "x")) { - - // If width, density and future-compat-h are not all absent, then let error - // be yes. - if (w || d || h) {pError = true;} - - // Apply the rules for parsing floating-point number values to the descriptor. - // If the result is less than zero, let error be yes. Otherwise, let density - // be the result. - if (floatVal < 0) {pError = true;} else {d = floatVal;} - - // If the descriptor consists of a valid non-negative integer followed by - // a U+0068 LATIN SMALL LETTER H character - } else if (regexNonNegativeInteger.test(value) && (lastChar === "h")) { - - // If height and density are not both absent, then let error be yes. - if (h || d) {pError = true;} - - // Apply the rules for parsing non-negative integers to the descriptor. - // If the result is zero, let error be yes. Otherwise, let future-compat-h - // be the result. - if (intVal === 0) {pError = true;} else {h = intVal;} - - // Anything else, Let error be yes. - } else {pError = true;} - } // (close step 13 for loop) - - // 15. If error is still no, then append a new image source to candidates whose - // URL is url, associated with a width width if not absent and a pixel - // density density if not absent. Otherwise, there is a parse error. - if (!pError) { - candidate.url = url; - - if (w) { candidate.w = w;} - if (d) { candidate.d = d;} - if (h) { candidate.h = h;} - if (!h && !d && !w) {candidate.d = 1;} - if (candidate.d === 1) {set.has1x = true;} - candidate.set = set; - - candidates.push(candidate); - } - } // (close parseDescriptors fn) - - /** - * Tokenizes descriptor properties prior to parsing - * Returns undefined. - * (Again, this fn is defined before it is used, in order to pass JSHINT. - * Unfortunately this breaks the logical sequencing of the spec comments. :/ ) - */ - function tokenize() { - - // 8.1. Descriptor tokeniser: Skip whitespace - collectCharacters(regexLeadingSpaces); - - // 8.2. Let current descriptor be the empty string. - currentDescriptor = ""; - - // 8.3. Let state be in descriptor. - state = "in descriptor"; - - while (true) { - - // 8.4. Let c be the character at position. - c = input.charAt(pos); - - // Do the following depending on the value of state. - // For the purpose of this step, "EOF" is a special character representing - // that position is past the end of input. - - // In descriptor - if (state === "in descriptor") { - // Do the following, depending on the value of c: - - // Space character - // If current descriptor is not empty, append current descriptor to - // descriptors and let current descriptor be the empty string. - // Set state to after descriptor. - if (isSpace(c)) { - if (currentDescriptor) { - descriptors.push(currentDescriptor); - currentDescriptor = ""; - state = "after descriptor"; - } - - // U+002C COMMA (,) - // Advance position to the next character in input. If current descriptor - // is not empty, append current descriptor to descriptors. Jump to the step - // labeled descriptor parser. - } else if (c === ",") { - pos += 1; - if (currentDescriptor) { - descriptors.push(currentDescriptor); - } - parseDescriptors(); - return; - - // U+0028 LEFT PARENTHESIS (() - // Append c to current descriptor. Set state to in parens. - } else if (c === "\u0028") { - currentDescriptor = currentDescriptor + c; - state = "in parens"; - - // EOF - // If current descriptor is not empty, append current descriptor to - // descriptors. Jump to the step labeled descriptor parser. - } else if (c === "") { - if (currentDescriptor) { - descriptors.push(currentDescriptor); - } - parseDescriptors(); - return; - - // Anything else - // Append c to current descriptor. - } else { - currentDescriptor = currentDescriptor + c; - } - // (end "in descriptor" - - // In parens - } else if (state === "in parens") { - - // U+0029 RIGHT PARENTHESIS ()) - // Append c to current descriptor. Set state to in descriptor. - if (c === ")") { - currentDescriptor = currentDescriptor + c; - state = "in descriptor"; - - // EOF - // Append current descriptor to descriptors. Jump to the step labeled - // descriptor parser. - } else if (c === "") { - descriptors.push(currentDescriptor); - parseDescriptors(); - return; - - // Anything else - // Append c to current descriptor. - } else { - currentDescriptor = currentDescriptor + c; - } - - // After descriptor - } else if (state === "after descriptor") { - - // Do the following, depending on the value of c: - // Space character: Stay in this state. - if (isSpace(c)) { - - // EOF: Jump to the step labeled descriptor parser. - } else if (c === "") { - parseDescriptors(); - return; - - // Anything else - // Set state to in descriptor. Set position to the previous character in input. - } else { - state = "in descriptor"; - pos -= 1; - - } - } - - // Advance position to the next character in input. - pos += 1; - - // Repeat this step. - } // (close while true loop) - } - - // 4. Splitting loop: Collect a sequence of characters that are space - // characters or U+002C COMMA characters. If any U+002C COMMA characters - // were collected, that is a parse error. - while (true) { - collectCharacters(regexLeadingCommasOrSpaces); - - // 5. If position is past the end of input, return candidates and abort these steps. - if (pos >= inputLength) { - return candidates; // (we're done, this is the sole return path) - } - - // 6. Collect a sequence of characters that are not space characters, - // and let that be url. - url = collectCharacters(regexLeadingNotSpaces); - - // 7. Let descriptors be a new empty list. - descriptors = []; - - // 8. If url ends with a U+002C COMMA character (,), follow these substeps: - // (1). Remove all trailing U+002C COMMA characters from url. If this removed - // more than one character, that is a parse error. - if (url.slice(-1) === ",") { - url = url.replace(regexTrailingCommas, ""); - // (Jump ahead to step 9 to skip tokenization and just push the candidate). - parseDescriptors(); - - // Otherwise, follow these substeps: - } else { - tokenize(); - } // (close else of step 8) - - // 16. Return to the step labeled splitting loop. - } // (Close of big while loop.) - } - - /* - * Sizes Parser - * - * By Alex Bell | MIT License - * - * Non-strict but accurate and lightweight JS Parser for the string value - * - * Reference algorithm at: - * https://html.spec.whatwg.org/multipage/embedded-content.html#parse-a-sizes-attribute - * - * Most comments are copied in directly from the spec - * (except for comments in parens). - * - * Grammar is: - * = # [ , ]? | - * = - * = - * http://www.w3.org/html/wg/drafts/html/master/embedded-content.html#attr-img-sizes - * - * E.g. "(max-width: 30em) 100vw, (max-width: 50em) 70vw, 100vw" - * or "(min-width: 30em), calc(30vw - 15px)" or just "30vw" - * - * Returns the first valid with a media condition that evaluates to true, - * or "100vw" if all valid media conditions evaluate to false. - * - */ - - function parseSizes(strValue) { - - // (Percentage CSS lengths are not allowed in this case, to avoid confusion: - // https://html.spec.whatwg.org/multipage/embedded-content.html#valid-source-size-list - // CSS allows a single optional plus or minus sign: - // http://www.w3.org/TR/CSS2/syndata.html#numbers - // CSS is ASCII case-insensitive: - // http://www.w3.org/TR/CSS2/syndata.html#characters ) - // Spec allows exponential notation for type: - // http://dev.w3.org/csswg/css-values/#numbers - var regexCssLengthWithUnits = /^(?:[+-]?[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?(?:ch|cm|em|ex|in|mm|pc|pt|px|rem|vh|vmin|vmax|vw)$/i; - - // (This is a quick and lenient test. Because of optional unlimited-depth internal - // grouping parens and strict spacing rules, this could get very complicated.) - var regexCssCalc = /^calc\((?:[0-9a-z \.\+\-\*\/\(\)]+)\)$/i; - - var i; - var unparsedSizesList; - var unparsedSizesListLength; - var unparsedSize; - var lastComponentValue; - var size; - - // UTILITY FUNCTIONS - - // (Toy CSS parser. The goals here are: - // 1) expansive test coverage without the weight of a full CSS parser. - // 2) Avoiding regex wherever convenient. - // Quick tests: http://jsfiddle.net/gtntL4gr/3/ - // Returns an array of arrays.) - function parseComponentValues(str) { - var chrctr; - var component = ""; - var componentArray = []; - var listArray = []; - var parenDepth = 0; - var pos = 0; - var inComment = false; - - function pushComponent() { - if (component) { - componentArray.push(component); - component = ""; - } - } - - function pushComponentArray() { - if (componentArray[0]) { - listArray.push(componentArray); - componentArray = []; - } - } - - // (Loop forwards from the beginning of the string.) - while (true) { - chrctr = str.charAt(pos); - - if (chrctr === "") { // ( End of string reached.) - pushComponent(); - pushComponentArray(); - return listArray; - } else if (inComment) { - if ((chrctr === "*") && (str[pos + 1] === "/")) { // (At end of a comment.) - inComment = false; - pos += 2; - pushComponent(); - continue; - } else { - pos += 1; // (Skip all characters inside comments.) - continue; - } - } else if (isSpace(chrctr)) { - // (If previous character in loop was also a space, or if - // at the beginning of the string, do not add space char to - // component.) - if ( (str.charAt(pos - 1) && isSpace( str.charAt(pos - 1) ) ) || !component ) { - pos += 1; - continue; - } else if (parenDepth === 0) { - pushComponent(); - pos +=1; - continue; - } else { - // (Replace any space character with a plain space for legibility.) - chrctr = " "; - } - } else if (chrctr === "(") { - parenDepth += 1; - } else if (chrctr === ")") { - parenDepth -= 1; - } else if (chrctr === ",") { - pushComponent(); - pushComponentArray(); - pos += 1; - continue; - } else if ( (chrctr === "/") && (str.charAt(pos + 1) === "*") ) { - inComment = true; - pos += 2; - continue; - } - - component = component + chrctr; - pos += 1; - } - } - - function isValidNonNegativeSourceSizeValue(s) { - if (regexCssLengthWithUnits.test(s) && (parseFloat(s) >= 0)) {return true;} - if (regexCssCalc.test(s)) {return true;} - // ( http://www.w3.org/TR/CSS2/syndata.html#numbers says: - // "-0 is equivalent to 0 and is not a negative number." which means that - // unitless zero and unitless negative zero must be accepted as special cases.) - if ((s === "0") || (s === "-0") || (s === "+0")) {return true;} - return false; - } - - // When asked to parse a sizes attribute from an element, parse a - // comma-separated list of component values from the value of the element's - // sizes attribute (or the empty string, if the attribute is absent), and let - // unparsed sizes list be the result. - // http://dev.w3.org/csswg/css-syntax/#parse-comma-separated-list-of-component-values - - unparsedSizesList = parseComponentValues(strValue); - unparsedSizesListLength = unparsedSizesList.length; - - // For each unparsed size in unparsed sizes list: - for (i = 0; i < unparsedSizesListLength; i++) { - unparsedSize = unparsedSizesList[i]; - - // 1. Remove all consecutive s from the end of unparsed size. - // ( parseComponentValues() already omits spaces outside of parens. ) - - // If unparsed size is now empty, that is a parse error; continue to the next - // iteration of this algorithm. - // ( parseComponentValues() won't push an empty array. ) - - // 2. If the last component value in unparsed size is a valid non-negative - // , let size be its value and remove the component value - // from unparsed size. Any CSS function other than the calc() function is - // invalid. Otherwise, there is a parse error; continue to the next iteration - // of this algorithm. - // http://dev.w3.org/csswg/css-syntax/#parse-component-value - lastComponentValue = unparsedSize[unparsedSize.length - 1]; - - if (isValidNonNegativeSourceSizeValue(lastComponentValue)) { - size = lastComponentValue; - unparsedSize.pop(); - } else { - continue; - } - - // 3. Remove all consecutive s from the end of unparsed - // size. If unparsed size is now empty, return size and exit this algorithm. - // If this was not the last item in unparsed sizes list, that is a parse error. - if (unparsedSize.length === 0) { - return size; - } - - // 4. Parse the remaining component values in unparsed size as a - // . If it does not parse correctly, or it does parse - // correctly but the evaluates to false, continue to the - // next iteration of this algorithm. - // (Parsing all possible compound media conditions in JS is heavy, complicated, - // and the payoff is unclear. Is there ever an situation where the - // media condition parses incorrectly but still somehow evaluates to true? - // Can we just rely on the browser/polyfill to do it?) - unparsedSize = unparsedSize.join(" "); - if (!(pf.matchesMedia( unparsedSize ) ) ) { - continue; - } - - // 5. Return size and exit this algorithm. - return size; - } - - // If the above algorithm exhausts unparsed sizes list without returning a - // size value, return 100vw. - return "100vw"; - } - - // namespace - pf.ns = ("pf" + new Date().getTime()).substr(0, 9); - - // srcset support test - pf.supSrcset = "srcset" in image; - pf.supSizes = "sizes" in image; - pf.supPicture = !!window.HTMLPictureElement; - - // UC browser does claim to support srcset and picture, but not sizes, - // this extended test reveals the browser does support nothing - if (pf.supSrcset && pf.supPicture && !pf.supSizes) { - (function(image2) { - image.srcset = "data:,a"; - image2.src = "data:,a"; - pf.supSrcset = image.complete === image2.complete; - pf.supPicture = pf.supSrcset && pf.supPicture; - })(document.createElement("img")); - } - - // Safari9 has basic support for sizes, but does't expose the `sizes` idl attribute - if (pf.supSrcset && !pf.supSizes) { - - (function() { - var width2 = "data:image/gif;base64,R0lGODlhAgABAPAAAP///wAAACH5BAAAAAAALAAAAAACAAEAAAICBAoAOw=="; - var width1 = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; - var img = document.createElement("img"); - var test = function() { - var width = img.width; - - if (width === 2) { - pf.supSizes = true; - } - - alwaysCheckWDescriptor = pf.supSrcset && !pf.supSizes; - - isSupportTestReady = true; - // force async - setTimeout(picturefill); - }; - - img.onload = test; - img.onerror = test; - img.setAttribute("sizes", "9px"); - - img.srcset = width1 + " 1w," + width2 + " 9w"; - img.src = width1; - })(); - - } else { - isSupportTestReady = true; - } - - // using pf.qsa instead of dom traversing does scale much better, - // especially on sites mixing responsive and non-responsive images - pf.selShort = "picture>img,img[srcset]"; - pf.sel = pf.selShort; - pf.cfg = cfg; - - /** - * Shortcut property for `devicePixelRatio` ( for easy overriding in tests ) - */ - pf.DPR = (DPR || 1 ); - pf.u = units; - - // container of supported mime types that one might need to qualify before using - pf.types = types; - - pf.setSize = noop; - - /** - * Gets a string and returns the absolute URL - * @param src - * @returns {String} absolute URL - */ - - pf.makeUrl = memoize(function(src) { - anchor.href = src; - return anchor.href; - }); - - /** - * Gets a DOM element or document and a selctor and returns the found matches - * Can be extended with jQuery/Sizzle for IE7 support - * @param context - * @param sel - * @returns {NodeList|Array} - */ - pf.qsa = function(context, sel) { - return ( "querySelector" in context ) ? context.querySelectorAll(sel) : []; - }; - - /** - * Shortcut method for matchMedia ( for easy overriding in tests ) - * wether native or pf.mMQ is used will be decided lazy on first call - * @returns {boolean} - */ - pf.matchesMedia = function() { - if ( window.matchMedia && (matchMedia( "(min-width: 0.1em)" ) || {}).matches ) { - pf.matchesMedia = function( media ) { - return !media || ( matchMedia( media ).matches ); - }; - } else { - pf.matchesMedia = pf.mMQ; - } - - return pf.matchesMedia.apply( this, arguments ); - }; - - /** - * A simplified matchMedia implementation for IE8 and IE9 - * handles only min-width/max-width with px or em values - * @param media - * @returns {boolean} - */ - pf.mMQ = function( media ) { - return media ? evalCSS(media) : true; - }; - - /** - * Returns the calculated length in css pixel from the given sourceSizeValue - * http://dev.w3.org/csswg/css-values-3/#length-value - * intended Spec mismatches: - * * Does not check for invalid use of CSS functions - * * Does handle a computed length of 0 the same as a negative and therefore invalid value - * @param sourceSizeValue - * @returns {Number} - */ - pf.calcLength = function( sourceSizeValue ) { - - var value = evalCSS(sourceSizeValue, true) || false; - if (value < 0) { - value = false; - } - - return value; - }; - - /** - * Takes a type string and checks if its supported - */ - - pf.supportsType = function( type ) { - return ( type ) ? types[ type ] : true; - }; - - /** - * Parses a sourceSize into mediaCondition (media) and sourceSizeValue (length) - * @param sourceSizeStr - * @returns {*} - */ - pf.parseSize = memoize(function( sourceSizeStr ) { - var match = ( sourceSizeStr || "" ).match(regSize); - return { - media: match && match[1], - length: match && match[2] - }; - }); - - pf.parseSet = function( set ) { - if ( !set.cands ) { - set.cands = parseSrcset(set.srcset, set); - } - return set.cands; - }; - - /** - * returns 1em in css px for html/body default size - * function taken from respondjs - * @returns {*|number} - */ - pf.getEmValue = function() { - var body; - if ( !eminpx && (body = document.body) ) { - var div = document.createElement( "div" ), - originalHTMLCSS = docElem.style.cssText, - originalBodyCSS = body.style.cssText; - - div.style.cssText = baseStyle; - - // 1em in a media query is the value of the default font size of the browser - // reset docElem and body to ensure the correct value is returned - docElem.style.cssText = fsCss; - body.style.cssText = fsCss; - - body.appendChild( div ); - eminpx = div.offsetWidth; - body.removeChild( div ); - - //also update eminpx before returning - eminpx = parseFloat( eminpx, 10 ); - - // restore the original values - docElem.style.cssText = originalHTMLCSS; - body.style.cssText = originalBodyCSS; - - } - return eminpx || 16; - }; - - /** - * Takes a string of sizes and returns the width in pixels as a number - */ - pf.calcListLength = function( sourceSizeListStr ) { - // Split up source size list, ie ( max-width: 30em ) 100%, ( max-width: 50em ) 50%, 33% - // - // or (min-width:30em) calc(30% - 15px) - if ( !(sourceSizeListStr in sizeLengthCache) || cfg.uT ) { - var winningLength = pf.calcLength( parseSizes( sourceSizeListStr ) ); - - sizeLengthCache[ sourceSizeListStr ] = !winningLength ? units.width : winningLength; - } - - return sizeLengthCache[ sourceSizeListStr ]; - }; - - /** - * Takes a candidate object with a srcset property in the form of url/ - * ex. "images/pic-medium.png 1x, images/pic-medium-2x.png 2x" or - * "images/pic-medium.png 400w, images/pic-medium-2x.png 800w" or - * "images/pic-small.png" - * Get an array of image candidates in the form of - * {url: "/foo/bar.png", resolution: 1} - * where resolution is http://dev.w3.org/csswg/css-values-3/#resolution-value - * If sizes is specified, res is calculated - */ - pf.setRes = function( set ) { - var candidates; - if ( set ) { - - candidates = pf.parseSet( set ); - - for ( var i = 0, len = candidates.length; i < len; i++ ) { - setResolution( candidates[ i ], set.sizes ); - } - } - return candidates; - }; - - pf.setRes.res = setResolution; - - pf.applySetCandidate = function( candidates, img ) { - if ( !candidates.length ) {return;} - var candidate, - i, - j, - length, - bestCandidate, - curSrc, - curCan, - candidateSrc, - abortCurSrc; - - var imageData = img[ pf.ns ]; - var dpr = pf.DPR; - - curSrc = imageData.curSrc || img[curSrcProp]; - - curCan = imageData.curCan || setSrcToCur(img, curSrc, candidates[0].set); - - // if we have a current source, we might either become lazy or give this source some advantage - if ( curCan && curCan.set === candidates[ 0 ].set ) { - - // if browser can abort image request and the image has a higher pixel density than needed - // and this image isn't downloaded yet, we skip next part and try to save bandwidth - abortCurSrc = (supportAbort && !img.complete && curCan.res - 0.1 > dpr); - - if ( !abortCurSrc ) { - curCan.cached = true; - - // if current candidate is "best", "better" or "okay", - // set it to bestCandidate - if ( curCan.res >= dpr ) { - bestCandidate = curCan; - } - } - } - - if ( !bestCandidate ) { - - candidates.sort( ascendingSort ); - - length = candidates.length; - bestCandidate = candidates[ length - 1 ]; - - for ( i = 0; i < length; i++ ) { - candidate = candidates[ i ]; - if ( candidate.res >= dpr ) { - j = i - 1; - - // we have found the perfect candidate, - // but let's improve this a little bit with some assumptions ;-) - if (candidates[ j ] && - (abortCurSrc || curSrc !== pf.makeUrl( candidate.url )) && - chooseLowRes(candidates[ j ].res, candidate.res, dpr, candidates[ j ].cached)) { - - bestCandidate = candidates[ j ]; - - } else { - bestCandidate = candidate; - } - break; - } - } - } - - if ( bestCandidate ) { - - candidateSrc = pf.makeUrl( bestCandidate.url ); - - imageData.curSrc = candidateSrc; - imageData.curCan = bestCandidate; - - if ( candidateSrc !== curSrc ) { - pf.setSrc( img, bestCandidate ); - } - pf.setSize( img ); - } - }; - - pf.setSrc = function( img, bestCandidate ) { - var origWidth; - img.src = bestCandidate.url; - - // although this is a specific Safari issue, we don't want to take too much different code paths - if ( bestCandidate.set.type === "image/svg+xml" ) { - origWidth = img.style.width; - img.style.width = (img.offsetWidth + 1) + "px"; - - // next line only should trigger a repaint - // if... is only done to trick dead code removal - if ( img.offsetWidth + 1 ) { - img.style.width = origWidth; - } - } - }; - - pf.getSet = function( img ) { - var i, set, supportsType; - var match = false; - var sets = img [ pf.ns ].sets; - - for ( i = 0; i < sets.length && !match; i++ ) { - set = sets[i]; - - if ( !set.srcset || !pf.matchesMedia( set.media ) || !(supportsType = pf.supportsType( set.type )) ) { - continue; - } - - if ( supportsType === "pending" ) { - set = supportsType; - } - - match = set; - break; - } - - return match; - }; - - pf.parseSets = function( element, parent, options ) { - var srcsetAttribute, imageSet, isWDescripor, srcsetParsed; - - var hasPicture = parent && parent.nodeName.toUpperCase() === "PICTURE"; - var imageData = element[ pf.ns ]; - - if ( imageData.src === undefined || options.src ) { - imageData.src = getImgAttr.call( element, "src" ); - if ( imageData.src ) { - setImgAttr.call( element, srcAttr, imageData.src ); - } else { - removeImgAttr.call( element, srcAttr ); - } - } - - if ( imageData.srcset === undefined || options.srcset || !pf.supSrcset || element.srcset ) { - srcsetAttribute = getImgAttr.call( element, "srcset" ); - imageData.srcset = srcsetAttribute; - srcsetParsed = true; - } - - imageData.sets = []; - - if ( hasPicture ) { - imageData.pic = true; - getAllSourceElements( parent, imageData.sets ); - } - - if ( imageData.srcset ) { - imageSet = { - srcset: imageData.srcset, - sizes: getImgAttr.call( element, "sizes" ) - }; - - imageData.sets.push( imageSet ); - - isWDescripor = (alwaysCheckWDescriptor || imageData.src) && regWDesc.test(imageData.srcset || ""); - - // add normal src as candidate, if source has no w descriptor - if ( !isWDescripor && imageData.src && !getCandidateForSrc(imageData.src, imageSet) && !imageSet.has1x ) { - imageSet.srcset += ", " + imageData.src; - imageSet.cands.push({ - url: imageData.src, - d: 1, - set: imageSet - }); - } - - } else if ( imageData.src ) { - imageData.sets.push( { - srcset: imageData.src, - sizes: null - } ); - } - - imageData.curCan = null; - imageData.curSrc = undefined; - - // if img has picture or the srcset was removed or has a srcset and does not support srcset at all - // or has a w descriptor (and does not support sizes) set support to false to evaluate - imageData.supported = !( hasPicture || ( imageSet && !pf.supSrcset ) || (isWDescripor && !pf.supSizes) ); - - if ( srcsetParsed && pf.supSrcset && !imageData.supported ) { - if ( srcsetAttribute ) { - setImgAttr.call( element, srcsetAttr, srcsetAttribute ); - element.srcset = ""; - } else { - removeImgAttr.call( element, srcsetAttr ); - } - } - - if (imageData.supported && !imageData.srcset && ((!imageData.src && element.src) || element.src !== pf.makeUrl(imageData.src))) { - if (imageData.src === null) { - element.removeAttribute("src"); - } else { - element.src = imageData.src; - } - } - - imageData.parsed = true; - }; - - pf.fillImg = function(element, options) { - var imageData; - var extreme = options.reselect || options.reevaluate; - - // expando for caching data on the img - if ( !element[ pf.ns ] ) { - element[ pf.ns ] = {}; - } - - imageData = element[ pf.ns ]; - - // if the element has already been evaluated, skip it - // unless `options.reevaluate` is set to true ( this, for example, - // is set to true when running `picturefill` on `resize` ). - if ( !extreme && imageData.evaled === evalId ) { - return; - } - - if ( !imageData.parsed || options.reevaluate ) { - pf.parseSets( element, element.parentNode, options ); - } - - if ( !imageData.supported ) { - applyBestCandidate( element ); - } else { - imageData.evaled = evalId; - } - }; - - pf.setupRun = function() { - if ( !alreadyRun || isVwDirty || (DPR !== window.devicePixelRatio) ) { - updateMetrics(); - } - }; - - // If picture is supported, well, that's awesome. - if ( pf.supPicture ) { - picturefill = noop; - pf.fillImg = noop; - } else { - - // Set up picture polyfill by polling the document - (function() { - var isDomReady; - var regReady = window.attachEvent ? /d$|^c/ : /d$|^c|^i/; - - var run = function() { - var readyState = document.readyState || ""; - - timerId = setTimeout(run, readyState === "loading" ? 200 : 999); - if ( document.body ) { - pf.fillImgs(); - isDomReady = isDomReady || regReady.test(readyState); - if ( isDomReady ) { - clearTimeout( timerId ); - } - - } - }; - - var timerId = setTimeout(run, document.body ? 9 : 99); - - // Also attach picturefill on resize and readystatechange - // http://modernjavascript.blogspot.com/2013/08/building-better-debounce.html - var debounce = function(func, wait) { - var timeout, timestamp; - var later = function() { - var last = (new Date()) - timestamp; - - if (last < wait) { - timeout = setTimeout(later, wait - last); - } else { - timeout = null; - func(); - } - }; - - return function() { - timestamp = new Date(); - - if (!timeout) { - timeout = setTimeout(later, wait); - } - }; - }; - var lastClientWidth = docElem.clientHeight; - var onResize = function() { - isVwDirty = Math.max(window.innerWidth || 0, docElem.clientWidth) !== units.width || docElem.clientHeight !== lastClientWidth; - lastClientWidth = docElem.clientHeight; - if ( isVwDirty ) { - pf.fillImgs(); - } - }; - - on( window, "resize", debounce(onResize, 99 ) ); - on( document, "readystatechange", run ); - })(); - } - - pf.picturefill = picturefill; - //use this internally for easy monkey patching/performance testing - pf.fillImgs = picturefill; - pf.teardownRun = noop; - - /* expose methods for testing */ - picturefill._ = pf; - - window.picturefillCFG = { - pf: pf, - push: function(args) { - var name = args.shift(); - if (typeof pf[name] === "function") { - pf[name].apply(pf, args); - } else { - cfg[name] = args[0]; - if (alreadyRun) { - pf.fillImgs( { reselect: true } ); - } - } - } - }; - - while (setOptions && setOptions.length) { - window.picturefillCFG.push(setOptions.shift()); - } - - /* expose picturefill */ - window.picturefill = picturefill; - - /* expose picturefill */ - if ( typeof module === "object" && typeof module.exports === "object" ) { - // CommonJS, just export - module.exports = picturefill; - } else if ( typeof define === "function" && define.amd ) { - // AMD support - define( "picturefill", function() { return picturefill; } ); - } - - // IE8 evals this sync, so it must be the last thing we do - if ( !pf.supPicture ) { - types[ "image/webp" ] = detectTypeSupport("image/webp", "data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAABBxAR/Q9ERP8DAABWUDggGAAAADABAJ0BKgEAAQADADQlpAADcAD++/1QAA==" ); - } - -} )( window, document ); diff --git a/Resources/Private/Javascripts/picturefill.min.js b/Resources/Private/Javascripts/picturefill.min.js deleted file mode 100644 index 9df7198..0000000 --- a/Resources/Private/Javascripts/picturefill.min.js +++ /dev/null @@ -1,5 +0,0 @@ -/*! picturefill - v3.0.2 - 2016-02-12 - * https://scottjehl.github.io/picturefill/ - * Copyright (c) 2016 https://github.com/scottjehl/picturefill/blob/master/Authors.txt; Licensed MIT - */ -!function(a){var b=navigator.userAgent;a.HTMLPictureElement&&/ecko/.test(b)&&b.match(/rv\:(\d+)/)&&RegExp.$1<45&&addEventListener("resize",function(){var b,c=document.createElement("source"),d=function(a){var b,d,e=a.parentNode;"PICTURE"===e.nodeName.toUpperCase()?(b=c.cloneNode(),e.insertBefore(b,e.firstElementChild),setTimeout(function(){e.removeChild(b)})):(!a._pfLastSize||a.offsetWidth>a._pfLastSize)&&(a._pfLastSize=a.offsetWidth,d=a.sizes,a.sizes+=",100vw",setTimeout(function(){a.sizes=d}))},e=function(){var a,b=document.querySelectorAll("picture > img, img[srcset][sizes]");for(a=0;a2.7?h=c+1:(f=b-c,e=Math.pow(a-.6,1.5),g=f*e,d&&(g+=.1*e),h=a+g):h=c>1?Math.sqrt(a*b):a,h>c}function h(a){var b,c=s.getSet(a),d=!1;"pending"!==c&&(d=r,c&&(b=s.setRes(c),s.applySetCandidate(b,a))),a[s.ns].evaled=d}function i(a,b){return a.res-b.res}function j(a,b,c){var d;return!c&&b&&(c=a[s.ns].sets,c=c&&c[c.length-1]),d=k(b,c),d&&(b=s.makeUrl(b),a[s.ns].curSrc=b,a[s.ns].curCan=d,d.res||aa(d,d.set.sizes)),d}function k(a,b){var c,d,e;if(a&&b)for(e=s.parseSet(b),a=s.makeUrl(a),c=0;cc;c++)e=g[c],e[s.ns]=!0,f=e.getAttribute("srcset"),f&&b.push({srcset:f,media:e.getAttribute("media"),type:e.getAttribute("type"),sizes:e.getAttribute("sizes")})}function m(a,b){function c(b){var c,d=b.exec(a.substring(m));return d?(c=d[0],m+=c.length,c):void 0}function e(){var a,c,d,e,f,i,j,k,l,m=!1,o={};for(e=0;el?m=!0:c=l):X.test(j)&&"h"===i?((d||c)&&(m=!0),0===k?m=!0:d=k):m=!0;m||(o.url=g,a&&(o.w=a),c&&(o.d=c),d&&(o.h=d),d||c||a||(o.d=1),1===o.d&&(b.has1x=!0),o.set=b,n.push(o))}function f(){for(c(T),i="",j="in descriptor";;){if(k=a.charAt(m),"in descriptor"===j)if(d(k))i&&(h.push(i),i="",j="after descriptor");else{if(","===k)return m+=1,i&&h.push(i),void e();if("("===k)i+=k,j="in parens";else{if(""===k)return i&&h.push(i),void e();i+=k}}else if("in parens"===j)if(")"===k)i+=k,j="in descriptor";else{if(""===k)return h.push(i),void e();i+=k}else if("after descriptor"===j)if(d(k));else{if(""===k)return void e();j="in descriptor",m-=1}m+=1}}for(var g,h,i,j,k,l=a.length,m=0,n=[];;){if(c(U),m>=l)return n;g=c(V),h=[],","===g.slice(-1)?(g=g.replace(W,""),e()):f()}}function n(a){function b(a){function b(){f&&(g.push(f),f="")}function c(){g[0]&&(h.push(g),g=[])}for(var e,f="",g=[],h=[],i=0,j=0,k=!1;;){if(e=a.charAt(j),""===e)return b(),c(),h;if(k){if("*"===e&&"/"===a[j+1]){k=!1,j+=2,b();continue}j+=1}else{if(d(e)){if(a.charAt(j-1)&&d(a.charAt(j-1))||!f){j+=1;continue}if(0===i){b(),j+=1;continue}e=" "}else if("("===e)i+=1;else if(")"===e)i-=1;else{if(","===e){b(),c(),j+=1;continue}if("/"===e&&"*"===a.charAt(j+1)){k=!0,j+=2;continue}}f+=e,j+=1}}}function c(a){return k.test(a)&&parseFloat(a)>=0?!0:l.test(a)?!0:"0"===a||"-0"===a||"+0"===a?!0:!1}var e,f,g,h,i,j,k=/^(?:[+-]?[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?(?:ch|cm|em|ex|in|mm|pc|pt|px|rem|vh|vmin|vmax|vw)$/i,l=/^calc\((?:[0-9a-z \.\+\-\*\/\(\)]+)\)$/i;for(f=b(a),g=f.length,e=0;g>e;e++)if(h=f[e],i=h[h.length-1],c(i)){if(j=i,h.pop(),0===h.length)return j;if(h=h.join(" "),s.matchesMedia(h))return j}return"100vw"}b.createElement("picture");var o,p,q,r,s={},t=!1,u=function(){},v=b.createElement("img"),w=v.getAttribute,x=v.setAttribute,y=v.removeAttribute,z=b.documentElement,A={},B={algorithm:""},C="data-pfsrc",D=C+"set",E=navigator.userAgent,F=/rident/.test(E)||/ecko/.test(E)&&E.match(/rv\:(\d+)/)&&RegExp.$1>35,G="currentSrc",H=/\s+\+?\d+(e\d+)?w/,I=/(\([^)]+\))?\s*(.+)/,J=a.picturefillCFG,K="position:absolute;left:0;visibility:hidden;display:block;padding:0;border:none;font-size:1em;width:1em;overflow:hidden;clip:rect(0px, 0px, 0px, 0px)",L="font-size:100%!important;",M=!0,N={},O={},P=a.devicePixelRatio,Q={px:1,"in":96},R=b.createElement("a"),S=!1,T=/^[ \t\n\r\u000c]+/,U=/^[, \t\n\r\u000c]+/,V=/^[^ \t\n\r\u000c]+/,W=/[,]+$/,X=/^\d+$/,Y=/^-?(?:[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/,Z=function(a,b,c,d){a.addEventListener?a.addEventListener(b,c,d||!1):a.attachEvent&&a.attachEvent("on"+b,c)},$=function(a){var b={};return function(c){return c in b||(b[c]=a(c)),b[c]}},_=function(){var a=/^([\d\.]+)(em|vw|px)$/,b=function(){for(var a=arguments,b=0,c=a[0];++b in a;)c=c.replace(a[b],a[++b]);return c},c=$(function(a){return"return "+b((a||"").toLowerCase(),/\band\b/g,"&&",/,/g,"||",/min-([a-z-\s]+):/g,"e.$1>=",/max-([a-z-\s]+):/g,"e.$1<=",/calc([^)]+)/g,"($1)",/(\d+[\.]*[\d]*)([a-z]+)/g,"($1 * e.$2)",/^(?!(e.[a-z]|[0-9\.&=|><\+\-\*\(\)\/])).*/gi,"")+";"});return function(b,d){var e;if(!(b in N))if(N[b]=!1,d&&(e=b.match(a)))N[b]=e[1]*Q[e[2]];else try{N[b]=new Function("e",c(b))(Q)}catch(f){}return N[b]}}(),aa=function(a,b){return a.w?(a.cWidth=s.calcListLength(b||"100vw"),a.res=a.w/a.cWidth):a.res=a.d,a},ba=function(a){if(t){var c,d,e,f=a||{};if(f.elements&&1===f.elements.nodeType&&("IMG"===f.elements.nodeName.toUpperCase()?f.elements=[f.elements]:(f.context=f.elements,f.elements=null)),c=f.elements||s.qsa(f.context||b,f.reevaluate||f.reselect?s.sel:s.selShort),e=c.length){for(s.setupRun(f),S=!0,d=0;e>d;d++)s.fillImg(c[d],f);s.teardownRun(f)}}};o=a.console&&console.warn?function(a){console.warn(a)}:u,G in v||(G="src"),A["image/jpeg"]=!0,A["image/gif"]=!0,A["image/png"]=!0,A["image/svg+xml"]=b.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1"),s.ns=("pf"+(new Date).getTime()).substr(0,9),s.supSrcset="srcset"in v,s.supSizes="sizes"in v,s.supPicture=!!a.HTMLPictureElement,s.supSrcset&&s.supPicture&&!s.supSizes&&!function(a){v.srcset="data:,a",a.src="data:,a",s.supSrcset=v.complete===a.complete,s.supPicture=s.supSrcset&&s.supPicture}(b.createElement("img")),s.supSrcset&&!s.supSizes?!function(){var a="data:image/gif;base64,R0lGODlhAgABAPAAAP///wAAACH5BAAAAAAALAAAAAACAAEAAAICBAoAOw==",c="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",d=b.createElement("img"),e=function(){var a=d.width;2===a&&(s.supSizes=!0),q=s.supSrcset&&!s.supSizes,t=!0,setTimeout(ba)};d.onload=e,d.onerror=e,d.setAttribute("sizes","9px"),d.srcset=c+" 1w,"+a+" 9w",d.src=c}():t=!0,s.selShort="picture>img,img[srcset]",s.sel=s.selShort,s.cfg=B,s.DPR=P||1,s.u=Q,s.types=A,s.setSize=u,s.makeUrl=$(function(a){return R.href=a,R.href}),s.qsa=function(a,b){return"querySelector"in a?a.querySelectorAll(b):[]},s.matchesMedia=function(){return a.matchMedia&&(matchMedia("(min-width: 0.1em)")||{}).matches?s.matchesMedia=function(a){return!a||matchMedia(a).matches}:s.matchesMedia=s.mMQ,s.matchesMedia.apply(this,arguments)},s.mMQ=function(a){return a?_(a):!0},s.calcLength=function(a){var b=_(a,!0)||!1;return 0>b&&(b=!1),b},s.supportsType=function(a){return a?A[a]:!0},s.parseSize=$(function(a){var b=(a||"").match(I);return{media:b&&b[1],length:b&&b[2]}}),s.parseSet=function(a){return a.cands||(a.cands=m(a.srcset,a)),a.cands},s.getEmValue=function(){var a;if(!p&&(a=b.body)){var c=b.createElement("div"),d=z.style.cssText,e=a.style.cssText;c.style.cssText=K,z.style.cssText=L,a.style.cssText=L,a.appendChild(c),p=c.offsetWidth,a.removeChild(c),p=parseFloat(p,10),z.style.cssText=d,a.style.cssText=e}return p||16},s.calcListLength=function(a){if(!(a in O)||B.uT){var b=s.calcLength(n(a));O[a]=b?b:Q.width}return O[a]},s.setRes=function(a){var b;if(a){b=s.parseSet(a);for(var c=0,d=b.length;d>c;c++)aa(b[c],a.sizes)}return b},s.setRes.res=aa,s.applySetCandidate=function(a,b){if(a.length){var c,d,e,f,h,k,l,m,n,o=b[s.ns],p=s.DPR;if(k=o.curSrc||b[G],l=o.curCan||j(b,k,a[0].set),l&&l.set===a[0].set&&(n=F&&!b.complete&&l.res-.1>p,n||(l.cached=!0,l.res>=p&&(h=l))),!h)for(a.sort(i),f=a.length,h=a[f-1],d=0;f>d;d++)if(c=a[d],c.res>=p){e=d-1,h=a[e]&&(n||k!==s.makeUrl(c.url))&&g(a[e].res,c.res,p,a[e].cached)?a[e]:c;break}h&&(m=s.makeUrl(h.url),o.curSrc=m,o.curCan=h,m!==k&&s.setSrc(b,h),s.setSize(b))}},s.setSrc=function(a,b){var c;a.src=b.url,"image/svg+xml"===b.set.type&&(c=a.style.width,a.style.width=a.offsetWidth+1+"px",a.offsetWidth+1&&(a.style.width=c))},s.getSet=function(a){var b,c,d,e=!1,f=a[s.ns].sets;for(b=0;bf?c=setTimeout(e,b-f):(c=null,a())};return function(){d=new Date,c||(c=setTimeout(e,b))}},h=z.clientHeight,i=function(){M=Math.max(a.innerWidth||0,z.clientWidth)!==Q.width||z.clientHeight!==h,h=z.clientHeight,M&&s.fillImgs()};Z(a,"resize",g(i,99)),Z(b,"readystatechange",e)}(),s.picturefill=ba,s.fillImgs=ba,s.teardownRun=u,ba._=s,a.picturefillCFG={pf:s,push:function(a){var b=a.shift();"function"==typeof s[b]?s[b].apply(s,a):(B[b]=a[0],S&&s.fillImgs({reselect:!0}))}};for(;J&&J.length;)a.picturefillCFG.push(J.shift());a.picturefill=ba,"object"==typeof module&&"object"==typeof module.exports?module.exports=ba:"function"==typeof define&&define.amd&&define("picturefill",function(){return ba}),s.supPicture||(A["image/webp"]=e("image/webp","data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAABBxAR/Q9ERP8DAABWUDggGAAAADABAJ0BKgEAAQADADQlpAADcAD++/1QAA=="))}(window,document); \ No newline at end of file