title: Are HTML5 Track Element Cue Events Buggy?
body: <p>I'm trying to dynamically assign functions to HTML5's <code>cue.onenter</code> events. This is a really new feature, currently only supported in Chrome with specific flags enabled (See example on HTML5 Rocks <a href="http://www.html5rocks.com/en/tutorials/track/basics/" rel="nofollow">here</a>).</p>
  
  <p>However, either it is really buggy due to early development or I'm doing something wrong. Basically, I have a track element on a normal HTML page. Nothing freaky.</p>
  
  <pre><code>&lt;audio id="audiocast" controls oncanplay="setReadyToPlay();"&gt;
              &lt;source id="audiosource" src="SomeWorkingUrlwithaudio" &gt;&lt;/source&gt;
              &lt;track kind="metadata" id="audioTrack" label="slides" src="/data.vtt" default &gt;
              &lt;/track&gt;
              Your Browser does not support HTML5
          &lt;/audio&gt;
  </code></pre>
  
  <p>And I access it via JavaScript like so:</p>
  
  <pre><code>//(...)
  var trackElements = $("#audiocast")[0].children("track")[0];
          _track = trackElements.track;
          _cues = _track.cues;
  
          for (var j = 0; j &lt; _cues.length; ++j) {
              var cue = _cues[j];
              cue.onenter = getOnEnter(j);
          }
  
  //(...)
  function getOnEnter(idx) {
      return function() {
          setCueIdx(idx);
          updateURL(idx);
          var json = JSON.parse(this.text);
  
          _currImgSrc = json.src;
          drawIt();
      }
  };
  </code></pre>
  
  <p>However, this works only some of the time. Sometimes I have to reload the browser 3 times for this to work (for instance, when I draw an image when entering a cue), or <em>when</em> it works, it randomly works until reaching cue 4 of 10 and then stops working.</p>
  
  <p>It just doesn't make sense, but maybe some of you guys have an idea how to provide fallback and stability solutions for this.</p>
  
  <p>Additional information: This runs as a Node.js application. Full source of the Media Player handling the cues is below.</p>
  
  <p>Note that the snippet won't work due to Stack Overflow's sandboxing of scripts.</p>
  
  <p><div class="snippet" data-lang="js" data-hide="true">
  <div class="snippet-code snippet-currently-hidden">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>/*
   * slideCastController.js
   * © by pschne2s, nkopp2s, 2012
   *
   * Basically encapsulates the Media Player Object, handling the function of the HTML5-Player
   */
  
  /****************************OBJECT DEFINITION***********************************/
  
  CanvasRenderingContext2D.prototype.clear = CanvasRenderingContext2D.prototype.clear ||
    function(preserveTransform) {
      if (preserveTransform) {
        this.save();
        this.setTransform(1, 0, 0, 1, 0, 0);
      }
  
      this.clearRect(0, 0, this.canvas.width, this.canvas.height);
  
      if (preserveTransform) {
        this.restore();
      }
    };
  
  var mediaPlayer = (function() {
    //Members
    var _cueIdx = 0;
    var _cues = [];
    var _target;
    var _track;
    var _readyToPlay = false;
    var _currImgSrc;
  
    function setupCues() {
      if (_readyToPlay) {
        if (_track === undefined) {
          // var trackElements = document.getElementById("audioTrack");
          var trackElements = $(_target).children("track")[0];
          _track = trackElements.track;
        }
        _cues = _track.cues;
  
        $("#maxcues").text(_cues.length - 1);
        for (var j = 0; j &lt; _cues.length; ++j) {
          var cue = _cues[j];
          cue.onenter = getOnEnter(j);
        }
  
        _track.oncuechange = function() {
          //updateURL(mediaPlayerInfo.cueIdx);
        };
  
        checkFragments();
      } else {
        setTimeout(setupCues, 1);
      }
    };
  
    function getOnEnter(idx) {
      return function() {
        setCueIdx(idx);
        updateURL(idx);
        var json = JSON.parse(this.text);
  
        _currImgSrc = json.src;
        drawIt();
      }
    };
  
    function drawIt() {
      var ctx = $("#slide")[0].getContext('2d');
      ctx.clear();
  
      var tmpImg = new Image();
      tmpImg.onload = function() {
        ctx.drawImage(tmpImg, 0, 0, 592, 256);
      }
  
      tmpImg.src = _currImgSrc;
  
    }
  
    function checkFragments() {
      var fragments = purl(window.document.URL);
      var slide = fragments.fparam("slide");
      if (slide !== undefined) {
        gotoCue(slide);
      }
      var action = fragments.fparam("action");
      if (action == "play") {
        _target.play();
      }
    };
  
    function setCueIdx(idx) {
      //alert("Cue idx set to: " + idx);
      _cueIdx = idx;
      $("#cueidx").text(idx);
    };
  
    function gotoCue(index) {
      if (index &gt;= 0 &amp;&amp; index &lt; _cues.length) {
        _cueIdx = index;
        var audioElement = $("#audiocast").get(0);
        //mediaPlayerInfo.cues[mediaPlayerInfo.cueIdx].onenter();
        audioElement.currentTime = _cues[index].startTime;
      }
    };
  
    var updateURL = (function() {
      // set Base-URL (without query/hash-String)
      var url = location.protocol + "//" + location.host + location.pathname;
      var html5 = window.history.replaceState !== undefined ? true : false;
  
      return function(slideIdx) {
        if (html5)
          window.history.replaceState(null, document.title + " | Slide #" + slideIdx, url + "#slide=" + slideIdx);
        else
          location.href = url + "#slide=" + slideIdx;
        // No nice browser history
      };
    })();
  
    return {
      init: function() {
        setupCues();
      },
      setTarget: function(obj) {
        _target = obj;
      },
      setTrack: function(obj) {
        _track = obj;
      },
      setReadyToPlay: function() {
        _readyToPlay = true;
      },
      gotoNextCue: function() {
        gotoCue(_cueIdx + 1);
      },
      gotoPrevCue: function() {
        gotoCue(_cueIdx - 1);
      }
    };
  })();
  
  $(document).ready(function() {
    // "Unobstrusive" Function-Bindings
    $("#btnPrevCue").bind("click", mediaPlayer.gotoPrevCue);
    $("#btnNextCue").bind("click", mediaPlayer.gotoNextCue);
  
    mediaPlayer.setTarget($("#audiocast")[0]);
    mediaPlayer.init();
  });
  
  window.setReadyToPlay = mediaPlayer.setReadyToPlay;
  // does not work using other ways atm</code></pre>
  </div>
  </div>
  </p>