1
This commit is contained in:
@@ -1,511 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
(function ($, videojs, undefined) {
|
||||
// default setting
|
||||
var defaultSetting = {
|
||||
markerStyle: {
|
||||
'width': '8px',
|
||||
'border-radius': '30%',
|
||||
'background':'url(/ananas/modules/video/marker/point.png) no-repeat center center',
|
||||
'background-size':'100%'
|
||||
},
|
||||
markerStyle2: {
|
||||
'width': '8px',
|
||||
'border-radius': '30%',
|
||||
'background':'url(/ananas/modules/video/marker/point2.png) no-repeat center center',
|
||||
'background-size':'100%'
|
||||
},
|
||||
markerTip: {
|
||||
display: true,
|
||||
text: function text(marker) {
|
||||
return "Break: " + marker.text;
|
||||
},
|
||||
time: function time(marker) {
|
||||
return marker.time;
|
||||
},
|
||||
timeFormat: function timeFormate(marker) {
|
||||
var a = marker.time;
|
||||
if (a < 0) {
|
||||
return "NaN";
|
||||
}
|
||||
var hh = parseInt(a / 3600);
|
||||
if (hh == 0) {
|
||||
hh = "";
|
||||
} else {
|
||||
hh = hh < 10 ? "0" + hh : hh;
|
||||
}
|
||||
var mm = parseInt((a - hh * 3600) / 60);
|
||||
if (mm < 10) {
|
||||
mm = "0" + mm;
|
||||
}
|
||||
var ss = parseInt((a - hh * 3600) % 60);
|
||||
if (ss < 10) {
|
||||
ss = "0" + ss;
|
||||
}
|
||||
return hh > 0 ? hh + ":" + mm + ":" + ss : mm + ":" + ss;
|
||||
|
||||
},
|
||||
timeFormat1: function timeFormat1(a) {
|
||||
if (a < 0) {
|
||||
return "NaN";
|
||||
}
|
||||
var hh = parseInt(a / 3600);
|
||||
if (hh == 0) {
|
||||
hh = "";
|
||||
} else {
|
||||
hh = hh < 10 ? "0" + hh : hh;
|
||||
}
|
||||
var mm = parseInt((a - hh * 3600) / 60);
|
||||
if (mm < 10) {
|
||||
mm = "0" + mm;
|
||||
}
|
||||
var ss = parseInt((a - hh * 3600) % 60);
|
||||
if (ss < 10) {
|
||||
ss = "0" + ss;
|
||||
}
|
||||
return hh > 0 ? hh + ":" + mm + ":" + ss : mm + ":" + ss;
|
||||
|
||||
}
|
||||
},
|
||||
breakOverlay: {
|
||||
display: false,
|
||||
displayTime: 3,
|
||||
text: function text(marker) {
|
||||
return "Break overlay: " + marker.overlayText;
|
||||
},
|
||||
style: {
|
||||
'width': '100%',
|
||||
'height': '20%',
|
||||
'background-color': 'rgba(0,0,0,0.7)',
|
||||
'color': 'white',
|
||||
'font-size': '17px'
|
||||
}
|
||||
},
|
||||
onMarkerClick: function onMarkerClick(marker) {},
|
||||
onMarkerReached: function onMarkerReached(marker, index) {},
|
||||
markers: []
|
||||
};
|
||||
|
||||
// create a non-colliding random number
|
||||
function generateUUID() {
|
||||
var d = new Date().getTime();
|
||||
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
var r = (d + Math.random() * 16) % 16 | 0;
|
||||
d = Math.floor(d / 16);
|
||||
return (c == 'x' ? r : r & 0x3 | 0x8).toString(16);
|
||||
});
|
||||
return uuid;
|
||||
};
|
||||
|
||||
var NULL_INDEX = -1;
|
||||
|
||||
function registerVideoJsMarkersPlugin(options) {
|
||||
/**
|
||||
* register the markers plugin (dependent on jquery)
|
||||
*/
|
||||
|
||||
var setting = $.extend(true, {}, defaultSetting, options),
|
||||
markersMap = {},
|
||||
markersList = [],
|
||||
// list of markers sorted by time
|
||||
videoWrapper = $(this.el()),
|
||||
currentMarkerIndex = NULL_INDEX,
|
||||
player = this,
|
||||
markerTip = null,
|
||||
breakOverlay = null,
|
||||
overlayIndex = NULL_INDEX;
|
||||
|
||||
function sortMarkersList() {
|
||||
// sort the list by time in asc order
|
||||
markersList.sort(function (a, b) {
|
||||
return setting.markerTip.time(a) - setting.markerTip.time(b);
|
||||
});
|
||||
}
|
||||
|
||||
function addMarkers(newMarkers) {
|
||||
newMarkers.forEach(function (marker) {
|
||||
marker.key = generateUUID();
|
||||
|
||||
if(marker.type == "KnowledgePoint" && marker.endTime > 0) {
|
||||
videoWrapper.find('.vjs-progress-holder').append(createMarkerDiv(marker)).append(createPeriodDiv(marker));
|
||||
}else {
|
||||
videoWrapper.find('.vjs-progress-holder').append(createMarkerDiv(marker));
|
||||
}
|
||||
// store marker in an internal hash map
|
||||
markersMap[marker.key] = marker;
|
||||
markersList.push(marker);
|
||||
});
|
||||
|
||||
sortMarkersList();
|
||||
}
|
||||
|
||||
function getPosition(marker) {
|
||||
return setting.markerTip.time(marker) / player.duration() * 100;
|
||||
}
|
||||
function getTipPosition(marker) {
|
||||
var percent = setting.markerTip.time(marker) / player.duration();
|
||||
if (percent * setting.vjsProgressWidth < parseFloat(markerTip.width()) / 2) {
|
||||
return "-20px";
|
||||
} else if (percent * setting.vjsProgressWidth + parseFloat(markerTip.width()) / 2 > setting.vjsProgressWidth){
|
||||
return setting.vjsProgressWidth - parseFloat(markerTip.width()) / 2 + "px";
|
||||
} else {
|
||||
return setting.markerTip.time(marker) / player.duration() * 100 + "%";
|
||||
}
|
||||
}
|
||||
function getTipMarginLeft(marker) {
|
||||
var percent = setting.markerTip.time(marker) / player.duration();
|
||||
if (percent * setting.vjsProgressWidth < parseFloat(markerTip.width()) / 2) {
|
||||
return "0px";
|
||||
} else {
|
||||
return -parseFloat(markerTip.outerWidth()) / 2 + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
function createMarkerDiv(marker) {
|
||||
var markerDiv = $("<div class='vjs-marker'></div>");
|
||||
var styleStr = setting.markerStyle;
|
||||
if(marker.text == "片头" || marker.text == "片尾") {
|
||||
styleStr = setting.markerStyle2;
|
||||
}
|
||||
|
||||
markerDiv.css(styleStr).css({
|
||||
"margin-left": -parseFloat(markerDiv.css("width")) / 2 + 'px',
|
||||
"left": getPosition(marker) + '%'
|
||||
}).attr("data-marker-key", marker.key).attr("data-marker-time", setting.markerTip.time(marker));
|
||||
|
||||
// add user-defined class to marker
|
||||
if (marker.class) {
|
||||
markerDiv.addClass(marker.class);
|
||||
}
|
||||
|
||||
// bind click event to seek to marker time
|
||||
markerDiv.on('click', function (e) {
|
||||
var preventDefault = false;
|
||||
if (typeof setting.onMarkerClick === "function") {
|
||||
// if return false, prevent default behavior
|
||||
preventDefault = setting.onMarkerClick(marker) === false;
|
||||
}
|
||||
|
||||
if (!preventDefault) {
|
||||
var key = $(this).data('marker-key');
|
||||
player.currentTime(setting.markerTip.time(markersMap[key]));
|
||||
}
|
||||
});
|
||||
|
||||
if (setting.markerTip.display) {
|
||||
registerMarkerTipHandler(markerDiv);
|
||||
}
|
||||
|
||||
return markerDiv;
|
||||
}
|
||||
|
||||
function createPeriodDiv(marker) {
|
||||
var periodDiv = $('<div class="marker-period"></div>');
|
||||
var left = getPosition(marker);
|
||||
var right = marker.endTime / player.duration() * 100;
|
||||
var width = right - left;
|
||||
periodDiv.css({
|
||||
"margin-left": '-4px',
|
||||
"left": left + '%',
|
||||
"width": width + "%"
|
||||
}).attr("data-marker-key", marker.key);
|
||||
|
||||
|
||||
if (setting.markerTip.display) {
|
||||
registerPeriodTipHandler(periodDiv);
|
||||
}
|
||||
|
||||
return periodDiv;
|
||||
}
|
||||
|
||||
function updateMarkers() {
|
||||
// update UI for markers whose time changed
|
||||
markersList.forEach(function (marker) {
|
||||
var markerDiv = videoWrapper.find(".vjs-marker[data-marker-key='" + marker.key + "']");
|
||||
var markerTime = setting.markerTip.time(marker);
|
||||
|
||||
if (markerDiv.data('marker-time') !== markerTime) {
|
||||
markerDiv.css({ "left": getPosition(marker) + '%' }).attr("data-marker-time", markerTime);
|
||||
}
|
||||
});
|
||||
sortMarkersList();
|
||||
}
|
||||
|
||||
function removeMarkers(indexArray) {
|
||||
// reset overlay
|
||||
if (!!breakOverlay) {
|
||||
overlayIndex = NULL_INDEX;
|
||||
breakOverlay.css("visibility", "hidden");
|
||||
}
|
||||
currentMarkerIndex = NULL_INDEX;
|
||||
|
||||
var deleteIndexList = [];
|
||||
indexArray.forEach(function (index) {
|
||||
var marker = markersList[index];
|
||||
if (marker) {
|
||||
// delete from memory
|
||||
delete markersMap[marker.key];
|
||||
deleteIndexList.push(index);
|
||||
|
||||
// delete from dom
|
||||
videoWrapper.find(".vjs-marker[data-marker-key='" + marker.key + "']").remove();
|
||||
}
|
||||
});
|
||||
|
||||
// clean up markers array
|
||||
deleteIndexList.reverse();
|
||||
deleteIndexList.forEach(function (deleteIndex) {
|
||||
markersList.splice(deleteIndex, 1);
|
||||
});
|
||||
|
||||
// sort again
|
||||
sortMarkersList();
|
||||
}
|
||||
|
||||
// attach hover event handler
|
||||
function registerMarkerTipHandler(markerDiv) {
|
||||
markerDiv.on('mouseover', function () {
|
||||
var marker = markersMap[$(markerDiv).data('marker-key')];
|
||||
|
||||
if (!!markerTip) {
|
||||
var sTime = setting.markerTip.timeFormat(marker);
|
||||
var left = getTipPosition(marker);
|
||||
|
||||
var endText = '';
|
||||
if (marker.type == "KnowledgePoint" && marker.endTime > 0) {
|
||||
endText = '-' + setting.markerTip.timeFormat1(marker.endTime);
|
||||
}
|
||||
var textDiv = '<div class="vjs-tip-title">' + setting.markerTip.text(marker) + '</div><div class="vjs-tip-time">' + sTime + endText + '</div>';
|
||||
markerTip.find('.vjs-tip-inner').html(textDiv);
|
||||
if (marker.type == "KnowledgePoint") {
|
||||
markerTip.find('.vjs-tip-inner').addClass("vjs-tip-knowledge");
|
||||
}
|
||||
|
||||
// margin-left needs to minus the padding length to align correctly with the marker
|
||||
markerTip.css({
|
||||
"left": left,
|
||||
"margin-left": getTipMarginLeft(marker),
|
||||
"visibility": "visible"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
markerDiv.on('mouseout', function () {
|
||||
!!markerTip && markerTip.css("visibility", "hidden");
|
||||
});
|
||||
}
|
||||
|
||||
function registerPeriodTipHandler(periodDiv) {
|
||||
periodDiv.on('mouseover', function () {
|
||||
if (!!markerTip) {
|
||||
var marker = markersMap[$(periodDiv).data('marker-key')];
|
||||
var left = $('.vjs-mouse-display')[0].style.left;
|
||||
var sTime = $('.vjs-mouse-display .vjs-time-tooltip').text()
|
||||
var textDiv = '<div class="vjs-tip-title">' + setting.markerTip.text(marker) + '</div><div class="vjs-tip-time">' + sTime + '</div>';
|
||||
markerTip.find('.vjs-tip-inner').html(textDiv);
|
||||
if (marker.type == "KnowledgePoint") {
|
||||
markerTip.find('.vjs-tip-inner').addClass("vjs-tip-knowledge");
|
||||
}
|
||||
|
||||
// margin-left needs to minus the padding length to align correctly with the marker
|
||||
markerTip.css({
|
||||
"left": left,
|
||||
"margin-left": getTipMarginLeft(marker),
|
||||
"visibility": "visible"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
periodDiv.on('mouseout', function () {
|
||||
!!markerTip && markerTip.css("visibility", "hidden");
|
||||
});
|
||||
}
|
||||
|
||||
function initializeMarkerTip() {
|
||||
markerTip = $("<div class='vjs-tip'><div class='vjs-tip-arrow'></div><div class='vjs-tip-inner'></div></div>");
|
||||
videoWrapper.find('.vjs-progress-holder').append(markerTip);
|
||||
|
||||
var markerTip2 = $("<div class='vjs-tip2'><div class='vjs-tip-arrow2'></div><div class='vjs-tip-inner2'></div></div>");
|
||||
videoWrapper.find('.vjs-progress-holder').append(markerTip2);
|
||||
}
|
||||
|
||||
// show or hide break overlays
|
||||
function updateBreakOverlay() {
|
||||
if (!setting.breakOverlay.display || currentMarkerIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentTime = player.currentTime();
|
||||
var marker = markersList[currentMarkerIndex];
|
||||
var markerTime = setting.markerTip.time(marker);
|
||||
|
||||
if (currentTime >= markerTime && currentTime <= markerTime + setting.breakOverlay.displayTime) {
|
||||
if (overlayIndex !== currentMarkerIndex) {
|
||||
overlayIndex = currentMarkerIndex;
|
||||
breakOverlay && breakOverlay.find('.vjs-break-overlay-text').html(setting.breakOverlay.text(marker));
|
||||
}
|
||||
|
||||
breakOverlay && breakOverlay.css('visibility', "visible");
|
||||
} else {
|
||||
overlayIndex = NULL_INDEX;
|
||||
breakOverlay && breakOverlay.css("visibility", "hidden");
|
||||
}
|
||||
}
|
||||
|
||||
// problem when the next marker is within the overlay display time from the previous marker
|
||||
function initializeOverlay() {
|
||||
breakOverlay = $("<div class='vjs-break-overlay'><div class='vjs-break-overlay-text'></div></div>").css(setting.breakOverlay.style);
|
||||
videoWrapper.append(breakOverlay);
|
||||
overlayIndex = NULL_INDEX;
|
||||
}
|
||||
|
||||
function onTimeUpdate() {
|
||||
onUpdateMarker();
|
||||
updateBreakOverlay();
|
||||
options.onTimeUpdateAfterMarkerUpdate && options.onTimeUpdateAfterMarkerUpdate();
|
||||
}
|
||||
|
||||
function onUpdateMarker() {
|
||||
/*
|
||||
check marker reached in between markers
|
||||
the logic here is that it triggers a new marker reached event only if the player
|
||||
enters a new marker range (e.g. from marker 1 to marker 2). Thus, if player is on marker 1 and user clicked on marker 1 again, no new reached event is triggered)
|
||||
*/
|
||||
if (!markersList.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var getNextMarkerTime = function getNextMarkerTime(index) {
|
||||
if (index < markersList.length - 1) {
|
||||
return setting.markerTip.time(markersList[index + 1]);
|
||||
}
|
||||
// next marker time of last marker would be end of video time
|
||||
return player.duration();
|
||||
};
|
||||
var currentTime = player.currentTime();
|
||||
var newMarkerIndex = NULL_INDEX;
|
||||
|
||||
if (currentMarkerIndex !== NULL_INDEX) {
|
||||
// check if staying at same marker
|
||||
var nextMarkerTime = getNextMarkerTime(currentMarkerIndex);
|
||||
if (currentTime >= setting.markerTip.time(markersList[currentMarkerIndex]) && currentTime < nextMarkerTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check for ending (at the end current time equals player duration)
|
||||
if (currentMarkerIndex === markersList.length - 1 && currentTime === player.duration()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// check first marker, no marker is selected
|
||||
if (currentTime < setting.markerTip.time(markersList[0])) {
|
||||
newMarkerIndex = NULL_INDEX;
|
||||
} else {
|
||||
// look for new index
|
||||
for (var i = 0; i < markersList.length; i++) {
|
||||
nextMarkerTime = getNextMarkerTime(i);
|
||||
if (currentTime >= setting.markerTip.time(markersList[i]) && currentTime < nextMarkerTime) {
|
||||
newMarkerIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set new marker index
|
||||
if (newMarkerIndex !== currentMarkerIndex) {
|
||||
// trigger event if index is not null
|
||||
if (newMarkerIndex !== NULL_INDEX && options.onMarkerReached) {
|
||||
options.onMarkerReached(markersList[newMarkerIndex], newMarkerIndex);
|
||||
}
|
||||
currentMarkerIndex = newMarkerIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// setup the whole thing
|
||||
function initialize() {
|
||||
if (setting.markerTip.display) {
|
||||
initializeMarkerTip();
|
||||
}
|
||||
|
||||
// remove existing markers if already initialized
|
||||
player.markers.removeAll();
|
||||
addMarkers(options.markers);
|
||||
|
||||
if (setting.breakOverlay.display) {
|
||||
initializeOverlay();
|
||||
}
|
||||
onTimeUpdate();
|
||||
player.on("timeupdate", onTimeUpdate);
|
||||
}
|
||||
|
||||
// setup the plugin after we loaded video's meta data
|
||||
player.on("loadedmetadata", function () {
|
||||
initialize();
|
||||
});
|
||||
|
||||
// exposed plugin API
|
||||
player.markers = {
|
||||
getMarkers: function getMarkers() {
|
||||
return markersList;
|
||||
},
|
||||
next: function next() {
|
||||
// go to the next marker from current timestamp
|
||||
var currentTime = player.currentTime();
|
||||
for (var i = 0; i < markersList.length; i++) {
|
||||
var markerTime = setting.markerTip.time(markersList[i]);
|
||||
if (markerTime > currentTime) {
|
||||
player.currentTime(markerTime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
prev: function prev() {
|
||||
// go to previous marker
|
||||
var currentTime = player.currentTime();
|
||||
for (var i = markersList.length - 1; i >= 0; i--) {
|
||||
var markerTime = setting.markerTip.time(markersList[i]);
|
||||
// add a threshold
|
||||
if (markerTime + 0.5 < currentTime) {
|
||||
player.currentTime(markerTime);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
add: function add(newMarkers) {
|
||||
// add new markers given an array of index
|
||||
addMarkers(newMarkers);
|
||||
},
|
||||
remove: function remove(indexArray) {
|
||||
// remove markers given an array of index
|
||||
removeMarkers(indexArray);
|
||||
},
|
||||
removeAll: function removeAll() {
|
||||
var indexArray = [];
|
||||
for (var i = 0; i < markersList.length; i++) {
|
||||
indexArray.push(i);
|
||||
}
|
||||
removeMarkers(indexArray);
|
||||
},
|
||||
updateTime: function updateTime() {
|
||||
// notify the plugin to update the UI for changes in marker times
|
||||
updateMarkers();
|
||||
},
|
||||
reset: function reset(newMarkers) {
|
||||
// remove all the existing markers and add new ones
|
||||
player.markers.removeAll();
|
||||
addMarkers(newMarkers);
|
||||
},
|
||||
destroy: function destroy() {
|
||||
// unregister the plugins and clean up even handlers
|
||||
player.markers.removeAll();
|
||||
breakOverlay && breakOverlay.remove();
|
||||
markerTip && markerTip.remove();
|
||||
player.off("timeupdate", updateBreakOverlay);
|
||||
delete player.markers;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
videojs.registerPlugin('markers', registerVideoJsMarkersPlugin);
|
||||
})(jQuery, window.videojs);
|
||||
Reference in New Issue
Block a user