[BUGFIX] Use strict comparison for EXT:mediace arrays
[Packages/TYPO3.CMS.git] / typo3 / sysext / mediace / Classes / ContentObject / FlowPlayerContentObject.php
1 <?php
2 namespace TYPO3\CMS\Mediace\ContentObject;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\ArrayUtility;
18
19 /**
20 * Contains FlowPlayer class object.
21 *
22 * @author Stanislas Rolland
23 */
24 class FlowPlayerContentObject extends \TYPO3\CMS\Frontend\ContentObject\AbstractContentObject {
25
26 /**
27 * File extension to mime type
28 *
29 * @var array
30 */
31 public $mimeTypes = array(
32 'aif' => array(
33 'audio' => 'audio/aiff'
34 ),
35 'au' => array(
36 'audio' => 'audio/x-au'
37 ),
38 'avi' => array(
39 'audio' => 'video/x-msvideo'
40 ),
41 'asf' => array(
42 'video' => 'video/x-ms-asf'
43 ),
44 'class' => array(
45 'audio' => 'application/java',
46 'video' => 'application/java'
47 ),
48 'dcr' => array(
49 'video' => 'application/x-director'
50 ),
51 'flac' => array(
52 'audio' => 'audio/flac'
53 ),
54 'flv' => array(
55 'video' => 'video/x-flv'
56 ),
57 'mp3' => array(
58 'audio' => 'audio/mpeg'
59 ),
60 'mp4' => array(
61 'video' => 'video/mp4'
62 ),
63 'oga' => array(
64 'audio' => 'audio/ogg'
65 ),
66 'ogg' => array(
67 'audio' => 'audio/ogg',
68 'video' => 'video/ogg'
69 ),
70 'ogv' => array(
71 'video' => 'video/ogg'
72 ),
73 'swa' => array(
74 'audio' => 'audio/x-m4a'
75 ),
76 'mov' => array(
77 'video' => 'video/quicktime'
78 ),
79 'm4a' => array(
80 'audio' => 'audio/mp4a-latm'
81 ),
82 'm4v' => array(
83 'video' => 'video/x-m4v'
84 ),
85 'qt' => array(
86 'video' => 'video/quicktime'
87 ),
88 'swa' => array(
89 'audio' => 'application/x-director'
90 ),
91 'swf' => array(
92 'audio' => 'application/x-shockwave-flash',
93 'video' => 'application/x-shockwave-flash'
94 ),
95 'wav' => array(
96 'audio' => 'audio/wave'
97 ),
98 'webm' => array(
99 'audio' => 'audio/webm',
100 'video' => 'video/webm'
101 ),
102 'wmv' => array(
103 'audio' => 'audio/x-ms-wmv'
104 )
105 );
106
107 /**
108 * VideoJS options
109 *
110 * @var array
111 */
112 public $videoJsOptions = array(
113 // Use the browser's controls (iPhone)
114 'useBuiltInControls',
115 // Display control bar below video vs. in front of
116 'controlsBelow',
117 // Make controls visible when page loads
118 'controlsAtStart',
119 // Hide controls when not over the video
120 'controlsHiding',
121 // Will be overridden by localStorage volume if available
122 'defaultVolume',
123 // Players and order to use them
124 'playerFallbackOrder'
125 );
126
127 /**
128 * html5 tag attributes
129 *
130 * @var array
131 */
132 public $html5TagAttributes = array(
133 'autoPlay',
134 'controls',
135 'loop',
136 'preload'
137 );
138
139 /**
140 * Flowplayer captions plugin configuration
141 *
142 * @var array
143 */
144 public $flowplayerCaptionsConfig = array(
145 'plugins' => array(
146 // The captions plugin
147 'captions' => array(
148 'url' => 'plugins/flowplayer.captions-3.2.9.swf',
149 // Pointer to a content plugin (see below)
150 'captionTarget' => 'content'
151 ),
152 // Configure a content plugin so that it looks good for showing captions
153 'content' => array(
154 'url' => 'plugins/flowplayer.content-3.2.8.swf',
155 'bottom' => 5,
156 'height' => 40,
157 'backgroundColor' => 'transparent',
158 'backgroundGradient' => 'none',
159 'border' => 0,
160 'textDecoration' => 'outline',
161 'style' => array(
162 'body' => array(
163 'fontSize' => 14,
164 'fontFamily' => 'Arial',
165 'textAlign' => 'center',
166 'color' => '#ffffff'
167 )
168 )
169 )
170 )
171 );
172
173 /**
174 * Flowplayer audio configuration
175 *
176 * @var array
177 */
178 public $flowplayerAudioConfig = array(
179 'provider' => 'audio',
180 'plugins' => array(
181 'audio' => array(
182 'url' => 'plugins/flowplayer.audio-3.2.10.swf'
183 ),
184 'controls' => array(
185 'autoHide' => FALSE,
186 'fullscreen' => FALSE
187 )
188 )
189 );
190
191 /**
192 * Flowplayer configuration for the audio description
193 *
194 * @var array
195 */
196 public $flowplayerAudioDescriptionConfig = array(
197 // The controls plugin
198 'plugins' => array(
199 'controls' => NULL
200 )
201 );
202
203 /**
204 * Rendering the cObject, SWFOBJECT
205 *
206 * @param array $conf Array of TypoScript properties
207 * @return string Output
208 */
209 public function render($conf = array()) {
210 /** @var $pageRenderer \TYPO3\CMS\Core\Page\PageRenderer */
211 $pageRenderer = $GLOBALS['TSFE']->getPageRenderer();
212 $params = ($prefix = '');
213 if ($GLOBALS['TSFE']->baseUrl) {
214 $prefix = $GLOBALS['TSFE']->baseUrl;
215 }
216 if ($GLOBALS['TSFE']->absRefPrefix) {
217 $prefix = $GLOBALS['TSFE']->absRefPrefix;
218 }
219 // Initialize content
220 $replaceElementIdString = str_replace('.', '', uniqid('mmswf', TRUE));
221 $GLOBALS['TSFE']->register['MMSWFID'] = $replaceElementIdString;
222 $layout = isset($conf['layout.']) ? $this->cObj->stdWrap($conf['layout'], $conf['layout.']) : $conf['layout'];
223 $content = str_replace('###ID###', $replaceElementIdString, $layout);
224 $type = isset($conf['type.']) ? $this->cObj->stdWrap($conf['type'], $conf['type.']) : $conf['type'];
225 $typeConf = $conf[$type . '.'];
226 // Add Flowplayer js-file
227 $pageRenderer->addJsFile($this->getPathToLibrary('flowplayer/flowplayer-3.2.12.min.js'));
228 // Add Flowpayer css for exprss install
229 $pageRenderer->addCssFile($this->getPathToLibrary('flowplayer/express-install/express-install.css'));
230 // Add videoJS js-file
231 $pageRenderer->addJsFile($this->getPathToLibrary('videojs/video-js/video.js'));
232 // Add videoJS css-file
233 $pageRenderer->addCssFile($this->getPathToLibrary('videojs/video-js/video-js.css'));
234 // Add extended videoJS control bar
235 $pageRenderer->addJsFile($this->getPathToLibrary('videojs/video-js/controls/control-bar.js'));
236 $pageRenderer->addCssFile($this->getPathToLibrary('videojs/video-js/controls/control-bar.css'));
237 // Build Flash configuration
238 $player = isset($typeConf['player.']) ? $this->cObj->stdWrap($typeConf['player'], $typeConf['player.']) : $typeConf['player'];
239 if (!$player) {
240 $player = $prefix . $this->getPathToLibrary('flowplayer/flowplayer-3.2.16.swf');
241 } elseif (strpos($player, 'EXT:') === 0) {
242 $player = $prefix . $GLOBALS['TSFE']->tmpl->getFileName($player);
243 }
244 $installUrl = isset($conf['installUrl.']) ? $this->cObj->stdWrap($conf['installUrl'], $conf['installUrl.']) : $conf['installUrl'];
245 if (!$installUrl) {
246 $installUrl = $prefix . $this->getPathToLibrary('flowplayer/expressinstall.swf');
247 } elseif (strpos($installUrl, 'EXT:') === 0) {
248 $installUrl = $prefix . $GLOBALS['TSFE']->tmpl->getFileName($installUrl);
249 }
250 $flashVersion = isset($conf['flashVersion.']) ? $this->cObj->stdWrap($conf['flashVersion'], $conf['flashVersion.']) : $conf['flashVersion'];
251 if (!$flashVersion) {
252 $flashVersion = array(9, 115);
253 }
254 $flashConfiguration = array(
255 // Flowplayer component
256 'src' => $player,
257 // Express install url
258 'expressInstall' => $installUrl,
259 // Require at least this Flash version
260 'version' => $flashVersion,
261 // Older versions will see a message
262 'onFail' => '###ONFAIL###'
263 );
264 $flashDownloadUrl = 'http://www.adobe.com/go/getflashplayer';
265 $onFail = 'function() {
266 if (!(flashembed.getVersion()[0] > 0)) {
267 var message = "<p>" + "' . $GLOBALS['TSFE']->sL('LLL:EXT:cms/locallang_ttc.xlf:media.needFlashPlugin') . '" + "</p>" + "<p>" + "<a href=\\"' . $flashDownloadUrl . '\\">' . $GLOBALS['TSFE']->sL('LLL:EXT:cms/locallang_ttc.xlf:media.downloadFlash') . '</a>" + "</p>";
268 document.getElementById("' . $replaceElementIdString . '_flash_install_info").innerHTML = "<div class=\\"message\\">" + message + "</div>";
269 }
270 }';
271 $flashConfiguration = json_encode($flashConfiguration);
272 $flashConfiguration = str_replace('"###ONFAIL###"', $onFail, $flashConfiguration);
273 $filename = isset($conf['file.']) ? $this->cObj->stdWrap($conf['file'], $conf['file.']) : $conf['file'];
274 if ($filename) {
275 if (strpos($filename, '://') !== FALSE) {
276 $conf['flashvars.']['url'] = $filename;
277 } else {
278 if ($prefix) {
279 $conf['flashvars.']['url'] = $prefix . $filename;
280 } else {
281 $conf['flashvars.']['url'] = str_repeat('../', substr_count($player, '/')) . $filename;
282 }
283 }
284 }
285 if (is_array($conf['sources'])) {
286 foreach ($conf['sources'] as $key => $source) {
287 if (strpos($source, '://') === FALSE) {
288 $conf['sources'][$key] = $prefix . $source;
289 }
290 }
291 }
292 if (is_array($conf['audioSources'])) {
293 foreach ($conf['audioSources'] as $key => $source) {
294 if (strpos($source, '://') === FALSE) {
295 $conf['audioSources'][$key] = $prefix . $source;
296 }
297 }
298 }
299 if (isset($conf['audioFallback']) && strpos($conf['audioFallback'], '://') === FALSE) {
300 $conf['audioFallback'] = $prefix . $conf['audioFallback'];
301 }
302 if (isset($conf['caption']) && strpos($conf['caption'], '://') === FALSE) {
303 $conf['caption'] = $prefix . $conf['caption'];
304 }
305 // Write calculated values in conf for the hook
306 $conf['player'] = $player ?: $filename;
307 $conf['installUrl'] = $installUrl;
308 $conf['filename'] = $conf['flashvars.']['url'];
309 $conf['prefix'] = $prefix;
310 // merge with default parameters
311 $conf['flashvars.'] = array_merge((array)$typeConf['default.']['flashvars.'], (array)$conf['flashvars.']);
312 $conf['params.'] = array_merge((array)$typeConf['default.']['params.'], (array)$conf['params.']);
313 $conf['attributes.'] = array_merge((array)$typeConf['default.']['attributes.'], (array)$conf['attributes.']);
314 $conf['embedParams'] = 'flashvars, params, attributes';
315 // Hook for manipulating the conf array, it's needed for some players like flowplayer
316 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/hooks/class.tx_cms_mediaitems.php']['swfParamTransform'])) {
317 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/hooks/class.tx_cms_mediaitems.php']['swfParamTransform'] as $classRef) {
318 \TYPO3\CMS\Core\Utility\GeneralUtility::callUserFunction($classRef, $conf, $this);
319 }
320 }
321 // Flowplayer config
322 $flowplayerVideoConfig = array();
323 $flowplayerAudioConfig = array();
324 if (is_array($conf['flashvars.']) && is_array($typeConf['mapping.']['flashvars.'])) {
325 ArrayUtility::remapArrayKeys($conf['flashvars.'], $typeConf['mapping.']['flashvars.']);
326 } else {
327 $conf['flashvars.'] = array();
328 }
329 $conf['videoflashvars'] = $conf['flashvars.'];
330 $conf['audioflashvars'] = $conf['flashvars.'];
331 $conf['audioflashvars']['url'] = $conf['audioFallback'];
332 // Render video sources
333 $videoSources = '';
334 if (is_array($conf['sources'])) {
335 foreach ($conf['sources'] as $source) {
336 $fileinfo = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($source);
337 $mimeType = $this->mimeTypes[$fileinfo['fileext']]['video'];
338 $videoSources .= '<source src="' . $source . '"' . ($mimeType ? ' type="' . $mimeType . '"' : '') . ' />' . LF;
339 }
340 }
341 // Render audio sources
342 $audioSources = '';
343 if (is_array($conf['audioSources'])) {
344 foreach ($conf['audioSources'] as $source) {
345 $fileinfo = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($source);
346 $mimeType = $this->mimeTypes[$fileinfo['fileext']]['audio'];
347 $audioSources .= '<source src="' . $source . '"' . ($mimeType ? ' type="' . $mimeType . '"' : '') . ' />' . LF;
348 }
349 }
350 // Configure captions
351 if ($conf['type'] === 'video' && isset($conf['caption'])) {
352 // Assemble captions track tag
353 $videoCaptions = '<track id="' . $replaceElementIdString . '_captions_track" kind="captions" src="' . $conf['caption'] . '" default>' . LF;
354 // Add videoJS extension for captions
355 $pageRenderer->addJsFile($this->getPathToLibrary('videojs/video-js/controls/captions.js'));
356 // Flowplayer captions
357 $conf['videoflashvars']['captionUrl'] = $conf['caption'];
358 // Flowplayer captions plugin configuration
359 $flowplayerVideoConfig = array_merge_recursive($flowplayerVideoConfig, $this->flowplayerCaptionsConfig);
360 }
361 // Configure flowplayer audio fallback
362 if (isset($conf['audioFallback'])) {
363 $flowplayerAudioConfig = array_merge_recursive($flowplayerAudioConfig, $this->flowplayerAudioConfig);
364 }
365 // Configure audio description
366 if ($conf['type'] == 'video') {
367 if (is_array($conf['audioSources']) && count($conf['audioSources'])) {
368 // Add videoJS audio description toggle
369 $pageRenderer->addJsFile($this->getPathToLibrary('videojs/video-js/controls/audio-description.js'));
370 }
371 if (isset($conf['audioFallback'])) {
372 // Audio description flowplayer config (remove controls)
373 $flowplayerAudioConfig = array_merge_recursive($flowplayerAudioConfig, $this->flowplayerAudioDescriptionConfig);
374 }
375 }
376 // Assemble Flowplayer configuration
377 if (count($conf['videoflashvars'])) {
378 $flowplayerVideoConfig = array_merge_recursive($flowplayerVideoConfig, array('clip' => $conf['videoflashvars']));
379 }
380 $flowplayerVideoJsonConfig = str_replace(array('"true"', '"false"'), array('true', 'false'), json_encode($flowplayerVideoConfig));
381 if (count($conf['audioflashvars'])) {
382 $flowplayerAudioConfig = array_merge_recursive($flowplayerAudioConfig, array('clip' => $conf['audioflashvars']));
383 }
384 $flowplayerAudioJsonConfig = str_replace(array('"true"', '"false"'), array('true', 'false'), json_encode($flowplayerAudioConfig));
385 // Assemble param tags (required?)
386 if (is_array($conf['params.']) && is_array($typeConf['mapping.']['params.'])) {
387 ArrayUtility::remapArrayKeys($conf['params.'], $typeConf['mapping.']['params.']);
388 }
389 $videoFlashParams = '';
390 if (is_array($conf['params.'])) {
391 foreach ($conf['params.'] as $name => $value) {
392 $videoFlashParams .= '<param name="' . $name . '" value="' . $value . '" />' . LF;
393 }
394 }
395 $audioFlashParams = $videoFlashParams;
396 // Required param tags
397 $videoFlashParams .= '<param name="movie" value="' . $player . '" />' . LF;
398 $videoFlashParams .= '<param name="flashvars" value=\'config=' . $flowplayerVideoJsonConfig . '\' />' . LF;
399 $audioFlashParams .= '<param name="movie" value="' . $player . '" />' . LF;
400 $audioFlashParams .= '<param name="flashvars" value=\'config=' . $flowplayerAudioJsonConfig . '\' />' . LF;
401 // Assemble audio/video tag attributes
402 $attributes = '';
403 if (is_array($conf['attributes.']) && is_array($typeConf['attributes.']['params.'])) {
404 ArrayUtility::remapArrayKeys($conf['attributes.'], $typeConf['attributes.']['params.']);
405 }
406 foreach ($this->html5TagAttributes as $attribute) {
407 if ($conf['attributes.'][$attribute] === 'true' || $conf['attributes.'][$attribute] === strtolower($attribute) || $conf['attributes.'][$attribute] === $attribute) {
408 $attributes .= strtolower($attribute) . '="' . strtolower($attribute) . '" ';
409 }
410 }
411 // Media dimensions
412 $width = isset($conf['width.']) ? $this->cObj->stdWrap($conf['width'], $conf['width.']) : $conf['width'];
413 if (!$width) {
414 $width = $conf[$type . '.']['defaultWidth'];
415 }
416 $height = isset($conf['height.']) ? $this->cObj->stdWrap($conf['height'], $conf['height.']) : $conf['height'];
417 if (!$height) {
418 $height = $conf[$type . '.']['defaultHeight'];
419 }
420 // Alternate content
421 $alternativeContent = isset($conf['alternativeContent.']) ? $this->cObj->stdWrap($conf['alternativeContent'], $conf['alternativeContent.']) : $conf['alternativeContent'];
422 // Render video
423 if ($conf['type'] === 'video') {
424 if ($conf['preferFlashOverHtml5']) {
425 // Flash with video tag fallback
426 $conf['params.']['playerFallbackOrder'] = array('flash', 'html5');
427 $flashDivContent = $videoFlashParams . LF . '<video id="' . $replaceElementIdString . '_video_js" class="video-js" ' . $attributes . 'controls="controls" mediagroup="' . $replaceElementIdString . '" width="' . $width . '" height="' . $height . '">' . LF . $videoSources . $videoCaptions . $alternativeContent . LF . '</video>' . LF;
428 $divContent = '
429 <div id="' . $replaceElementIdString . '_flash_install_info" class="flash-install-info"></div>' . LF . '<noscript>' . LF . '<object id="' . $replaceElementIdString . '_vjs_flash" type="application/x-shockwave-flash" data="' . $player . '" width="' . $width . '" height="' . $height . '">' . LF . $flashDivContent . '</object>' . LF . '</noscript>' . LF;
430 $content = str_replace('###SWFOBJECT###', '<div id="' . $replaceElementIdString . '_video" class="flashcontainer" style="width:' . $width . 'px; height:' . $height . 'px;">' . LF . $divContent . '</div>', $content);
431 } else {
432 // Video tag with Flash fallback
433 $conf['params.']['playerFallbackOrder'] = array('html5', 'flash');
434 $videoTagContent = $videoSources . $videoCaptions;
435 if (isset($conf['videoflashvars']['url'])) {
436 $videoTagContent .= '
437 <noscript>' . LF . '<object class="vjs-flash-fallback" id="' . $replaceElementIdString . '_vjs_flash_fallback" type="application/x-shockwave-flash" data="' . $player . '" width="' . $width . '" height="' . $height . '">' . LF . $videoFlashParams . LF . $alternativeContent . LF . '</object>' . LF . '</noscript>';
438 }
439 $divContent = '
440 <div id="' . $replaceElementIdString . '_flash_install_info" class="flash-install-info"></div>' . LF . '<video id="' . $replaceElementIdString . '_video_js" class="video-js" ' . $attributes . 'controls="controls" mediagroup="' . $replaceElementIdString . '" width="' . $width . '" height="' . $height . '">' . LF . $videoTagContent . '</video>';
441 $content = str_replace('###SWFOBJECT###', '<div id="' . $replaceElementIdString . '_video" class="video-js-box" style="width:' . $width . 'px; height:' . $height . 'px;">' . LF . $divContent . '</div>', $content);
442 }
443 }
444 // Render audio
445 if ($conf['type'] === 'audio' || $audioSources || isset($conf['audioFallback'])) {
446 if ($conf['preferFlashOverHtml5']) {
447 // Flash with audio tag fallback
448 $flashDivContent = $audioFlashParams . LF . '<audio id="' . $replaceElementIdString . '_audio_element"' . $attributes . ($conf['type'] === 'video' ? ' mediagroup="' . $replaceElementIdString . 'style="position:absolute;left:-10000px;"' : ' controls="controls"') . ' style="width:' . $width . 'px; height:' . $height . 'px;">' . LF . $audioSources . $alternativeContent . LF . '</audio>' . LF;
449 $divContent = ($conf['type'] === 'video' ? '' : '<div id="' . $replaceElementIdString . '_flash_install_info" class="flash-install-info"></div>' . LF) . '<noscript>' . LF . '<object id="' . $replaceElementIdString . '_audio_flash" type="application/x-shockwave-flash" data="' . $player . '" width="' . ($conf['type'] === 'video' ? 0 : $width) . '" height="' . ($conf['type'] === 'video' ? 0 : $height) . '">' . LF . $flashDivContent . '</object>' . LF . '</noscript>' . LF;
450 $audioContent = '<div id="' . $replaceElementIdString . '_audio_box" class="audio-flash-container" style="width:' . ($conf['type'] === 'video' ? 0 : $width) . 'px; height:' . ($conf['type'] === 'video' ? 0 : $height) . 'px;">' . LF . $divContent . '</div>';
451 } else {
452 // Audio tag with Flash fallback
453 $audioTagContent = $audioSources;
454 if (isset($conf['audioflashvars']['url'])) {
455 $audioTagContent .= '
456 <noscript>' . LF . '<object class="audio-flash-fallback" id="' . $replaceElementIdString . '_audio_flash" type="application/x-shockwave-flash" data="' . $player . '" width="' . $width . '" height="' . $height . '">' . LF . $audioFlashParams . LF . $alternativeContent . LF . '</object>' . LF . '</noscript>';
457 }
458 $divContent = ($conf['type'] === 'video' ? '' : '<div id="' . $replaceElementIdString . '_flash_install_info" class="flash-install-info"></div>' . LF) . '<audio id="' . $replaceElementIdString . '_audio_element" class="audio-element"' . $attributes . ($conf['type'] === 'video' ? ' mediagroup="' . $replaceElementIdString . '" style="position:absolute;left:-10000px;"' : ' controls="controls"') . '>' . LF . $audioTagContent . '</audio>' . LF . $audioSourcesEmbeddingJsScript;
459 $audioContent = '<div id="' . $replaceElementIdString . '_audio_box" class="audio-box" style="width:' . ($conf['type'] === 'video' ? 0 : $width) . 'px; height:' . ($conf['type'] === 'video' ? 0 : $height) . 'px;">' . LF . $divContent . '</div>';
460 }
461 if ($conf['type'] === 'audio') {
462 $content = str_replace('###SWFOBJECT###', $audioContent, $content);
463 } else {
464 $content .= LF . $audioContent;
465 }
466 }
467 // Assemble inline JS code
468 $videoJsSetup = '';
469 $flowplayerHandlers = '';
470 if ($conf['type'] === 'video') {
471 // Assemble videoJS options
472 $videoJsOptions = array();
473 foreach ($this->videoJsOptions as $videoJsOption) {
474 if (isset($conf['params.'][$videoJsOption])) {
475 $videoJsOptions[$videoJsOption] = $conf['params.'][$videoJsOption];
476 }
477 }
478 $videoJsOptions = count($videoJsOptions) ? json_encode($videoJsOptions) : '{}';
479 // videoJS setup and videoJS listeners for audio description synchronisation
480 if ($audioSources || isset($conf['audioFallback'])) {
481 $videoJsSetup = '
482 var ' . $replaceElementIdString . '_video = VideoJS.setup("' . $replaceElementIdString . '_video_js", ' . $videoJsOptions . ');
483 var ' . $replaceElementIdString . '_video_element = document.getElementById("' . $replaceElementIdString . '_video_js");
484 var ' . $replaceElementIdString . '_audio_element = document.getElementById("' . $replaceElementIdString . '_audio_element");
485 if (!!' . $replaceElementIdString . '_video_element && !!' . $replaceElementIdString . '_audio_element) {
486 ' . $replaceElementIdString . '_audio_element.muted = true;
487 VideoJS.addListener(' . $replaceElementIdString . '_video_element, "pause", function () { document.getElementById("' . $replaceElementIdString . '_audio_element").pause(); });
488 VideoJS.addListener(' . $replaceElementIdString . '_video_element, "play", function () { try {document.getElementById("' . $replaceElementIdString . '_audio_element").currentTime = document.getElementById("' . $replaceElementIdString . '_video_js").currentTime} catch(e) {}; document.getElementById("' . $replaceElementIdString . '_audio_element").play(); });
489 VideoJS.addListener(' . $replaceElementIdString . '_video_element, "seeked", function () { document.getElementById("' . $replaceElementIdString . '_audio_element").currentTime = document.getElementById("' . $replaceElementIdString . '_video_js").currentTime; });
490 VideoJS.addListener(' . $replaceElementIdString . '_video_element, "volumechange", function () { document.getElementById("' . $replaceElementIdString . '_audio_element").volume = document.getElementById("' . $replaceElementIdString . '_video_js").volume; });
491 }';
492 } else {
493 $videoJsSetup = '
494 var ' . $replaceElementIdString . '_video = VideoJS.setup("' . $replaceElementIdString . '_video_js", ' . $videoJsOptions . ');
495 ';
496 }
497 // Prefer Flash or fallback to Flash
498 $videoSourcesEmbedding = '';
499 // If we have a video file for Flash
500 if (isset($conf['filename'])) {
501 // If we prefer Flash
502 if ($conf['preferFlashOverHtml5']) {
503 $videoTagAssembly = '';
504 // Create "source" elements
505 if (is_array($conf['sources']) && count($conf['sources'])) {
506 foreach ($conf['sources'] as $source) {
507 $fileinfo = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($source);
508 $mimeType = $this->mimeTypes[$fileinfo['fileext']]['video'];
509 $videoTagAssembly .= '
510 ' . $replaceElementIdString . '_video_js.appendChild($f.extend(document.createElement("source"), {
511 src: "' . $source . '",
512 type: "' . $mimeType . '"
513 }));';
514 }
515 // Create "track" elements
516 if (isset($conf['caption'])) {
517 // Assemble captions track tag
518 // It will take a while before the captions are loaded and parsed...
519 $videoTagAssembly .= '
520 var track = document.createElement("track");
521 track.setAttribute("src", "' . $conf['caption'] . '");
522 track.setAttribute("id", "' . $replaceElementIdString . '_captions_track");
523 track.setAttribute("kind", "captions");
524 track.setAttribute("default", "default");
525 ' . $replaceElementIdString . '_video_js.appendChild(track);';
526 }
527 $videoTagAssembly .= '
528 $f.extend(' . $replaceElementIdString . '_video_js, {
529 id: "' . $replaceElementIdString . '_video_js",
530 className: "video-js",
531 controls: "controls",
532 mediagroup: "' . $replaceElementIdString . '",
533 preload: "none",
534 width: "' . $width . '",
535 height: "' . $height . '"
536 });
537 ' . $replaceElementIdString . '_video.appendChild(' . $replaceElementIdString . '_video_js);
538 ' . $replaceElementIdString . '_video.className = "video-js-box";';
539 $videoTagAssembly .= $videoJsSetup;
540 }
541 $videoSourcesEmbedding = '
542 var ' . $replaceElementIdString . '_video = document.getElementById("' . $replaceElementIdString . '_video");
543 var ' . $replaceElementIdString . '_video_js = document.createElement("video");
544 if (flashembed.getVersion()[0] > 0) {
545 // Flash is available
546 var videoPlayer = flowplayer("' . $replaceElementIdString . '_video", ' . $flashConfiguration . ', ' . $flowplayerVideoJsonConfig . ').load();
547 videoPlayer.onBeforeUnload(function () { return false; });
548 } else if (!!' . $replaceElementIdString . '_video_js.canPlayType) {
549 // Flash is not available: fallback to videoJS if video tag is supported
550 ' . $videoTagAssembly . '
551 } else {
552 // Neither Flash nor video is available: offer to install Flash
553 flashembed("' . $replaceElementIdString . '_video", ' . $flashConfiguration . ');
554 }';
555 } elseif (is_array($conf['sources'])) {
556 // HTML5 is the preferred rendering method
557 // Test whether the browser supports any of types of the provided sources
558 $supported = array();
559 foreach ($conf['sources'] as $source) {
560 $fileinfo = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($source);
561 $mimeType = $this->mimeTypes[$fileinfo['fileext']]['video'];
562 $supported[] = $replaceElementIdString . '_videoTag.canPlayType("' . $mimeType . '") != ""';
563 }
564 // Testing whether the browser supports the video tag with any of the provided source types
565 // If no support, embed flowplayer
566 $videoSourcesEmbedding = '
567 var ' . $replaceElementIdString . '_videoTag = document.createElement(\'video\');
568 var ' . $replaceElementIdString . '_video_box = document.getElementById("' . $replaceElementIdString . '_video");
569 if (' . $replaceElementIdString . '_video_box) {
570 if (!' . $replaceElementIdString . '_videoTag || !' . $replaceElementIdString . '_videoTag.canPlayType || !(' . (count($supported) ? implode(' || ', $supported) : 'false') . ')) {
571 // Avoid showing an empty video element
572 if (document.getElementById("' . $replaceElementIdString . '_video_js")) {
573 document.getElementById("' . $replaceElementIdString . '_video_js").style.display = "none";
574 }
575 if (flashembed.getVersion()[0] > 0) {
576 // Flash is available
577 var videoPlayer = flowplayer("' . $replaceElementIdString . '_video", ' . $flashConfiguration . ', ' . $flowplayerVideoJsonConfig . ').load();
578 videoPlayer.onBeforeUnload(function () { return false; });
579 } else {
580 // Neither Flash nor video is available: offer to install Flash
581 flashembed("' . $replaceElementIdString . '_video", ' . $flashConfiguration . ');
582 }
583 } else {' . $videoJsSetup . '
584 }
585 }';
586 }
587 }
588 }
589 // Audio fallback to Flash
590 $audioSourcesEmbedding = '';
591 // If we have an audio file for Flash
592 if (isset($conf['audioFallback'])) {
593 // If we prefer Flash in
594 if ($conf['preferFlashOverHtml5']) {
595 $audioTagAssembly = '';
596 // Create "source" elements
597 if (is_array($conf['audioSources']) && count($conf['audioSources'])) {
598 foreach ($conf['audioSources'] as $source) {
599 $fileinfo = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($source);
600 $mimeType = $this->mimeTypes[$fileinfo['fileext']]['audio'];
601 $audioTagAssembly .= '
602 ' . $replaceElementIdString . '_audio_element.appendChild($f.extend(document.createElement("source"), {
603 src: "' . $source . '",
604 type: "' . $mimeType . '"
605 }));';
606 }
607 $audioTagAssembly .= '
608 $f.extend(' . $replaceElementIdString . '_audio_element, {
609 id: "' . $replaceElementIdString . '_audio_element",
610 className: "audio-element",
611 controls: "' . ($conf['type'] === 'video' ? '' : 'controls') . '",
612 mediagroup: "' . $replaceElementIdString . '",
613 preload: "none",
614 width: "' . ($conf['type'] === 'video' ? 0 : $width) . 'px",
615 height: "' . ($conf['type'] === 'video' ? 0 : $height) . 'px"
616 });
617 ' . $replaceElementIdString . '_audio_box.appendChild(' . $replaceElementIdString . '_audio_element);
618 ' . $replaceElementIdString . '_audio_box.className = "audio-box";';
619 }
620 $audioSourcesEmbedding = '
621 var ' . $replaceElementIdString . '_audio_box = document.getElementById("' . $replaceElementIdString . '_audio_box");
622 var ' . $replaceElementIdString . '_audio_element = document.createElement("audio");
623 if (flashembed.getVersion()[0] > 0) {
624 // Flash is available
625 var audioPlayer = flowplayer("' . $replaceElementIdString . '_audio_box", ' . $flashConfiguration . ', ' . $flowplayerAudioJsonConfig . ').load();
626 audioPlayer.onBeforeUnload(function () { return false; });
627 ' . ($conf['type'] === 'video' ? 'audioPlayer.mute();' : '') . '
628 } else if (!!' . $replaceElementIdString . '_audio_element.canPlayType) {
629 // Flash is not available: fallback to audio element if audio tag is supported
630 ' . $audioTagAssembly . '
631 } else {
632 // Neither Flash nor audio is available: offer to install Flash if this is not an audio description of a video
633 ' . ($conf['type'] === 'video' ? '' : 'flashembed("' . $replaceElementIdString . '_audio_box", ' . $flashConfiguration . ');') . '
634 }';
635 } elseif (is_array($conf['audioSources'])) {
636 // HTML5 is the preferred rendering method
637 // Test whether the browser supports any of types of the provided sources
638 $supported = array();
639 foreach ($conf['audioSources'] as $source) {
640 $fileinfo = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($source);
641 $mimeType = $this->mimeTypes[$fileinfo['fileext']]['audio'];
642 $supported[] = $replaceElementIdString . '_audioTag.canPlayType("' . $mimeType . '") != ""';
643 }
644 // Testing whether the browser supports the audio tag with any of the provided source types
645 // If no support, embed flowplayer
646 $audioSourcesEmbedding = '
647 var ' . $replaceElementIdString . '_audioTag = document.createElement(\'audio\');
648 var ' . $replaceElementIdString . '_audio_box = document.getElementById("' . $replaceElementIdString . '_audio_box");
649 if (' . $replaceElementIdString . '_audio_box) {
650 if (!' . $replaceElementIdString . '_audioTag || !' . $replaceElementIdString . '_audioTag.canPlayType || !(' . (count($supported) ? implode(' || ', $supported) : 'false') . ')) {
651 // Avoid showing an empty audio element
652 if (document.getElementById("' . $replaceElementIdString . '_audio_element")) {
653 document.getElementById("' . $replaceElementIdString . '_audio_element").style.display = "none";
654 }
655 if (flashembed.getVersion()[0] > 0) {
656 var audioPlayer = flowplayer("' . $replaceElementIdString . '_audio_box", ' . $flashConfiguration . ', ' . $flowplayerAudioJsonConfig . ').load();
657 audioPlayer.onBeforeUnload(function () { return false; });
658 ' . ($conf['type'] === 'video' ? 'audioPlayer.mute()' : '') . '
659 } else {
660 // Neither Flash nor audio is available: offer to install Flash if this is not an audio description of a video
661 ' . ($conf['type'] === 'video' ? '' : 'flashembed("' . $replaceElementIdString . '_audio_box", ' . $flashConfiguration . ');') . '
662 }
663 }
664 }';
665 }
666 // Flowplayer eventHandlers for audio description synchronisation
667 $flowplayerHandlers = '';
668 if ($conf['type'] === 'video') {
669 $flowplayerHandlers = '
670 if (flashembed.getVersion()[0] > 0) {
671 // Flash is available
672 var videoPlayer = flowplayer("' . $replaceElementIdString . '_video");
673 if (videoPlayer) {
674 // Control audio description through video control bar
675 videoPlayer.onVolume(function (volume) { flowplayer("' . $replaceElementIdString . '_audio_box").setVolume(volume); });
676 videoPlayer.onMute(function () { flowplayer("' . $replaceElementIdString . '_audio_box").mute(); });
677 videoPlayer.onUnmute(function () { flowplayer("' . $replaceElementIdString . '_audio_box").unmute(); });
678 videoPlayer.onPause(function () { flowplayer("' . $replaceElementIdString . '_audio_box").pause(); });
679 videoPlayer.onResume(function () { flowplayer("' . $replaceElementIdString . '_audio_box").resume(); });
680 videoPlayer.onStart(function () { flowplayer("' . $replaceElementIdString . '_audio_box").play(); });
681 videoPlayer.onStop(function () { flowplayer("' . $replaceElementIdString . '_audio_box").stop(); });
682 videoPlayer.onSeek(function (clip, seconds) { flowplayer("' . $replaceElementIdString . '_audio_box").seek(seconds); });
683 // Mute audio description on start
684 flowplayer("' . $replaceElementIdString . '_audio_box").onStart(function () { this.mute()});
685 // Audio description toggle
686 var videoContainer = document.getElementById("' . $replaceElementIdString . '_video");
687 var buttonContainer = document.createElement("div");
688 $f.extend(buttonContainer, {
689 id: "' . $replaceElementIdString . '_audio_description_toggle",
690 className: "vjs-audio-description-control"
691 });
692 var button = document.createElement("div");
693 buttonContainer.appendChild(button);
694 buttonContainer.style.position = "relative";
695 buttonContainer.style.left = (parseInt(' . $width . ', 10)-27) + "px";
696 videoContainer.parentNode.insertBefore(buttonContainer, videoContainer.nextSibling);
697 VideoJS.addListener(buttonContainer, "click", function () {
698 var buttonContainer = document.getElementById("' . $replaceElementIdString . '_audio_description_toggle");
699 var state = buttonContainer.getAttribute("data-state");
700 if (state == "enabled") {
701 buttonContainer.setAttribute("data-state", "disabled");
702 flowplayer("' . $replaceElementIdString . '_audio_box").mute();
703 } else {
704 buttonContainer.setAttribute("data-state", "enabled");
705 flowplayer("' . $replaceElementIdString . '_audio_box").unmute();
706 }
707 });
708 }
709 }';
710 }
711 }
712 // Wrap up inline JS code
713 $jsInlineCode = $audioSourcesEmbedding . $videoSourcesEmbedding . $flowplayerHandlers;
714 if ($jsInlineCode) {
715 $jsInlineCode = 'VideoJS.DOMReady(function(){' . $jsInlineCode . LF . '});';
716 }
717 $pageRenderer->addJsInlineCode($replaceElementIdString, $jsInlineCode);
718 if (isset($conf['stdWrap.'])) {
719 $content = $this->cObj->stdWrap($content, $conf['stdWrap.']);
720 }
721 return $content;
722 }
723
724 /**
725 * resolves the path to the extensions' Contrib directory
726 *
727 * @param string $fileAndFolderName the file to be located
728 * @return string
729 */
730 protected function getPathToLibrary($fileAndFolderName) {
731 return $GLOBALS['TSFE']->tmpl->getFileName('EXT:mediace/Resources/Contrib/' . $fileAndFolderName);
732 }
733 }