Create New Item
×
Item Type
File
Folder
Item Name
×
Search file in folder and subfolders...
File Manager
/
wp-content
/
plugins
/
depicter
/
app
/
src
/
Document
/
Models
/
Elements
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php namespace Depicter\Document\Models\Elements; use Averta\Core\Utility\Data; use Averta\Core\Utility\Media; use Averta\WordPress\Utility\JSON; use Depicter; use Depicter\Document\CSS\Breakpoints; use Depicter\Document\Models; use Depicter\Front\Preview; use Depicter\Html\Html; use Depicter\Media\Image\FileResizedFinder; use Depicter\Services\MediaBridge; class Image extends Models\Element { use Models\Traits\MediaSourceTrait; /** * SVG Mime type */ const SVG_MIME = 'image/svg+xml'; /** * SVG Mime type */ const GIF_MIME = 'image/gif'; /** * @var array */ protected $renderArgs = []; /** * Whether in preview mode or not * * @var bool */ protected $isPreviewMode; /** * List of breakpoint sizes * * @var array */ protected $breakpoints; /** * Stores inherited crop data options * * @var array */ protected $inheritedOptions = []; /** * Calculates render options * * @return $this */ protected function setRenderOptions(){ $this->breakpoints = Breakpoints::all(); $this->breakpoints['default'] = 1025; $this->inheritedOptions = [ 'resizeW' => null, 'resizeH' => null, 'cropW' => null, 'cropH' => null, 'focalX' => 0.5, 'focalY' => 0.5, 'cropData'=> null ]; $this->renderArgs['assetId'] = $this->hasDataSheet() ? $this->maybeReplaceDataSheetTags( $this->options->source ) : $this->options->source; if ( empty( $this->renderArgs['assetId'] ) ) { return ''; } $this->renderArgs['isPreview'] = Depicter::front()->preview()->isPreview(); $this->renderArgs['isSVG'] = $this->isSvg( $this->renderArgs['assetId'] ); $this->renderArgs['isGif'] = $this->isGif( $this->renderArgs['assetId'] ); $this->renderArgs['sizeUnit'] = $this->size->default->width->unit ?: 'px'; $this->renderArgs['attachmentId'] = Depicter::media()->getAttachmentId( $this->renderArgs['assetId'] ); $this->renderArgs['isAttachment'] = is_numeric( $this->renderArgs['attachmentId'] ); $this->renderArgs['altText'] = Depicter::media()->getAltText( $this->renderArgs['attachmentId'] ); $this->isPreviewMode = $this->renderArgs['isPreview'] || $this->renderArgs['isSVG'] || $this->renderArgs['isGif'] || ! $this->renderArgs['isAttachment']; return $this; } /** * Whether it's preview mode or not * * @return bool */ protected function isPreviewMode(){ return $this->isPreviewMode; } protected function getUnitSize( $device = 'default' ){ return $this->size->{$device}->width->unit ?: 'px'; } protected function hasRelativeUnitSize( $device = 'default' ){ $relativeUnits = ['%']; if( isset( $this->size->{$device}->width->unit ) && in_array( $this->size->{$device}->width->unit, $relativeUnits ) ){ return true; } if( isset( $this->size->{$device}->height->unit ) && in_array( $this->size->{$device}->height->unit, $relativeUnits ) ){ return true; } return false; } /** * Render the element wrapper tag * * @throws \JsonMapper_Exception */ protected function renderPictureWrapper(){ $args = $this->getDefaultAttributes(); $devices = Breakpoints::names(); foreach( $devices as $device ) { $dataAttrName = $device == 'default' ? 'data-crop' : 'data-' . $device . '-crop'; $hasRelativeUnitSize = $this->hasRelativeUnitSize( $device ); if ( $this->isPreviewMode || $hasRelativeUnitSize ) { if( ! empty( $this->cropData->{$device} ) ){ $mediaWidth = $this->cropData->{$device}->mediaSize->width; $mediaHeight = $this->cropData->{$device}->mediaSize->height; if( $this->hasDataSheet() ){ $attachment = wp_get_attachment_image_src( $this->renderArgs['attachmentId'], 'full' ); if ( !$attachment ) { continue; } $originalMediaWidth = $attachment[1] ?: $mediaWidth; $originalMediaHeight = $attachment[2] ?: $mediaHeight; [ $mediaWidth, $mediaHeight ] = Media::fitInBox( 'contain', $mediaWidth, $mediaHeight, $originalMediaWidth, $originalMediaHeight ); } $args[ $dataAttrName ] = [ 'mediaSize'=> [ 'width' => $mediaWidth, 'height' => $mediaHeight ] ]; $args[ $dataAttrName ]['focalPoint'] = [ 'x' => ! empty( $this->cropData->{$device}->focalPoint->x ) ? $this->cropData->{$device}->focalPoint->x : 0.5, 'y' => ! empty( $this->cropData->{$device}->focalPoint->y ) ? $this->cropData->{$device}->focalPoint->y : 0.5 ]; $args[ $dataAttrName ] = JSON::encode( $args[ $dataAttrName ] ); } } elseif( ! $hasRelativeUnitSize && ! empty( $this->cropData->{$device} ) && $this->inheritedOptions['cropData'] !== "false" ){ $args[ $dataAttrName ] = "false"; } if( isset( $args[ $dataAttrName ] ) ){ $this->inheritedOptions['cropData'] = $args[ $dataAttrName ]; } } $this->markup = Html::picture( $args ); } /** * Renders a default image tag * * @return mixed * @throws \Exception */ protected function renderImageTag(){ [ $resizeWidth, $resizeHeight, $cropWidth, $cropHeight, $args ] = $this->getImageEditOptions( 'default' ); $hasRelativeUnitSize = $this->hasRelativeUnitSize( 'default' ); if ( $this->isPreviewMode ) { $imageSource = Depicter::media()->getSourceUrl( $this->renderArgs['assetId'], [ $cropWidth, $cropHeight ], false, [ 'dry' => true ] ); } else { $args['upscale'] = true; if( $hasRelativeUnitSize ){ $cropWidth = null; $cropHeight = null; } $imageSource = Depicter::media()->resizeSourceUrl( $this->renderArgs['attachmentId'], $resizeWidth, $resizeHeight, $cropWidth, $cropHeight, $args ); } $img = Html::img( '', [ 'src' => \Depicter::media()::IMAGE_PLACEHOLDER_SRC, 'data-depicter-src' => $imageSource, 'alt' => $this->renderArgs['altText'] ] ); if ( $this->isPreviewMode && ! $this->renderArgs['isSVG'] && ! $hasRelativeUnitSize ) { $img = Html::source([ 'data-depicter-srcset' => $imageSource, ]) . $img; } if( $this->renderArgs['isSVG'] ){ $this->addMediaUlrToDictionary( $imageSource, '', '', self::SVG_MIME ); } $this->markup->nest( $img . "\n" ); if ( false !== $a = $this->getLinkTag() ) { $this->markup = $a->nest( "\n". $this->markup . "\n" ); } return $this->markup; } /** * Renders element markup * * @return string|void * @throws \JsonMapper_Exception * @throws \Exception */ public function render() { $this->setRenderOptions(); if ( empty( $this->renderArgs['assetId'] ) ) { return ''; } $this->renderPictureWrapper(); if ( false === strpos( $this->renderArgs['assetId'], 'http' ) ) { if ( $this->hasFrame() ) { Depicter::symbolsProvider()->addClipPath( $this->options->clipPath ); } $this->renderSourceTags(); $this->renderImageTag(); } else { $img = Html::img( '', [ 'src' => \Depicter::media()::IMAGE_PLACEHOLDER_SRC, 'data-depicter-src' => $this->renderArgs['assetId'] ] ); $this->markup->nest( $img . "\n" ); } return $this->markup; } /** * Retrieves attachment mime type * * @param string|int $assetId * * @return false|string */ protected function getMimeType( $assetId ){ $attachmentId = Depicter::media()->getAttachmentId( $assetId ); return is_numeric( $attachmentId ) ? get_post_mime_type( $attachmentId ) : false; } /** * Whether it's svg file or not * * @param string|int $assetId * * @return bool */ public function isSvg( $assetId ) { return $this->getMimeType( $assetId ) == self::SVG_MIME; } /** * Whether it's gif file or not * * @param string|int $assetId * * @return bool */ public function isGif( $assetId ) { return $this->getMimeType( $assetId ) == self::GIF_MIME; } /** * Retrieves image edit options for a breakpoint * * @param string $device * * @return array */ protected function getImageEditOptions( $device = 'default' ){ $params = []; $this->inheritedOptions['resizeW'] = ! empty( $this->cropData->{$device}->mediaSize->width ) ? round( $this->cropData->{$device}->mediaSize->width ) : null; $this->inheritedOptions['resizeH'] = ! empty( $this->cropData->{$device}->mediaSize->height ) ? round( $this->cropData->{$device}->mediaSize->height ) : null; if( $this->hasDataSheet() ){ $attachment = wp_get_attachment_image_src( $this->renderArgs['attachmentId'], 'full' ); if ( !$attachment ) { return; } $originalMediaWidth = $attachment[1] ?: null; $originalMediaHeight = $attachment[2] ?: null; [ $mediaWidth, $mediaHeight ] = Media::fitInBox( 'contain', $this->inheritedOptions['resizeW'], $this->inheritedOptions['resizeH'], $originalMediaWidth, $originalMediaHeight ); $params[] = $mediaWidth; $params[] = $mediaHeight; } else { $params[] = $this->inheritedOptions['resizeW']; $params[] = $this->inheritedOptions['resizeH']; } $params[] = $this->inheritedOptions['cropW'] = !empty( $this->size->{$device}->width->value ) ? round( $this->size->{$device}->width->value ) : null; $params[] = $this->inheritedOptions['cropH'] = !empty( $this->size->{$device}->height->value ) ? round( $this->size->{$device}->height->value ) : null; $this->inheritedOptions['focalX'] = !empty( $this->cropData->{$device}->focalPoint->x ) ? $this->cropData->{$device}->focalPoint->x : $this->inheritedOptions['focalX']; $this->inheritedOptions['focalY'] = !empty( $this->cropData->{$device}->focalPoint->y ) ? $this->cropData->{$device}->focalPoint->y : $this->inheritedOptions['focalY']; $params[] = [ 'focalX' => $this->inheritedOptions['focalX'], 'focalY' => $this->inheritedOptions['focalY'] ]; /** * @example array [ * 0 => resizeW * 1 => resizeH * 2 => cropW * 3 => cropH * 4 => [ * 0 => focalX * 1 => focalY * ] * ] */ $params[0] = $params[0] ?? $params[2]; $params[1] = $params[1] ?? $params[3]; return $params; } /** * Retrieves source urls (srcset) for a breakpoint in array * * @param string $device * * @return array */ protected function getSourceUrls( $device = 'default' ){ [ $resizeWidth, $resizeHeight, $cropWidth, $cropHeight, $args ] = $this->getImageEditOptions( $device ); if( ! $cropWidth && ! $cropHeight && ! $resizeWidth && ! $resizeHeight ){ return []; } $args['upscale'] = true; if( $this->hasRelativeUnitSize( $device ) ){ $cropWidth = null; $cropHeight = null; } $imageSources = []; $imageSources[] = Depicter::media()->resizeSourceUrl( $this->renderArgs['attachmentId'], $resizeWidth, $resizeHeight, $cropWidth, $cropHeight, $args ); if( empty( $imageSources ) ){ return []; } // $args['upscale'] = false; $retinaImageSource = Depicter::media()->resizeSourceUrl( $this->renderArgs['attachmentId'], $resizeWidth ? $resizeWidth * 2 : $resizeWidth, $resizeHeight ? $resizeHeight * 2 : $resizeHeight, $cropWidth ? $cropWidth * 2 : $cropWidth, $cropHeight ? $cropHeight * 2 : $cropHeight, $args ); if( $retinaImageSource ){ $imageSources[] = $retinaImageSource . ' 2x'; } return $imageSources; } /** * Generates and appends a source tag with media query to element markup * * @param array $imageSources * @param string $mediaQueryCondition * @param int $mediaQuerySize */ protected function appendSourceTag( $imageSources = [], $mediaQueryCondition = 'max-width', $mediaQuerySize = null ){ if( ! $imageSources ){ return; } $this->addMediaUlrToDictionary( $imageSources, $mediaQueryCondition, $mediaQuerySize ); $attributes = [ 'data-depicter-srcset' => trim( implode( ', ', $imageSources ), ', ' ), 'srcset' => \Depicter::media()::IMAGE_PLACEHOLDER_SRC, ]; if( $mediaQueryCondition && $mediaQuerySize ){ $attributes['media'] = '(' . $mediaQueryCondition . ': ' . $mediaQuerySize . 'px)'; } $sourceTag = Html::source( $attributes ); $this->markup->nest( "\n" . $sourceTag . "\n" ); } /** * Renders and appends necessary source tags with media queries for breakpoints */ protected function renderSourceTags(){ if( $this->isPreviewMode ){ return; } $desktopSources = $this->getSourceUrls( 'default' ); $tabletSources = $this->getSourceUrls( 'tablet' ); $mobileSources = $this->getSourceUrls( 'mobile' ); if( ! $tabletSources && ! $mobileSources ){ $this->appendSourceTag( $desktopSources ); return; } if( $desktopSources == $tabletSources ){ if( $tabletSources == $mobileSources ){ // if all breakpoints sources are the same $this->appendSourceTag( $desktopSources ); } else { // if desktop and tablet sources are the same $this->appendSourceTag( $mobileSources, 'max-width', $this->breakpoints['mobile'] ); $this->appendSourceTag( $desktopSources, 'min-width', $mobileSources ? (int) $this->breakpoints['mobile'] + 1 : 0 ); } } elseif( $tabletSources == $mobileSources ){ // if tablet and mobile sources are the same $this->appendSourceTag( $tabletSources, 'max-width', $this->breakpoints['tablet'] ); $this->appendSourceTag( $desktopSources, 'min-width', ( $tabletSources ? (int) $this->breakpoints['tablet'] + 1 : 0 ) ); } else { $this->appendSourceTag( $mobileSources, 'max-width', $this->breakpoints['mobile'] ); $this->appendSourceTag( $tabletSources, 'max-width', $this->breakpoints['tablet'] ); $mediaQuerySize = (int) $this->breakpoints['default']; if( ! $tabletSources ){ $mediaQuerySize = $mobileSources ? (int) $this->breakpoints['mobile'] + 1 : 0; } $this->appendSourceTag( $desktopSources, 'min-width', $mediaQuerySize ); } } /** * check if image has frame or not * * @return bool */ protected function hasFrame(): bool{ return ! empty( $this->options->clipPath ); } /** * Get list of selector and CSS for element * * @return array * @throws \JsonMapper_Exception */ public function getSelectorAndCssList(){ parent::getSelectorAndCssList(); // Add clip path style if ( $this->hasFrame() ) { $this->selectorCssList[ '.' . $this->getStyleSelector() ]['default']['clip-path'] = 'url(#' . $this->options->clipPath . ')'; } return $this->selectorCssList; } }