diff --git a/README.md b/README.md
index 2bc4495..8fe94d9 100644
--- a/README.md
+++ b/README.md
@@ -2,25 +2,9 @@
Creates interactive transcripts from text tracks.
-## Warning
-
-:no_entry: **Warning! This code is an early work in progress and contains bugs, typos, unimplemented features, debug cruft, and other defects. Don't use it yet.**
-
-### TODO:
-- [x] Seek video to transcript position when transcript is clicked
-- [ ] Add more setup options
-- [x] Nicer default CSS
-- [x] More CSS examples
-- [x] Handle multiple tracks
-- [x] Automatically switch caption track when user selects a different track on the video.
-- [x] Separate track switcher
-- [x] Autoscrolling transcript
-- [x] *Smooth*, animated scrolling
-- [x] Clean up scrolling logic.
-- [x] Prevent autoscrolling when user is interacting with transcript (nice to have)
-- [x] Tests!
-- [ ] More Tests!
-- [ ] More...
+## Alpha Release 1
+
+Please report any issues or feature requests on the tracker. Thank you!
## Getting Started
@@ -29,13 +13,33 @@ Once you've added the plugin script to your page, you can use it with any video:
```html
+
+
```
-
There's also a [working example](example.html) of the plugin you can check out if you're having trouble.
+You'll also want to include one of the css files.
+You can style the plugin as you like but there are a few examples in the /css folder to get you started.
+
## Documentation
### Plugin Options
@@ -53,6 +57,35 @@ Set to false to disable autoscrolling.
Set which elements in the transcript are clickable.
Options are 'timestamp', 'text', the whole 'line', or 'none'.
+#### showTitle
+**Default:** true
+
+Show a title with the transcript widget.
+
+(Currently the title only says 'Transcript')
+
+#### showTrackSelector
+**Default:** true
+
+Show a track selector with the transcript widget.
+
+#### followPlayerTrack
+**Default:** true
+
+When you change the caption track on the video, the transcript changes tracks as well.
+
+#### stopScrollWhenInUse
+**Default:** true
+
+Don't autoscroll the transcript when the user is trying to scroll it.
+
+(This probably still has a few glitches to work out on touch screens and stuff right now)
+
+### Plugin Methods
+**el()**
+
+Returns the DOM element containing the html transcript widget. You'll need to append this to your page.
+
## Release History
- - 0.1.0: Initial release
+ - 0.7.1: Alpha Release 1
diff --git a/css/videojs-transcript3.css b/css/videojs-transcript3.css
index a8c606b..ef56319 100644
--- a/css/videojs-transcript3.css
+++ b/css/videojs-transcript3.css
@@ -1,14 +1,34 @@
+.video-container {
+ margin: 40px auto;
+ position: relative;
+ width: 900px;
+}
+#video {
+ margin: 0;
+ position: absolute;
+}
#transcript {
- width: 600px;
- margin: auto;
+ position: absolute;
+ left: 600px;
+ width: 298px;
font-family: Arial, sans-serif;
+ overflow-x: scroll;
+ height: 298px;
+ border: 1px solid #111;
+}
+.transcript-header {
+ height: 19px;
+ padding: 2px;
+ font-weight: bold;
+}
+.transcript-selector {
+ height: 25px;
}
-
.transcript-body {
- height: 200px;
+ width: 600px;
overflow-y: scroll;
- border: 1px solid #111;
background-color: #e7e7e7;
+ height: 250px;
}
.transcript-line {
@@ -49,3 +69,4 @@
color: #e7e7e7;
}
+
diff --git a/dist/videojs-transcript.js b/dist/videojs-transcript.js
index 918d7bb..f8bff51 100644
--- a/dist/videojs-transcript.js
+++ b/dist/videojs-transcript.js
@@ -1,4 +1,4 @@
-/*! videojs-transcript - v0.0.0 - 2014-10-08
+/*! videojs-transcript - v0.7.1 - 2014-10-10
* Copyright (c) 2014 Matthew Walsh; Licensed MIT */
(function (window, videojs) {
'use strict';
@@ -78,6 +78,12 @@ if (!Array.prototype.forEach) {
};
}
+// classList polyfill
+/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/
+;if("document" in self&&!("classList" in document.createElement("_"))){(function(j){"use strict";if(!("Element" in j)){return}var a="classList",f="prototype",m=j.Element[f],b=Object,k=String[f].trim||function(){return this.replace(/^\s+|\s+$/g,"")},c=Array[f].indexOf||function(q){var p=0,o=this.length;for(;p (parent.scrollTop + parent.clientHeight)) {
+ } else if (relBottom > (parent.scrollTop + parent.clientHeight)) {
newPos = elementOffsetBottom - parentOffsetBottom;
}
@@ -345,8 +356,8 @@ var widget = function (plugin) {
var clickedTime = event.target.getAttribute('data-begin') || event.target.parentElement.getAttribute('data-begin');
if (clickedTime !== undefined && clickedTime !== null) { // can be zero
if ((plugin.settings.clickArea === 'line') || // clickArea: 'line' activates on all elements
- (plugin.settings.clickArea === 'timestamp' && clickedClasses.contains(myPrefix + '-timestamp')) ||
- (plugin.settings.clickArea === 'text' && clickedClasses.contains(myPrefix + '-text'))) {
+ (plugin.settings.clickArea === 'timestamp' && clickedClasses.contains(plugin.prefix + '-timestamp')) ||
+ (plugin.settings.clickArea === 'text' && clickedClasses.contains(plugin.prefix + '-text'))) {
plugin.player.currentTime(clickedTime);
}
}
@@ -393,10 +404,14 @@ var widget = function (plugin) {
var el = document.createElement('div');
my.element = el;
el.setAttribute('id', plugin.prefix + '-' + plugin.player.id());
- var title = createTitle();
- el.appendChild(title);
- var selector = createSelector();
- el.appendChild(selector);
+ if (plugin.settings.showTitle) {
+ var title = createTitle();
+ el.appendChild(title);
+ }
+ if (plugin.settings.showTrackSelector) {
+ var selector = createSelector();
+ el.appendChild(selector);
+ }
my.body = utils.createEl('div', '-body');
el.appendChild(my.body);
setTrack(plugin.currentTrack);
@@ -421,7 +436,7 @@ var widget = function (plugin) {
if (time > begin && time < end) {
if (!line.classList.contains('is-active')) { // don't update if it hasn't changed
line.classList.add('is-active');
- if (plugin.settings.autoscroll && !my.body.scroll.inUse()) {
+ if (plugin.settings.autoscroll && !(plugin.settings.stopScrollWhenInUse && my.body.scroll.inUse())) {
my.body.scroll.to(line);
}
}
@@ -460,8 +475,10 @@ var transcript = function (options) {
if (my.validTracks.length > 0) {
updateTrack();
my.player.on('timeupdate', timeUpdate);
- my.player.on('captionstrackchange', updateTrack);
- my.player.on('subtitlestrackchange', updateTrack);
+ if (my.settings.followPlayerTrack) {
+ my.player.on('captionstrackchange', updateTrack);
+ my.player.on('subtitlestrackchange', updateTrack);
+ }
} else {
throw new Error('videojs-transcript: No tracks found!');
}
diff --git a/dist/videojs-transcript.min.js b/dist/videojs-transcript.min.js
index d5c88dc..58fb6b8 100644
--- a/dist/videojs-transcript.min.js
+++ b/dist/videojs-transcript.min.js
@@ -1,3 +1,3 @@
-/*! videojs-transcript - v0.0.0 - 2014-10-08
+/*! videojs-transcript - v0.7.1 - 2014-10-10
* Copyright (c) 2014 Matthew Walsh; Licensed MIT */
-!function(a,b){"use strict";!function(){for(var b=0,c=["ms","moz","webkit","o"],d=0;d1)throw Error("Second argument not supported");if("object"!=typeof b)throw TypeError("Argument must be an object");a.prototype=b;var c=new a;return a.prototype=null,c}}()),Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var c,d;if(null==this)throw new TypeError(" this is null or not defined");var e=Object(this),f=e.length>>>0;if("function"!=typeof a)throw new TypeError(a+" is not a function");for(arguments.length>1&&(c=b),d=0;f>d;){var g;d in e&&(g=e[d],a.call(c,g,d,e)),d++}});var c={};c.settings={},c.prefix="transcript",c.player=this;var d={autoscroll:!0,clickArea:"line"},e=function(a){return{secondsToTime:function(a){var b=Math.floor(a/3600),c=Math.floor(a%3600/60),d=Math.floor(a%60);return d=10>d?"0"+d:d,c=b>0&&10>c?"0"+c:c,b>0?b+":"+c+":"+d:c+":"+d},localize:function(a){return a},createEl:function(b,c){c=c||"";var d=document.createElement(b);return d.className=a.prefix+c,d},extend:function(a){var b=typeof a;if(!("function"===b||"object"===b&&a))return a;for(var c,d,e=1,f=arguments.length;f>e;e++){c=arguments[e];for(d in c)a[d]=c[d]}return a}}}(c),f={handlers_:[],on:function(a,b,c){if("function"!=typeof c)throw new TypeError("Callback is not a function.");this.handlers_.push([a,b,c])},trigger:function(a,b){this.handlers_.forEach(function(c){c[0]===a&&c[1]===b&&c[2].apply()})}},g=function(){var a=function(a){var b=this;a.addEventListener("scroll",function(){b.isAutoScrolling?b.isAutoScrolling=!1:(b.userIsScrolling=!0,a.classList.add("is-inuse"))}),a.addEventListener("mouseenter",function(){b.mouseIsOverTranscript=!0}),a.addEventListener("mouseleave",function(){b.mouseIsOverTranscript=!1,setTimeout(function(){b.mouseIsOverTranscript||(b.userIsScrolling=!1,a.classList.remove("is-inuse"))},1e3)})},b=function(b){return this.element=b,this.userIsScrolling=!1,this.mouseIsOverTranscript=!0,this.isAutoScrolling=!0,a.call(this,this.element),this},c=function(a,b,c,d){return b+c*Math.sin(Math.min(1,a/d)*(Math.PI/2))},d=function(a,b,d){var e=Date.now(),f=a.scrollTop,g=this;b=Math.max(0,b),b=Math.min(a.scrollHeight-a.clientHeight,b);var h=b-f,i=function(){var j=Date.now(),k=j-e;g.isAutoScrolling=!0,a.scrollTop=c(k,f,h,d),a.scrollTop!==b&&requestAnimationFrame(i,a)};requestAnimationFrame(i,a)},e=function(a){if(this.canScroll()){var b,c=a.parentElement,e=c.offsetTop+c.clientHeight,f=a.offsetTop+a.clientHeight,g=a.offsetTop-c.offsetTop;gc.scrollTop+c.clientHeight&&(b=f-e),void 0!==b&&c.scrollTop!==b&&d.call(this,c,b,400)}},f=function(){var a=this.element;return a.scrollHeight>a.offsetHeight},g=function(){return this.userIsScrolling};return{init:b,to:e,canScroll:f,inUse:g}}(c),h=function(a){return Object.create(g).init(a)},i=function(){var a;return{get:function(){var a=[];return c.tracks=c.player.textTracks(),c.tracks.forEach(function(b){("captions"===b.kind()||"subtitles"===b.kind())&&a.push(b)}),a},active:function(b){return b.forEach(function(b){return 2===b.mode()?(a=b,b):void 0}),a||b[0]}}}(c),j=function(a){var b={};b.element={},b.body={};var c=function(a,b){f.on(this,a,b)},d=function(a){f.trigger(this,a)},g=function(){var a=e.createEl("header","-header");return a.textContent=e.localize("Transcript"),a},i=function(){var b=e.createEl("select","-selector");return a.validTracks.forEach(function(a,c){var d=document.createElement("option");d.value=c,d.textContent=a.label()+" ("+a.language()+")",b.appendChild(d)}),b.addEventListener("change",function(){n(document.querySelector("#"+a.prefix+"-"+a.player.id()+" option:checked").value),d("trackchanged")}),b},j=function(b){var c=b.target.classList,d=b.target.getAttribute("data-begin")||b.target.parentElement.getAttribute("data-begin");void 0!==d&&null!==d&&("line"===a.settings.clickArea||"timestamp"===a.settings.clickArea&&c.contains(myPrefix+"-timestamp")||"text"===a.settings.clickArea&&c.contains(myPrefix+"-text"))&&a.player.currentTime(d)},k=function(a){var b=e.createEl("div","-line"),c=e.createEl("span","-timestamp"),d=e.createEl("span","-text");return b.setAttribute("data-begin",a.startTime),c.textContent=e.secondsToTime(a.startTime),d.innerHTML=a.text,b.appendChild(c),b.appendChild(d),b},l=function(b){"object"!=typeof b&&(b=a.player.textTracks()[b]);var c,d,f=e.createEl("div","-body"),g=document.createDocumentFragment(),i=function(){var a=b.cues();for(d=0;df&&g>c?e.classList.contains("is-active")||(e.classList.add("is-active"),a.settings.autoscroll&&!b.body.scroll.inUse()&&b.body.scroll.to(e)):e.classList.remove("is-active")},p=function(){return b.element};return{create:m,setTrack:n,setCue:o,el:p,on:c,trigger:d}}(c),k=function(a){c.player=this,c.validTracks=i.get(),c.currentTrack=i.active(c.validTracks),c.settings=b.util.mergeOptions(d,a),c.widget=j.create();var e=function(){c.widget.setCue(c.player.currentTime())},f=function(){c.currentTrack=i.active(c.validTracks),c.widget.setTrack(c.currentTrack)};if(!(c.validTracks.length>0))throw new Error("videojs-transcript: No tracks found!");return f(),c.player.on("timeupdate",e),c.player.on("captionstrackchange",f),c.player.on("subtitlestrackchange",f),{el:function(){return c.widget.el()},setTrack:c.widget.setTrack}};b.plugin("transcript",k)}(window,videojs);
\ No newline at end of file
+!function(a,b){"use strict";!function(){for(var b=0,c=["ms","moz","webkit","o"],d=0;d1)throw Error("Second argument not supported");if("object"!=typeof b)throw TypeError("Argument must be an object");a.prototype=b;var c=new a;return a.prototype=null,c}}()),Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var c,d;if(null==this)throw new TypeError(" this is null or not defined");var e=Object(this),f=e.length>>>0;if("function"!=typeof a)throw new TypeError(a+" is not a function");for(arguments.length>1&&(c=b),d=0;f>d;){var g;d in e&&(g=e[d],a.call(c,g,d,e)),d++}}),"document"in self&&!("classList"in document.createElement("_"))&&!function(a){if("Element"in a){var b="classList",c="prototype",d=a.Element[c],e=Object,f=String[c].trim||function(){return this.replace(/^\s+|\s+$/g,"")},g=Array[c].indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(b in this&&this[b]===a)return b;return-1},h=function(a,b){this.name=a,this.code=DOMException[a],this.message=b},i=function(a,b){if(""===b)throw new h("SYNTAX_ERR","An invalid or illegal string was specified");if(/\s/.test(b))throw new h("INVALID_CHARACTER_ERR","String contains an invalid character");return g.call(a,b)},j=function(a){for(var b=f.call(a.getAttribute("class")||""),c=b?b.split(/\s+/):[],d=0,e=c.length;e>d;d++)this.push(c[d]);this._updateClassName=function(){a.setAttribute("class",this.toString())}},k=j[c]=[],l=function(){return new j(this)};if(h[c]=Error[c],k.item=function(a){return this[a]||null},k.contains=function(a){return a+="",-1!==i(this,a)},k.add=function(){var a,b=arguments,c=0,d=b.length,e=!1;do a=b[c]+"",-1===i(this,a)&&(this.push(a),e=!0);while(++cd?"0"+d:d,c=b>0&&10>c?"0"+c:c,b>0?b+":"+c+":"+d:c+":"+d},localize:function(a){return a},createEl:function(b,c){c=c||"";var d=document.createElement(b);return d.className=a.prefix+c,d},extend:function(a){var b=typeof a;if(!("function"===b||"object"===b&&a))return a;for(var c,d,e=1,f=arguments.length;f>e;e++){c=arguments[e];for(d in c)a[d]=c[d]}return a}}}(c),f={handlers_:[],on:function(a,b,c){if("function"!=typeof c)throw new TypeError("Callback is not a function.");this.handlers_.push([a,b,c])},trigger:function(a,b){this.handlers_.forEach(function(c){c[0]===a&&c[1]===b&&c[2].apply()})}},g=function(){var a=function(a){var b=this;a.addEventListener("scroll",function(){b.isAutoScrolling?b.isAutoScrolling=!1:(b.userIsScrolling=!0,a.classList.add("is-inuse"))}),a.addEventListener("mouseenter",function(){b.mouseIsOverTranscript=!0}),a.addEventListener("mouseleave",function(){b.mouseIsOverTranscript=!1,setTimeout(function(){b.mouseIsOverTranscript||(b.userIsScrolling=!1,a.classList.remove("is-inuse"))},1e3)})},b=function(b){return this.element=b,this.userIsScrolling=!1,this.mouseIsOverTranscript=!0,this.isAutoScrolling=!0,a.call(this,this.element),this},c=function(a,b,c,d){return b+c*Math.sin(Math.min(1,a/d)*(Math.PI/2))},d=function(a,b,d){var e=Date.now(),f=a.scrollTop,g=this;b=Math.max(0,b),b=Math.min(a.scrollHeight-a.clientHeight,b);var h=b-f,i=function(){var j=Date.now(),k=j-e;g.isAutoScrolling=!0,a.scrollTop=c(k,f,h,d),a.scrollTop!==b&&requestAnimationFrame(i,a)};requestAnimationFrame(i,a)},e=function(a){if(this.canScroll()){var b,c=a.parentElement,e=c.offsetTop+c.clientHeight,f=a.offsetTop+a.clientHeight,g=a.offsetTop-c.offsetTop,h=a.offsetTop+a.clientHeight-c.offsetTop;gc.scrollTop+c.clientHeight&&(b=f-e),void 0!==b&&c.scrollTop!==b&&d.call(this,c,b,400)}},f=function(){var a=this.element;return a.scrollHeight>a.offsetHeight},g=function(){return this.userIsScrolling};return{init:b,to:e,canScroll:f,inUse:g}}(c),h=function(a){return Object.create(g).init(a)},i=function(){var a;return{get:function(){var a=[];return c.tracks=c.player.textTracks(),c.tracks.forEach(function(b){("captions"===b.kind()||"subtitles"===b.kind())&&a.push(b)}),a},active:function(b){return b.forEach(function(b){return 2===b.mode()?(a=b,b):void 0}),a||b[0]}}}(c),j=function(a){var b={};b.element={},b.body={};var c=function(a,b){f.on(this,a,b)},d=function(a){f.trigger(this,a)},g=function(){var a=e.createEl("header","-header");return a.textContent=e.localize("Transcript"),a},i=function(){var b=e.createEl("select","-selector");return a.validTracks.forEach(function(a,c){var d=document.createElement("option");d.value=c,d.textContent=a.label()+" ("+a.language()+")",b.appendChild(d)}),b.addEventListener("change",function(){n(document.querySelector("#"+a.prefix+"-"+a.player.id()+" option:checked").value),d("trackchanged")}),b},j=function(b){var c=b.target.classList,d=b.target.getAttribute("data-begin")||b.target.parentElement.getAttribute("data-begin");void 0!==d&&null!==d&&("line"===a.settings.clickArea||"timestamp"===a.settings.clickArea&&c.contains(a.prefix+"-timestamp")||"text"===a.settings.clickArea&&c.contains(a.prefix+"-text"))&&a.player.currentTime(d)},k=function(a){var b=e.createEl("div","-line"),c=e.createEl("span","-timestamp"),d=e.createEl("span","-text");return b.setAttribute("data-begin",a.startTime),c.textContent=e.secondsToTime(a.startTime),d.innerHTML=a.text,b.appendChild(c),b.appendChild(d),b},l=function(b){"object"!=typeof b&&(b=a.player.textTracks()[b]);var c,d,f=e.createEl("div","-body"),g=document.createDocumentFragment(),i=function(){var a=b.cues();for(d=0;df&&g>c?e.classList.contains("is-active")||(e.classList.add("is-active"),!a.settings.autoscroll||a.settings.stopScrollWhenInUse&&b.body.scroll.inUse()||b.body.scroll.to(e)):e.classList.remove("is-active")},p=function(){return b.element};return{create:m,setTrack:n,setCue:o,el:p,on:c,trigger:d}}(c),k=function(a){c.player=this,c.validTracks=i.get(),c.currentTrack=i.active(c.validTracks),c.settings=b.util.mergeOptions(d,a),c.widget=j.create();var e=function(){c.widget.setCue(c.player.currentTime())},f=function(){c.currentTrack=i.active(c.validTracks),c.widget.setTrack(c.currentTrack)};if(!(c.validTracks.length>0))throw new Error("videojs-transcript: No tracks found!");return f(),c.player.on("timeupdate",e),c.settings.followPlayerTrack&&(c.player.on("captionstrackchange",f),c.player.on("subtitlestrackchange",f)),{el:function(){return c.widget.el()},setTrack:c.widget.setTrack}};b.plugin("transcript",k)}(window,videojs);
\ No newline at end of file
diff --git a/example.html b/example.html
index d66ffde..a43bb43 100644
--- a/example.html
+++ b/example.html
@@ -5,7 +5,9 @@
Video.js Transcript
-
+
+
+