PluginProbe
Depicter — Popup & Slider Builder / trunk
Depicter — Popup & Slider Builder vtrunk
4.8.1 trunk 1.0.0 1.1.0 1.1.2 1.1.4 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.3.0 1.3.1 1.3.2 1.3.3 1.3.5 1.3.8 1.5.0 1.5.1 1.5.2 1.5.5 1.6.0 1.6.1 1.6.2 1.7.0 All 76 releases
depicter / app / src / Document / Models / Common / Background.php

Background.php in Depicter — Popup & Slider Builder trunk, at app/src/Document/Models/Common/Background.php

625 lines 18.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Depicter\Document\Models\Common;
3
4
5 use Averta\Core\Utility\Arr;
6 use Averta\Core\Utility\Embed;
7 use Averta\WordPress\Utility\JSON;
8 use Depicter\Document\CSS\Breakpoints;
9 use Depicter\Document\CSS\Selector;
10 use Depicter\Document\Models\Traits\HasDataSheetTrait;
11 use Depicter\Document\Models\Traits\MediaSourceTrait;
12 use Depicter\Html\Html;
13 use Depicter\Services\MediaBridge;
14
15 class Background
16 {
17 use HasDataSheetTrait;
18
19 use MediaSourceTrait;
20
21 /**
22 * @var Color
23 */
24 public $color;
25
26 /**
27 * @var string
28 */
29 public $fitMode;
30
31 /**
32 * @var object
33 */
34 public $video;
35
36 /**
37 * @var object
38 */
39 public $image;
40
41 /**
42 * @var Color
43 */
44 public $overlay;
45
46 /**
47 * @var Styles\Filter
48 */
49 public $filter;
50
51 /**
52 * @var array
53 */
54 public $kenBurnsData = [];
55
56 /**
57 * @var string
58 */
59 protected $markup;
60
61 /**
62 * Whether in preview mode or not
63 *
64 * @var bool
65 */
66 protected $isPreviewMode;
67
68 /**
69 * @var mixed|string[]
70 */
71 private $breakpoints;
72
73 /**
74 * @var mixed
75 */
76 private $renderArgs;
77
78 /**
79 * Check if section has background or not
80 *
81 * @return boolean
82 */
83 public function hasBackground() {
84 return !empty( $this->image->src->default ) || !empty( $this->video->src );
85 }
86
87 /**
88 * Check if section has background image or not
89 *
90 * @return boolean
91 */
92 public function hasBackgroundImage() {
93 return !empty( $this->image->src->default ) || !empty( $this->image->src->tablet ) || !empty( $this->image->src->mobile );
94 }
95
96 /**
97 * Calculates render options
98 *
99 * @return $this
100 */
101 protected function setRenderOptions(){
102 $this->breakpoints = Breakpoints::all();
103 $this->breakpoints['default'] = 1025;
104
105 $defaultAssetID = $this->image->src->default;
106 $tabletAssetID = $this->image->src->tablet ?? $this->image->src->default;
107 $mobileAssetID = $this->image->src->mobile ?? ( $this->image->src->tablet ?? $this->image->src->default );
108
109 $this->renderArgs['default']['assetId'] = $this->hasDataSheet() ? $this->maybeReplaceDataSheetTags( $defaultAssetID ) : $defaultAssetID;
110 $this->renderArgs['tablet']['assetId'] = $this->hasDataSheet() ? $this->maybeReplaceDataSheetTags( $tabletAssetID ) : $tabletAssetID;
111 $this->renderArgs['mobile']['assetId'] = $this->hasDataSheet() ? $this->maybeReplaceDataSheetTags( $mobileAssetID ) : $mobileAssetID;
112
113 $this->renderArgs['tablet']['assetId'] = $this->renderArgs['tablet']['assetId'] ?? $this->renderArgs['default']['assetId'];
114 $this->renderArgs['mobile']['assetId'] = $this->renderArgs['mobile']['assetId'] ?? $this->renderArgs['tablet']['assetId'];
115
116
117 $this->renderArgs['isPreview'] = \Depicter::front()->preview()->isPreview();
118 $this->renderArgs['default']['attachmentId'] = \Depicter::media()->getAttachmentId( $this->renderArgs['default']['assetId'] );
119 $this->renderArgs['tablet']['attachmentId'] = !empty( $this->renderArgs['tablet']['assetId'] ) ? \Depicter::media()->getAttachmentId( $this->renderArgs['tablet']['assetId'] ) : $this->renderArgs['default']['attachmentId'];
120 $this->renderArgs['mobile']['attachmentId'] = !empty( $this->renderArgs['mobile']['assetId'] ) ? \Depicter::media()->getAttachmentId( $this->renderArgs['mobile']['assetId'] ) : $this->renderArgs['tablet']['attachmentId'];
121
122 $this->renderArgs['isAttachment'] = is_numeric( $this->renderArgs['default']['attachmentId'] );
123 $this->renderArgs['altText'] = \Depicter::media()->getAltText( $this->renderArgs['default']['attachmentId'] );
124
125 $this->isPreviewMode = $this->renderArgs['isPreview'] || ! $this->renderArgs['isAttachment'];
126
127 return $this;
128 }
129
130 /**
131 * Render background markup
132 *
133 * @return string
134 * @throws \Exception
135 */
136 public function render() {
137 $this->markup = '';
138
139 if ( $this->hasBackgroundImage() ) {
140
141 $this->setRenderOptions();
142
143 $this->renderPictureWrapper();
144
145 $this->renderSourceTags();
146 $this->renderImageTag();
147
148 return $this->markup;
149 }
150
151 if ( !empty( $this->video->src ) ) {
152 $sourceID = \Depicter::media()->getAttachmentId( $this->video->src );
153 $sourceID = $sourceID ?? $this->video->src;
154 $url = false === strpos($sourceID, 'http') ? \Depicter::media()->getSourceUrl($this->video->src) : $this->video->src;
155 $this->markup .= $this->renderVideo( $url );
156 }
157
158 return $this->markup;
159 }
160
161 /**
162 * Render images
163 *
164 *
165 * @return Html
166 */
167 protected function renderImageTag() {
168 $imageID = \Depicter::media()->getAttachmentId( $this->renderArgs['default']['assetId'] );
169
170 $imageUrl = \Depicter::media()->getSourceUrl( $this->renderArgs['default']['assetId'] );
171
172 $args = [
173 'src' => \Depicter::media()::IMAGE_PLACEHOLDER_SRC,
174 'alt' => is_numeric( $imageID ) ? \Depicter::media()->getAltText( $imageID ) : ''
175 ];
176
177 if( !empty( $this->renderArgs['default']['assetId'] ) ){
178 $args[ 'data-depicter-src' ] = $imageUrl;
179 }
180
181 if ( !empty( $this->image->alt ) ) {
182 $args['alt'] = $this->image->alt;
183 }
184
185 $available_args = [
186 'responsiveArgs' => [
187 'data-object-fit' => 'fitMode',
188 'data-object-position' => 'position'
189 ]
190 ];
191
192 $args = $this->getElementAttributes( 'image', $args, $available_args );
193
194 if ( !empty( $this->kenBurnsData ) ) {
195 $args = Arr::merge( $args, $this->kenBurnsData );
196 }
197
198 $cropAttributes = $this->getCropAttributes( 'image' );
199
200 $img = Html::img( $imageUrl, Arr::merge( $cropAttributes, $args ) );
201
202 $this->markup->nest( $img . "\n" );
203
204 return $this->markup;
205 }
206
207 /**
208 * Render the element wrapper tag
209 *
210 * @throws \JsonMapper_Exception
211 */
212 protected function renderPictureWrapper(){
213
214 $defaultBackgroundImageID = $this->renderArgs['default']['assetId'] ?? ( $this->renderArgs['tablet']['assetId'] ?? $this->renderArgs['mobile']['assetId'] );
215 $imageID = \Depicter::media()->getAttachmentId( $defaultBackgroundImageID );
216
217 $args = [
218 'class' => 'depicter-bg',
219 'alt' => is_numeric( $imageID ) ? \Depicter::media()->getAltText( $imageID ) : ''
220 ];
221
222 if ( !empty( $this->image->alt ) ) {
223 $args['alt'] = $this->image->alt;
224 }
225
226 $this->markup = Html::picture( $args );
227 }
228
229 /**
230 * Renders and appends necessary source tags with media queries for breakpoints
231 */
232 protected function renderSourceTags(){
233
234 $desktopSources = $this->getSourceUrls( 'default' );
235 $tabletSources = $this->getSourceUrls( 'tablet' );
236 $mobileSources = $this->getSourceUrls( 'mobile' );
237
238 if( ! $tabletSources && ! $mobileSources ){
239 $this->appendSourceTag( $desktopSources );
240 return;
241 }
242
243 if( $desktopSources == $tabletSources ){
244 if( $tabletSources == $mobileSources ){
245 // if all breakpoints sources are the same
246 $this->appendSourceTag( $desktopSources );
247 } else {
248 // if desktop and tablet sources are the same
249 $this->appendSourceTag( $mobileSources, 'max-width', $this->breakpoints['mobile'] );
250 $this->appendSourceTag( $desktopSources, 'min-width', $mobileSources ? (int) $this->breakpoints['mobile'] + 1 : 0 );
251 }
252 } elseif( $tabletSources == $mobileSources ){
253 // if tablet and mobile sources are the same
254 $this->appendSourceTag( $tabletSources, 'max-width', $this->breakpoints['tablet'] );
255 $this->appendSourceTag( $desktopSources, 'min-width', ( $tabletSources ? (int) $this->breakpoints['tablet'] + 1 : 0 ) );
256 } else {
257 $this->appendSourceTag( $mobileSources, 'max-width', $this->breakpoints['mobile'] );
258 $this->appendSourceTag( $tabletSources, 'max-width', $this->breakpoints['tablet'] );
259
260 $mediaQuerySize = (int) $this->breakpoints['default'];
261 if( ! $tabletSources ){
262 $mediaQuerySize = $mobileSources ? (int) $this->breakpoints['mobile'] + 1 : 0;
263 }
264 $this->appendSourceTag( $desktopSources, 'min-width', $mediaQuerySize );
265 }
266
267 }
268
269 /**
270 * Retrieves source urls (srcset) for a breakpoint in array
271 *
272 * @param string $device
273 *
274 * @return array
275 */
276 protected function getSourceUrls( $device = 'default' ){
277
278 $imageSources = [];
279
280 try{
281 if ( $device == 'default' && ! empty( $this->renderArgs['default']['assetId'] ) ) {
282 $imageSources[] = \Depicter::media()->getSourceUrl( $this->renderArgs['default']['assetId'] );
283 if ( !empty( $imageSources ) && null !== $defaultAssetID = \Depicter::media()->getAttachmentId( $this->renderArgs['default']['assetId'] ) ) {
284 [ $imageSource, $width, $height ] = wp_get_attachment_image_src( $defaultAssetID, 'full');
285 $retinaImageSource = \Depicter::media()->resizeSourceUrl(
286 $defaultAssetID,
287 $width * 2,
288 $height * 2 );
289
290 if( $retinaImageSource ){
291 $imageSources[] = $retinaImageSource . ' 2x';
292 }
293 }
294 } else if ( $device == 'tablet' && !empty( $this->renderArgs['tablet']['assetId'] ) ) {
295 if ( $this->renderArgs['tablet']['assetId'] == $this->renderArgs['default']['assetId'] ) {
296 $imageSources[] = \Depicter::media()->getSourceUrl( $this->renderArgs['default']['assetId'], 'medium');
297 $imageSources[] = \Depicter::media()->getSourceUrl( $this->renderArgs['default']['assetId'], 'full') . ' 2x';
298 } else {
299 $imageSources[] = \Depicter::media()->getSourceUrl( $this->renderArgs['tablet']['assetId'] );
300 if ( !empty( $imageSources ) && null !== $tabletAssetID = \Depicter::media()->getAttachmentId( $this->renderArgs['tablet']['assetId'] ) ) {
301 [ $imageSource, $width, $height ] = wp_get_attachment_image_src( $tabletAssetID, 'full');
302 $retinaImageSource = \Depicter::media()->resizeSourceUrl(
303 $tabletAssetID,
304 $width * 2,
305 $height * 2 );
306
307 if( $retinaImageSource ){
308 $imageSources[] = $retinaImageSource . ' 2x';
309 }
310 }
311 }
312 } else if ( $device == 'mobile' && !empty( $this->renderArgs['mobile']['assetId'] ) ) {
313 if ( $this->renderArgs['mobile']['assetId'] == $this->renderArgs['default']['assetId'] || $this->renderArgs['mobile']['assetId'] == $this->renderArgs['tablet']['assetId'] ) {
314 $imageSources[] = \Depicter::media()->getSourceUrl( $this->renderArgs['mobile']['assetId'], 'medium');
315 $imageSources[] = \Depicter::media()->getSourceUrl( $this->renderArgs['mobile']['assetId'], 'large') . ' 2x';
316 } else {
317 $imageSources[] = \Depicter::media()->getSourceUrl( $this->renderArgs['mobile']['assetId'] );
318 if ( !empty( $imageSources ) && null !== $mobileAssetID = \Depicter::media()->getAttachmentId( $this->renderArgs['mobile']['assetId'] ) ) {
319 [ $imageSource, $width, $height ] = wp_get_attachment_image_src( $mobileAssetID, 'full');
320 $retinaImageSource = \Depicter::media()->resizeSourceUrl(
321 $mobileAssetID,
322 $width * 2,
323 $height * 2 );
324
325 if( $retinaImageSource ){
326 $imageSources[] = $retinaImageSource . ' 2x';
327 }
328 }
329 }
330 }
331 } catch( \Exception $e ){
332 }
333
334 return $imageSources;
335 }
336
337 /**
338 * Generates and appends a source tag with media query to element markup
339 *
340 * @param array $imageSources
341 * @param string $mediaQueryCondition
342 * @param int $mediaQuerySize
343 */
344 protected function appendSourceTag( $imageSources = [], $mediaQueryCondition = 'max-width', $mediaQuerySize = null ){
345 if( ! $imageSources ){
346 return;
347 }
348
349 $this->addMediaUlrToDictionary( $imageSources, $mediaQueryCondition, $mediaQuerySize );
350
351 $attributes = [
352 'data-depicter-srcset' => trim( implode( ', ', $imageSources ), ', ' ),
353 'srcset' => \Depicter::media()::IMAGE_PLACEHOLDER_SRC,
354 ];
355 if( $mediaQueryCondition && $mediaQuerySize ){
356 $attributes['media'] = '(' . $mediaQueryCondition . ': ' . $mediaQuerySize . 'px)';
357 }
358 $sourceTag = Html::source( $attributes );
359
360 $this->markup->nest( "\n" . $sourceTag . "\n" );
361 }
362
363 /**
364 * Renders video tag
365 *
366 * @param $videoUrl
367 *
368 * @return \TypeRocket\Html\Html
369 */
370 public function renderVideo( $videoUrl ) {
371
372 $args = [
373 'class' => Selector::prefixify( 'bg-video' ),
374 'data-video-src' => $videoUrl,
375 'preload' => 'metadata',
376 'playsinline' => "true"
377 ];
378
379 $available_args = [
380 'data-loop' => 'loop',
381 'data-goto-next' => 'goNextSlide',
382 'data-auto-pause' => 'pause',
383 'data-player-type' => 'type',
384 'responsiveArgs' => [
385 'data-object-fit' => 'fitMode',
386 'data-object-position' => 'position'
387 ]
388 ];
389
390 $args = $this->getElementAttributes( 'video', $args, $available_args );
391
392 return Html::div( $args );
393 }
394
395 /**
396 * Get crop attributes
397 *
398 * @param string $type
399 *
400 * @return array
401 */
402 protected function getCropAttributes( string $type = 'image' ){
403 if ( ! $this->hasCustomFitMode( $type ) ) {
404 return [];
405 }
406
407 $attributes = [];
408 $breakpointNames = Breakpoints::names();
409
410 foreach ( $breakpointNames as $device ) {
411 if( ! empty( $this->{$type}->cropData->{$device} ) ){
412 $attributeDeviceValue = $this->{$type}->cropData->{$device};
413 $attributeDeviceValue = is_object( $attributeDeviceValue ) || is_array( $attributeDeviceValue ) ? JSON::encode( $attributeDeviceValue ) : '';
414 $attributeDeviceName = $device === 'default' ? 'data-crop' : "data-{$device}-crop";
415 $attributes[ $attributeDeviceName ] = $attributeDeviceValue;
416 }
417 }
418
419 return $attributes;
420 }
421
422 /**
423 * Whether background types has custom fit mode or not
424 *
425 * @param string $type
426 *
427 * @return bool
428 */
429 protected function hasCustomFitMode( string $type = 'image' ){
430 if ( empty( $this->{$type}->fitMode->default ) ) {
431 return false;
432 }
433
434 $hasCustomFitMode= false;
435 $breakpointNames = Breakpoints::names();
436
437 foreach ( $breakpointNames as $device ) {
438 if( !empty( $this->{$type}->fitMode->{$device} ) && $this->{$type}->fitMode->{$device} === 'custom' ){
439 return true;
440 }
441 }
442
443 return false;
444 }
445
446 public function getElementAttributes( $element_type, $args, $available_args ) {
447 foreach ( $available_args as $attribute => $property ) {
448 // for object properties that has responsive value like fitMode and position
449 if ( $attribute == 'responsiveArgs' ) {
450 foreach ( $property as $key => $value ) {
451 $breakpointNames = Breakpoints::names();
452 if ( !empty( $this->{$element_type}->{$value} ) ) {
453 foreach ( $breakpointNames as $device ) {
454 $args[ $key ][] = !empty( $this->{$element_type}->{$value}->{$device} ) ? $this->{$element_type}->{$value}->{$device} : '';
455 }
456 $args[ $key ] = implode(',', $args[ $key ] );
457 }
458 }
459
460 // for simple object properties
461 } elseif ( !empty( $this->{$element_type}->{$property} ) ) {
462 $value = $this->{$element_type}->{$property} == "1" ? "true" : $this->{$element_type}->{$property};
463 $args[ $attribute ] = $value;
464 }
465
466 // In video background, if each of `muted` or `loop` are not set, consider it as `true` by default
467 if( $element_type === 'video' ){
468 if( $property == 'loop' && ! isset( $this->{$element_type}->{$property} ) ) {
469 $args[ $attribute ] = 'true';
470 }
471
472 if ( $property == 'type' && ! empty( $this->{$element_type}->{$property} ) ) {
473 if ( $this->{$element_type}->{$property} == 'selfHostedVideo' ) {
474 $args[ $attribute ] = 'native';
475 } else if ( $this->{$element_type}->{$property} == 'embedVideo' ) {
476 $args[ $attribute ] = $this->{$element_type}->embedType;
477 if ( $this->{$element_type}->embedType == 'youtube' && ! empty( $args['data-video-src'] ) ) {
478 $args['data-video-poster'] = Embed::getYouTubePosterUrl( $args['data-video-src'] );
479 }
480 }
481 }
482 }
483 }
484 return $args;
485 }
486
487 /**
488 * Get css background color
489 *
490 * @return array
491 */
492 public function getColor() {
493 $devices = Breakpoints::names();
494
495 $css = [];
496 foreach ( $devices as $device ) {
497 if ( !empty( $this->color->{$device} ) ) {
498 if ( false == strpos( $this->color->{$device}, 'gradient' ) ) {
499 $css[ $device]['background-color'] = $this->color->{$device};
500 } else {
501 $css[ $device]['background-image'] = $this->color->{$device};
502 }
503 }
504 }
505
506 return $css;
507 }
508
509 /**
510 * Get background overlay styles
511 *
512 * @return array
513 */
514 public function getOverlayStyles() {
515 $default = [
516 "content" => '""',
517 "display" => "block",
518 "position"=> "absolute",
519 "top" => "0",
520 "bottom" => "0",
521 "right" => "0",
522 "left" => "0",
523 "z-index" => "1",
524 ];
525
526 $devices = Breakpoints::names();
527
528 $css = [];
529 foreach ( $devices as $device ) {
530 if ( !empty( $this->overlay->{$device} ) && ('transparent' !== $this->overlay->{$device} ) ) {
531 $css[ $device] = $default;
532 if ( false == strpos( $this->overlay->{$device}, 'gradient' ) ) {
533 $css[ $device]['background-color'] = $this->overlay->{$device};
534 } else {
535 $css[ $device]['background-image'] = $this->overlay->{$device};
536 }
537 }
538 }
539
540 return $css;
541 }
542
543 /**
544 * Get filter styles of background
545 *
546 * @return array
547 */
548 public function getSectionBackgroundFilter() {
549 if( empty( $this->filter ) ){
550 return [];
551 }
552 return $this->filter->set([]);
553 }
554
555 /**
556 * Get class name of background container
557 *
558 * @return string
559 */
560 public function getContainerClassName() {
561 if ( !empty( $this->video->src ) && ( ( $this->video->type ?? '' ) !== "embedVideo" ) ) {
562 return Selector::prefixify('bg-video');
563 }
564
565 return Selector::prefixify('section-background');
566 }
567
568 /**
569 * Set ken burns data
570 *
571 * @param array $kenBurnsData
572 *
573 * @return void
574 */
575 public function setKenBurnsData( array $kenBurnsData = [] ) {
576 $this->kenBurnsData = $kenBurnsData;
577 }
578
579 /**
580 * Renders embed video tag
581 *
582 * @return \TypeRocket\Html\Html
583 */
584 public function renderEmbedVideo() {
585 $embedUrl = $this->video->src;
586
587 if ( $this->video->embedType == 'youtube' ) {
588 if( $embedUrl = Embed::getYouTubeVimeoEmbedUrl( $embedUrl ) ){
589 $embedUrl = add_query_arg([
590 'controls' => '0',
591 'mute' => '1',
592 'rel' => '0'
593 ], $embedUrl );
594 }
595 } else if ( $this->video->embedType == 'vimeo' ) {
596 if( $embedUrl = Embed::getYouTubeVimeoEmbedUrl( $embedUrl ) ){
597 $embedUrl = add_query_arg([
598 'controls' => '0',
599 'background' => '1'
600 ], $embedUrl );
601 }
602 }
603
604 $iframe = Html::iframe([
605 'data-depicter-src' => $embedUrl,
606 "frameborder" => "0",
607 "allowfullscreen" => "",
608 'data-type' => $this->video->embedType,
609 'data-width' => $this->video->size->width,
610 'data-height' => $this->video->size->height,
611 'data-goto-next' => $this->video->goNextSlide ?? false,
612 'data-auto-pause' => $this->video->pause ?? false,
613 'data-loop' => $this->video->loop ?? true
614 ]);
615
616 if ( $this->video->embedType == 'youtube' ) {
617 $iframe .= Html::img( Embed::getYouTubePosterUrl( $this->video->src ), [
618 'class' => Selector::prefixify( 'bg-embed-poster' )
619 ]);
620 }
621
622 return Html::div(['class' => 'depicter-bg-embed'], $iframe );
623 }
624 }
625