This commit is contained in:
emmymayo
2025-02-05 23:15:46 +01:00
commit 7269c99357
16995 changed files with 3389680 additions and 0 deletions
@@ -0,0 +1,108 @@
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
/**
* Lets you round the numeric elements of an array to integers while preserving their sum.
*
* Usage:
*
* Jetpack_Constrained_Array_Rounding::get_rounded_constrained_array( $bound_array )
* if a specific sum doesn't need to be specified for the bound array
*
* Jetpack_Constrained_Array_Rounding::get_rounded_constrained_array( $bound_array, $sum )
* If the sum of $bound_array must equal $sum after rounding.
*
* If $sum is less than the sum of the floor of the elements of the array, the class defaults to using the sum of the array elements.
*/
class Jetpack_Constrained_Array_Rounding {
/**
* Get the rounded constrained array.
*
* @param array $bound_array - the array we're rounding.
* @param int $sum - the sum of the array.
*
* @return array
*/
public static function get_rounded_constrained_array( $bound_array, $sum = false ) {
// Convert associative arrays before working with them and convert them back before returning the values
$keys = array_keys( $bound_array );
$bound_array = array_values( $bound_array );
$bound_array_int = self::get_int_floor_array( $bound_array );
$lower_sum = array_sum( wp_list_pluck( $bound_array_int, 'floor' ) );
if ( ! $sum || ( $sum < $lower_sum ) ) {
// If value of sum is not supplied or is invalid, calculate the sum that the returned array is constrained to match
$sum = array_sum( $bound_array );
}
$diff_sum = $sum - $lower_sum;
self::adjust_constrained_array( $bound_array_int, $diff_sum );
$bound_array_fin = wp_list_pluck( $bound_array_int, 'floor' );
return array_combine( $keys, $bound_array_fin );
}
/**
* Get int floor of array values.
*
* @param array $bound_array - the array we're getting floor values for.
*
* @return array
*/
private static function get_int_floor_array( $bound_array ) {
$bound_array_int_floor = array();
foreach ( $bound_array as $i => $value ) {
$bound_array_int_floor[ $i ] = array(
'floor' => (int) floor( $value ),
'fraction' => $value - floor( $value ),
'index' => $i,
);
}
return $bound_array_int_floor;
}
/**
* Adjust the constrained array.
*
* @param array $bound_array_int - the array we're adjusting.
* @param int $adjustment - how much we're adjusting the array.
*/
private static function adjust_constrained_array( &$bound_array_int, $adjustment ) {
usort( $bound_array_int, array( self::class, 'cmp_desc_fraction' ) );
$start = 0;
$end = $adjustment - 1;
$length = count( $bound_array_int );
for ( $i = $start; $i <= $end; $i++ ) {
++$bound_array_int[ $i % $length ]['floor'];
}
usort( $bound_array_int, array( self::class, 'cmp_asc_index' ) );
}
/**
* Compare fraction values of two arrays.
*
* @param array $a - the first array we're comparing.
* @param array $b - the second array we're comparing.
*
* @return int
*/
private static function cmp_desc_fraction( $a, $b ) {
return $b['fraction'] <=> $a['fraction'];
}
/**
* Compare index values of two arrays.
*
* @param array $a - the first array.
* @param array $b - the second array.
*
* @return int
*/
private static function cmp_asc_index( $a, $b ) {
return $a['index'] <=> $b['index'];
}
}
@@ -0,0 +1,382 @@
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
use Automattic\Jetpack\Assets;
use Automattic\Jetpack\Image_CDN\Image_CDN;
use Automattic\Jetpack\Status;
// Include the class file containing methods for rounding constrained array elements.
// Here the constrained array element is the dimension of a row, group or an image in the tiled gallery.
require_once __DIR__ . '/math/class-constrained-array-rounding.php';
// Layouts
require_once __DIR__ . '/tiled-gallery/tiled-gallery-rectangular.php';
require_once __DIR__ . '/tiled-gallery/tiled-gallery-square.php';
require_once __DIR__ . '/tiled-gallery/tiled-gallery-circle.php';
/**
* Jetpack tiled gallery class.
*/
class Jetpack_Tiled_Gallery {
/**
* Shortcode attributes.
*
* @var array
*/
public $atts;
/**
* Text direction (right or left).
*
* @var string
*/
public $float;
/**
* Supported gallery design types.
*
* @var array
*/
private static $talaveras = array( 'rectangular', 'square', 'circle', 'rectangle', 'columns' );
/**
* Class constructor.
*/
public function __construct() {
add_action( 'admin_init', array( $this, 'settings_api_init' ) );
add_filter( 'jetpack_gallery_types', array( $this, 'jetpack_gallery_types' ), 9 );
add_filter( 'jetpack_default_gallery_type', array( $this, 'jetpack_default_gallery_type' ) );
}
/**
* Check whether tiling is enabled.
*
* @return bool
*/
public function tiles_enabled() {
return '' !== Jetpack_Options::get_option_and_ensure_autoload( 'tiled_galleries', '' );
}
/**
* Set attributes.
*
* @param array $atts - the attributes.
*/
public function set_atts( $atts ) {
global $post;
$this->atts = shortcode_atts(
array(
'order' => 'ASC',
'orderby' => 'menu_order ID',
'id' => isset( $post->ID ) ? $post->ID : 0,
'include' => '',
'exclude' => '',
'type' => '',
'grayscale' => false,
'link' => '',
'columns' => 3,
),
$atts,
'gallery'
);
$this->atts['id'] = (int) $this->atts['id'];
$this->float = is_rtl() ? 'right' : 'left';
// Default to rectangular is tiled galleries are checked
if ( $this->tiles_enabled() && ( ! $this->atts['type'] || 'default' === $this->atts['type'] ) ) {
/** This filter is already documented in class-jetpack-gallery-settings.php */
$this->atts['type'] = apply_filters( 'jetpack_default_gallery_type', 'rectangular' );
}
if ( ! $this->atts['orderby'] ) {
$this->atts['orderby'] = sanitize_sql_orderby( $this->atts['orderby'] );
if ( ! $this->atts['orderby'] ) {
$this->atts['orderby'] = 'menu_order ID';
}
}
if ( 'rand' === strtolower( $this->atts['order'] ) ) {
$this->atts['orderby'] = 'rand';
}
// We shouldn't have more than 20 columns.
if ( ! is_numeric( $this->atts['columns'] ) || 20 < $this->atts['columns'] ) {
$this->atts['columns'] = 3;
}
}
/**
* Get the media attachments.
*
* @return WP_Post[]
*/
public function get_attachments() {
$atts = $this->atts;
if ( ! empty( $atts['include'] ) ) {
$include = preg_replace( '/[^0-9,]+/', '', $atts['include'] );
$_attachments = get_posts(
array(
'include' => $include,
'post_status' => 'inherit',
'post_type' => 'attachment',
'post_mime_type' => 'image',
'order' => $atts['order'],
'orderby' => $atts['orderby'],
'suppress_filters' => false,
)
);
$attachments = array();
foreach ( $_attachments as $key => $val ) {
$attachments[ $val->ID ] = $_attachments[ $key ];
}
} elseif ( 0 === $atts['id'] ) {
/*
* Should NEVER Happen but infinite_scroll_load_other_plugins_scripts means it does
* Querying with post_parent == 0 can generate stupidly memcache sets
* on sites with 10000's of unattached attachments as get_children puts every post in the cache.
* TODO Fix this properly.
*/
$attachments = array();
} elseif ( ! empty( $atts['exclude'] ) ) {
$exclude = preg_replace( '/[^0-9,]+/', '', $atts['exclude'] );
$attachments = get_children(
array(
'post_parent' => $atts['id'],
'exclude' => $exclude,
'post_status' => 'inherit',
'post_type' => 'attachment',
'post_mime_type' => 'image',
'order' => $atts['order'],
'orderby' => $atts['orderby'],
'suppress_filters' => false,
)
);
} else {
$attachments = get_children(
array(
'post_parent' => $atts['id'],
'post_status' => 'inherit',
'post_type' => 'attachment',
'post_mime_type' => 'image',
'order' => $atts['order'],
'orderby' => $atts['orderby'],
'suppress_filters' => false,
)
);
}
return $attachments;
}
/**
* Enqueue the default scripts and styles.
*/
public static function default_scripts_and_styles() {
wp_enqueue_script(
'tiled-gallery',
Assets::get_file_url_for_environment(
'_inc/build/tiled-gallery/tiled-gallery/tiled-gallery.min.js',
'modules/tiled-gallery/tiled-gallery/tiled-gallery.js'
),
array(),
JETPACK__VERSION,
array(
'in_footer' => true,
'strategy' => 'defer',
)
);
wp_enqueue_style( 'tiled-gallery', plugins_url( 'tiled-gallery/tiled-gallery.css', __FILE__ ), array(), '2023-08-21' );
wp_style_add_data( 'tiled-gallery', 'rtl', 'replace' );
}
/**
* The gallery shortcode.
*
* @param mixed $val - the value.
* @param array $atts - the attributes.
*
* @return string
*/
public function gallery_shortcode( $val, $atts ) {
if ( ! empty( $val ) ) { // something else is overriding post_gallery, like a custom VIP shortcode
return $val;
}
$this->set_atts( $atts );
$attachments = $this->get_attachments();
if ( empty( $attachments ) ) {
return '';
}
if ( is_feed() || defined( 'IS_HTML_EMAIL' ) ) {
return '';
}
/**
* Filters the permissible Tiled Gallery types.
*
* @module tiled-gallery
*
* @since 3.7.0
*
* @param array Array of allowed types. Default: 'rectangular', 'square', 'circle', 'rectangle', 'columns'.
*/
$talaveras = apply_filters( 'jetpack_tiled_gallery_types', self::$talaveras );
if ( in_array( $this->atts['type'], $talaveras, true ) ) {
// Enqueue styles and scripts
self::default_scripts_and_styles();
// Generate gallery HTML
$gallery_class = 'Jetpack_Tiled_Gallery_Layout_' . ucfirst( $this->atts['type'] );
$gallery = new $gallery_class( $attachments, $this->atts['link'], $this->atts['grayscale'], (int) $this->atts['columns'] );
$gallery_html = $gallery->HTML();
if ( $gallery_html && class_exists( 'Jetpack' ) && class_exists( Image_CDN::class ) ) {
// Tiled Galleries in Jetpack require that Photon be active.
// If it's not active, run it just on the gallery output.
if ( ! Image_CDN::is_enabled() && ! ( new Status() )->is_offline_mode() ) {
$gallery_html = Image_CDN::filter_the_content( $gallery_html );
}
}
return trim( preg_replace( '/\s+/', ' ', $gallery_html ) ); // remove any new lines from the output so that the reader parses it better
}
return '';
}
/**
* See if gallery is already defined.
*
* @return bool
*/
public static function gallery_already_redefined() {
global $shortcode_tags;
$redefined = false;
if ( ! isset( $shortcode_tags['gallery'] ) || $shortcode_tags['gallery'] !== 'gallery_shortcode' ) {
$redefined = true;
}
/**
* Filter the output of the check for another plugin or theme affecting WordPress galleries.
*
* This will let folks that replace cores shortcode confirm feature parity with it, so Jetpack's Tiled Galleries can still work.
*
* @module tiled-gallery
*
* @since 3.1.0
*
* @param bool $redefined Does another plugin or theme already redefines the default WordPress gallery?
*/
return apply_filters( 'jetpack_tiled_gallery_shortcode_redefined', $redefined );
}
/**
* Initialize the tiled gallery.
*/
public static function init() {
if ( self::gallery_already_redefined() ) {
return;
}
$gallery = new Jetpack_Tiled_Gallery();
add_filter( 'post_gallery', array( $gallery, 'gallery_shortcode' ), 1001, 2 );
}
/**
* Get the width of the gallery.
*
* @return int
*/
public static function get_content_width() {
$tiled_gallery_content_width = Jetpack::get_content_width();
if ( ! $tiled_gallery_content_width ) {
$tiled_gallery_content_width = 500;
}
/**
* Filter overwriting the default content width.
*
* @module tiled-gallery
*
* @since 2.1.0
*
* @param string $tiled_gallery_content_width Default Tiled Gallery content width.
*/
return apply_filters( 'tiled_gallery_content_width', $tiled_gallery_content_width );
}
/**
* Media UI integration
*
* @param array $types - the type of gallery.
*
* @return array
*/
public function jetpack_gallery_types( $types ) {
if ( get_option( 'tiled_galleries' ) && isset( $types['default'] ) ) {
// Tiled is set as the default, meaning that type='default'
// will still display the mosaic.
$types['thumbnails'] = $types['default'];
unset( $types['default'] );
}
$types['rectangular'] = __( 'Tiled Mosaic', 'jetpack' );
$types['square'] = __( 'Square Tiles', 'jetpack' );
$types['circle'] = __( 'Circles', 'jetpack' );
$types['columns'] = __( 'Tiled Columns', 'jetpack' );
return $types;
}
/**
* Get the default gallery type.
*
* @return string
*/
public function jetpack_default_gallery_type() {
return ( get_option( 'tiled_galleries' ) ? 'rectangular' : 'default' );
}
/**
* Get the talaveras.
*
* @return array
*/
public static function get_talaveras() {
return self::$talaveras;
}
/**
* Add a checkbox field to the Carousel section in Settings > Media
* for setting tiled galleries as the default.
*/
public function settings_api_init() {
global $wp_settings_sections;
// Add the setting field [tiled_galleries] and place it in Settings > Media
if ( isset( $wp_settings_sections['media']['carousel_section'] ) ) {
$section = 'carousel_section';
} else {
$section = 'default';
}
add_settings_field( 'tiled_galleries', __( 'Tiled Galleries', 'jetpack' ), array( $this, 'setting_html' ), 'media', $section );
register_setting( 'media', 'tiled_galleries', 'esc_attr' );
}
/**
* Render the settings HTML.
*/
public function setting_html() {
echo '<label><input name="tiled_galleries" type="checkbox" value="1" ' .
checked( 1, '' !== get_option( 'tiled_galleries' ), false ) . ' /> ' .
esc_html__( 'Display all your gallery pictures in a cool mosaic.', 'jetpack' ) . '</br></label>';
}
}
add_action( 'init', array( 'Jetpack_Tiled_Gallery', 'init' ) );
@@ -0,0 +1,96 @@
/* This file was automatically generated on Oct 01 2015 20:17:19 */
/* =Tiled Gallery Default Styles
-------------------------------------------------------------- */
.tiled-gallery {
clear: both;
margin: 0 0 20px;
overflow: hidden;
}
.tiled-gallery img {
margin: 2px !important; /* Ensure that this value isn't overridden by themes that give content images blanket margins */
}
.tiled-gallery .gallery-group {
float: right;
position: relative;
}
.tiled-gallery .tiled-gallery-item {
float: right;
margin: 0;
position: relative;
width: inherit; /* prevents ie8 bug with inline width styles */
}
.tiled-gallery .gallery-row {
overflow: hidden;
}
.tiled-gallery .tiled-gallery-item a { /* Needs to reset some properties for theme compatibility */
background: transparent;
border: none;
color: inherit;
margin: 0;
padding: 0;
text-decoration: none;
width: auto;
}
.tiled-gallery .tiled-gallery-item img,
.tiled-gallery .tiled-gallery-item img:hover { /* Needs to reset some properties for theme compatibility */
background: none;
border: none;
box-shadow: none;
max-width: 100%;
padding: 0;
vertical-align: middle;
}
.tiled-gallery-caption { /* Captions */
background: #eee;
background: rgba( 255,255,255,0.8 );
color: #333;
font-size: 13px;
font-weight: 400;
overflow: hidden;
padding: 10px 0;
position: absolute;
bottom: 0;
text-indent: 10px;
text-overflow: ellipsis;
width: 100%;
white-space: nowrap;
}
.tiled-gallery .tiled-gallery-item-small .tiled-gallery-caption { /* Smaller captions */
font-size: 11px;
}
/* Hide galleries in widgets until they've been resized to fit.
Gallery widgets are almost guaranteed to need resizing, and
the jump is a little more obvious than galleries in content. */
.widget-gallery .tiled-gallery-unresized {
visibility: hidden;
height: 0px;
overflow: hidden;
}
/* =Greyscale
-------------------------------------------------------------- */
.tiled-gallery .tiled-gallery-item img.grayscale {
position: absolute;
right: 0;
top: 0;
}
.tiled-gallery .tiled-gallery-item img.grayscale:hover {
opacity: 0;
}
/* =Circles Layout
-------------------------------------------------------------- */
.tiled-gallery.type-circle .tiled-gallery-item img {
border-radius: 50% !important; /* Ensure that circles are displayed in themes that add border-radius to all images as a default */
}
.tiled-gallery.type-circle .tiled-gallery-caption {
display: none;
opacity: 0;
}
@@ -0,0 +1,23 @@
<?php
/**
* Encode extra carousel container data.
*
* @html-template Jetpack_Tiled_Gallery_Layout::template
* @package jetpack
*/
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- HTML template, let Phan handle it.
// Using JSON_HEX_AMP avoids breakage due to `esc_attr()` refusing to double-encode.
$extra = wp_json_encode( $this->get_container_extra_data(), JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT );
?>
<div
class="tiled-gallery type-<?php echo esc_html( $this->type ); ?> tiled-gallery-unresized"
data-original-width="<?php echo esc_attr( Jetpack_Tiled_Gallery::get_content_width() ); ?>"
<?php if ( isset( $extra ) ) : ?>
data-carousel-extra='<?php echo esc_attr( $extra ); ?>'
<?php endif; ?>
itemscope itemtype="http://schema.org/ImageGallery"
>
<?php $this->template( "$this->type-layout", $context ); ?>
</div>
@@ -0,0 +1,11 @@
<?php
/**
* Square layout Tiled Gallery template.
*
* @html-template Jetpack_Tiled_Gallery_Layout::template
* @package automattic/jetpack
*/
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- Defined by the caller. Let Phan handle it.
$this->template( 'square-layout', $context );
@@ -0,0 +1,30 @@
<?php
/**
* Template used to display arguments used to build the carousel modal.
*
* @html-template Jetpack_Tiled_Gallery_Layout::partial
* @package automattic/jetpack
*/
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- HTML template, let Phan handle it.
$item = $context['item'];
$fuzzy_image_meta = $item->fuzzy_image_meta(); // See https://github.com/Automattic/jetpack/issues/2765 .
if ( isset( $fuzzy_image_meta['keywords'] ) ) {
unset( $fuzzy_image_meta['keywords'] );
}
// Using JSON_HEX_AMP avoids breakage due to `esc_attr()` refusing to double-encode.
$fuzzy_image_meta = wp_json_encode( map_deep( $fuzzy_image_meta, 'strval' ), JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT );
?>
data-attachment-id="<?php echo esc_attr( $item->image->ID ); ?>"
data-orig-file="<?php echo esc_url( wp_get_attachment_url( $item->image->ID ) ); ?>"
data-orig-size="<?php echo esc_attr( $item->meta_width() ); ?>,<?php echo esc_attr( $item->meta_height() ); ?>"
data-comments-opened="<?php echo esc_attr( comments_open( $item->image->ID ) ); ?>"
data-image-meta="<?php echo esc_attr( $fuzzy_image_meta ); ?>"
<?php // The two lines below use `esc_attr( htmlspecialchars( ) )` because esc_attr tries to be too smart and won't double-encode, and we need that here. ?>
data-image-title="<?php echo esc_attr( htmlspecialchars( wptexturize( $item->image->post_title ), ENT_COMPAT ) ); ?>"
data-image-description="<?php echo esc_attr( htmlspecialchars( wpautop( wptexturize( $item->image->post_content ) ), ENT_COMPAT ) ); ?>"
data-medium-file="<?php echo esc_url( $item->medium_file() ); ?>"
data-large-file="<?php echo esc_url( $item->large_file() ); ?>"
@@ -0,0 +1,54 @@
<?php
/**
* Handles more photo metadata.
*
* @html-template Jetpack_Tiled_Gallery_Layout::partial
* @package jetpack
*/
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- HTML template, let Phan handle it.
$item = $context['item'];
$add_link = 'none' !== $this->link;
// We do this for accessibility. Titles without alt's break screen readers.
if ( empty( $item->image_alt ) && ! empty( $item->image_title ) ) {
$item->image_alt = $item->image_title;
}
?>
<div class="tiled-gallery-item
<?php
if ( isset( $item->size ) ) {
echo esc_attr( " tiled-gallery-item-$item->size" );}
?>
" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<?php if ( $add_link ) : ?>
<a href="<?php echo esc_url( $item->link ); ?>" border="0" itemprop="url">
<?php endif; ?>
<meta itemprop="width" content="<?php echo esc_attr( $item->image->width ); ?>">
<meta itemprop="height" content="<?php echo esc_attr( $item->image->height ); ?>">
<img
class="<?php echo empty( $this->grayscale ) ? '' : 'grayscale'; ?>"
<?php $this->partial( 'carousel-image-args', array( 'item' => $item ) ); ?>
src="<?php echo esc_url( $item->img_src ); ?>"
<?php echo $item->img_srcset ? 'srcset="' . esc_attr( $item->img_srcset ) . '"' : ''; ?>
width="<?php echo esc_attr( $item->image->width ); ?>"
height="<?php echo esc_attr( $item->image->height ); ?>"
loading="lazy"
data-original-width="<?php echo esc_attr( $item->image->width ); ?>"
data-original-height="<?php echo esc_attr( $item->image->height ); ?>"
itemprop="http://schema.org/image"
title="<?php echo esc_attr( $item->image_title ); ?>"
alt="<?php echo esc_attr( $item->image_alt ); ?>"
style="width: <?php echo esc_attr( $item->image->width ); ?>px; height: <?php echo esc_attr( $item->image->height ); ?>px;"
/>
<?php if ( $add_link ) : ?>
</a>
<?php endif; ?>
<?php if ( trim( $item->image->post_excerpt ) ) : ?>
<div class="tiled-gallery-caption" itemprop="caption description">
<?php echo wptexturize( $item->image->post_excerpt ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<?php endif; ?>
</div>
@@ -0,0 +1,41 @@
<?php
/**
* Rectangular layout Tiled Gallery template.
*
* @html-template Jetpack_Tiled_Gallery_Layout::template
* @package automattic/jetpack
*/
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- HTML template, let Phan handle it.
foreach ( $context['rows'] as $row ) :
?>
<div
class="gallery-row"
style="width: <?php echo esc_attr( $row->width ); ?>px; height: <?php echo esc_attr( $row->height ); ?>px;"
data-original-width="<?php echo esc_attr( $row->width ); ?>"
data-original-height="<?php echo esc_attr( $row->height ); ?>"
>
<?php foreach ( $row->groups as $group ) : ?>
<div
class="gallery-group images-<?php echo esc_attr( is_countable( $group->images ) ? count( $group->images ) : 0 ); ?>"
style="width: <?php echo esc_attr( $group->width ); ?>px; height: <?php echo esc_attr( $group->height ); ?>px;"
data-original-width="<?php echo esc_attr( $group->width ); ?>"
data-original-height="<?php echo esc_attr( $group->height ); ?>"
>
<?php
foreach ( $group->items( $context['needs_attachment_link'], $context['grayscale'] ) as $item ) :
$this->partial(
'item',
array(
'item' => $item,
'link' => $context['link'],
)
);
?>
<?php endforeach; ?>
</div> <!-- close group -->
<?php endforeach; ?>
</div> <!-- close row -->
<?php endforeach; ?>
@@ -0,0 +1,36 @@
<?php
/**
* Square layout Tiled Gallery template.
*
* @html-template Jetpack_Tiled_Gallery_Layout::template
* @package automattic/jetpack
*/
// phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable -- HTML template, let Phan handle it.
foreach ( $context['rows'] as $row ) :
?>
<div class="gallery-row"
style="width: <?php echo esc_attr( $row->width ); ?>px; height: <?php echo esc_attr( $row->height ); ?>px;"
data-original-width="<?php echo esc_attr( $row->width ); ?>"
data-original-height="<?php echo esc_attr( $row->height ); ?>"
>
<?php foreach ( $row->images as $item ) : ?>
<div class="gallery-group"
style="width: <?php echo esc_attr( $row->group_size ); ?>px; height: <?php echo esc_attr( $row->group_size ); ?>px;"
data-original-width="<?php echo esc_attr( $row->group_size ); ?>"
data-original-height="<?php echo esc_attr( $row->group_size ); ?>"
>
<?php
$this->partial(
'item',
array(
'item' => $item,
'link' => $context['link'],
)
);
?>
</div>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
@@ -0,0 +1,14 @@
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
require_once __DIR__ . '/tiled-gallery-square.php';
/**
* Jetpack tiled gallery layout circle class.
*/
class Jetpack_Tiled_Gallery_Layout_Circle extends Jetpack_Tiled_Gallery_Layout_Square {
/**
* Type of tiled gallery.
*
* @var string
*/
protected $type = 'circle';
}
@@ -0,0 +1,213 @@
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
use Automattic\Jetpack\Image_CDN\Image_CDN_Core;
/**
* Jetpack Tiled Gallery Item class.
*/
abstract class Jetpack_Tiled_Gallery_Item {
/**
* Is the image grayscale.
*
* @var bool
*/
public $grayscale;
/**
* The image title.
*
* @var string
*/
public $image_title;
/**
* The image alt.
*
* @var string
*/
public $image_alt;
/**
* The image size.
*
* @var string|null
*/
public $size;
/**
* The original file.
*
* @var string|bool
*/
public $orig_file;
/**
* The image attachment link.
*
* @var string
*/
public $link;
/**
* The image URL.
*
* @var string
*/
public $img_src;
/**
* The image srcset.
*
* @var string
*/
public $img_srcset;
/**
* The image data.
*
* @var object
*/
public $image;
/**
* Constructor function.
*
* @param object $attachment_image - the attachment image.
* @param string $needs_attachment_link - the attachment link.
* @param bool $grayscale - if the image is in grayscale.
*/
public function __construct( $attachment_image, $needs_attachment_link, $grayscale ) {
$this->image = $attachment_image;
$this->grayscale = $grayscale;
$this->image_title = $this->image->post_title;
$this->image_alt = get_post_meta( $this->image->ID, '_wp_attachment_image_alt', true );
// If no Alt value, use the caption
if ( empty( $this->image_alt ) && ! empty( $this->image->post_excerpt ) ) {
$this->image_alt = trim( wp_strip_all_tags( $this->image->post_excerpt ) );
}
// If still no Alt value, use the title
if ( empty( $this->image_alt ) && ! empty( $this->image->post_title ) ) {
$this->image_alt = trim( wp_strip_all_tags( $this->image->post_title ) );
}
$this->orig_file = wp_get_attachment_url( $this->image->ID );
$this->link = $needs_attachment_link
? get_attachment_link( $this->image->ID )
// The filter will photonize the URL if and only if Photon is active
: apply_filters( 'jetpack_photon_url', $this->orig_file );
$img_args = array(
'w' => $this->image->width,
'h' => $this->image->height,
);
// If h and w are the same, there's a reasonably good chance the image will need cropping to avoid being stretched.
if ( $this->image->height === $this->image->width ) {
$img_args['crop'] = true;
}
// The function will always photonoize the URL (even if Photon is
// not active). We need to photonize the URL to set the width/height.
$this->img_src = Image_CDN_Core::cdn_url( $this->orig_file, $img_args );
$image_meta = wp_get_attachment_metadata( $attachment_image->ID );
$size_array = array( absint( $this->image->width ), absint( $this->image->height ) );
$this->img_srcset = wp_calculate_image_srcset( $size_array, $this->img_src, $image_meta, $attachment_image->ID );
}
/**
* Handle the fuzzy image meta.
*
* @return array
*/
public function fuzzy_image_meta() {
$meta = wp_get_attachment_metadata( $this->image->ID );
$img_meta = ( ! empty( $meta['image_meta'] ) ) ? (array) $meta['image_meta'] : array();
if ( ! empty( $img_meta ) ) {
foreach ( $img_meta as $k => $v ) {
if ( 'latitude' === $k || 'longitude' === $k ) {
unset( $img_meta[ $k ] );
}
}
}
return $img_meta;
}
/**
* Return the meta width.
*
* @return int|string
*/
public function meta_width() {
$meta = wp_get_attachment_metadata( $this->image->ID );
return isset( $meta['width'] ) ? (int) $meta['width'] : '';
}
/**
* Return the meta height.
*
* @return int|string
*/
public function meta_height() {
$meta = wp_get_attachment_metadata( $this->image->ID );
return isset( $meta['height'] ) ? (int) $meta['height'] : '';
}
/**
* Return the medium file info.
*
* @return array|string
*/
public function medium_file() {
$medium_file_info = wp_get_attachment_image_src( $this->image->ID, 'medium' );
$medium_file = isset( $medium_file_info[0] ) ? $medium_file_info[0] : '';
return $medium_file;
}
/**
* Return large file info.
*
* @return array|string
*/
public function large_file() {
$large_file_info = wp_get_attachment_image_src( $this->image->ID, 'large' );
$large_file = isset( $large_file_info[0] ) ? $large_file_info[0] : '';
return $large_file;
}
}
/**
* Tiled gallery rectangular item class.
*/
class Jetpack_Tiled_Gallery_Rectangular_Item extends Jetpack_Tiled_Gallery_Item { // phpcs:ignore Generic.Files.OneObjectStructurePerFile.MultipleFound, Generic.Classes.OpeningBraceSameLine.ContentAfterBrace
/**
* Constructor function.
*
* @param object $attachment_image - the attachment image.
* @param string $needs_attachment_link - the attachment link.
* @param bool $grayscale - if the image is in grayscale.
*/
public function __construct( $attachment_image, $needs_attachment_link, $grayscale ) {
parent::__construct( $attachment_image, $needs_attachment_link, $grayscale );
$this->size = 'large';
if ( $this->image->width < 250 ) {
$this->size = 'small';
}
}
}
/**
* Tiled gallery square item class.
*/
class Jetpack_Tiled_Gallery_Square_Item extends Jetpack_Tiled_Gallery_Item { // phpcs:ignore Generic.Files.OneObjectStructurePerFile.MultipleFound, Generic.Classes.OpeningBraceSameLine.ContentAfterBrace
}
/**
* Tiled gallery circle item class.
*/
class Jetpack_Tiled_Gallery_Circle_Item extends Jetpack_Tiled_Gallery_Square_Item { // phpcs:ignore Generic.Files.OneObjectStructurePerFile.MultipleFound, Generic.Classes.OpeningBraceSameLine.ContentAfterBrace
}
@@ -0,0 +1,184 @@
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
/**
* Tiled gallery layout class.
*/
abstract class Jetpack_Tiled_Gallery_Layout {
/**
* Template allow list.
*
* @var array
*/
private static $templates = array( 'carousel-container', 'circle-layout', 'rectangular-layout', 'square-layout' );
/**
* Partial list.
*
* @var array
*/
private static $partials = array( 'carousel-image-args', 'item' );
/**
* Type of gallery - defined in parent class.
*
* @var string
*/
protected $type;
/**
* The attachments.
*
* @var object
*/
public $attachments;
/**
* The attachment link.
*
* @var string
*/
public $link;
/**
* If the image is in grayscale.
*
* @var bool
*/
public $grayscale;
/**
* How many columns.
*
* @var int
*/
public $columns;
/**
* Attachment link
*
* @var bool
*/
public $needs_attachment_link;
/**
* Constructor function.
*
* @param object $attachments - the attachmed image.
* @param string $link - the attachment link.
* @param bool $grayscale - if the image is in grayscale.
* @param int $columns - how many columns.
*/
public function __construct( $attachments, $link, $grayscale, $columns ) {
$this->attachments = $attachments;
$this->link = $link;
$this->needs_attachment_link = $link !== 'file';
$this->grayscale = $grayscale;
$this->columns = $columns;
}
/**
* Render carousel container template.
*
* @param array $context - the context.
* @return string HTML
*/
public function HTML( $context = array() ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
// Render the carousel container template, which will take the
// appropriate strategy to fill it
ob_start();
$this->template(
'carousel-container',
array_merge(
$context,
array(
'attachments' => $this->attachments,
'link' => $this->link,
'needs_attachment_link' => $this->needs_attachment_link,
'grayscale' => $this->grayscale,
)
)
);
$html = ob_get_clean();
return $html;
}
/**
* Handle tiled gallery template path.
*
* @html-template-var array $context
*
* @param string $name Template name.
* @param array $context Context array passed to the template.
*/
private function template( $name, $context = array() ) {
if ( ! in_array( $name, self::$templates, true ) ) {
return;
}
/**
* Filters the Tiled Gallery template path
*
* @module tiled-gallery
* @since 4.4.0
*
* @param string $path Template path.
* @param string $path Template name.
* @param array $context Context array passed to the template.
*/
require apply_filters( 'jetpack_tiled_gallery_template', __DIR__ . "/templates/$name.php", $name, $context );
}
/**
* Handle tiled gallery partial path.
*
* @html-template-var array $context
*
* @param string $name - the name.
* @param array $context Context array passed to the partial.
*/
private function partial( $name, $context = array() ) {
if ( ! in_array( $name, self::$partials, true ) ) {
return;
}
/**
* Filters the Tiled Gallery partial path
*
* @module tiled-gallery
* @since 4.4.0
*
* @param string $path Partial path.
* @param string $path Partial name.
* @param array $context Context array passed to the partial.
*/
require apply_filters( 'jetpack_tiled_gallery_partial', __DIR__ . "/templates/partials/$name.php", $name, $context );
}
/**
* Get extra container data.
*/
protected function get_container_extra_data() {
global $post;
$blog_id = (int) get_current_blog_id();
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
$likes_blog_id = $blog_id;
} else {
$likes_blog_id = Jetpack_Options::get_option( 'id' );
}
if ( class_exists( 'Jetpack_Carousel' ) || in_array( 'carousel', Jetpack::get_active_modules(), true ) || 'carousel' === $this->link ) {
$extra_data = array(
'blog_id' => $blog_id,
'permalink' => get_permalink( isset( $post->ID ) ? $post->ID : 0 ),
'likes_blog_id' => $likes_blog_id,
);
} else {
$extra_data = null;
}
return $extra_data;
}
}
@@ -0,0 +1,471 @@
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
require_once __DIR__ . '/tiled-gallery-layout.php';
require_once __DIR__ . '/tiled-gallery-shape.php';
require_once __DIR__ . '/tiled-gallery-item.php';
// phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound
/**
* Tiled gallery rectangular layout.
*/
class Jetpack_Tiled_Gallery_Layout_Rectangular extends Jetpack_Tiled_Gallery_Layout {
/**
* The layout type.
*
* @var string
*/
protected $type = 'rectangular';
/**
* The HTML function.
*
* @param array $context - the context array, unused.
* @return string HTML
*/
public function HTML( $context = array() ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
$grouper = new Jetpack_Tiled_Gallery_Grouper( $this->attachments );
Jetpack_Tiled_Gallery_Shape::reset_last_shape();
return parent::HTML( array( 'rows' => $grouper->grouped_images ) );
}
}
/**
* Tiled gallery layout columns class.
*/
class Jetpack_Tiled_Gallery_Layout_Columns extends Jetpack_Tiled_Gallery_Layout {
/**
* The layout type.
*
* @var string
*/
protected $type = 'rectangular'; // It doesn't need separate template for now
/**
* The HTML function.
*
* @param array $context - the context array, unused.
* @return string HTML
*/
public function HTML( $context = array() ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
$grouper = new Jetpack_Tiled_Gallery_Grouper( $this->attachments, array( 'Three_Columns', 'Two' ) );
return parent::HTML( array( 'rows' => $grouper->grouped_images ) );
}
}
/**
* Gallery layout rectangle alis.
*/
class Jetpack_Tiled_Gallery_Layout_Rectangle extends Jetpack_Tiled_Gallery_Layout_Rectangular {}
/**
* Image grouping and HTML generation logic class.
*/
class Jetpack_Tiled_Gallery_Grouper {
/**
* The margin.
*
* @var int
*/
public $margin = 4;
/**
* Shapes array.
* This list is ordered. If you put a shape that's likely to occur on top, it will happen all the time.
*
* @var array
*/
public $shapes = array(
'Reverse_Symmetric_Row',
'Long_Symmetric_Row',
'Symmetric_Row',
'One_Three',
'Three_One',
'One_Two',
'Five',
'Four',
'Three',
'Two_One',
'Panoramic',
);
/**
* Shape of the last row in gallery.
*
* @var string
*/
public $last_shape = '';
/**
* All images to be displayed.
*
* @var array
*/
public $images = array();
/**
* Array of tiled gallery rows (groups of images).
*
* @var array
*/
public $grouped_images = array();
/**
* Constructor function.
*
* @param object $attachments - the attachments.
* @param array $shapes - the shapes.
*/
public function __construct( $attachments, $shapes = array() ) {
$content_width = Jetpack_Tiled_Gallery::get_content_width();
$this->overwrite_shapes( $shapes );
$this->last_shape = '';
$this->images = $this->get_images_with_sizes( $attachments );
$this->grouped_images = $this->get_grouped_images();
$this->apply_content_width( $content_width );
}
/**
* Overwrite the shapes.
*
* @param array $shapes - the shapes.
*/
public function overwrite_shapes( $shapes ) {
if ( ! empty( $shapes ) ) {
$this->shapes = $shapes;
}
}
/**
* Get the current row size.
*
* @return array
*/
public function get_current_row_size() {
$images_left = is_countable( $this->images ) ? count( $this->images ) : 0;
if ( $images_left < 3 ) {
return array_fill( 0, $images_left, 1 );
}
foreach ( $this->shapes as $shape_name ) {
$class_name = "Jetpack_Tiled_Gallery_$shape_name";
$shape = new $class_name( $this->images );
if ( $shape->is_possible() ) {
Jetpack_Tiled_Gallery_Shape::set_last_shape( $class_name );
return $shape->shape;
}
}
Jetpack_Tiled_Gallery_Shape::set_last_shape( 'Two' );
return array( 1, 1 );
}
/**
* Get images with sizes.
*
* @param object $attachments - the attachments.
*
* @return array
*/
public function get_images_with_sizes( $attachments ) {
$images_with_sizes = array();
foreach ( $attachments as $image ) {
$meta = wp_get_attachment_metadata( $image->ID );
$image->width_orig = ( isset( $meta['width'] ) && $meta['width'] > 0 ) ? $meta['width'] : 1;
$image->height_orig = ( isset( $meta['height'] ) && $meta['height'] > 0 ) ? $meta['height'] : 1;
$image->ratio = $image->width_orig / $image->height_orig;
$image->ratio = $image->ratio ? $image->ratio : 1;
$images_with_sizes[] = $image;
}
return $images_with_sizes;
}
/**
* Get the current row size.
*
* @return array
*/
public function read_row() {
$vector = $this->get_current_row_size();
$row = array();
foreach ( $vector as $group_size ) {
$row[] = new Jetpack_Tiled_Gallery_Group( array_splice( $this->images, 0, $group_size ) );
}
return $row;
}
/**
* Get grouped images.
*
* @return array
*/
public function get_grouped_images() {
$grouped_images = array();
while ( ! empty( $this->images ) ) {
$grouped_images[] = new Jetpack_Tiled_Gallery_Row( $this->read_row() );
}
return $grouped_images;
}
/**
* Apply content width.
*
* @param int $width - the width.
*
* @todo split in functions
* @todo do not stretch images
*/
public function apply_content_width( $width ) {
foreach ( $this->grouped_images as $row ) {
$row->width = $width;
$group_count = is_countable( $row->groups ) ? count( $row->groups ) : 0;
$row->raw_height = 1 / $row->ratio * ( $width - $this->margin * ( $group_count - $row->weighted_ratio ) );
$row->height = round( $row->raw_height );
$this->calculate_group_sizes( $row );
}
}
/**
* Calculate group sizes.
*
* @param object $row - the row.
*/
public function calculate_group_sizes( $row ) {
// Storing the calculated group heights in an array for rounding them later while preserving their sum
// This fixes the rounding error that can lead to a few ugly pixels sticking out in the gallery
$group_widths_array = array();
foreach ( $row->groups as $group ) {
$group->height = $row->height;
$image_count = is_countable( $group->images ) ? count( $group->images ) : 0;
// Storing the raw calculations in a separate property to prevent rounding errors from cascading down and for diagnostics
$group->raw_width = ( $row->raw_height - $this->margin * $image_count ) * $group->ratio + $this->margin;
$group_widths_array[] = $group->raw_width;
}
$rounded_group_widths_array = Jetpack_Constrained_Array_Rounding::get_rounded_constrained_array( $group_widths_array, $row->width );
foreach ( $row->groups as $group ) {
$group->width = array_shift( $rounded_group_widths_array );
$this->calculate_image_sizes( $group );
}
}
/**
* Calculate image sizes.
*
* @param object $group - the group of images.
*/
public function calculate_image_sizes( $group ) {
// Storing the calculated image heights in an array for rounding them later while preserving their sum
// This fixes the rounding error that can lead to a few ugly pixels sticking out in the gallery
$image_heights_array = array();
foreach ( $group->images as $image ) {
$image->width = $group->width - $this->margin;
// Storing the raw calculations in a separate property for diagnostics
$image->raw_height = ( $group->raw_width - $this->margin ) / $image->ratio;
$image_heights_array[] = $image->raw_height;
}
$image_height_sum = $group->height - count( $image_heights_array ) * $this->margin;
$rounded_image_heights_array = Jetpack_Constrained_Array_Rounding::get_rounded_constrained_array( $image_heights_array, $image_height_sum );
foreach ( $group->images as $image ) {
$image->height = array_shift( $rounded_image_heights_array );
}
}
}
/**
* Jetpack tiled row class.
*/
class Jetpack_Tiled_Gallery_Row {
/**
* Groups of images
*
* @var object[]
*/
public $groups;
/**
* Image ratio.
*
* @var float
*/
public $ratio;
/**
* Weighted ratio based on all the images.
*
* @var float
*/
public $weighted_ratio;
/**
* Rounded value of the raw width.
*
* @var int
*/
public $width;
/**
* Rounded value of the raw height.
*
* @var int
*/
public $height;
/**
* Raw width of the row.
*
* @var int
*/
public $raw_width;
/**
* Raw height of the row.
*
* @var int
*/
public $raw_height;
/**
* Constructor class.
*
* @param object[] $groups - the group of images.
*/
public function __construct( $groups ) {
$this->groups = $groups;
$this->ratio = $this->get_ratio();
$this->weighted_ratio = $this->get_weighted_ratio();
}
/**
* Get the ratio.
*
* @return float
*/
public function get_ratio() {
$ratio = 0;
foreach ( $this->groups as $group ) {
$ratio += $group->ratio;
}
return $ratio > 0 ? $ratio : 1;
}
/**
* Get weighted ratio.
*
* @return float
*/
public function get_weighted_ratio() {
$weighted_ratio = 0;
foreach ( $this->groups as $group ) {
$image_count = is_countable( $group->images ) ? count( $group->images ) : 0;
$weighted_ratio += $group->ratio * $image_count;
}
return $weighted_ratio > 0 ? $weighted_ratio : 1;
}
}
/**
* Tiled gallery group class.
*/
class Jetpack_Tiled_Gallery_Group {
/**
* Images to be displayed in group.
*
* @var object[]
*/
public $images;
/**
* Image ratio.
*
* @var float
*/
public $ratio;
/**
* Rounded value of the raw width.
*
* @var int
*/
public $width;
/**
* Rounded value of the raw height.
*
* @var int
*/
public $height;
/**
* Raw width of the group.
*
* @var int
*/
public $raw_width;
/**
* Raw height of the group.
*
* @var int
*/
public $raw_height;
/**
* Constructor class.
*
* @param object[] $images - the images.
*/
public function __construct( $images ) {
$this->images = $images;
$this->ratio = $this->get_ratio();
}
/**
* Get the ratio.
*
* @return float
*/
public function get_ratio() {
$ratio = 0;
foreach ( $this->images as $image ) {
if ( $image->ratio ) {
$ratio += 1 / $image->ratio;
}
}
if ( ! $ratio ) {
return 1;
}
return 1 / $ratio;
}
/**
* The items.
*
* @param string $needs_attachment_link - the attachment link.
* @param bool $grayscale - if the image is in grayscale.
*
* @return array
*/
public function items( $needs_attachment_link, $grayscale ) {
$items = array();
foreach ( $this->images as $image ) {
$items[] = new Jetpack_Tiled_Gallery_Rectangular_Item( $image, $needs_attachment_link, $grayscale );
}
return $items;
}
}
@@ -0,0 +1 @@
.tiled-gallery{clear:both;margin:0 0 20px;overflow:hidden}.tiled-gallery img{margin:2px!important}.tiled-gallery .gallery-group{float:right;position:relative}.tiled-gallery .tiled-gallery-item{float:right;margin:0;position:relative;width:inherit}.tiled-gallery .gallery-row{overflow:hidden}.tiled-gallery .tiled-gallery-item a{background:#0000;border:none;color:inherit;margin:0;padding:0;text-decoration:none;width:auto}.tiled-gallery .tiled-gallery-item img,.tiled-gallery .tiled-gallery-item img:hover{background:none;border:none;box-shadow:none;max-width:100%;padding:0;vertical-align:middle}.tiled-gallery-caption{background:#f0f0f1;background:#fffc;bottom:0;color:#333;font-size:13px;font-weight:400;overflow:hidden;padding:10px 0;position:absolute;text-indent:10px;text-overflow:ellipsis;white-space:nowrap;width:100%}.tiled-gallery .tiled-gallery-item-small .tiled-gallery-caption{font-size:11px}.widget-gallery .tiled-gallery-unresized{height:0;overflow:hidden;visibility:hidden}.tiled-gallery .tiled-gallery-item img.grayscale{-ms-filter:grayscale(1);-o-filter:grayscale(1);filter:grayscale(1)}.tiled-gallery .tiled-gallery-item:hover img.grayscale{-ms-filter:none;-o-filter:none;filter:none}.tiled-gallery.type-circle .tiled-gallery-item img{border-radius:50%!important;object-fit:cover}.tiled-gallery.type-circle .tiled-gallery-caption{display:none}.tiled-gallery.type-square .tiled-gallery-item img{object-fit:cover}
@@ -0,0 +1,457 @@
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
// phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound
/**
* Jetpack tiled gallery shape class.
*/
class Jetpack_Tiled_Gallery_Shape {
/**
* Shapes used.
*
* @var array
*/
public static $shapes_used = array();
/**
* The images to include in gallery.
*
* @var object[]
*/
protected $images;
/**
* How many images are left after each row.
*
* @var int
*/
protected $images_left;
/**
* Constructor class.
*
* @param object[] $images - the images.
*/
public function __construct( $images ) {
$this->images = $images;
$this->images_left = is_countable( $images ) ? count( $images ) : 0;
}
/**
* Return the sum of ratio images.
*
* @param int $number_of_images - the number of images.
*
* @return int
*/
public function sum_ratios( $number_of_images = 3 ) {
return array_sum( array_slice( wp_list_pluck( $this->images, 'ratio' ), 0, $number_of_images ) );
}
/**
* Check that the next images are symmetric
*
* @return bool
*/
public function next_images_are_symmetric() {
return $this->images_left > 2 && $this->images[0]->ratio === $this->images[2]->ratio;
}
/**
* Is not as previous.
*
* @param int $n - the previous image.
*
* @return bool
*/
public function is_not_as_previous( $n = 1 ) {
return ! in_array( get_class( $this ), array_slice( self::$shapes_used, -$n ), true );
}
/**
* Check if the theme is wide.
*
* @return bool
*/
public function is_wide_theme() {
return Jetpack::get_content_width() > 1000;
}
/**
* Check if the image is landscape.
*
* @param object $image - the image.
*
* @return bool
*/
public function image_is_landscape( $image ) {
return $image->ratio >= 1 && $image->ratio < 2;
}
/**
* Check if the image is portrait.
*
* @param object $image - the image.
*
* @return bool
*/
public function image_is_portrait( $image ) {
return $image->ratio < 1;
}
/**
* Check if the image is panoramic.
*
* @param object $image - the image.
*
* @return bool
*/
public function image_is_panoramic( $image ) {
return $image->ratio >= 2;
}
/**
* Set the last shape.
*
* @param string $last_shape - the last shape.
*/
public static function set_last_shape( $last_shape ) {
self::$shapes_used[] = $last_shape;
}
/**
* Reset the last shape.
*/
public static function reset_last_shape() {
self::$shapes_used = array();
}
}
/**
* Jetpack tiled gallery three class.
*/
class Jetpack_Tiled_Gallery_Three extends Jetpack_Tiled_Gallery_Shape {
/**
* The shape.
*
* @var array
*/
public $shape = array( 1, 1, 1 );
/**
* Checks if there's enough images.
*
* @return array
*/
public function is_possible() {
$ratio = $this->sum_ratios( 3 );
$has_enough_images = $this->images_left >= 3 && ! in_array( $this->images_left, array( 4, 6 ), true );
return $has_enough_images && $this->is_not_as_previous( 3 ) &&
( ( $ratio < 2.5 ) || ( $ratio < 5 && $this->next_images_are_symmetric() ) || $this->is_wide_theme() );
}
}
/**
* Jetpack tiled gallery four class.
*/
class Jetpack_Tiled_Gallery_Four extends Jetpack_Tiled_Gallery_Shape {
/**
* The shape.
*
* @var array
*/
public $shape = array( 1, 1, 1, 1 );
/**
* Check if it's possible.
*
* @return bool
*/
public function is_possible() {
return $this->is_not_as_previous() &&
(
( $this->sum_ratios( 4 ) < 3.5 && $this->images_left > 5 ) ||
( $this->sum_ratios( 4 ) < 7 && $this->images_left === 4 )
);
}
}
/**
* Jetpack tiled gallery five class.
*/
class Jetpack_Tiled_Gallery_Five extends Jetpack_Tiled_Gallery_Shape {
/**
* The shape.
*
* @var array
*/
public $shape = array( 1, 1, 1, 1, 1 );
/**
* Check if it's possible.
*
* @return bool
*/
public function is_possible() {
return $this->is_wide_theme() && $this->is_not_as_previous() && $this->sum_ratios( 5 ) < 5 &&
( $this->images_left === 5 || ( $this->images_left !== 10 && $this->images_left > 6 ) );
}
}
/**
* Jetpack tiled gallery two one class.
*/
class Jetpack_Tiled_Gallery_Two_One extends Jetpack_Tiled_Gallery_Shape {
/**
* The shape.
*
* @var array
*/
public $shape = array( 2, 1 );
/**
* Check if it's possible.
*
* @return bool
*/
public function is_possible() {
return $this->is_not_as_previous( 3 ) && $this->images_left >= 2 &&
$this->images[2]->ratio < 1.6 && $this->images[0]->ratio >= 0.9 && $this->images[0]->ratio < 2.0 && $this->images[1]->ratio >= 0.9 && $this->images[1]->ratio < 2.0;
}
}
/**
* Jetpack tiled gallery one two class.
*/
class Jetpack_Tiled_Gallery_One_Two extends Jetpack_Tiled_Gallery_Shape {
/**
* The shape.
*
* @var array
*/
public $shape = array( 1, 2 );
/**
* Check if it's possible.
*
* @return bool
*/
public function is_possible() {
return $this->is_not_as_previous( 3 ) && $this->images_left >= 2 &&
$this->images[0]->ratio < 1.6 && $this->images[1]->ratio >= 0.9 && $this->images[1]->ratio < 2.0 && $this->images[2]->ratio >= 0.9 && $this->images[2]->ratio < 2.0;
}
}
/**
* Jetpack tiled gallery one three class.
*/
class Jetpack_Tiled_Gallery_One_Three extends Jetpack_Tiled_Gallery_Shape {
/**
* The shape.
*
* @var array
*/
public $shape = array( 1, 3 );
/**
* Check if it's possible.
*
* @return bool
*/
public function is_possible() {
return $this->is_not_as_previous( 3 ) && $this->images_left > 3 &&
$this->image_is_portrait( $this->images[0] ) &&
$this->image_is_landscape( $this->images[1] ) &&
$this->image_is_landscape( $this->images[2] ) &&
$this->image_is_landscape( $this->images[3] );
}
}
/**
* Jetpack tiled gallery three one class.
*/
class Jetpack_Tiled_Gallery_Three_One extends Jetpack_Tiled_Gallery_Shape {
/**
* The shape.
*
* @var array
*/
public $shape = array( 3, 1 );
/**
* Check if it's possible.
*
* @return bool
*/
public function is_possible() {
return $this->is_not_as_previous( 3 ) && $this->images_left > 3 &&
$this->image_is_portrait( $this->images[3] ) &&
$this->image_is_landscape( $this->images[0] ) &&
$this->image_is_landscape( $this->images[1] ) &&
$this->image_is_landscape( $this->images[2] );
}
}
/**
* Jetpack tiled gallery panoramic class.
*/
class Jetpack_Tiled_Gallery_Panoramic extends Jetpack_Tiled_Gallery_Shape {
/**
* The shape.
*
* @var array
*/
public $shape = array( 1 );
/**
* Check if it's possible.
*
* @return bool
*/
public function is_possible() {
return $this->image_is_panoramic( $this->images[0] );
}
}
/**
* Jetpack tiled gallery symmetric class.
*/
class Jetpack_Tiled_Gallery_Symmetric_Row extends Jetpack_Tiled_Gallery_Shape {
/**
* The shape.
*
* @var array
*/
public $shape = array( 1, 2, 1 );
/**
* Check if it's possible.
*
* @return bool
*/
public function is_possible() {
return $this->is_not_as_previous( 5 ) &&
$this->images_left > 3 &&
$this->images_left !== 5 &&
$this->image_is_portrait( $this->images[0] ) &&
$this->image_is_landscape( $this->images[1] ) &&
$this->image_is_landscape( $this->images[2] ) &&
$this->image_is_portrait( $this->images[3] );
}
}
/**
* Jetpack tiled gallery reverse symmetric row class.
*/
class Jetpack_Tiled_Gallery_Reverse_Symmetric_Row extends Jetpack_Tiled_Gallery_Shape {
/**
* The shape.
*
* @var array
*/
public $shape = array( 2, 1, 2 );
/**
* Check if it's possible.
*
* @return bool
*/
public function is_possible() {
return $this->is_not_as_previous( 5 ) && $this->images_left > 15 &&
$this->image_is_landscape( $this->images[0] ) &&
$this->image_is_landscape( $this->images[1] ) &&
$this->image_is_portrait( $this->images[2] ) &&
$this->image_is_landscape( $this->images[3] ) &&
$this->image_is_landscape( $this->images[4] );
}
}
/**
* Jetpack tiled gallery long symmetric row class.
*/
class Jetpack_Tiled_Gallery_Long_Symmetric_Row extends Jetpack_Tiled_Gallery_Shape {
/**
* The shape.
*
* @var array
*/
public $shape = array( 3, 1, 3 );
/**
* Check if it's possible.
*
* @return bool
*/
public function is_possible() {
return $this->is_not_as_previous( 5 ) && $this->images_left > 15 &&
$this->image_is_landscape( $this->images[0] ) &&
$this->image_is_landscape( $this->images[1] ) &&
$this->image_is_landscape( $this->images[2] ) &&
$this->image_is_portrait( $this->images[3] ) &&
$this->image_is_landscape( $this->images[4] ) &&
$this->image_is_landscape( $this->images[5] ) &&
$this->image_is_landscape( $this->images[6] );
}
}
/**
* Jetpack tiled gallery three columns class.
*/
class Jetpack_Tiled_Gallery_Three_Columns extends Jetpack_Tiled_Gallery_Shape {
/**
* The shape.
*
* @var array
*/
public $shape = array();
/**
* Constructor class.
*
* @param object $images - the images.
*/
public function __construct( $images ) {
parent::__construct( $images );
$total_ratio = $this->sum_ratios( $this->images_left );
$approximate_column_ratio = $total_ratio / 3;
$column_one_images = 0;
$column_two_images = 0;
$column_three_images = 0;
$sum = 0;
foreach ( $this->images as $image ) {
if ( $sum <= $approximate_column_ratio ) {
++$column_one_images;
}
if ( $sum > $approximate_column_ratio && $sum <= 2 * $approximate_column_ratio ) {
++$column_two_images;
}
$sum += $image->ratio;
}
$column_three_images = $this->images_left - $column_two_images - $column_one_images;
if ( $column_one_images ) {
$this->shape[] = $column_one_images;
}
if ( $column_two_images ) {
$this->shape[] = $column_two_images;
}
if ( $column_three_images ) {
$this->shape[] = $column_three_images;
}
}
/**
* Check if it's possible.
*
* @return bool
*/
public function is_possible() {
return ! empty( $this->shape );
}
}
@@ -0,0 +1,90 @@
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
require_once __DIR__ . '/tiled-gallery-layout.php';
require_once __DIR__ . '/tiled-gallery-item.php';
/**
* Jetpack tiled gallery square layout class.
*/
class Jetpack_Tiled_Gallery_Layout_Square extends Jetpack_Tiled_Gallery_Layout {
/**
* Layout type.
*
* @var string
*/
protected $type = 'square';
/**
* Compute the items.
*/
private function compute_items() {
$content_width = Jetpack_Tiled_Gallery::get_content_width();
$images_per_row = ( $this->columns > 1 ? $this->columns : 1 );
$margin = 2;
$margin_space = ( $images_per_row * $margin ) * 2;
$size = floor( ( $content_width - $margin_space ) / $images_per_row );
$remainder_size = $size;
$img_size = $remainder_size;
$attachment_count = is_countable( $this->attachments ) ? count( $this->attachments ) : 0;
$remainder = $attachment_count % $images_per_row;
if ( $remainder > 0 ) {
$remainder_space = ( $remainder * $margin ) * 2;
$remainder_size = floor( ( $content_width - $remainder_space ) / $remainder );
}
$c = 1;
$items_in_row = 0;
$rows = array();
$row = new stdClass();
$row->images = array();
foreach ( $this->attachments as $image ) {
if ( $remainder > 0 && $c <= $remainder ) {
$img_size = $remainder_size;
} else {
$img_size = $size;
}
$image->width = $img_size;
$image->height = $image->width;
$item = new Jetpack_Tiled_Gallery_Square_Item( $image, $this->needs_attachment_link, $this->grayscale );
$row->images[] = $item;
++$c;
++$items_in_row;
if ( $images_per_row === $items_in_row || $remainder + 1 === $c ) {
$rows[] = $row;
$items_in_row = 0;
$row->height = $img_size + $margin * 2;
$row->width = $content_width;
$row->group_size = $img_size + 2 * $margin;
$row = new stdClass();
$row->images = array();
}
}
if ( ! empty( $row->images ) ) {
$row->height = $img_size + $margin * 2;
$row->width = $content_width;
$row->group_size = $img_size + 2 * $margin;
$rows[] = $row;
}
return $rows;
}
/**
* The html.
*
* @param array $context - the context, unused.
* @return string HTML
*/
public function HTML( $context = array() ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
return parent::HTML( array( 'rows' => $this->compute_items() ) );
}
}
@@ -0,0 +1,104 @@
/* =Tiled Gallery Default Styles
-------------------------------------------------------------- */
.tiled-gallery {
clear: both;
margin: 0 0 20px;
overflow: hidden;
}
.tiled-gallery img {
margin: 2px !important; /* Ensure that this value isn't overridden by themes that give content images blanket margins */
}
.tiled-gallery .gallery-group {
float: left;
position: relative;
}
.tiled-gallery .tiled-gallery-item {
float: left;
margin: 0;
position: relative;
width: inherit; /* prevents ie8 bug with inline width styles */
}
.tiled-gallery .gallery-row {
overflow: hidden;
}
.tiled-gallery .tiled-gallery-item a { /* Needs to reset some properties for theme compatibility */
background: transparent;
border: none;
color: inherit;
margin: 0;
padding: 0;
text-decoration: none;
width: auto;
}
.tiled-gallery .tiled-gallery-item img,
.tiled-gallery .tiled-gallery-item img:hover { /* Needs to reset some properties for theme compatibility */
background: none;
border: none;
box-shadow: none;
max-width: 100%;
padding: 0;
vertical-align: middle;
}
.tiled-gallery-caption { /* Captions */
background: #f0f0f1;
background: rgba( 255,255,255,0.8 );
color: #333;
font-size: 13px;
font-weight: 400;
overflow: hidden;
padding: 10px 0;
position: absolute;
bottom: 0;
text-indent: 10px;
text-overflow: ellipsis;
width: 100%;
white-space: nowrap;
}
.tiled-gallery .tiled-gallery-item-small .tiled-gallery-caption { /* Smaller captions */
font-size: 11px;
}
/* Hide galleries in widgets until they've been resized to fit.
Gallery widgets are almost guaranteed to need resizing, and
the jump is a little more obvious than galleries in content. */
.widget-gallery .tiled-gallery-unresized {
visibility: hidden;
height: 0px;
overflow: hidden;
}
/* =Greyscale
-------------------------------------------------------------- */
.tiled-gallery .tiled-gallery-item img.grayscale {
-webkit-filter: grayscale(1);
-ms-filter: grayscale(1);
-o-filter: grayscale(1);
filter: grayscale(1);
}
.tiled-gallery .tiled-gallery-item:hover img.grayscale {
-webkit-filter: none;
-ms-filter: none;
-o-filter: none;
filter: none;
}
/* =Circles Layout
-------------------------------------------------------------- */
.tiled-gallery.type-circle .tiled-gallery-item img {
border-radius: 50% !important; /* Ensure that circles are displayed in themes that add border-radius to all images as a default */
object-fit: cover;
}
.tiled-gallery.type-circle .tiled-gallery-caption {
display: none;
}
/* =Square Layout
-------------------------------------------------------------- */
.tiled-gallery.type-square .tiled-gallery-item img {
object-fit: cover;
}
@@ -0,0 +1,192 @@
( function () {
function TiledGalleryCollection() {
this.galleries = [];
this.findAndSetupNewGalleries();
}
TiledGalleryCollection.prototype.findAndSetupNewGalleries = function () {
var self = this;
var unresizedGalleries = document.querySelectorAll( '.tiled-gallery.tiled-gallery-unresized' );
Array.prototype.forEach.call( unresizedGalleries, function ( el ) {
self.galleries.push( new TiledGallery( el ) );
} );
};
TiledGalleryCollection.prototype.resizeAll = function () {
Array.prototype.forEach.call( this.galleries, function ( gallery ) {
gallery.resize();
} );
};
function TiledGallery( galleryElem ) {
this.gallery = galleryElem;
this.addCaptionEvents();
// Resize when initialized to fit the gallery to window dimensions
this.resize();
// Displays the gallery and prevents it from being initialized again
this.gallery.classList.remove( 'tiled-gallery-unresized' );
}
/**
* Selector for all resizeable elements inside a Tiled Gallery
*/
TiledGallery.prototype.resizeableElementsSelector =
'.gallery-row, .gallery-group, .tiled-gallery-item img';
/**
* Story
*/
TiledGallery.prototype.addCaptionEvents = function () {
// Hide captions
var galleryCaptions = this.gallery.querySelectorAll( '.tiled-gallery-caption' );
Array.prototype.forEach.call( galleryCaptions, function ( el ) {
el.style.display = 'none';
} );
var mouseHoverHandler = function ( e ) {
var itemEl = e.target.closest( '.tiled-gallery-item' );
var displayValue = 'mouseover' === e.type ? 'block' : 'none';
if ( itemEl ) {
var itemCaption = itemEl.querySelector( '.tiled-gallery-caption' );
if ( itemCaption ) {
itemCaption.style.display = displayValue;
}
}
};
// Add hover effects to bring the caption up and down for each item
this.gallery.addEventListener( 'mouseover', mouseHoverHandler );
this.gallery.addEventListener( 'mouseout', mouseHoverHandler );
};
TiledGallery.prototype.getExtraDimension = function ( el, attribute, mode ) {
if ( mode === 'horizontal' ) {
var left = attribute === 'border' ? 'borderLeftWidth' : attribute + 'Left';
var right = attribute === 'border' ? 'borderRightWidth' : attribute + 'Right';
return ( parseInt( el.style[ left ], 10 ) || 0 ) + ( parseInt( el.style[ right ], 10 ) || 0 );
} else if ( mode === 'vertical' ) {
var top = attribute === 'border' ? 'borderTopWidth' : attribute + 'Top';
var bottom = attribute === 'border' ? 'borderBottomWidth' : attribute + 'Bottom';
return ( parseInt( el.style[ top ], 10 ) || 0 ) + ( parseInt( el.style[ bottom ], 10 ) || 0 );
}
return 0;
};
TiledGallery.prototype.resize = function () {
// Resize everything in the gallery based on the ratio of the current content width
// to the original content width;
var originalWidth = parseInt( this.gallery.dataset.originalWidth, 10 );
var currentWidth = parseFloat(
getComputedStyle( this.gallery.parentNode, null ).width.replace( 'px', '' )
);
var resizeRatio = Math.min( 1, currentWidth / originalWidth );
var self = this;
var resizableElements = this.gallery.querySelectorAll( this.resizeableElementsSelector );
Array.prototype.forEach.call( resizableElements, function ( el ) {
var marginWidth = self.getExtraDimension( el, 'margin', 'horizontal' );
var marginHeight = self.getExtraDimension( el, 'margin', 'vertical' );
var paddingWidth = self.getExtraDimension( el, 'padding', 'horizontal' );
var paddingHeight = self.getExtraDimension( el, 'padding', 'vertical' );
var borderWidth = self.getExtraDimension( el, 'border', 'horizontal' );
var borderHeight = self.getExtraDimension( el, 'border', 'vertical' );
// Take all outer dimensions into account when resizing so that images
// scale with constant empty space between them
var outerWidth =
parseInt( el.dataset.originalWidth, 10 ) + paddingWidth + borderWidth + marginWidth;
var outerHeight =
parseInt( el.dataset.originalHeight, 10 ) + paddingHeight + borderHeight + marginHeight;
// Subtract margins so that images don't overflow on small browser windows
el.style.width = Math.floor( resizeRatio * outerWidth ) - marginWidth + 'px';
el.style.height = Math.floor( resizeRatio * outerHeight ) - marginHeight + 'px';
} );
};
/**
* Resizing logic
*/
var requestAnimationFrame =
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
function attachResizeInAnimationFrames( tiledGalleries ) {
var resizing = false;
var resizeTimeout = null;
function handleFrame() {
tiledGalleries.resizeAll();
if ( resizing ) {
requestAnimationFrame( handleFrame );
}
}
window.addEventListener( 'resize', function () {
clearTimeout( resizeTimeout );
if ( ! resizing ) {
requestAnimationFrame( handleFrame );
}
resizing = true;
resizeTimeout = setTimeout( function () {
resizing = false;
}, 15 );
} );
}
function attachPlainResize( tiledGalleries ) {
window.addEventListener( 'resize', function () {
tiledGalleries.resizeAll();
} );
}
/**
* Ready, set...
*/
function ready( fn ) {
if ( document.readyState !== 'loading' ) {
fn();
} else {
document.addEventListener( 'DOMContentLoaded', fn );
}
}
ready( function () {
var tiledGalleries = new TiledGalleryCollection();
document.body.addEventListener( 'is.post-load', function () {
tiledGalleries.findAndSetupNewGalleries();
} );
// Chrome is a unique snow flake and will start lagging on occasion
// It helps if we only resize on animation frames
//
// For other browsers it seems like there is no lag even if we resize every
// time there is an event
if ( window.chrome && requestAnimationFrame ) {
attachResizeInAnimationFrames( tiledGalleries );
} else {
attachPlainResize( tiledGalleries );
}
if ( 'undefined' !== typeof wp && wp.customize && wp.customize.selectiveRefresh ) {
wp.customize.selectiveRefresh.bind( 'partial-content-rendered', function ( placement ) {
if ( wp.isJetpackWidgetPlaced( placement, 'gallery' ) ) {
tiledGalleries.findAndSetupNewGalleries();
}
} );
}
} );
} )();