PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.3.4
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.3.4
1.3.4 1.3.3 1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 All 30 releases
← All changes | includes/class-video-facade.php +56 -0 1.1.41.3.4 View file →
@@ -170,8 +170,51 @@
170 170 return $markup;
171 171 }
172 172
173 173 /**
174 + * Build the facade markup for one self-hosted <video>. (#309)
175 + *
176 + * Same contract as render(): a focusable <button>, the original
177 + * element preserved whole inside <noscript>, the swap done by
178 + * facade_script(). Differences that matter:
179 + *
180 + * - the poster comes from the element's own poster attribute, never
181 + * derived — the caller has already refused to build a facade
182 + * without one, because a blank black box is worse than the video.
183 + * - the source URL travels in data-xspeed-video-native, a separate
184 + * attribute from the iframe player URL, so the click handler knows
185 + * to build a <video controls autoplay> rather than an <iframe>.
186 + *
187 + * @param string $original_markup The untouched <video …>…</video> element.
188 + * @param string $src The video file URL to load on click.
189 + * @param string $poster The element's own poster URL.
190 + * @param string $title Accessible label for the play button.
191 + */
192 + public static function render_native( string $original_markup, string $src, string $poster, string $title = '' ): string {
193 + $label = '' !== $title
194 + ? sprintf(
195 + /* translators: %s: video title. */
196 + __( 'Play video: %s', 'xspeed' ),
197 + $title
198 + )
199 + : __( 'Play video', 'xspeed' );
200 +
201 + $style = 'position:relative;display:block;width:100%;padding:0;border:0;cursor:pointer;background:#000;aspect-ratio:16/9;';
202 + $style .= 'background-image:url(' . esc_url( $poster ) . ');background-size:cover;background-position:center;';
203 +
204 + $markup = '<button type="button" class="xspeed-video-facade" data-xspeed-video-native="' . esc_url( $src ) . '"';
205 + $markup .= ' data-xspeed-poster="' . esc_url( $poster ) . '"';
206 + $markup .= ' aria-label="' . esc_attr( $label ) . '" style="' . esc_attr( $style ) . '">';
207 + $markup .= '<span class="xspeed-video-facade__play" aria-hidden="true" style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:68px;height:48px;border-radius:14px;background:rgba(0,0,0,.7);display:flex;align-items:center;justify-content:center;">';
208 + $markup .= '<svg width="24" height="24" viewBox="0 0 24 24" fill="#fff" focusable="false"><path d="M8 5v14l11-7z"/></svg>';
209 + $markup .= '</span>';
210 + $markup .= '</button>';
211 + $markup .= '<noscript>' . $original_markup . '</noscript>';
212 +
213 + return $markup;
214 + }
215 +
216 + /**
174 217 * The one CSS rule the facade can't express as an inline style.
175 218 *
176 219 * Gutenberg wraps an embed in
177 220 * `figure.wp-has-aspect-ratio > div.wp-block-embed__wrapper`, gives the
@@ -202,8 +245,21 @@
202 245 return <<<'JS'
203 246 document.addEventListener('click',function(e){
204 247 var b=e.target.closest&&e.target.closest('.xspeed-video-facade');
205 248 if(!b)return;
249 +var n=b.getAttribute('data-xspeed-video-native');
250 +if(n){
251 +var v=document.createElement('video');
252 +v.setAttribute('src',n);
253 +var p=b.getAttribute('data-xspeed-poster');if(p)v.setAttribute('poster',p);
254 +v.setAttribute('controls','');
255 +v.setAttribute('autoplay','');
256 +v.setAttribute('playsinline','');
257 +v.setAttribute('style','width:100%;aspect-ratio:16/9;background:#000;');
258 +var nt=b.getAttribute('aria-label');if(nt)v.setAttribute('title',nt);
259 +b.parentNode.replaceChild(v,b);
260 +return;
261 +}
206 262 var u=b.getAttribute('data-xspeed-video');if(!u)return;
207 263 var f=document.createElement('iframe');
208 264 f.setAttribute('src',u);
209 265 f.setAttribute('frameborder','0');