PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 8.9.4
Jetpack – WP Security, Backup, Speed, & Growth v8.9.4
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 All 500 releases
jetpack / modules / shortcodes / presentations.php
presentations.php
493 lines 14.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php //phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3 use Automattic\Jetpack\Assets;
4
5 /**
6 * Presentations
7 * Presentations plugin based on the work done by <a href="http://darylkoop.com/">Daryl Koopersmith</a>. Powered by jmpress.js
8 *
9 * HOW TO: How the plugin settings are organized and which features are supported.
10 *
11 * The entire presentation should be wrapped with a [presentation] shortcode, and every
12 * individual slide should be wrapped with a [slide] shortcode. Any settings supported
13 * by [slide] can be set into [presentation], which will apply that setting for the entire
14 * presentation unless overridden by individual slides.
15 *
16 * - [presentation] only settings:
17 * - duration: transition durations, default is one second.
18 * - height: content height, default is 400px
19 * - width: content width, default is 550px
20 * - autoplay: delay between transitions in seconds, default 3s
21 * when set the presentation will automatically transition between slides
22 * as long as the presentation remains in focus
23 *
24 * - [slide] settings:
25 * - transition: specifies where the next slide will be placed relative
26 * to the last one before it. Supported values are "up", "down"
27 * "left", "right", or "none". Default value is "down".
28 *
29 * - scale: scales the content relative to other slides, default value is one
30 *
31 * - rotate: rotates the content by the specified degrees, default is zero
32 *
33 * - fade: slides will fade in and out during transition. Values of "on" or
34 * "true" will enable fading, while values of "no" or "false" will
35 * disable it. Default value is "on"
36 *
37 * - bgcolor: specifies a background color for the slides. Any CSS valid value
38 * is permitted. Default color is transparent.
39 *
40 * - bgimg: specifies an image url which will fill the background. Image is
41 * set to fill the background 100% width and height
42 *
43 * - fadebullets: any html <li> tags will start out with an opacity of 0 and any
44 * subsequent slide transitions will show the bullets one by one
45 *
46 * Known issues:
47 *
48 * - IE 7/8 are not supported by jmpress and presentations will not work
49 * - IE 9 will not animate transitions at all, though it's possible to at least
50 * switch between slides.
51 * - Infinite Scroll themes will not load presentations properly unless the post
52 * happens to be on the first loaded page. The permalink page will function
53 * properly, however.
54 * - Exiting fullscreen mode will not properly reset the scroll locations in Safari
55 *
56 * @package Jetpack
57 */
58
59 if ( ! class_exists( 'Presentations' ) ) :
60 /**
61 * Create a shortcode to display Presentations and slides.
62 */
63 class Presentations {
64
65 /**
66 * Presentation settings.
67 *
68 * @var array
69 */
70 private $presentation_settings;
71 /**
72 * Do we have a Presentation shortcode to be displayed.
73 *
74 * @var bool
75 */
76 private $presentation_initialized;
77 /**
78 * Were scripts and styles enqueued already.
79 *
80 * @var bool
81 */
82 private $scripts_and_style_included;
83
84 /**
85 * Constructor
86 */
87 public function __construct() {
88 $this->presentation_initialized = false;
89 $this->scripts_and_style_included = false;
90
91 // Registers shortcodes.
92 add_action( 'wp_head', array( &$this, 'add_scripts' ), 1 );
93
94 add_shortcode( 'presentation', array( &$this, 'presentation_shortcode' ) );
95 add_shortcode( 'slide', array( &$this, 'slide_shortcode' ) );
96 }
97
98 /**
99 * Enqueue all scripts and styles.
100 */
101 public function add_scripts() {
102 $this->scripts_and_style_included = false;
103
104 if ( empty( $GLOBALS['posts'] ) || ! is_array( $GLOBALS['posts'] ) ) {
105 return;
106 }
107
108 foreach ( $GLOBALS['posts'] as $p ) {
109 if ( has_shortcode( $p->post_content, 'presentation' ) ) {
110 $this->scripts_and_style_included = true;
111 break;
112 }
113 }
114
115 if ( ! $this->scripts_and_style_included ) {
116 return;
117 }
118
119 $plugin = plugin_dir_url( __FILE__ );
120 // Add CSS.
121 wp_enqueue_style( 'presentations', $plugin . 'css/style.css', array(), JETPACK__VERSION );
122 // Add JavaScript.
123 wp_enqueue_script( 'jquery' );
124 wp_enqueue_script(
125 'jmpress',
126 Assets::get_file_url_for_environment( '_inc/build/shortcodes/js/jmpress.min.js', 'modules/shortcodes/js/jmpress.js' ),
127 array( 'jquery' ),
128 JETPACK__VERSION,
129 true
130 );
131 wp_enqueue_script(
132 'presentations',
133 Assets::get_file_url_for_environment( '_inc/build/shortcodes/js/main.min.js', 'modules/shortcodes/js/main.js' ),
134 array( 'jquery', 'jmpress' ),
135 JETPACK__VERSION,
136 true
137 );
138 }
139
140 /**
141 * Main Presentation shortcode.
142 *
143 * @param array $atts Shortcode attributes.
144 * @param string $content Post content.
145 */
146 public function presentation_shortcode( $atts, $content = '' ) {
147 // Mark that we've found a valid [presentation] shortcode.
148 $this->presentation_initialized = true;
149
150 $atts = shortcode_atts(
151 array(
152 'duration' => '',
153 'height' => '',
154 'width' => '',
155 'bgcolor' => '',
156 'bgimg' => '',
157 'autoplay' => '',
158
159 // Settings.
160 'transition' => '',
161 'scale' => '',
162 'rotate' => '',
163 'fade' => '',
164 'fadebullets' => '',
165 ),
166 $atts,
167 'presentation'
168 );
169
170 $this->presentation_settings = array(
171 'transition' => 'down',
172 'scale' => 1,
173 'rotate' => 0,
174 'fade' => 'on',
175 'fadebullets' => 0,
176 'last' => array(
177 'x' => 0,
178 'y' => 0,
179 'scale' => 1,
180 'rotate' => 0,
181 ),
182 );
183
184 // Set the presentation-wide settings.
185 if ( '' !== trim( $atts['transition'] ) ) {
186 $this->presentation_settings['transition'] = $atts['transition'];
187 }
188
189 if ( '' !== trim( $atts['scale'] ) ) {
190 $this->presentation_settings['scale'] = floatval( $atts['scale'] );
191 }
192
193 if ( '' !== trim( $atts['rotate'] ) ) {
194 $this->presentation_settings['rotate'] = floatval( $atts['rotate'] );
195 }
196
197 if ( '' !== trim( $atts['fade'] ) ) {
198 $this->presentation_settings['fade'] = $atts['fade'];
199 }
200
201 if ( '' !== trim( $atts['fadebullets'] ) ) {
202 $this->presentation_settings['fadebullets'] = $atts['fadebullets'];
203 }
204
205 // Set any settings the slides don't care about.
206 if ( '' !== trim( $atts['duration'] ) ) {
207 $duration = floatval( $atts['duration'] ) . 's';
208 } else {
209 $duration = '1s';
210 }
211
212 // Autoplay durations are set in milliseconds.
213 if ( '' !== trim( $atts['autoplay'] ) ) {
214 $autoplay = floatval( $atts['autoplay'] ) * 1000;
215 } else {
216 $autoplay = 0;
217 } // No autoplay
218
219 // Set the presentation size as specified or with some nicely sized dimensions.
220 if ( '' !== trim( $atts['width'] ) ) {
221 $this->presentation_settings['width'] = intval( $atts['width'] );
222 } else {
223 $this->presentation_settings['width'] = 480;
224 }
225
226 if ( '' !== trim( $atts['height'] ) ) {
227 $this->presentation_settings['height'] = intval( $atts['height'] );
228 } else {
229 $this->presentation_settings['height'] = 370;
230 }
231
232 // Hide the content by default in case the scripts fail.
233 $style = 'display: none; width: ' . $this->presentation_settings['width'] . 'px; height: ' . $this->presentation_settings['height'] . 'px;';
234
235 /*
236 * Check for background color XOR background image
237 * Use a white background if nothing specified
238 */
239 if ( preg_match( '/https?\:\/\/[^\'"\s]*/', $atts['bgimg'], $matches ) ) {
240 $style .= ' background-image: url("' . esc_url( $matches[0] ) . '");';
241 } elseif ( '' !== trim( $atts['bgcolor'] ) ) {
242 $style .= ' background-color: ' . esc_attr( $atts['bgcolor'] ) . ';';
243 } else {
244 $style .= ' background-color: #fff;';
245 }
246
247 // Not supported message style is inlined incase the style sheet doesn't get included.
248 $out = "<section class='presentation-wrapper'>";
249 $out .= "<p class='not-supported-msg' style='display: inherit; padding: 25%; text-align: center;'>";
250 $out .= __( 'This slideshow could not be started. Try refreshing the page or viewing it in another browser.', 'jetpack' ) . '</p>';
251
252 // Bail out unless the scripts were added.
253 if ( $this->scripts_and_style_included ) {
254 $out .= sprintf(
255 '<div class="presentation" duration="%s" data-autoplay="%s" style="%s">',
256 esc_attr( $duration ),
257 esc_attr( $autoplay ),
258 esc_attr( $style )
259 );
260 $out .= "<div class='nav-arrow-left'></div>";
261 $out .= "<div class='nav-arrow-right'></div>";
262 $out .= "<div class='nav-fullscreen-button'></div>";
263
264 if ( $autoplay ) {
265 $out .= '<div class="autoplay-overlay" style="display: none;"><p class="overlay-msg">';
266 $out .= __( 'Click to autoplay the presentation!', 'jetpack' );
267 $out .= '</p></div>';
268 }
269
270 $out .= do_shortcode( $content );
271 }
272
273 $out .= '</section>';
274
275 $this->presentation_initialized = false;
276
277 return $out;
278 }
279
280 /**
281 * Slide shortcode.
282 *
283 * @param array $atts Shortcode attributes.
284 * @param string $content Post content.
285 */
286 public function slide_shortcode( $atts, $content = '' ) {
287 // Bail out unless wrapped by a [presentation] shortcode.
288 if ( ! $this->presentation_initialized ) {
289 return $content;
290 }
291
292 $atts = shortcode_atts(
293 array(
294 'transition' => '',
295 'scale' => '',
296 'rotate' => '',
297 'fade' => '',
298 'fadebullets' => '',
299 'bgcolor' => '',
300 'bgimg' => '',
301 ),
302 $atts,
303 'slide'
304 );
305
306 // Determine positioning based on transition.
307 if ( '' === trim( $atts['transition'] ) ) {
308 $atts['transition'] = $this->presentation_settings['transition'];
309 }
310
311 // Setting the content scale.
312 if ( '' === trim( $atts['scale'] ) ) {
313 $atts['scale'] = $this->presentation_settings['scale'];
314 }
315
316 if ( '' === trim( $atts['scale'] ) ) {
317 $scale = 1;
318 } else {
319 $scale = floatval( $atts['scale'] );
320 }
321
322 if ( $scale < 0 ) {
323 $scale *= -1;
324 }
325
326 // Setting the content rotation.
327 if ( '' === trim( $atts['rotate'] ) ) {
328 $atts['rotate'] = $this->presentation_settings['rotate'];
329 }
330
331 if ( '' === trim( $atts['rotate'] ) ) {
332 $rotate = 0;
333 } else {
334 $rotate = floatval( $atts['rotate'] );
335 }
336
337 // Setting if the content should fade.
338 if ( '' === trim( $atts['fade'] ) ) {
339 $atts['fade'] = $this->presentation_settings['fade'];
340 }
341
342 if ( 'on' === $atts['fade'] || 'true' === $atts['fade'] ) {
343 $fade = 'fade';
344 } else {
345 $fade = '';
346 }
347
348 // Setting if bullets should fade on step changes.
349 if ( '' === trim( $atts['fadebullets'] ) ) {
350 $atts['fadebullets'] = $this->presentation_settings['fadebullets'];
351 }
352
353 if ( 'on' === $atts['fadebullets'] || 'true' === $atts['fadebullets'] ) {
354 $fadebullets = 'fadebullets';
355 } else {
356 $fadebullets = '';
357 }
358
359 $coords = $this->get_coords(
360 array(
361 'transition' => $atts['transition'],
362 'scale' => $scale,
363 'rotate' => $rotate,
364 )
365 );
366
367 $x = $coords['x'];
368 $y = $coords['y'];
369
370 /*
371 * Check for background color XOR background image
372 * Use a white background if nothing specified
373 */
374 if ( preg_match( '/https?\:\/\/[^\'"\s]*/', $atts['bgimg'], $matches ) ) {
375 $style = 'background-image: url("' . esc_url( $matches[0] ) . '");';
376 } elseif ( '' !== trim( $atts['bgcolor'] ) ) {
377 $style = 'background-color: ' . esc_attr( $atts['bgcolor'] ) . ';';
378 } else {
379 $style = '';
380 }
381
382 // Put everything together and let jmpress do the magic!
383 $out = sprintf(
384 '<div class="step %s %s" data-x="%s" data-y="%s" data-scale="%s" data-rotate="%s" style="%s">',
385 esc_attr( $fade ),
386 esc_attr( $fadebullets ),
387 esc_attr( $x ),
388 esc_attr( $y ),
389 esc_attr( $scale ),
390 esc_attr( $rotate ),
391 esc_attr( $style )
392 );
393
394 $out .= '<div class="slide-content">';
395 $out .= do_shortcode( $content );
396 $out .= '</div></div>';
397
398 return $out;
399 }
400
401 /**
402 * Determines the position of the next slide based on the position and scaling of the previous slide.
403 *
404 * @param array $args {
405 * Array of key-value pairs.
406 *
407 * @type string $transition: the transition name, "up", "down", "left", or "right".
408 * @type float $scale: the scale of the next slide (used to determine the position of the slide after that).
409 * }
410 *
411 * @return array with the 'x' and 'y' coordinates of the slide.
412 */
413 private function get_coords( $args ) {
414 if ( 0 === $args['scale'] ) {
415 $args['scale'] = 1;
416 }
417
418 $width = $this->presentation_settings['width'];
419 $height = $this->presentation_settings['height'];
420 $last = $this->presentation_settings['last'];
421 $scale = $last['scale'];
422
423 $next = array(
424 'x' => $last['x'],
425 'y' => $last['y'],
426 'scale' => $args['scale'],
427 'rotate' => $args['rotate'],
428 );
429
430 // All angles are measured from the vertical axis, so everything is backwards!
431 $diag_angle = atan2( $width, $height );
432 $diagonal = sqrt( pow( $width, 2 ) + pow( $height, 2 ) );
433
434 /*
435 * We offset the angles by the angle formed by the diagonal so that
436 * we can multiply the sines directly against the diagonal length
437 */
438 $theta = deg2rad( $last['rotate'] ) - $diag_angle;
439 $phi = deg2rad( $next['rotate'] ) - $diag_angle;
440
441 // We start by displacing by the slide dimensions.
442 $total_horiz_disp = $width * $scale;
443 $total_vert_disp = $height * $scale;
444
445 /*
446 * If the previous slide was rotated, we add the incremental offset from the rotation
447 * Namely the difference between the regular dimension (no rotation) and the component
448 * of the diagonal for that angle
449 */
450 $total_horiz_disp += ( ( ( abs( sin( $theta ) ) * $diagonal ) - $width ) / 2 ) * $scale;
451 $total_vert_disp += ( ( ( abs( cos( $theta ) ) * $diagonal ) - $height ) / 2 ) * $scale;
452
453 /*
454 * Similarly, we check if the current slide has been rotated and add whatever additional
455 * offset has been added. This is so that two rotated corners don't clash with each other.
456 * Note: we are checking the raw angle relative to the vertical axis, NOT the diagonal angle.
457 */
458 if ( 0 !== $next['rotate'] % 180 ) {
459 $total_horiz_disp += ( abs( ( sin( $phi ) * $diagonal ) - $width ) / 2 ) * $next['scale'];
460 $total_vert_disp += ( abs( ( cos( $phi ) * $diagonal ) - $height ) / 2 ) * $next['scale'];
461 }
462
463 switch ( trim( $args['transition'] ) ) {
464 case 'none':
465 break;
466
467 case 'left':
468 $next['x'] -= $total_horiz_disp;
469 break;
470
471 case 'right':
472 $next['x'] += $total_horiz_disp;
473 break;
474
475 case 'up':
476 $next['y'] -= $total_vert_disp;
477 break;
478
479 case 'down':
480 default:
481 $next['y'] += $total_vert_disp;
482 break;
483 }
484
485 $this->presentation_settings['last'] = $next;
486
487 return $next;
488 }
489 }
490
491 $GLOBALS['presentations'] = new Presentations();
492 endif;
493