Last active
July 25, 2017 05:06
-
-
Save mindon/4eec55058e191e5e2306053f2cd28328 to your computer and use it in GitHub Desktop.
Audio/Video Track Feature
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // track feature | |
| // author: Mindon <mindon@gmail.com> | |
| // updated: July 13, 2017. Shenzhen, Guangdong, China P.R. | |
| // track.bind(audio-element, cues) | |
| // track.bind(audio-element, cues, track-bubble-container-element) | |
| // cues-array = track.get(cues-data or cues-dataUrl, cb) // cb = (cues)=>{} | |
| // supports VTT and lyric format | |
| var track = (function(){ | |
| // ------ track namespace begin | |
| var nxp = /^\s$/; | |
| var dur = /^((?:\d{2}:)?\d{2}:\d{2}(?:[,.]\d{1,3})?)\s+-->\s+((?:\d{2}:)?\d{2}:\d{2}(?:[,.]\d{1,3})?)(.*)/; | |
| var sets = /\w+:[^\s]+/g; | |
| function t2ms(t) { | |
| var v = t.split(":"), i = 0; | |
| var h = v.length == 2 ? 0 : parseInt(v[i++], 10); | |
| return h*3600*1000 + parseInt(v[i++], 10)*60*1000 + Math.round(parseFloat(v[i++].replace(",", ".").replace(/^0+/, "")||'0') * 1000); | |
| } | |
| var voiceTag = /<v\s+([^>]+)\s*>/gi; | |
| var classTag = /<c\.([^>\s]+)\s*>/gi, classTagEnd = /<\/c>/gi; | |
| var delayTag = /<((\d{2}:)?\d{2}:\d{2}[,.]\d{1,3})[^>]*>/g; | |
| function sortLines(a, b) {return a[0] < b[0] ? -1: (a[0] > b[0] ? 1: 0)}; | |
| function sortCues(a, b) {return a.from < b.from ? -1: (a.from > b.from ? 1: 0)}; | |
| function cueSettings(cftext) { | |
| var ms = cftext.match(sets); | |
| if(!ms) return null; | |
| var settings = {}; | |
| for(var j=0; j<ms.length; j++) { | |
| var n = ms[j].indexOf(":"); | |
| settings[ms[j].substr(0,n)] = ms[j].substr(n+1); | |
| } | |
| return settings; | |
| } | |
| function cues(body, kind) { | |
| var c = [], cid = 0; lines = body.split(/\r?\n/); | |
| for(var i=0, imax=lines.length; i<imax; i++) { | |
| var line = lines[i]; | |
| if(!line || nxp.test(line)) { | |
| continue; | |
| } | |
| var mr = line.match(dur); | |
| if(!mr) { | |
| continue; | |
| } | |
| var cue = {id: ++cid}; | |
| var last = lines[i-1]; | |
| if(last && !nxp.test(last)) { | |
| cue.id = last; | |
| } | |
| // cue.range = {from: mr[1], to: mr[2]}; | |
| cue.from = t2ms(mr[1]); | |
| cue.to = t2ms(mr[2]); | |
| var settings = cueSettings(mr[3]); | |
| if(settings) { | |
| cue.settings = settings; | |
| } | |
| var texts = []; | |
| i++; line = lines[i]; | |
| while(line && !nxp.test(line)) { | |
| texts.push(line); | |
| i++; line = lines[i]; | |
| } | |
| cue.text = texts.join("\n"); | |
| var dtm = cue.text.match(delayTag); | |
| if(dtm) { | |
| var k = 0, from = cue.from, waits = [], wet = []; | |
| cue.text = cue.text.replace(delayTag, function(s){ | |
| var t = t2ms(s.substr(1, s.length-2)) - from; | |
| waits.push(t); | |
| wet.push('</span>'); | |
| return '<span id="_t__'+from+'_' +t+'" class="' +delayClass +'">'; | |
| }) + wet.join(''); | |
| cue.waits = waits; | |
| } | |
| c.push(cue); | |
| } | |
| c.sort(sortCues); | |
| return c; | |
| } | |
| var lrc = /^((?:\[\d{2}:\d{2}(?:(?:\.\d+)?)\])+)(.*)\s*$/; | |
| function lyric(body) { | |
| var c = [], cid = 0; lines = body.split(/\r?\n/), darr = []; | |
| for(var i=0, imax=lines.length; i<imax; i++) { | |
| var line = lines[i]; | |
| if(!line || nxp.test(line)) { | |
| continue; | |
| } | |
| var mr = line.match(lrc); | |
| var tms = mr[1]; | |
| var text = mr[2] || ''; | |
| while(i<imax && !lrc.test(lines[i+1])) { | |
| i++; | |
| if(lines[i] && !nxp.test(lines[i])) { | |
| text += "\n" + lines[i]; | |
| } | |
| } | |
| var tarr = tms.replace(/\s*\[|\]\s*$/g, '').split(']'); | |
| for(var j=0, jmax=tarr.length; j<jmax; j++) { | |
| darr.push([tarr[j], text.trim()]); | |
| } | |
| } | |
| darr.sort(sortLines); | |
| for(var i=0, imax=darr.length; i<imax; i++) { | |
| var d = darr[i]; | |
| var from = t2ms(d[0]); | |
| var to = i<imax-1 ? t2ms(darr[i+1][0]) : from + 10000; | |
| var cue = {id: ++cid, from: from, to: to, text: d[1]}; | |
| c.push(cue); | |
| } | |
| return c; | |
| } | |
| var bubbleClass='-aTrAcK-bubble', bubbleOnClass = '-aTrAcK-bubble-on'; | |
| var delayClass='-aTrAcK-delay', delayOnClass = '-aTrAcK-delay-on'; | |
| // /(A|align):(start|middle|end)/i | |
| // /(S|size):(100|\d{1,2})%/i | |
| // /(T|position):(100|\d{1,2})%/i | |
| // /(D|vertical):(vertical-lr|vertical|lr|rl)/i | |
| // /(L|line):(-?[0-9]{0,3})(%?)/i; | |
| // /(bubble):([\w-\.]+)/i; | |
| function bubble(prefix, cue) { | |
| var tob = document.createElement("div"); | |
| tob.id = prefix + cue.id; | |
| var classList = [bubbleClass] | |
| var style = tob.style; | |
| if(cue.settings) { | |
| var s = cue.settings.L || cue.settings.line; | |
| if(s) { | |
| var ng = s.charAt(0)=='-'; | |
| if(ng) s = s.substr(1); | |
| style[ng?'bottom':'top'] = s.indexOf('%') > 0 ? s : s +'em'; | |
| } | |
| s = cue.settings.T || cue.settings.position; | |
| var p = ''; | |
| if(s) { | |
| var ng = s.charAt(0)=='-'; | |
| p = s.indexOf('%') > 0 ? s : s +'%'; | |
| if(ng) s = s.substr(1); | |
| style[ng?'right':'left'] = ng ? p.substr(1): p; | |
| if(ng) { | |
| style.textAlign = 'right'; | |
| } | |
| } | |
| s = cue.settings.S || cue.settings.size; | |
| if(s) { | |
| var v = parseInt(s, 10); | |
| if(p) { | |
| if(p.charAt(0)=='-') { | |
| style.left = (100 - v + parseInt(p, 10)) +'%'; | |
| } else { | |
| style.right = (100 - v - parseInt(p, 10)) +'%'; | |
| } | |
| } else { | |
| style.left = (100 - v/2) +'%'; | |
| style.right = (100 - v/2) +'%'; | |
| } | |
| } | |
| s = cue.settings.A || cue.settings.align; | |
| if(s) { | |
| style.textAlign = /end/i.test(s)?'right':(/start/i.test(s)?'left':'center'); | |
| } | |
| s = cue.settings.D || cue.settings.vertical; | |
| if(s) { | |
| var rl = /rl/i.test(s); | |
| if(rl) { | |
| style.unicodeBidi = 'bidi-override'; | |
| style.direction = 'rtl'; | |
| } | |
| } | |
| if(cue.settings.class) { | |
| classList.push(cue.settings.class); | |
| } | |
| } else { | |
| style.bottom = '.5em'; | |
| } | |
| var text = cue.text; | |
| if(cue.waits && cue.waits.length > 0) { | |
| text = text.replace(/(<span id=")(_t__)/g, '$1'+prefix+'$2'); | |
| } | |
| text = text.replace(voiceTag, '<span class="_track_voice">[$1]</span> '); | |
| if(classTagEnd.test(text)) { | |
| text = text.replace(classTag, stylize); | |
| text = text.replace(classTagEnd, '</span>'); | |
| } | |
| tob.className = classList.join(' '); | |
| tob.innerHTML = text.replace("\n", "<br/>"); | |
| return tob; | |
| } | |
| function stylize(s, c) { | |
| return '<span class="' +c.replace(/\./g, ' ') +'">'; | |
| } | |
| var tsn = 1; | |
| function _prefix(){return ('_aTrAcK_' +tsn++) +"_bubbles_"} | |
| function prepare(cues, doc, prefix, options) { | |
| var tp = [], tpis = {}, tpie = {}; | |
| if(!prefix) prefix = _prefix(); | |
| for(var i=0, imax=cues.length; i<imax; i++) { | |
| var cue = cues[i]; | |
| var tpiss = tpis[cue.from]; | |
| if(!tpiss) { | |
| tpiss = [i]; | |
| tpis[cue.from] = tpiss; | |
| } else { | |
| tpiss.push(i); | |
| } | |
| if(tp.indexOf(cue.from) < 0) { | |
| tp.push(cue.from); | |
| } | |
| var tpies = tpie[cue.to]; | |
| if(!tpies) { | |
| tpies = [i]; | |
| tpie[cue.to] = tpies; | |
| } else { | |
| tpies.push(i); | |
| } | |
| if(tp.indexOf(cue.to) < 0) { | |
| tp.push(cue.to); | |
| } | |
| if(!options || !options.silence) { | |
| var tob = bubble(prefix, cue); | |
| tob.style.transitionDuration = (0 +(Math.floor((cue.to - cue.from)/1000)||1)) +'s'; | |
| if(options && options.style) { | |
| tob.classList.add(options.style); | |
| } | |
| doc.appendChild(tob); | |
| tob = null; | |
| } | |
| } | |
| return {points: tp.sort((a,b)=>a>b?1:(a<b?-1:0)), starts: tpis, stops: tpie, prefix: prefix} | |
| } | |
| var lasts = {}; | |
| function clearDelay(tob) { | |
| var tags = tob.getElementsByTagName('span'); | |
| for(var k=0; k<tags.length; k++) { | |
| if(tags[k].classList.contains(delayOnClass)) { | |
| tags[k].classList.remove(delayOnClass); | |
| } | |
| } | |
| } | |
| function hideCue(cue, stage, prefix, current, ani) { | |
| var tob = document.getElementById(prefix + cue.id); | |
| if(!tob) { | |
| return | |
| } | |
| if(ani) ani(tob, cue, current, false); | |
| stage.doc.appendChild(tob); | |
| tob.classList.remove(bubbleOnClass); | |
| clearDelay(tob); | |
| } | |
| function showCue(cue, stage, prefix, current, ani) { | |
| var tob = stage.doc.getElementById(prefix + cue.id); | |
| if(tob && !tob.classList.contains(bubbleOnClass)) { | |
| tob.classList.add(bubbleOnClass); | |
| // show(tob, cues[j]); | |
| stage.view.appendChild(tob); | |
| if(ani) { | |
| tob.style.bottom = "auto"; | |
| tob.style.top = "1em"; | |
| setTimeout(()=>ani(tob, cue, current, true),50); | |
| } | |
| } | |
| } | |
| function display(prefix, idxes, cues, current, stage, direction, bubbleAni) { | |
| var doc = stage.doc || document, view = stage.view; | |
| var last = lasts[prefix]||[]; | |
| if(!idxes) idxes = []; | |
| idxes = unique(idxes); | |
| var tmp = idxes.slice(), keeps = []; | |
| for(var i=0; i<last.length; i++) { | |
| var j = last[i], n = idxes.indexOf(j); | |
| if(n < 0) { | |
| if((direction > 0 && cues[j].to < current) || | |
| (direction < 0 && cues[j].from > current)) { | |
| hideCue(cues[j], stage, prefix, current, bubbleAni); | |
| } else { | |
| keeps.push(j); | |
| } | |
| } else { | |
| keeps.push(idxes.splice(n, 1)[0]); | |
| } | |
| } | |
| for(var i=0; i<idxes.length; i++) { | |
| var j = idxes[i]; | |
| showCue(cues[j], stage, prefix, current, bubbleAni); | |
| } | |
| // sub delays tag | |
| for(var i=0; i<keeps.length; i++) { | |
| var j = keeps[i], waits = cues[j].waits; | |
| if(tmp.indexOf(j)<0) { | |
| tmp.push(j); | |
| } | |
| if(!waits) continue; | |
| var from = cues[j].from, offset = current - from; | |
| for(var k=0; k<waits.length; k++) { | |
| var dt = document.getElementById(prefix +'_t__' + from +'_' +waits[k]); | |
| if(!dt || !dt.className) continue; | |
| dt.className = waits[k] > offset ? delayClass:delayClass +' ' + delayOnClass; | |
| } | |
| } | |
| lasts[prefix] = tmp; | |
| } | |
| function unique(d) { | |
| // ES6 return [...new Set(d)]; | |
| var r = [], j = 0; | |
| for(var k=0, kmax=d.length; k<kmax; k++) { | |
| if(r.indexOf(d[k]) < 0) { | |
| r[j++]=d[k]; | |
| } | |
| } | |
| return r; | |
| } | |
| var _stageStyleAttr = 'stage-style'; | |
| var _dotBubbleOn = '.' +bubbleOnClass; | |
| var _styleBody = `.-aTrAcK-stage { | |
| position: relative; min-height: 180px; | |
| overflow: hidden; | |
| } ` +' .' +bubbleClass +` { | |
| position: absolute; z-index: 999; | |
| font-size: 12px; text-align: center; | |
| color: #fff; display: none; | |
| width: 100%; | |
| transition: all 3s cubic-bezier(.97,.25,.61,.9); | |
| transform: translate3d(0,0,0); | |
| opacity: 1.0; | |
| } ` +_dotBubbleOn +` { | |
| display: block; | |
| } ` +_dotBubbleOn +' span.' +delayClass+` { | |
| visibility: hidden; | |
| } ` +_dotBubbleOn +' span.' + delayOnClass +` { | |
| visibility: visible; | |
| } .-aTrAcK-fill{position: absolute; left: 0; right: 0; top: 0; bottom: 0;}`; | |
| // http://cubic-bezier.com/#.97,.25,.61,.9 | |
| function _style() { | |
| if(!_styleBody) return; | |
| var el= document.createElement('style'); | |
| el.type= "text/css"; | |
| if(el.styleSheet) el.styleSheet.cssText= _styleBody; | |
| else el.appendChild(document.createTextNode(_styleBody)); | |
| document.getElementsByTagName('head')[0].appendChild(el); | |
| _styleBody = ''; // clear | |
| _style = undefined; | |
| } | |
| var vttRxp = /\d{2}:[.:\d]+\s+-->\s+\d{2}:\d{2}/; | |
| var lyricRxp = /\n\[\d{2}:\d{2}[:\.\d]*\]/; | |
| var dataUrl = /^(((http[s]:)?\/\/)|[.]{0,2}\/)\w+.+/i; | |
| function get(d, cb) { | |
| var c = []; | |
| if(d.indexOf('\n') > -1) { | |
| if(vttRxp.test(d)) { | |
| c = cues(d); | |
| } else if(lyricRxp.test(d)) { | |
| c = lyric(d); | |
| } else { | |
| throw new Error('Invalid cues data :' + d); | |
| } | |
| } else { // if(dataUrl.test(d)) { | |
| var xhr = new XMLHttpRequest(); | |
| xhr.open('GET', d, true); | |
| xhr.onload = function(){ | |
| var c = get(xhr.responseText, cb); | |
| if(cb && c!==false) cb(c); | |
| }; | |
| xhr.send(null); | |
| return false; | |
| } | |
| return c; | |
| } | |
| function bind(aob, dCues, options) { | |
| if(_style) _style(); | |
| if(!aob) { // || aob.tracked || (cob && cob.tracked) | |
| return; | |
| } | |
| if(!options) options = {}; | |
| var cob = options.container; | |
| if(typeof dCues == 'string') { | |
| dCues = get(dCues, function(c){ | |
| bind(aob, c, options); | |
| }); | |
| if(dCues === false) { | |
| return; // | |
| } | |
| } | |
| var cues = dCues; | |
| var p = cob||aob.parentElement, className = '', playground; | |
| if(cob) { | |
| playground = cob.playground; | |
| cob.tracked = true; | |
| } else { | |
| playground = aob.playground; | |
| aob.tracked = true; | |
| } | |
| var prefix = _prefix(); | |
| if(playground) { | |
| var series = playground.series; | |
| var ps = prepare(cues, playground.stage.doc, prefix, options); | |
| var tp = ps.points, imax = tp.length, tmax = tp[imax-1] +10000; | |
| series.push({cues: cues, ps: ps, dur: tmax, ani: options.ani, style: options.style}); | |
| return; | |
| } | |
| var div = document.createElement("div"), style = '', height = 0, videoInside = false; | |
| if(cob) { | |
| if(cob && cob.style.positon == 'static') cob.style.positon = 'relative'; | |
| div.classList.add("-aTrAcK-fill"); | |
| className = cob.getAttribute(_stageStyleAttr); | |
| } else { | |
| if(aob.offsetWidth > 0) { | |
| div.style.width = aob.offsetWidth +'px'; | |
| } | |
| if(aob.tagName == 'VIDEO') { | |
| height = parseInt(aob.offsetHeight, 10) +20; | |
| div.style.minHeight = height +'px'; | |
| div.style.height = height+'px'; | |
| videoInside = true; | |
| } | |
| className = aob.getAttribute(_stageStyleAttr); | |
| } | |
| div.classList.add('-aTrAcK-stage'); | |
| if(className) { | |
| div.classList.add(className); | |
| } | |
| if(cob) { | |
| cob.appendChild(div); | |
| } else { | |
| p.insertBefore(div, aob); | |
| } | |
| if(videoInside) { // video | |
| var subDiv = div.cloneNode(); | |
| subDiv.style.height = (height - 32) +'px'; | |
| subDiv.style.minHeight = (height - 32) +'px'; | |
| subDiv.style.zIndex = 1; | |
| div.className += ' -aTrAcK-video'; | |
| aob.style.position = 'absolute'; | |
| aob.style.zIndex = 0; | |
| aob.style.left = 0; | |
| aob.style.top = 0; | |
| div.appendChild(subDiv); | |
| div.appendChild(aob); | |
| div= subDiv; | |
| } | |
| var playground = {stage: {view: div}, series: []}; | |
| if(cob) { | |
| cob.playground = playground; | |
| } else { | |
| aob.playground = playground; | |
| } | |
| var doc = document.createDocumentFragment(); | |
| playground.stage.doc = doc; | |
| var ps = prepare(cues, doc, prefix, options); | |
| var tp = ps.points, imax = tp.length, tmax = tp[imax-1] +10000, lastMoment = 0; | |
| playground.series.push({cues: cues, ps: ps, dur: tmax, ani: options.ani, style: options.style}); | |
| playground.dur = tmax; | |
| aob.addEventListener("durationchange", function(){ | |
| playground.dur = Math.round(this.duration * 1000); | |
| window.dispatchEvent(new CustomEvent("audio-ready", {detail: {prefix: prefix, audio: aob, tracker: cob||aob}})); | |
| }); | |
| aob.addEventListener("play", function(){ | |
| var series = playground.series, dur = playground.dur; | |
| for(var s=0, smax=series.length; s<smax; s++) { | |
| series[s].dur = dur; | |
| } | |
| }); | |
| aob.addEventListener("paused", function(){ | |
| window.dispatchEvent(new CustomEvent("audio-paused", {detail: {prefix: prefix, current: ms2t(playground.current), moment: playground.current}})); | |
| }); | |
| aob.addEventListener("ended", function(){ | |
| var last = lasts[prefix]||[]; | |
| for(var i=0; i<last.length; i++) { | |
| var j = last[i]; | |
| if(cues[j] && !cues[j].fin) hideCue(cues[j], playground.stage, prefix, playground.current, options.ani); | |
| } | |
| window.dispatchEvent(new CustomEvent("audio-end", {detail: {prefix: prefix, duration: playground.dur}})); | |
| }); | |
| aob.addEventListener("timeupdate", function(event){ | |
| var t = Math.round(this.currentTime * 1000); | |
| var direction = t - lastMoment; | |
| playground.current = t; | |
| playground.direction = direction; | |
| lastMoment = t; | |
| var series = playground.series; | |
| for(var s=0, smax=series.length; s<smax; s++) { | |
| var cps = series[s]; | |
| play(playground, cps, t, direction, cps.ani); | |
| } | |
| window.dispatchEvent(new CustomEvent("audio-playing", {detail: {prefix: prefix, current: ms2t(t), moment: t}})); | |
| }); | |
| } | |
| function play(playground, cps, t, direction, bubbleAni) { | |
| var stage = playground.stage, cues = cps.cues, ps = cps.ps, prefix = ps.prefix; | |
| var tp = ps.points, tb = ps.starts, te = ps.stops, imax = tp.length, tmax = cps.dur; | |
| if(t <= tp[imax -1]) { | |
| var ones = [], i = 0, t2i = Math.floor(imax/2), nmax = imax; | |
| if(t >= tp[t2i]) { | |
| i = t2i; | |
| } else { | |
| nmax = t2i +1; | |
| } | |
| var wq = [], xq = []; | |
| for(; i<nmax; i++) { | |
| var ti = tp[i]; | |
| if(t >=ti && t < tp[i+1] && tb[ti]) { | |
| ones = ones.concat(tb[ti]); | |
| } else if( ti >= t) { | |
| if(tb[ti] && tb[ti].length > 0) wq = wq.concat(tb[ti]); | |
| if(te[ti] && te[ti].length > 0) xq = xq.concat(te[ti]); | |
| } | |
| } | |
| for(var k in xq) { | |
| var ki = xq[k]; | |
| if(wq.indexOf(ki) > -1) continue; | |
| if(ones.indexOf(ki) < 0) { | |
| ones.push(ki); | |
| } | |
| } | |
| display(prefix, ones, cues, t, stage, direction, bubbleAni); | |
| } else if( t < tmax) { | |
| ti = tp[imax -1]; | |
| display(prefix, [], cues, t, stage, direction, bubbleAni); | |
| cps.dur = t; | |
| } | |
| } | |
| function join(tracker, cue, dur, idx) { | |
| var playground = tracker.playground; | |
| var series = playground.series; | |
| if(idx === undefined) idx = series.length -1; | |
| var s = series[idx]; | |
| if(!s||!s.cues) return; | |
| if(typeof cue == 'string') { | |
| cue = {text: cue}; | |
| } | |
| if(dur === undefined) { | |
| dur = 3000; // default 3 seconds | |
| } | |
| cue.id = s.cues.length +1; | |
| if(cue.from === undefined) { | |
| cue.from = (playground.current||0) + 500; | |
| } else if(typeof cue.from == 'string') { | |
| cue.from = t2ms(cue.from); | |
| } | |
| cue.to = cue.from + dur; | |
| s.cues.push(cue); | |
| var prefix = s.ps.prefix, doc = playground.stage.doc; | |
| var ps = prepare(s.cues, doc, prefix, {silence:true, style: s.style}); | |
| var tp = ps.points, imax = tp.length, tmax = tp[imax-1] +1000; | |
| s.ps = ps; | |
| if(s.dur < tmax) { | |
| s.dur = tmax; | |
| } | |
| var tob = bubble(prefix, cue); | |
| if(s.style) { | |
| tob.classList.add(s.style); | |
| } | |
| tob.style.transitionDuration = (0 +(Math.floor((cue.to - cue.from)/1000)||1)) +'s'; | |
| // tob.style.left = Math.round(100 * ((playground.current/1000) %5)/7) +'%'; | |
| doc.appendChild(tob); | |
| return tob; | |
| } | |
| function ms2t(ms) { | |
| var m = ms%1000, s = Math.floor((ms-m)/1000), sec = s%60, minutes = Math.floor((s - sec)%3600/60), hours = Math.floor((s - sec - minutes*60)/3600); | |
| return (hours>0?(hours<10?'0'+hours:hours)+':':'') + (minutes<10?'0'+minutes:minutes) +':' + | |
| (sec<10?'0'+sec:sec) + (m>0?'.'+(m<100?'00':(m<10?'0':''))+m: ''); | |
| } | |
| function raw(tracker, idx) { | |
| var playground = tracker.playground; | |
| var series = playground.series; | |
| var s = series[idx]; | |
| if(!s||!s.cues) return false; | |
| var d = ["WEBVTT"]; | |
| for(var i=0, cues = s.cues, imax=cues.length; i<imax; i++) { | |
| var cue = cues[i], c = ms2t(cue.from) +' --> ' + ms2t(cue.to); | |
| if(cue.settings) { | |
| for(var k in cue.settings) { | |
| c += ' ' +k +':'+ cue.settings[k]; | |
| } | |
| } | |
| c += '\n' +cue.text; | |
| d.push(c); | |
| } | |
| return d.join('\n\n'); | |
| } | |
| // export | |
| return {get: get, bind: bind, join: join, raw: raw, conf: cueSettings}; | |
| // ------ track namespace end | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment