[yelp-xsl] [mal2html-media.xsl] Added support for TTML captions for <media>
- From: Shaun McCance <shaunm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [yelp-xsl] [mal2html-media.xsl] Added support for TTML captions for <media>
- Date: Mon, 8 Nov 2010 23:20:40 +0000 (UTC)
commit 5d9076be549780aaaeb1d24fc9bbd0ca9e34290b
Author: Shaun McCance <shaunm gnome org>
Date: Mon Nov 8 18:20:13 2010 -0500
[mal2html-media.xsl] Added support for TTML captions for <media>
xslt/common/html.xsl | 191 ++++++++++++++++++++++++++++++++++
xslt/mallard/html/mal2html-media.xsl | 184 ++++++++++++++++++++++++++++++++-
2 files changed, 371 insertions(+), 4 deletions(-)
---
diff --git a/xslt/common/html.xsl b/xslt/common/html.xsl
index 6743c29..6199c6e 100644
--- a/xslt/common/html.xsl
+++ b/xslt/common/html.xsl
@@ -214,6 +214,7 @@ as ${node} to this template.
<xsl:apply-templates mode="html.title.mode" select="$node"/>
</title>
<xsl:call-template name="html.css"/>
+ <xsl:call-template name="html.js"/>
<xsl:call-template name="html.head.custom"/>
</head>
<body>
@@ -858,6 +859,40 @@ pre span.prompt {
}
span.sys { font-family: monospace; }
span.var { font-style: italic; }
+div.media-video > div.inner { display: inline-block; }
+div.media-video > div.inner video { margin: 0; }
+div.media-controls {
+ margin: 0; padding: 2px;
+ background-color: </xsl:text>
+ <xsl:value-of select="$color.gray_background"/><xsl:text>;
+ border: solid 1px </xsl:text>
+ <xsl:value-of select="$color.gray_border"/><xsl:text>;
+}
+div.media-controls button, div.media-controls input { margin: 0; }
+div.media-controls button.media-play { padding: 0; line-height: 0; }
+div.media-controls button.media-play canvas { margin: 0; }
+div.media-controls input.media-range { height: 20px; }
+
+
+div.media-ttml {
+ margin: 0; padding: 0;
+}
+div.media-ttml-p {
+ display: none;
+ margin: 6px 0 0 0;
+ padding: 6px;
+ max-width: 24em;
+ border: solid 1px </xsl:text>
+ <xsl:value-of select="$color.yellow_border"/><xsl:text>;
+ background-color: </xsl:text>
+ <xsl:value-of select="$color.yellow_background"/><xsl:text>;
+ box-shadow: 2px 2px 4px </xsl:text>
+ <xsl:value-of select="$color.gray_border"/><xsl:text>;
+ -webkit-box-shadow: 2px 2px 4px </xsl:text>
+ <xsl:value-of select="$color.gray_border"/><xsl:text>;
+ -moz-box-shadow: 2px 2px 4px </xsl:text>
+ <xsl:value-of select="$color.gray_border"/><xsl:text>;
+}
</xsl:text>
</xsl:template>
@@ -890,6 +925,162 @@ template to provide additional CSS that will be used by all HTML output.
</xsl:param>
</xsl:template>
+
+<xsl:template name="html.js">
+ <xsl:param name="node" select="."/>
+ <script type="text/javascript" language="javascript">
+<xsl:text><![CDATA[
+Node.prototype.is_a = function (tag, cls) {
+ if (this.nodeType == Node.ELEMENT_NODE) {
+ if (tag == null || this.tagName == tag) {
+ if (cls == null)
+ return true;
+ var clss = this.className.split(' ');
+ for (var i = 0; i < clss.length; i++) {
+ if (cls == clss[i])
+ return true;
+ }
+ }
+ }
+ return false;
+};
+function yelp_init_media (media) {
+ var control;
+ var controlsDiv;
+ var playControl;
+ var rangeControl;
+ var currentSpan;
+ for (controlsDiv = media.nextSibling; controlsDiv; controlsDiv = controlsDiv.nextSibling)
+ if (controlsDiv.is_a('div', 'media-controls'))
+ break;
+ if (!controlsDiv)
+ return;
+ for (control = controlsDiv.firstChild; control; control = control.nextSibling) {
+ if (control.nodeType == Node.ELEMENT_NODE) {
+ if (control.is_a('button', 'media-play'))
+ playControl = control;
+ else if (control.is_a('input', 'media-range'))
+ rangeControl = control;
+ else if (control.is_a('span', 'media-current'))
+ currentSpan = control;
+ }
+ }
+
+ var ttmlDiv;
+ for (ttmlDiv = controlsDiv.nextSibling; ttmlDiv; ttmlDiv = ttmlDiv.nextSibling)
+ if (ttmlDiv.is_a('div', 'media-ttml'))
+ break;
+
+ var playCanvas;
+ for (playCanvas = playControl.firstChild; playCanvas; playCanvas = playCanvas.nextSibling)
+ if (playCanvas.is_a('canvas', null))
+ break;
+ var playCanvasCtxt = playCanvas.getContext('2d');
+ var paintPlayButton = function () {
+ playCanvasCtxt.fillStyle = ']]></xsl:text>
+<xsl:value-of select="$color.text_light"/><xsl:text><![CDATA['
+ playCanvasCtxt.clearRect(0, 0, 20, 20);
+ playCanvasCtxt.beginPath();
+ playCanvasCtxt.moveTo(5, 5);
+ playCanvasCtxt.lineTo(5, 15);
+ playCanvasCtxt.lineTo(15, 10);
+ playCanvasCtxt.lineTo(5, 5);
+ playCanvasCtxt.fill();
+ }
+ var paintPauseButton = function () {
+ playCanvasCtxt.fillStyle = ']]></xsl:text>
+<xsl:value-of select="$color.text_light"/><xsl:text><![CDATA['
+ playCanvasCtxt.clearRect(0, 0, 20, 20);
+ playCanvasCtxt.beginPath();
+ playCanvasCtxt.moveTo(5, 5);
+ playCanvasCtxt.lineTo(9, 5);
+ playCanvasCtxt.lineTo(9, 15);
+ playCanvasCtxt.lineTo(5, 15);
+ playCanvasCtxt.lineTo(5, 5);
+ playCanvasCtxt.fill();
+ playCanvasCtxt.beginPath();
+ playCanvasCtxt.moveTo(11, 5);
+ playCanvasCtxt.lineTo(15, 5);
+ playCanvasCtxt.lineTo(15, 15);
+ playCanvasCtxt.lineTo(11, 15);
+ playCanvasCtxt.lineTo(11, 5);
+ playCanvasCtxt.fill();
+ }
+ paintPlayButton();
+
+ var mediaChange = function () {
+ if (media.ended)
+ media.pause()
+ if (media.paused) {
+ playControl.setAttribute('value', playControl.getAttribute('data-play-label'));
+ paintPlayButton();
+ }
+ else {
+ playControl.setAttribute('value', playControl.getAttribute('data-pause-label'));
+ paintPauseButton();
+ }
+ }
+ media.addEventListener('play', mediaChange, false);
+ media.addEventListener('pause', mediaChange, false);
+ media.addEventListener('ended', mediaChange, false);
+
+ var playClick = function () {
+ if (media.paused || media.ended)
+ media.play();
+ else
+ media.pause();
+ };
+ playControl.addEventListener('click', playClick, false);
+
+ var ttmlNodes = [];
+ var ttmlNodesFill = function (node) {
+ var child;
+ for (child = node.firstChild; child; child = child.nextSibling) {
+ if (child.nodeType == Node.ELEMENT_NODE) {
+ if (child.is_a(null, 'media-ttml-node'))
+ ttmlNodes[ttmlNodes.length] = child;
+ ttmlNodesFill(child);
+ }
+ }
+ }
+ ttmlNodesFill(ttmlDiv);
+
+ var timeUpdate = function () {
+ rangeControl.value = parseInt((media.currentTime / media.duration) * 100);
+ var mins = parseInt(media.currentTime / 60);
+ var secs = parseInt(media.currentTime - (60 * mins))
+ currentSpan.innerText = mins + (secs < 10 ? ':0' : ':') + secs;
+ for (var i = 0; i < ttmlNodes.length; i++) {
+ if (media.currentTime >= parseFloat(ttmlNodes[i].getAttribute('data-begin')) &&
+ (!ttmlNodes[i].hasAttribute('data-end') ||
+ media.currentTime < parseFloat(ttmlNodes[i].getAttribute('data-end')) )) {
+ if (ttmlNodes[i].tagName == 'span')
+ ttmlNodes[i].style.display = 'inline';
+ else
+ ttmlNodes[i].style.display = 'block';
+ }
+ else {
+ ttmlNodes[i].style.display = 'none';
+ }
+ }
+ };
+ media.addEventListener('timeupdate', timeUpdate, false);
+
+ var rangeChange = function () {
+ media.currentTime = (parseInt(rangeControl.value) / 100.0) * media.duration;
+ };
+ rangeControl.addEventListener('change', rangeChange, false);
+};
+document.addEventListener("DOMContentLoaded", function () {
+ var vids = document.getElementsByTagName('video');
+ for (var i = 0; i < vids.length; i++)
+ yelp_init_media(vids[i]);
+}, false);
+]]></xsl:text>
+ </script>
+</xsl:template>
+
+
<!--**==========================================================================
html.lang.attrs
Output #{lang} and #{dir} attributes.
diff --git a/xslt/mallard/html/mal2html-media.xsl b/xslt/mallard/html/mal2html-media.xsl
index a077121..933c3bf 100644
--- a/xslt/mallard/html/mal2html-media.xsl
+++ b/xslt/mallard/html/mal2html-media.xsl
@@ -18,6 +18,7 @@ Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:mal="http://projectmallard.org/1.0/"
+ xmlns:tt="http://www.w3.org/ns/ttml"
xmlns="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="mal"
version="1.0">
@@ -74,11 +75,14 @@ FIXME
<xsl:template name="mal2html.media.video">
<xsl:param name="node" select="."/>
<xsl:param name="inline" select="false()"/>
- <video src="{$node/@src}" autobuffer="autobuffer" controls="controls">
+ <video src="{$node/@src}" autobuffer="autobuffer">
<xsl:copy-of select="$node/@height"/>
<xsl:copy-of select="$node/@width"/>
<xsl:choose>
<xsl:when test="$inline">
+ <xsl:attribute name="controls">
+ <xsl:text>controls</xsl:text>
+ </xsl:attribute>
<xsl:apply-templates mode="mal2html.inline.mode" select="$node/node()"/>
</xsl:when>
<xsl:otherwise>
@@ -86,8 +90,28 @@ FIXME
</xsl:otherwise>
</xsl:choose>
</video>
+ <xsl:if test="not($inline)">
+ <div class="media-controls">
+ <button class="media-play">
+ <xsl:attribute name="value">
+ <xsl:text>Play</xsl:text>
+ </xsl:attribute>
+ <xsl:attribute name="data-play-label">
+ <xsl:text>Play</xsl:text>
+ </xsl:attribute>
+ <xsl:attribute name="data-pause-label">
+ <xsl:text>Pause</xsl:text>
+ </xsl:attribute>
+ <canvas width="20" height="20"/>
+ </button>
+ <input class="media-range" type="range" min="0" max="100" value="0" step="0"/>
+ <span class="media-current"/>
+ </div>
+ <xsl:apply-templates mode="mal2html.ttml.mode" select="tt:tt"/>
+ </xsl:if>
</xsl:template>
+
<!--**==========================================================================
mal2html.media.audio
FIXME
@@ -110,6 +134,152 @@ FIXME
</xsl:template>
+<!-- == TTML == -->
+
+<xsl:template mode="mal2html.block.mode" match="tt:*"/>
+
+<xsl:template mode="mal2html.ttml.mode" match="tt:tt">
+ <xsl:apply-templates mode="mal2html.ttml.mode" select="tt:body"/>
+</xsl:template>
+
+<xsl:template mode="mal2html.ttml.mode" match="tt:body">
+ <div class="media-ttml">
+ <xsl:apply-templates mode="mal2html.ttml.mode" select="tt:div">
+ <xsl:with-param name="range">
+ <xsl:call-template name="mal2html.ttml.time.range"/>
+ </xsl:with-param>
+ </xsl:apply-templates>
+ </div>
+</xsl:template>
+
+<xsl:template mode="mal2html.ttml.mode" match="tt:div">
+ <xsl:param name="range"/>
+ <xsl:apply-templates mode="mal2html.ttml.mode" select="tt:div | tt:p">
+ <xsl:with-param name="range">
+ <xsl:call-template name="mal2html.ttml.time.range">
+ <xsl:with-param name="range" select="$range"/>
+ </xsl:call-template>
+ </xsl:with-param>
+ </xsl:apply-templates>
+</xsl:template>
+
+<xsl:template mode="mal2html.ttml.mode" match="tt:p">
+ <xsl:param name="range"/>
+ <xsl:variable name="beginend">
+ <xsl:call-template name="mal2html.ttml.time.range">
+ <xsl:with-param name="range" select="$range"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <div class="media-ttml-node media-ttml-p">
+ <xsl:attribute name="data-begin">
+ <xsl:value-of select="substring-before($beginend, ',')"/>
+ </xsl:attribute>
+ <xsl:variable name="end" select="substring-after($beginend, ',')"/>
+ <xsl:if test="$end != 'â??'">
+ <xsl:attribute name="data-end">
+ <xsl:value-of select="$end"/>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:apply-templates mode="mal2html.inline.mode">
+ <xsl:with-param name="range" select="$beginend"/>
+ </xsl:apply-templates>
+ </div>
+</xsl:template>
+
+<xsl:template mode="mal2html.inline.mode" match="tt:span">
+ <xsl:param name="range"/>
+ <xsl:variable name="beginend">
+ <xsl:call-template name="mal2html.ttml.time.range">
+ <xsl:with-param name="range" select="$range"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <span class="media-ttml-node media-ttml-span">
+ <xsl:attribute name="data-begin">
+ <xsl:value-of select="substring-before($beginend, ',')"/>
+ </xsl:attribute>
+ <xsl:attribute name="data-end">
+ <xsl:value-of select="substring-after($beginend, ',')"/>
+ </xsl:attribute>
+ <xsl:apply-templates mode="mal2html.inline.mode"/>
+ </span>
+</xsl:template>
+
+<xsl:template name="mal2html.ttml.time.range">
+ <xsl:param name="range"/>
+ <xsl:param name="begin" select="@begin"/>
+ <xsl:param name="end" select="@end"/>
+ <xsl:variable name="range_">
+ <xsl:choose>
+ <xsl:when test="$range != ''">
+ <xsl:value-of select="$range"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="par" select="ancestor::tt:*[ begin][1]"/>
+ <xsl:choose>
+ <xsl:when test="$par">
+ <xsl:for-each select="$par">
+ <xsl:call-template name="mal2html.ttml.time.range"/>
+ </xsl:for-each>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="'0,â??'"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="begin_s">
+ <xsl:call-template name="mal2html.ttml.time.seconds">
+ <xsl:with-param name="time" select="$begin"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="number(substring-before($range_, ',')) + number($begin_s)"/>
+ <xsl:text>,</xsl:text>
+ <xsl:choose>
+ <xsl:when test="$end">
+ <xsl:variable name="end_s">
+ <xsl:call-template name="mal2html.ttml.time.seconds">
+ <xsl:with-param name="time" select="$end"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="end_ss" select="number(substring-before($range_, ',')) + number($end_s)"/>
+ <xsl:choose>
+ <xsl:when test="substring-after($range_, ',') = 'â??'">
+ <xsl:value-of select="$end_ss"/>
+ </xsl:when>
+ <xsl:when test="number(substring-after($range_, ',')) < $end_ss">
+ <xsl:value-of select="substring-after($range_, ',')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$end_ss"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="substring-after($range_, ',')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="mal2html.ttml.time.seconds">
+ <xsl:param name="time" select="0"/>
+ <xsl:choose>
+ <xsl:when test="substring($time, string-length($time) - 1) = 'ms'">
+ <xsl:variable name="ms">
+ <xsl:value-of select="substring($time, 1, string-length($time) - 2)"/>
+ </xsl:variable>
+ <xsl:value-of select="number($ms) div 1000"/>
+ </xsl:when>
+ <xsl:when test="substring($time, string-length($time)) = 's'">
+ <xsl:value-of select="substring($time, 1, string-length($time) - 1)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="0"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+
<!-- == Matched Templates == -->
<!-- = mal2html.block.mode % media = -->
@@ -136,7 +306,9 @@ FIXME
<xsl:text>media media-image</xsl:text>
<xsl:value-of select="$class"/>
</xsl:attribute>
- <xsl:call-template name="mal2html.media.image"/>
+ <div class="inner">
+ <xsl:call-template name="mal2html.media.image"/>
+ </div>
</div>
</xsl:when>
<xsl:when test="@type = 'video'">
@@ -145,7 +317,9 @@ FIXME
<xsl:text>media media-video</xsl:text>
<xsl:value-of select="$class"/>
</xsl:attribute>
- <xsl:call-template name="mal2html.media.video"/>
+ <div class="inner">
+ <xsl:call-template name="mal2html.media.video"/>
+ </div>
</div>
</xsl:when>
<xsl:when test="@type = 'audio'">
@@ -154,7 +328,9 @@ FIXME
<xsl:text>media media-audio</xsl:text>
<xsl:value-of select="$class"/>
</xsl:attribute>
- <xsl:call-template name="mal2html.media.audio"/>
+ <div class="inner">
+ <xsl:call-template name="mal2html.media.audio"/>
+ </div>
</div>
</xsl:when>
<xsl:otherwise>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]