diff options
Diffstat (limited to 'plugins/jetpack/modules/tiled-gallery')
5 files changed, 986 insertions, 0 deletions
diff --git a/plugins/jetpack/modules/tiled-gallery/math/class-constrained-array-rounding.php b/plugins/jetpack/modules/tiled-gallery/math/class-constrained-array-rounding.php new file mode 100644 index 00000000..dab5fd27 --- /dev/null +++ b/plugins/jetpack/modules/tiled-gallery/math/class-constrained-array-rounding.php @@ -0,0 +1,75 @@ +<?php + +/** + * 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 { + 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 ); + } + + 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; + } + + private static function adjust_constrained_array( &$bound_array_int, $adjustment ) { + usort( $bound_array_int, array( 'self', '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', 'cmp_asc_index' ) ); + } + + private static function cmp_desc_fraction( $a, $b ) { + if ( $a['fraction'] == $b['fraction'] ) + return 0; + return $a['fraction'] > $b['fraction'] ? -1 : 1; + } + + private static function cmp_asc_index( $a, $b ) { + if ( $a['index'] == $b['index'] ) + return 0; + return $a['index'] < $b['index'] ? -1 : 1; + } +} diff --git a/plugins/jetpack/modules/tiled-gallery/tiled-gallery.php b/plugins/jetpack/modules/tiled-gallery/tiled-gallery.php new file mode 100644 index 00000000..342e8a65 --- /dev/null +++ b/plugins/jetpack/modules/tiled-gallery/tiled-gallery.php @@ -0,0 +1,588 @@ +<?php + +// 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. +include_once dirname( __FILE__ ) . '/math/class-constrained-array-rounding.php'; + +class Jetpack_Tiled_Gallery { + + public function __construct() { + add_action( 'admin_init', array( $this, 'settings_api_init' ) ); + add_filter( 'jetpack_gallery_types', array( $this, 'jetpack_gallery_types' ), 9 ); + } + + public function tiles_enabled() { + // Check the setting status + return '' != get_option( 'tiled_galleries' ); + } + + public function set_atts( $atts ) { + global $post; + + $this->atts = shortcode_atts( array( + 'order' => 'ASC', + 'orderby' => 'menu_order ID', + 'id' => $post->ID, + 'include' => '', + 'exclude' => '', + 'type' => '', + 'grayscale' => false, + 'link' => '', + ), $atts ); + + $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->atts['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' == $this->atts['order'] ) + $this->atts['orderby'] = 'none'; + } + + public function get_attachments() { + extract( $this->atts ); + + if ( !empty( $include ) ) { + $include = preg_replace( '/[^0-9,]+/', '', $include ); + $_attachments = get_posts( array('include' => $include, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $order, 'orderby' => $orderby) ); + + $attachments = array(); + foreach ( $_attachments as $key => $val ) { + $attachments[$val->ID] = $_attachments[$key]; + } + } elseif ( !empty( $exclude ) ) { + $exclude = preg_replace( '/[^0-9,]+/', '', $exclude ); + $attachments = get_children( array('post_parent' => $id, 'exclude' => $exclude, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $order, 'orderby' => $orderby) ); + } else { + $attachments = get_children( array('post_parent' => $id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $order, 'orderby' => $orderby ) ); + } + return $attachments; + } + + public function get_attachment_link( $attachment_id, $orig_file ) { + if ( isset( $this->atts['link'] ) && $this->atts['link'] == 'file' ) + return $orig_file; + else + return get_attachment_link( $attachment_id ); + } + + public function default_scripts_and_styles() { + wp_enqueue_script( 'tiled-gallery', plugins_url( 'tiled-gallery/tiled-gallery.js', __FILE__ ), array( 'jquery' ) ); + wp_enqueue_style( 'tiled-gallery', plugins_url( 'tiled-gallery/tiled-gallery.css', __FILE__ ), array(), '2012-09-21' ); + } + + public function gallery_shortcode( $val, $atts ) { + if ( ! empty( $val ) ) // something else is overriding post_gallery, like a custom VIP shortcode + return $val; + + global $post; + + $this->set_atts( $atts ); + + $attachments = $this->get_attachments(); + if ( empty( $attachments ) ) + return ''; + + if ( is_feed() || defined( 'IS_HTML_EMAIL' ) ) + return ''; + + if ( method_exists( $this, $this->atts['type'] . '_talavera' ) ) { + // Enqueue styles and scripts + $this->default_scripts_and_styles(); + $gallery_html = call_user_func_array( array( $this, $this->atts['type'] . '_talavera' ), array( $attachments ) ); + + if ( $gallery_html && class_exists( 'Jetpack' ) && class_exists( 'Jetpack_Photon' ) ) { + // Tiled Galleries in Jetpack require that Photon be active. + // If it's not active, run it just on the gallery output. + if ( ! in_array( 'photon', Jetpack::get_active_modules() ) ) + $gallery_html = Jetpack_Photon::filter_the_content( $gallery_html ); + } + + return $gallery_html; + } + + return ''; + } + + public function rectangular_talavera( $attachments ) { + $grouper = new Jetpack_Tiled_Gallery_Grouper( $attachments ); + + Jetpack_Tiled_Gallery_Shape::reset_last_shape(); + + $output = $this->generate_carousel_container(); + foreach ( $grouper->grouped_images as $row ) { + $output .= '<div class="gallery-row" style="' . esc_attr( 'width: ' . $row->width . 'px; height: ' . ( $row->height - 4 ) . 'px;' ) . '">'; + foreach( $row->groups as $group ) { + $count = count( $group->images ); + $output .= '<div class="gallery-group images-' . esc_attr( $count ) . '" style="' . esc_attr( 'width: ' . $group->width . 'px; height: ' . $group->height . 'px;' ) . '">'; + foreach ( $group->images as $image ) { + + $size = 'large'; + if ( $image->width < 250 ) + $size = 'small'; + + $image_title = $image->post_title; + $orig_file = wp_get_attachment_url( $image->ID ); + $link = $this->get_attachment_link( $image->ID, $orig_file ); + + $img_src = add_query_arg( array( 'w' => $image->width, 'h' => $image->height ), $orig_file ); + + $output .= '<div class="tiled-gallery-item tiled-gallery-item-' . esc_attr( $size ) . '"><a href="' . esc_url( $link ) . '"><img ' . $this->generate_carousel_image_args( $image ) . ' src="' . esc_url( $img_src ) . '" width="' . esc_attr( $image->width ) . '" height="' . esc_attr( $image->height ) . '" align="left" title="' . esc_attr( $image_title ) . '" /></a>'; + + if ( $this->atts['grayscale'] == true ) { + $img_src_grayscale = jetpack_photon_url( $img_src, array( 'filter' => 'grayscale' ) ); + $output .= '<a href="'. esc_url( $link ) . '"><img ' . $this->generate_carousel_image_args( $image ) . ' class="grayscale" src="' . esc_url( $img_src_grayscale ) . '" width="' . esc_attr( $image->width ) . '" height="' . esc_attr( $image->height ) . '" align="left" title="' . esc_attr( $image_title ) . '" /></a>'; + } + + if ( trim( $image->post_excerpt ) ) + $output .= '<div class="tiled-gallery-caption">' . wptexturize( $image->post_excerpt ) . '</div>'; + + $output .= '</div>'; + } + $output .= '</div>'; + } + $output .= '</div>'; + } + $output .= '</div>'; + return $output; + } + + public function square_talavera( $attachments ) { + $content_width = self::get_content_width(); + $images_per_row = 3; + $margin = 2; + + $margin_space = ( $images_per_row * $margin ) * 2; + $size = floor( ( $content_width - $margin_space ) / $images_per_row ); + $remainder = count( $attachments ) % $images_per_row; + if ( $remainder > 0 ) { + $remainder_space = ( $remainder * $margin ) * 2; + $remainder_size = ceil( ( $content_width - $remainder_space - $margin ) / $remainder ); + } + $output = $this->generate_carousel_container(); + $c = 1; + foreach( $attachments as $image ) { + if ( $remainder > 0 && $c <= $remainder ) + $img_size = $remainder_size; + else + $img_size = $size; + + $orig_file = wp_get_attachment_url( $image->ID ); + $link = $this->get_attachment_link( $image->ID, $orig_file ); + $image_title = $image->post_title; + + $img_src = add_query_arg( array( 'w' => $img_size, 'h' => $img_size, 'crop' => 1 ), $orig_file ); + + $output .= '<div class="tiled-gallery-item">'; + $output .= '<a border="0" href="' . esc_url( $link ) . '"><img ' . $this->generate_carousel_image_args( $image ) . ' style="' . esc_attr( 'margin: ' . $margin . 'px' ) . '" src="' . esc_url( $img_src ) . '" width=' . esc_attr( $img_size ) . ' height=' . esc_attr( $img_size ) . ' title="' . esc_attr( $image_title ) . '" /></a>'; + + // Grayscale effect + if ( $this->atts['grayscale'] == true ) { + $src = urlencode( $image->guid ); + $output .= '<a border="0" href="' . esc_url( $link ) . '"><img ' . $this->generate_carousel_image_args( $image ) . ' style="margin: 2px" class="grayscale" src="' . esc_url( 'http://en.wordpress.com/imgpress?url=' . urlencode( $image->guid ) . '&resize=' . $img_size . ',' . $img_size . '&filter=grayscale' ) . '" width=' . esc_attr( $img_size ) . ' height=' . esc_attr( $img_size ) . ' title="' . esc_attr( $image_title ) . '" /></a>'; + } + + // Captions + if ( trim( $image->post_excerpt ) ) + $output .= '<div class="tiled-gallery-caption">' . wptexturize( $image->post_excerpt ) . '</div>'; + $output .= '</div>'; + $c ++; + } + $output .= '</div>'; + return $output; + } + + public function circle_talavera( $attachments ) { + return $this->square_talavera( $attachments ); + } + + public function rectangle_talavera( $attachments ) { + return $this->rectangular_talavera( $attachments ); + } + + function generate_carousel_container() { + global $post; + + $html = '<div '. $this->gallery_classes() . ' data-original-width="' . esc_attr( self::get_content_width() ) . '">'; + $blog_id = (int) get_current_blog_id(); + $extra_data = array( 'data-carousel-extra' => array( 'blog_id' => $blog_id, 'permalink' => get_permalink( $post->ID ) ) ); + + foreach ( (array) $extra_data as $data_key => $data_values ) { + $html = str_replace( '<div ', '<div ' . esc_attr( $data_key ) . "='" . json_encode( $data_values ) . "' ", $html ); + } + + return $html; + } + + function generate_carousel_image_args( $image ) { + $attachment_id = $image->ID; + $orig_file = wp_get_attachment_url( $attachment_id ); + $meta = wp_get_attachment_metadata( $attachment_id ); + $size = isset( $meta['width'] ) ? intval( $meta['width'] ) . ',' . intval( $meta['height'] ) : ''; + $img_meta = ( ! empty( $meta['image_meta'] ) ) ? (array) $meta['image_meta'] : array(); + $comments_opened = intval( comments_open( $attachment_id ) ); + + $medium_file_info = wp_get_attachment_image_src( $attachment_id, 'medium' ); + $medium_file = isset( $medium_file_info[0] ) ? $medium_file_info[0] : ''; + + $large_file_info = wp_get_attachment_image_src( $attachment_id, 'large' ); + $large_file = isset( $large_file_info[0] ) ? $large_file_info[0] : ''; + $attachment_title = wptexturize( $image->post_title ); + $attachment_desc = wpautop( wptexturize( $image->post_content ) ); + + // Not yet providing geo-data, need to "fuzzify" for privacy + if ( ! empty( $img_meta ) ) { + foreach ( $img_meta as $k => $v ) { + if ( 'latitude' == $k || 'longitude' == $k ) + unset( $img_meta[$k] ); + } + } + + $img_meta = json_encode( array_map( 'strval', $img_meta ) ); + + $output = sprintf( + 'data-attachment-id="%1$d" data-orig-file="%2$s" data-orig-size="%3$s" data-comments-opened="%4$s" data-image-meta="%5$s" data-image-title="%6$s" data-image-description="%7$s" data-medium-file="%8$s" data-large-file="%9$s"', + esc_attr( $attachment_id ), + esc_url( wp_get_attachment_url( $attachment_id ) ), + esc_attr( $size ), + esc_attr( $comments_opened ), + esc_attr( $img_meta ), + esc_attr( $attachment_title ), + esc_attr( $attachment_desc ), + esc_url( $medium_file ), + esc_url( $large_file ) + ); + return $output; + } + + public function gallery_classes() { + $classes = 'class="tiled-gallery type-' . esc_attr( $this->atts['type'] ) . '"'; + return $classes; + } + + public static function gallery_already_redefined() { + global $shortcode_tags; + if ( ! isset( $shortcode_tags[ 'gallery' ] ) || $shortcode_tags[ 'gallery' ] !== 'gallery_shortcode' ) + return true; + } + + 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 ); + } + + public static function get_content_width() { + global $content_width; + + $tiled_gallery_content_width = $content_width; + + if ( ! $tiled_gallery_content_width ) + $tiled_gallery_content_width = 500; + + return apply_filters( 'tiled_gallery_content_width', $tiled_gallery_content_width ); + } + + /** + * Media UI integration + */ + function jetpack_gallery_types( $types ) { + $types['rectangular'] = __( 'Tiles', 'jetpack' ); + $types['square'] = __( 'Square Tiles', 'jetpack' ); + $types['circle'] = __( 'Circles', 'jetpack' ); + return $types; + } + + /** + * Add a checkbox field to the Carousel section in Settings > Media + * for setting tiled galleries as the default. + */ + 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' ); + } + + function setting_html() { + echo '<label><input name="tiled_galleries" type="checkbox" value="1" ' . + checked( 1, '' != get_option( 'tiled_galleries' ), false ) . ' /> ' . + __( 'Display all your gallery pictures in a cool mosaic.', 'jetpack' ) . '</br></label>'; + } +} + +class Jetpack_Tiled_Gallery_Shape { + static $shapes_used = array(); + + public function __construct( $images ) { + $this->images = $images; + $this->images_left = count( $images ); + } + + public function sum_ratios( $number_of_images = 3 ) { + return array_sum( array_slice( wp_list_pluck( $this->images, 'ratio' ), 0, $number_of_images ) ); + } + + public function next_images_are_symmetric() { + return $this->images_left > 2 && $this->images[0]->ratio == $this->images[2]->ratio; + } + + public function is_not_as_previous( $n = 1 ) { + return ! in_array( get_class( $this ), array_slice( self::$shapes_used, -$n ) ); + } + + public function is_wide_theme() { + global $content_width; + return $content_width > 1000; + } + + public static function set_last_shape( $last_shape ) { + self::$shapes_used[] = $last_shape; + } + + public static function reset_last_shape() { + self::$shapes_used = array(); + } +} + +class Jetpack_Tiled_Gallery_Three extends Jetpack_Tiled_Gallery_Shape { + public $shape = array( 1, 1, 1 ); + + public function is_possible() { + $ratio = $this->sum_ratios( 3 ); + return $this->images_left > 2 && $this->is_not_as_previous() && + ( ( $ratio < 2.5 ) || ( $ratio < 5 && $this->next_images_are_symmetric() ) || $this->is_wide_theme() ); + } +} + +class Jetpack_Tiled_Gallery_Four extends Jetpack_Tiled_Gallery_Shape { + public $shape = array( 1, 1, 1, 1 ); + + public function is_possible() { + return $this->is_not_as_previous() && $this->sum_ratios( 4 ) < 3.5 && + ( $this->images_left == 4 || ( $this->images_left != 8 && $this->images_left > 5 ) ); + } +} + +class Jetpack_Tiled_Gallery_Five extends Jetpack_Tiled_Gallery_Shape { + public $shape = array( 1, 1, 1, 1, 1 ); + + 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 ) ); + } +} + +class Jetpack_Tiled_Gallery_Two_One extends Jetpack_Tiled_Gallery_Shape { + public $shape = array( 2, 1 ); + + 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[1]->ratio >= 0.9; + } +} + +class Jetpack_Tiled_Gallery_One_Two extends Jetpack_Tiled_Gallery_Shape { + public $shape = array( 1, 2 ); + + 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[2]->ratio >= 0.9; + } +} + +class Jetpack_Tiled_Gallery_One_Three extends Jetpack_Tiled_Gallery_Shape { + public $shape = array( 1, 3 ); + + public function is_possible() { + return $this->is_not_as_previous() && $this->images_left >= 3 && + $this->images[0]->ratio < 0.8 && $this->images[1]->ratio >=0.9 && $this->images[2]->ratio >= 0.9 && $this->images[3]->ratio >= 0.9; + } +} + +class Jetpack_Tiled_Gallery_Symmetric_Row extends Jetpack_Tiled_Gallery_Shape { + public $shape = array( 1, 2, 1 ); + + public function is_possible() { + return $this->is_not_as_previous() && $this->images_left >= 3 && $this->images_left != 5 && + $this->images[0]->ratio < 0.8 && $this->images[0]->ratio == $this->images[3]->ratio; + } +} + +class Jetpack_Tiled_Gallery_Grouper { + public $margin = 4; + public function __construct( $attachments ) { + $content_width = Jetpack_Tiled_Gallery::get_content_width(); + $ua_info = new Jetpack_User_Agent_Info(); + + $this->last_shape = ''; + $this->images = $this->get_images_with_sizes( $attachments ); + $this->grouped_images = $this->get_grouped_images(); + $this->apply_content_width( $content_width - 5 ); //reduce the margin hack to 5px. It will be further reduced when we fix more themes and the rounding error. + } + + public function get_current_row_size() { + $images_left = count( $this->images ); + if ( $images_left < 3 ) + return array_fill( 0, $images_left, 1 ); + + foreach ( array( 'One_Three', 'One_Two', 'Five', 'Four', 'Three', 'Two_One', 'Symmetric_Row' ) 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 ); + } + + 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 = ( $meta['width'] > 0 )? $meta['width'] : 1; + $image->height_orig = ( $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; + } + + 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; + } + + 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; + } + + // todo: split in functions + // todo: do not stretch images + public function apply_content_width( $width ) { + foreach ( $this->grouped_images as $row ) { + $row->width = $width; + $row->raw_height = 1 / $row->ratio * ( $width - $this->margin * ( count( $row->groups ) - $row->weighted_ratio ) ); + $row->height = round( $row->raw_height ); + + $this->calculate_group_sizes( $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; + // 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 * count( $group->images ) ) * $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 ); + } + } + + 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 ); + } + } +} + +class Jetpack_Tiled_Gallery_Row { + public function __construct( $groups ) { + $this->groups = $groups; + $this->ratio = $this->get_ratio(); + $this->weighted_ratio = $this->get_weighted_ratio(); + } + + public function get_ratio() { + $ratio = 0; + foreach ( $this->groups as $group ) { + $ratio += $group->ratio; + } + return $ratio > 0? $ratio : 1; + } + + public function get_weighted_ratio() { + $weighted_ratio = 0; + foreach ( $this->groups as $group ) { + $weighted_ratio += $group->ratio * count( $group->images ); + } + return $weighted_ratio > 0 ? $weighted_ratio : 1; + } +} + +class Jetpack_Tiled_Gallery_Group { + public function __construct( $images ) { + $this->images = $images; + $this->ratio = $this->get_ratio(); + } + + 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; + } +} + +add_action( 'init', array( 'Jetpack_Tiled_Gallery', 'init' ) ); + diff --git a/plugins/jetpack/modules/tiled-gallery/tiled-gallery/rtl/tiled-gallery-rtl.css b/plugins/jetpack/modules/tiled-gallery/tiled-gallery/rtl/tiled-gallery-rtl.css new file mode 100644 index 00000000..007c0ccf --- /dev/null +++ b/plugins/jetpack/modules/tiled-gallery/tiled-gallery/rtl/tiled-gallery-rtl.css @@ -0,0 +1,88 @@ +/* This file was automatically generated on Jan 05 2013 15:45:53 */ + +/* =Tiled Gallery Default Styles +-------------------------------------------------------------- */ + +.tiled-gallery { + clear: both; + margin: 0; + 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; + overflow-y: hidden; + position: relative; +} +.tiled-gallery .tiled-gallery-item { + float: right; + margin: 0; + position: relative; +} +.tiled-gallery .gallery-row { + overflow: hidden; + margin-bottom: 2px; +} +.tiled-gallery .tiled-gallery-item a { /* Needs to reset some properties for theme compatibility */ + background: transparent; + border: none; + color: none; + 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; +} + + +/* =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; +}
\ No newline at end of file diff --git a/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.css b/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.css new file mode 100644 index 00000000..3a1924df --- /dev/null +++ b/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.css @@ -0,0 +1,85 @@ +/* =Tiled Gallery Default Styles +-------------------------------------------------------------- */ + +.tiled-gallery { + clear: both; + margin: 0; + 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; +} +.tiled-gallery .gallery-row { + overflow: hidden; + margin-bottom: 2px; +} +.tiled-gallery .tiled-gallery-item a { /* Needs to reset some properties for theme compatibility */ + background: transparent; + border: none; + color: none; + 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; +} + + +/* =Greyscale +-------------------------------------------------------------- */ + +.tiled-gallery .tiled-gallery-item img.grayscale { + position: absolute; + left: 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; +}
\ No newline at end of file diff --git a/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.js b/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.js new file mode 100644 index 00000000..90f8ed76 --- /dev/null +++ b/plugins/jetpack/modules/tiled-gallery/tiled-gallery/tiled-gallery.js @@ -0,0 +1,150 @@ +( function($) { + +var TiledGallery = function() { + this.resizeTimeout = null; + + this.populate(); + + var self = this; + + $( window ).on( 'resize', function () { + clearTimeout( self.resizeTimeout ); + + self.resizeTimeout = setTimeout( function () { self.resize(); }, 150 ); + } ); + + // Make any new galleries loaded by Infinite Scroll flexible + $( 'body' ).on( 'post-load', $.proxy( self.initialize, self ) ); + + // Populate and set up captions on newdash galleries. + $( document ).on( 'page-rendered.wpcom-newdash', $.proxy( self.populate, self ) ); + + this.resize(); +}; + +TiledGallery.prototype.populate = function() { + this.gallery = $( '.tiled-gallery' ); + this.item = this.gallery.find( '.tiled-gallery-item' ); + this.caption = this.gallery.find( '.tiled-gallery-caption' ); + + this.Captions(); +}; + +TiledGallery.prototype.initialize = function() { + var self = this; + + self.populate(); + + // After each image load, run resize in case all images in the gallery are loaded. + self.gallery.find( 'img' ).off( 'load.tiled-gallery' ).on( 'load.tiled-gallery', function () { + self.resize(); + } ); + + // Run resize now in case all images loaded from cache. + self.resize(); +}; + +/** + * Story + */ +TiledGallery.prototype.Captions = function() { + /* Hide captions */ + this.caption.hide(); + + this.item.on( 'hover', function() { + $( this ).find( '.tiled-gallery-caption' ).slideToggle( 'fast' ); + }); +}; + +TiledGallery.prototype.resize = function() { + var resizeableElements = '.gallery-row, .gallery-group, .tiled-gallery-item img'; + + this.gallery.each( function ( galleryIndex, galleryElement ) { + var thisGallery = $( galleryElement ); + + // All images must be loaded before proceeding. + var imagesLoaded = true; + + thisGallery.find( 'img' ).each( function () { + if ( ! this.complete ) { + imagesLoaded = false; + return false; + } + } ); + + if ( ! imagesLoaded ) { + var loadCallback = arguments.callee; + + // Once all of the images have loaded, + // re-call this containing function. + $( window ).load( function () { + loadCallback( null, thisGallery ); + } ); + + return; + } + + if ( ! thisGallery.data( 'sizes-set' ) ) { + // Maintain a record of the original widths and heights of these elements + // for proper scaling. + thisGallery.data( 'sizes-set', true ); + + thisGallery.find( resizeableElements ).each( function () { + var thisGalleryElement = $( this ); + + // Don't change margins, but remember what they were so they can be + // accounted for in size calculations. When the screen width gets + // small enough, ignoring the margins can cause images to overflow + // into new rows. + var extraWidth = ( parseInt( thisGalleryElement.css( 'marginLeft' ), 10 ) || 0 ) + ( parseInt( thisGalleryElement.css( 'marginRight' ), 10 ) || 0 ); + var extraHeight = ( parseInt( thisGalleryElement.css( 'marginTop' ), 10 ) || 0 ) + ( parseInt( thisGalleryElement.css( 'marginBottom' ), 10 ) || 0 ) + + // In some situations, tiled galleries in Firefox have shown scrollbars on the images because + // the .outerWidth() call on the image returns a value larger than the container. Restrict + // widths used in the resizing functions to the maximum width of the container. + var parentElement = $( thisGalleryElement.parents( resizeableElements ).get( 0 ) ); + + if ( parentElement && parentElement.data( 'original-width' ) ) { + thisGalleryElement + .data( 'original-width', Math.min( parentElement.data( 'original-width' ), thisGalleryElement.outerWidth( true ) ) ) + .data( 'original-height', Math.min( parentElement.data( 'original-height' ), thisGalleryElement.outerHeight( true ) ) ); + } + else { + thisGalleryElement + .data( 'original-width', thisGalleryElement.outerWidth( true ) ) + .data( 'original-height', thisGalleryElement.outerHeight( true ) ); + } + + thisGalleryElement + .data( 'extra-width', extraWidth ) + .data( 'extra-height', extraHeight ); + } ); + } + + // Resize everything in the gallery based on the ratio of the current content width + // to the original content width; + var originalWidth = thisGallery.data( 'original-width' ); + var currentWidth = thisGallery.parent().width(); + var resizeRatio = Math.min( 1, currentWidth / originalWidth ); + + thisGallery.find( resizeableElements ).each( function () { + var thisGalleryElement = $( this ); + + thisGalleryElement + .width( Math.floor( resizeRatio * thisGalleryElement.data( 'original-width' ) ) - thisGalleryElement.data( 'extra-width' ) ) + .height( Math.floor( resizeRatio * thisGalleryElement.data( 'original-height' ) ) - thisGalleryElement.data( 'extra-height' ) ); + } ); + } ); +}; + +/** + * Ready, set... + */ +$( document ).ready( function() { + + // Instance! + var TiledGalleryInstance = new TiledGallery; + +}); + +})(jQuery);
\ No newline at end of file |