src/dynamics/video_player/controlbar/controlbar.js
Scoped.define("module:VideoPlayer.Dynamics.Controlbar", [
"dynamics:Dynamic",
"base:TimeFormat",
"base:Comparators",
"base:Maths",
"base:Objs",
"browser:Dom",
"module:Assets",
"browser:Info",
"media:Player.Support",
"base:Async",
"base:Timers.Timer",
"browser:Events"
], [
"dynamics:Partials.StylesPartial",
"dynamics:Partials.ShowPartial",
"dynamics:Partials.IfPartial",
"dynamics:Partials.ClickPartial",
"dynamics:Partials.RepeatElementPartial"
], function(Class, TimeFormat, Comparators, Maths, Objs, Dom, Assets, Info, PlayerSupport, Async, Timer, DomEvents, scoped) {
return Class.extend({
scoped: scoped
}, function(inherited) {
return {
template: "<%= template(dirname + '/video_player_controlbar.html') %>",
attrs: {
"css": "ba-videoplayer",
"csscommon": "ba-commoncss",
"cssplayer": "ba-player",
"duration": 0,
"position": 0,
"cached": 0,
"volume": 1.0,
"expandedprogress": true,
"playing": false,
"rerecordable": false,
"submittable": false,
"manuallypaused": false,
"streams": [],
"currentstream": null,
"fullscreen": true,
"fullscreened": false,
"activitydelta": 0,
"hidebarafter": 5000,
"preventinteraction": false,
"revertposition": false,
"title": "",
"settingsmenubutton": false,
"hoveredblock": false, // Set true when mouse hovered
"allowtexttrackupload": false,
"thumbisvisible": false,
"chapterslist": [],
"showchaptertext": true,
"visibleindex": -1,
"title_hide_class": "",
"controlbar_hide_class": ""
},
computed: {
"currentstream_label:currentstream": function() {
var cs = this.get("currentstream");
return cs ? (cs.label ? cs.label : PlayerSupport.resolutionToLabel(cs.width, cs.height)) : "";
}
},
events: {
"change:activitydelta": function(value) {
if (this.get("prominent_title")) {
this.set("title_hide_class", "");
} else {
if (value > this.get("hidebarafter") && this.get("hideoninactivity")) {
this.set("title_hide_class", this.get("cssplayer") + "-dashboard-hidden");
} else {
this.set("title_hide_class", "");
}
}
if (value > this.get("hidebarafter") && this.get("hideoninactivity")) {
this.set("controlbar_hide_class", this.get("cssplayer") + "-dashboard-hidden");
} else {
this.set("controlbar_hide_class", "");
}
}
},
functions: {
formatTime: function(time) {
time = Math.max(time || 0, 0.1);
return TimeFormat.format(TimeFormat.ELAPSED_MINUTES_SECONDS, time * 1000);
},
startUpdatePosition: function(event) {
if (this.get("disableseeking")) return;
// https://chromestatus.com/feature/5093566007214080
// touchstart and touchmove listeners added to the document will default to passive:true
if (event[0] && event[0].type.indexOf("touch") === -1)
event[0].preventDefault();
if (!this.__parent.get("playing") && this.__parent.player && !this.get("manuallypaused"))
this.__parent.player.play();
var target = event[0].currentTarget;
this.set("dimensions", target.getBoundingClientRect());
this.set("isseeking", true);
this.call("progressUpdatePosition", event[0]);
var events = this.get("events");
events.on(document, "mousemove touchmove", function(e) {
if (e.type.indexOf("touch") === -1)
e.preventDefault();
this.call("progressUpdatePosition", e);
}, this);
events.on(document, "mouseup touchend", function(e) {
if (e.type.indexOf("touch") === -1)
e.preventDefault();
this.call("stopUpdatePosition");
events.off(document, "mouseup touchend mousemove touchmove");
}, this);
},
progressUpdatePosition: function(event) {
var _dyn = this.__parent;
// Mouse or Touch Event
var clientX = event.clientX === 0 ? 0 : event.clientX || event.targetTouches[0].clientX;
var dimensions = this.get("dimensions");
var percentageFromStart = -1;
if (clientX < dimensions.left) percentageFromStart = 0;
else if (clientX > (dimensions.left + dimensions.width)) percentageFromStart = 1;
else {
percentageFromStart = (clientX - dimensions.left) / (dimensions.width || 1);
}
var onDuration = this.get("duration") * percentageFromStart;
if (!this.get("isseeking") && typeof _dyn.__trackTags === 'undefined')
return;
var player = _dyn.player;
if (typeof _dyn.__trackTags !== 'undefined') {
if (this.__parent.__trackTags.hasThumbs) {
if (this.get("visibleindex") > -1 && this.get("showchaptertext"))
return;
var _index;
var _trackTags = _dyn.__trackTags;
var _cuesCount = _dyn.get("thumbcuelist").length;
if (onDuration > 0) {
_index = Math.floor(_cuesCount * percentageFromStart);
for (var i = _index - 2; i < _cuesCount; i++) {
if (_dyn.get("thumbcuelist")[i]) {
var _cue = _dyn.get("thumbcuelist")[i];
if (_cue.startTime < onDuration && _cue.endTime > onDuration) {
_trackTags.showDurationThumb(i, clientX, onDuration);
break;
}
}
}
} else {
_index = Math.floor(_cuesCount * percentageFromStart);
_trackTags.showDurationThumb(_index, clientX);
}
this.set("thumbisvisible", true);
this.activeElement().appendChild(_trackTags.thumbContainer);
}
}
if (this.get("isseeking")) {
this.set("position", onDuration);
if (typeof player._broadcastingState !== 'undefined') {
if (player._broadcastingState.googleCastConnected) {
player.trigger('google-cast-seeking', this.get("position"));
return;
}
}
this.trigger("position", this.get("position"));
}
},
stopUpdatePosition: function() {
this.set("isseeking", false);
this._hideThumb();
},
startUpdateVolume: function(args, element) {
var event = args[0];
var moveEvent = event.type === "mousedown" ? "mousemove" : "touchmove";
var stopEvent = event.type === "mousedown" ? "mouseup" : "touchend";
var domRect = element.getBoundingClientRect();
event.preventDefault();
var updateVolume = function(event) {
var volume;
event.preventDefault();
if (domRect.width > domRect.height) {
// Horizontal slider
var x = event.clientX;
if (!x && event.touches) x = event.touches[0].clientX;
volume = Maths.clamp((x - domRect.x) / domRect.width, 0, 1);
} else {
// Vertical slider
var y = event.clientY;
if (!y && event.touches) y = event.touches[0].clientY;
volume = Maths.clamp((domRect.bottom - y) / domRect.height, 0, 1);
}
this.trigger("volume", volume);
}.bind(this);
updateVolume(event);
document.addEventListener(moveEvent, updateVolume);
document.addEventListener(stopEvent, function() {
document.removeEventListener(moveEvent, updateVolume);
}, {
once: true
});
},
showChapterText: function(chapter) {
this.set("visibleindex", chapter.index);
this._hideThumb();
},
hideChapterText: function() {
this.set("visibleindex", -1);
},
stopVerticallyUpdateVolume: function(event) {
event[0].preventDefault();
this.set("_updateVolume", false);
},
play: function() {
this.trigger("play");
},
pause: function() {
this.trigger("pause");
},
toggle_player: function() {
this.trigger("toggle_player");
},
toggle_volume: function() {
this.trigger("toggle_volume");
if (this.get("unmuteonclick")) return;
var volume = this.get("volume");
if (volume > 0) {
this.__oldVolume = volume;
volume = 0;
} else {
volume = this.__oldVolume || 1;
}
this.trigger("volume", volume);
},
toggle_position_info: function() {
this.set("revertposition", !this.get("revertposition"));
},
toggle_fullscreen: function() {
this.trigger("fullscreen");
},
toggle_settings_menu: function() {
this.trigger("settings_menu");
},
rerecord: function() {
this.trigger("rerecord");
},
seek: function(position) {
this.trigger("seek", position);
},
set_volume: function(volume) {
this.trigger("volume", volume);
},
submit: function() {
this.set("submittable", false);
this.set("rerecordable", false);
this.trigger("submit");
},
select_frame: function() {
var player = this.parent().player;
var position = player.position();
var imageSelected = false;
player.pause();
var timer = new Timer({
context: this,
start: false,
fire: function() {
player.setPosition(position);
player.play();
player.pause();
if (player.loaded()) {
player.createSnapshotPromise().success(function(blob) {
timer.stop();
if (!imageSelected) {
imageSelected = true;
this.parent().trigger("image-selected", blob);
}
}, this);
}
},
delay: 300,
fire_max: 5,
destroy_on_stop: true
});
if (player.loaded()) {
player.createSnapshotPromise().success(function(blob) {
this.parent().trigger("image-selected", blob);
}, this).error(function() {
timer.start();
}, this);
} else {
timer.start();
}
},
toggle_stream: function() {
var streams = this.get("streams");
var current = streams.length - 1;
streams.forEach(function(stream, i) {
if (Comparators.deepEqual(stream, this.get("currentstream")))
current = i;
}, this);
this.set("currentstream", streams[(current + 1) % streams.length]);
},
show_airplay_devices: function() {
var dynamic = this.__parent;
if (dynamic.player._broadcastingState.airplayConnected) {
dynamic._broadcasting.lookForAirplayDevices(dynamic.player._element);
}
},
// Start ro stop showing CC content
toggle_tracks: function() {
return this.parent().toggleTrackTags();
},
// Hover on CC button in controller
hover_cc: function(hover) {
// Not show CC on hover during settings block is open
// Reason why use parent not local settingsmenu_active,
// is that settings model also has to be aware it's state. So we need as a global variable
if (this.parent().get("settingsmenu_active")) return;
Async.eventually(function() {
this.parent().set("tracksshowselection", hover);
}, this, 300);
},
// Move between elements which has tabIndex attribute
tab_index_move: function(ev, nextSelector, focusingSelector) {
this.trigger("tab_index_move", ev[0], nextSelector, focusingSelector);
},
// Hover on block
hover_block: function(hover) {
Async.eventually(function() {
this.parent().set("hoveredblock", hover);
}, this, 300);
},
toggle_settings: function() {
this.trigger("toggle_settings");
},
trim: function() {
this.parent().trigger("video-trimmed", this.get("trimstart") || 0, this.get("trimend") || this.get("duration"));
},
addTrimmingEventListeners: function() {
var events = this.get("events");
var trimStartMarker = this.activeElement().querySelector('[data-selector="trim-start-marker"]');
var trimEndMarker = this.activeElement().querySelector('[data-selector="trim-end-marker"]');
events.on(trimStartMarker, "mousedown touchstart", function(e) {
e.preventDefault();
e.stopPropagation();
var boundingRect = this.get("progressbarElement").getBoundingClientRect();
this.call("updateTrimStart", e, boundingRect);
events.on(document, "mousemove touchmove", function(e) {
e.preventDefault();
this.call("updateTrimStart", e, boundingRect);
}, this);
events.on(document, "mouseup touchend", function(e) {
e.preventDefault();
events.off(document, "mouseup touchend mousemove touchmove");
}, this);
}, this);
events.on(trimEndMarker, "mousedown touchstart", function(e) {
e.preventDefault();
e.stopPropagation();
var boundingRect = this.get("progressbarElement").getBoundingClientRect();
this.call("updateTrimEnd", e, boundingRect);
events.on(document, "mousemove touchmove", function(e) {
e.preventDefault();
this.call("updateTrimEnd", e, boundingRect);
}, this);
events.on(document, "mouseup touchend", function(e) {
e.preventDefault();
events.off(document, "mouseup touchend mousemove touchmove");
}, this);
}, this);
},
updateTrimStart: function(event, boundingRect) {
var position = this.call("calculatePosition", event, boundingRect);
var trimEnd = this.get("trimend") || this.get("duration");
var timeMinLimit = this.get("timeminlimit") || 1;
if (position > trimEnd - timeMinLimit) {
this.set("trimstart", trimEnd - timeMinLimit);
} else {
this.set("trimstart", position);
}
},
updateTrimEnd: function(event, boundingRect) {
var position = this.call("calculatePosition", event, boundingRect);
var trimStart = this.get("trimstart") || 0;
var timeMinLimit = this.get("timeminlimit") || 1;
if (position < trimStart + timeMinLimit) {
this.set("trimend", trimStart + timeMinLimit);
} else {
this.set("trimend", position);
}
},
getClientX: function(event) {
return event.clientX === 0 ? 0 : event.clientX || event.targetTouches[0].clientX;
},
calculatePosition: function(event, dimensions) {
var clientX = this.call("getClientX", event);
var percentageFromStart = -1;
if (clientX < dimensions.left) percentageFromStart = 0;
else if (clientX > (dimensions.left + dimensions.width)) percentageFromStart = 1;
else {
percentageFromStart = (clientX - dimensions.left) / (dimensions.width || 1);
}
return this.get("duration") * percentageFromStart;
}
},
_hideThumb: function() {
if (typeof this.__parent.__trackTags !== "undefined") {
if (this.__parent.__trackTags.hasThumbs && this.get("thumbisvisible")) {
this.set("thumbisvisible", false);
this.__parent.__trackTags.hideDurationThumb();
}
}
},
create: function() {
this.set("ismobile", Info.isMobile());
this.set("events", new DomEvents());
this.set("progressbarElement", this.activeElement().querySelector('[data-selector="progress-bar-inner"]'));
if (this.get("trimmingmode"))
this.call("addTrimmingEventListeners");
}
};
})
.register("ba-videoplayer-controlbar")
.registerFunctions({
/*<%= template_function_cache(dirname + '/video_player_controlbar.html') %>*/
})
.attachStringTable(Assets.strings)
.addStrings({
"video-progress": "Progress",
"rerecord-video": "Redo?",
"submit-video": "Confirm",
"play-video": "Play",
"pause-video": "Pause",
"pause-video-disabled": "Pause not supported",
"elapsed-time": "Elapsed time",
"total-time": "Total length of",
"fullscreen-video": "Enter fullscreen",
"volume-button": "Set volume",
"volume-mute": "Mute sound",
"volume-unmute": "Unmute sound",
"change-resolution": "Change resolution",
"exit-fullscreen-video": "Exit fullscreen",
"close-tracks": "Close CC",
"show-tracks": "Show CC",
"player-speed": "Player speed",
"settings": "Settings",
"airplay": "Airplay",
"airplay-icon": "Airplay icon.",
"remaining-time": "Remaining time",
"select-frame": "Select",
"trim-video": "Trim"
});
});