[yelp-xsl] [mal2html-media.xsl] Added support for TTML captions for <media>



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 &gt; div.inner { display: inline-block; }
+div.media-video &gt; 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_, ',')) &lt; $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]