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.
*