diff --git a/lib/Cake/Test/Case/View/Helper/HtmlHelperTest.php b/lib/Cake/Test/Case/View/Helper/HtmlHelperTest.php index ecbb75f66..8a04a1ab0 100644 --- a/lib/Cake/Test/Case/View/Helper/HtmlHelperTest.php +++ b/lib/Cake/Test/Case/View/Helper/HtmlHelperTest.php @@ -1491,6 +1491,44 @@ class HtmlHelperTest extends CakeTestCase { $this->assertTags($result, array('p' => array('class' => 'class-name'), '<text>', '/p')); } +/** + * testVideo method + * + * @return void + */ + public function testVideo() { + $result = $this->Html->video('video.webm'); + $expected = array('video' => array('src' => 'files/video.webm')); + $this->assertTags($result, $expected); + + $result = $this->Html->video('video.webm', array( + 'text' => 'Your browser does not support the HTML5 Video element.' + )); + $expected = array('video' => array('src' => 'files/video.webm'), 'Your browser does not support the HTML5 Video element.', '/video'); + $this->assertTags($result, $expected); + + $result = $this->Html->video('video.webm', array('autoload', 'muted' => 'muted')); + $expected = array('video' => array( + 'src' => 'files/video.webm', + 'autoload' => 'autoload', + 'muted' => 'muted' + )); + $this->assertTags($result, $expected); + + $result = $this->Html->video( + array('video.webm', array('src' => 'video.ogv', 'type' => "video/ogg; codecs='theora, vorbis'")), + array('pathPrefix' => 'videos/', 'poster' => 'poster.jpg', 'text' => 'Your browser does not support the HTML5 Video element.') + ); + $expected = array( + 'video' => array('poster' => IMAGES_URL . 'poster.jpg'), + array('source' => array('src' => 'videos/video.webm', 'type' => 'video/webm')), + array('source' => array('src' => 'videos/video.ogv', 'type' => 'video/ogg; codecs='theora, vorbis'')), + 'Your browser does not support the HTML5 Video element.', + '/video' + ); + $this->assertTags($result, $expected); + } + /** * testCrumbList method * diff --git a/lib/Cake/View/Helper.php b/lib/Cake/View/Helper.php index f1fb11f04..f198f82e5 100644 --- a/lib/Cake/View/Helper.php +++ b/lib/Cake/View/Helper.php @@ -259,6 +259,34 @@ class Helper extends Object { return $webPath . $asset[1]; } +/** + * Generate url for given asset file. Depending on options passed provides full url with domain name. + * Also calls Helper::assetTimestamp() to add timestamp to local files + * + * @param string|array Path string or url array + * @param array $options Options array. Possible keys: + * `fullBase` Return full url with domain name + * `pathPrefix` Path prefix for relative urls + * @return string Generated url + */ + public function assetUrl($path, array $options) { + if (is_array($path)) { + $path = $this->url($path); + } elseif (strpos($path, '://') === false) { + if (!empty($options['pathPrefix']) && $path[0] !== '/') { + $path = $options['pathPrefix'] . $path; + } + $path = $this->assetTimestamp($this->webroot($path)); + } + + if (!empty($options['fullBase'])) { + $path = $this->url('/', true) . $path; + unset($options['fullBase']); + } + + return $path; + } + /** * Adds a timestamp to a file based resource based on the value of `Asset.timestamp` in * Configure. If Asset.timestamp is true and debug > 0, or Asset.timestamp == 'force' @@ -446,10 +474,10 @@ class Helper extends Object { // 0.name, 0.created.month style inputs. Excludes inputs with the modelScope in them. if ( - $count >= 2 && - is_numeric($parts[0]) && - !is_numeric($parts[1]) && - $this->_modelScope && + $count >= 2 && + is_numeric($parts[0]) && + !is_numeric($parts[1]) && + $this->_modelScope && strpos($entity, $this->_modelScope) === false ) { $entity = $this->_modelScope . '.' . $entity; diff --git a/lib/Cake/View/Helper/HtmlHelper.php b/lib/Cake/View/Helper/HtmlHelper.php index be6706223..720b53f47 100644 --- a/lib/Cake/View/Helper/HtmlHelper.php +++ b/lib/Cake/View/Helper/HtmlHelper.php @@ -18,6 +18,7 @@ */ App::uses('AppHelper', 'View/Helper'); +App::uses('CakeResponse', 'Network'); /** * Html Helper class for easy use of HTML widgets. @@ -101,7 +102,8 @@ class HtmlHelper extends AppHelper { */ protected $_minimizedAttributes = array( 'compact', 'checked', 'declare', 'readonly', 'disabled', 'selected', - 'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize' + 'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize', + 'autoplay', 'preload', 'controls', 'loop', 'muted' ); /** @@ -566,13 +568,13 @@ class HtmlHelper extends AppHelper { * ### Options * * - `safe` (boolean) Whether or not the $script should be wrapped in - * - `inline` (boolean) Whether or not the $script should be added to + * - `inline` (boolean) Whether or not the $script should be added to * `$scripts_for_layout` / `script` block, or output inline. (Deprecated, use `block` instead) * - `block` Which block you want this script block appended to. * Defaults to `script`. * * @param string $script The script to wrap - * @param array $options The options to use. Options not listed above will be + * @param array $options The options to use. Options not listed above will be * treated as HTML attributes. * @return mixed string or null depending on the value of `$options['block']` * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/html.html#HtmlHelper::scriptBlock @@ -765,8 +767,7 @@ class HtmlHelper extends AppHelper { * * - `url` If provided an image link will be generated and the link will point at * `$options['url']`. - * - `fullBase` If provided the src attribute will get a full addres (non-relative url) for - * the image file. + * - `fullBase` If true the src attribute will get a full address for the image file. * * @param string $path Path to the image file, relative to the app/webroot/img/ directory. * @param array $options Array of HTML attributes. See above for special options. @@ -774,19 +775,8 @@ class HtmlHelper extends AppHelper { * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/html.html#HtmlHelper::image */ public function image($path, $options = array()) { - if (is_array($path)) { - $path = $this->url($path); - } elseif (strpos($path, '://') === false) { - if ($path[0] !== '/') { - $path = IMAGES_URL . $path; - } - $path = $this->assetTimestamp($this->webroot($path)); - } - - if (!empty($options['fullBase'])) { - $path = $this->url('/', true) . $path; - unset($options['fullBase']); - } + $path = $this->assetUrl($path, $options + array('pathPrefix' => IMAGES_URL)); + $options = array_diff_key($options, array('fullBase' => '', 'pathPrefix' => '')); if (!isset($options['alt'])) { $options['alt'] = ''; @@ -977,6 +967,86 @@ class HtmlHelper extends AppHelper { return sprintf($this->_tags[$tag], $this->_parseAttributes($options, null, ' ', ''), $text); } +/** + * Returns a VIDEO element + * + * ### Usage + * + * Using single video file: + * + * `echo $this->Html->video('video.mp4', array('fullBase' => true, 'text' => 'Fallback text'));` + * + * Outputs: + * + * `` + * + * Using multiple video files: + * + * {{{ + * echo $this->Html->video( + * array('video.mp4', array('src' => 'video.ogg', 'type' => "video/ogg; codecs='theora, vorbis'")), + * array('autoplay') + * ); + * }}} + * + * Outputs: + * + * {{{ + * + * }}} + * + * ### Options + * + * - `text` Text to include inside the video tag + * - `pathPrefix` Path prefix to use for relative urls, defaults to 'files/' + * - `fullBase` If provided the src attribute will get a full address including domain name + * + * @param string|array $path Path to the video file, relative to the webroot/{$options['pathPrefix']} directory. + * Or an array where each item itself can be a path string or an associate array containing keys `src` and `type` + * @param array $options Array of HTML attributes, and special options above. + * @return string Generated video tag + */ + public function video($path, $options = array()) { + $options += array('pathPrefix' => 'files/', 'text' => ''); + + if (is_array($path)) { + $response = null; + $sourceTags = ''; + foreach ($path as $source) { + if (is_string($source)) { + $source = array( + 'src' => $source, + ); + } + if (!isset($source['type'])) { + if ($response === null) { + $response = new CakeResponse(); + } + $source['type'] = $response->getMimeType(pathinfo($source['src'], PATHINFO_EXTENSION)); + } + $source['src'] = $this->assetUrl($source['src'], $options); + $sourceTags .= $this->useTag('tagselfclosing', 'source', $source); + } + $options['text'] = $sourceTags . $options['text']; + unset($options['fullBase']); + } else { + $path = $this->assetUrl($path, $options); + $options['src'] = $path; + } + + if (isset($options['poster'])) { + $options['poster'] = $this->assetUrl($options['poster'], array('pathPrefix' => IMAGES_URL) + $options); + } + + $text = $options['text']; + + $options = array_diff_key($options, array('fullBase' => '', 'pathPrefix' => '', 'text' => '')); + return $this->tag('video', $text, $options); + } + /** * Build a nested list (UL/OL) out of an associative array. *