/**
* @module Player
*/
/**
* I am the ViewVideo. I am the most important user interface component. I contain
* all the visual elements of the hypervideo player, like the main <video> element,
* the containers for overlays, videolinks and annotations, their respective timelines
* and the player control elements.
*
* When I am initialized, I prepare all DOM elements and set up their event listeners.
*
* @class ViewVideo
* @static
*/
FrameTrail.defineModule('ViewVideo', function(){
var domElement = $( '<div id="ViewVideo">'
+ ' <div id="SlideArea">'
+ ' <div id="VideolinkContainer"></div>'
+ ' <div id="VideolinkTiles">'
+ ' <div class="tileSlider"></div>'
+ ' </div>'
+ ' <div id="PlayerContainer">'
+ ' <div id="VideolinkTimeline" class="timeline"></div>'
+ ' <div id="PlayerProgress">'
+ ' </div>'
+ ' <div id="HypervideoContainer">'
+ ' <div id="VideoContainer">'
+ ' <div id="Hypervideo">'
+ ' <video id="Video"></video>'
+ ' <div id="OverlayContainer"></div>'
+ ' <div id="CaptionContainer"></div>'
+ ' </div>'
+ ' <div id="ExpandButton">'
+ ' <div id="ExpandLabel">Expand</div>'
+ ' </div>'
+ ' <div id="WorkingIndicator">'
+ ' <div class="workingSpinner"></div>'
+ ' </div>'
+ ' </div>'
+ ' <div id="InfoAreaRight">'
+ ' <div id="AnnotationPreviewContainer"></div>'
+ ' <div id="EditPropertiesContainer"></div>'
+ ' </div>'
+ ' </div>'
+ ' <div id="OverlayTimeline" class="timeline"></div>'
+ ' <div id="Controls">'
+ ' <div id="LeftControlPanel">'
+ ' <button class="playerControl" id="PlayButton"></button>'
+ ' <div class="playerControl" id="TimeDisplay">'
+ ' <div id="CurrentTime">00:00</div>'
+ ' <div id="TotalDuration">00:00</div>'
+ ' </div>'
+ ' </div>'
+ ' <div id="RightControlPanel">'
+ ' <div class="playerControl" id="SettingsButton">'
+ ' <div id="SettingsContainer">'
+ ' <div id="LayoutSettingsWrapper">'
+ ' <div data-config="hv_config_videolinksVisible">Videolinks'
+ ' <div data-config="hv_config_annotationsPosition"></div>'
+ ' </div>'
+ ' <div id="PlayerWrapper">'
+ ' <div data-config="hv_config_overlaysVisible">Overlays</div>'
+ ' <div data-config="hv_config_annotationPreviewVisible">Annotation-Preview</div>'
+ ' </div>'
+ ' <div data-config="hv_config_annotationsVisible">Annotations'
+ ' <div data-config="hv_config_annotationsPosition"></div>'
+ ' </div>'
+ ' </div>'
+ ' <div id="GenericSettingsWrapper">Layout Mode'
+ ' <div data-config="hv_config_slidingMode">'
+ ' <div class="slidingMode" data-value="adjust">Adjust</div>'
+ ' <div class="slidingMode" data-value="overlay">Overlay</div>'
+ ' </div>'
+ ' </div>'
+ ' </div>'
+ ' </div>'
+ ' <div class="playerControl" id="CaptionsButton">'
+ ' <div id="CaptionSelectContainer">'
+ ' <div class="captionSelect none" data-lang="" data-config="hv_config_captionsVisible">None</div>'
+ ' <div id="CaptionSelectList"></div>'
+ ' </div>'
+ ' </div>'
+ ' <button class="playerControl" id="VolumeButton"></button>'
+ ' <button class="playerControl" id="FullscreenButton"></button>'
+ ' </div>'
+ ' </div>'
+ ' <div id="AnnotationTimeline" class="timeline"></div>'
+ ' </div>'
+ ' <div id="AnnotationTiles">'
+ ' <div class="tileSlider"></div>'
+ ' </div>'
+ ' <div id="AnnotationContainer">'
+ ' <div id="AnnotationSlider"></div>'
+ ' </div>'
+ ' </div>'
+ ' <div id="EditingOptions"></div>'
+ '</div>'),
slideArea = domElement.children('#SlideArea'),
PlayerContainer = domElement.find('#PlayerContainer'),
HypervideoContainer = domElement.find('#HypervideoContainer'),
VideoContainer = domElement.find('#VideoContainer'),
Hypervideo = domElement.find('#Hypervideo'),
CaptionContainer = domElement.find('#CaptionContainer'),
VideolinkContainer = domElement.find('#VideolinkContainer'),
VideolinkTiles = domElement.find('#VideolinkTiles'),
VideolinkTileSlider = domElement.find('#VideolinkTiles .tileSlider'),
VideolinkTimeline = domElement.find('#VideolinkTimeline'),
AnnotationTimeline = domElement.find('#AnnotationTimeline'),
AnnotationContainer = domElement.find('#AnnotationContainer'),
AnnotationTiles = domElement.find('#AnnotationTiles'),
AnnotationTileSlider = domElement.find('#AnnotationTiles .tileSlider'),
AnnotationSlider = domElement.find('#AnnotationSlider'),
OverlayTimeline = domElement.find('#OverlayTimeline'),
OverlayContainer = domElement.find('#OverlayContainer'),
Controls = domElement.find('#Controls'),
EditingOptions = domElement.find('#EditingOptions'),
CurrentTime = domElement.find('#CurrentTime'),
TotalDuration = domElement.find('#TotalDuration'),
PlayButton = domElement.find('#PlayButton'),
VolumeButton = domElement.find('#VolumeButton'),
FullscreenButton = domElement.find('#FullscreenButton'),
PlayerProgress = domElement.find('#PlayerProgress'),
Video = domElement.find('#Video')[0],
EditPropertiesContainer = domElement.find('#EditPropertiesContainer'),
AnnotationPreviewContainer = domElement.find('#AnnotationPreviewContainer'),
ExpandButton = domElement.find('#ExpandButton'),
shownDetails = null,
wasPlaying = false;
ExpandButton.click(function() {
showDetails(false);
});
Controls.find('#CaptionsButton').click(function() {
Controls.find('#RightControlPanel .active').not('[data-config], #CaptionsButton, #CaptionSelectContainer').removeClass('active');
if ( !$(this).children('#CaptionSelectContainer').hasClass('active') ) {
$(this).children('#CaptionSelectContainer').addClass('active');
VideoContainer.css('opacity', 0.3);
domElement.find('#InfoAreaRight').css('opacity', 0.3);
} else {
$(this).children('#CaptionSelectContainer').removeClass('active');
VideoContainer.css('opacity', 1);
domElement.find('#InfoAreaRight').css('opacity', 1);
}
});
Controls.find('.captionSelect.none').click(function() {
FrameTrail.changeState('hv_config_captionsVisible', false);
});
Controls.find('#SettingsButton').click(function(evt) {
var settingsButton = $(this);
if ( !settingsButton.hasClass('active') ) {
$('body').on('mouseup', function(evt) {
if ( !$(evt.target).attr('data-config') && $(evt.target).attr('id') != 'SettingsButton' ) {
settingsButton.removeClass('active');
VideoContainer.css('opacity', 1);
domElement.find('#InfoAreaRight').css('opacity', 1);
$('body').off('mouseup');
evt.preventDefault();
evt.stopPropagation();
}
});
Controls.find('#RightControlPanel .active').not('[data-config], #CaptionsButton').removeClass('active');
settingsButton.addClass('active');
VideoContainer.css('opacity', 0.3);
domElement.find('#InfoAreaRight').css('opacity', 0.3);
} else {
settingsButton.removeClass('active');
VideoContainer.css('opacity', 1);
domElement.find('#InfoAreaRight').css('opacity', 1);
}
});
Controls.find('#SettingsContainer').click(function(evt) {
if ( $(evt.target).attr('data-config') ) {
var config = $(evt.target).attr('data-config');
var configState = $(evt.target).hasClass('active');
if ( config != 'hv_config_annotationsPosition' && config != 'hv_config_slidingMode' ) {
FrameTrail.changeState(config, !configState);
} else if ( config == 'hv_config_slidingMode' ) {
if ( FrameTrail.getState('hv_config_slidingMode') == 'adjust' ) {
FrameTrail.changeState('hv_config_slidingMode', 'overlay');
} else {
FrameTrail.changeState('hv_config_slidingMode', 'adjust');
}
} else {
if ( FrameTrail.getState('hv_config_annotationsPosition') == 'top' ) {
FrameTrail.changeState('hv_config_annotationsPosition', 'bottom');
} else {
FrameTrail.changeState('hv_config_annotationsPosition', 'top');
}
}
FrameTrail.changeState('viewSize', FrameTrail.getState('viewSize'));
}
evt.preventDefault();
evt.stopPropagation();
});
VolumeButton.click(function() {
if ( $(this).hasClass('active') ) {
FrameTrail.module('HypervideoController').muted = false;
$(this).removeClass('active');
} else {
FrameTrail.module('HypervideoController').muted = true;
$(this).addClass('active');
}
});
VideolinkTiles.click(function(evt) {
if ( FrameTrail.module('VideolinksController').openedLink && $(evt.target).attr('id') == 'VideolinkTiles' ) {
FrameTrail.module('VideolinksController').openedLink = null;
}
});
AnnotationTiles.click(function(evt) {
if ( FrameTrail.module('AnnotationsController').openedAnnotation && $(evt.target).attr('id') == 'AnnotationTiles' ) {
FrameTrail.module('AnnotationsController').openedAnnotation = null;
}
});
document.addEventListener("fullscreenchange", toggleFullscreenState, false);
document.addEventListener("webkitfullscreenchange", toggleFullscreenState, false);
document.addEventListener("mozfullscreenchange", toggleFullscreenState, false);
Controls.find('#FullscreenButton').click(toggleNativeFullscreenState);
/**
* I am called from {{#crossLink "Interface/create:method"}}Interface/create(){{/crossLink}}.
* I update my local state from the global state variables and append my elements in the DOM tree.
* @method create
*/
function create() {
toggleViewMode(FrameTrail.getState('viewMode'));
toggleConfig_videolinksVisible(FrameTrail.getState('hv_config_videolinksVisible'));
toggleConfig_annotationsVisible(FrameTrail.getState('hv_config_annotationsVisible'));
toggleConfig_annotationPreviewVisible(FrameTrail.getState('hv_config_annotationPreviewVisible'));
toggleConfig_overlaysVisible(FrameTrail.getState('hv_config_overlaysVisible'));
toggleConfig_captionsVisible(FrameTrail.getState('hv_config_captionsVisible'))
changeSlidePosition(FrameTrail.getState('slidePosition'));
$('#MainContainer').append(domElement);
}
/**
* I am called when the global state "sidebarOpen" changes.
* @method toggleSidebarOpen
* @param {Boolean} opened
*/
function toggleSidebarOpen(opened) {
adjustHypervideo(true);
};
/**
* I am called when the global state "viewSize" changes (which it does after a window resize,
* and one time during app start, after all create methods of interface modules have been called).
* @method changeViewSize
* @param {Array} arrayWidthAndHeight
*/
function changeViewSize(arrayWidthAndHeight) {
slideArea.css({
'transition-duration': '0ms',
'-moz-transition-duration': '0ms',
'-webkit-transition-duration': '0ms',
'-o-transition-duration': '0ms'
});
adjustLayout();
adjustHypervideo();
slideArea.css({
'transition-duration': '',
'-moz-transition-duration': '',
'-webkit-transition-duration': '',
'-o-transition-duration': ''
});
};
/**
* I react to changes in the global state viewSizeChanged.
* The state changes after a window resize event
* and is meant to be used for performance-heavy operations.
*
* @method onViewSizeChanged
* @private
*/
function onViewSizeChanged() {
};
/**
* I am a central method of the ViewVideo. I rearrange all my child elements.
* Their position is defined by the global state "editMode" and by the various
* "hv_config_[...]" states, as well as the current width and height of the display area.
*
* I am called from many places, whenever one of the defining variables (see above) changes.
*
* @method adjustLayout
*/
function adjustLayout() {
var editMode = FrameTrail.getState('editMode'),
smallPlayerHeight = ( (FrameTrail.getState('embed') && !FrameTrail.getState('fullscreen') ) ? 120 : 280 ),
mediumPlayerHeight = 450,
playerMargin = parseInt(PlayerContainer.css('marginTop')),
editBorder = (editMode != false) ? (parseInt(domElement.css('borderTopWidth'))*2) : 0,
slidePosition = FrameTrail.getState('slidePosition'),
slidingMode = FrameTrail.getState('hv_config_slidingMode'),
annotationsPosition = FrameTrail.getState('hv_config_annotationsPosition'),
annotationsVisible = ( (editMode != false && editMode != 'preview') ? false : FrameTrail.getState('hv_config_annotationsVisible') ),
videolinksVisible = ( (editMode != false && editMode != 'preview') ? false : FrameTrail.getState('hv_config_videolinksVisible') ),
overlaysVisible = ( (editMode == 'overlays') ? true : FrameTrail.getState('hv_config_overlaysVisible') ),
annotationTimelineVisible = ( (editMode != false && editMode != 'preview') ? true : FrameTrail.getState('hv_config_annotationsVisible') ),
videolinkTimelineVisible = ( (editMode != false && editMode != 'preview') ? true : FrameTrail.getState('hv_config_videolinksVisible') );
if ( (slidePosition == 'middle' && (editMode == false || editMode == 'preview')) || ( slidingMode == 'overlay' && (editMode == false || editMode == 'preview') ) ) {
PlayerContainer.height(
$('#MainContainer').height()
- editBorder
- ((videolinksVisible) ? VideolinkTiles.height() + playerMargin : 0)
- ((annotationsVisible) ? AnnotationTiles.height() + playerMargin : 0)
);
} else if ( (editMode != false && editMode != 'preview') && FrameTrail.getState('viewSize')[1] > 650 ) {
PlayerContainer.height( mediumPlayerHeight );
} else {
PlayerContainer.height( smallPlayerHeight );
}
if ( annotationsVisible ) {
AnnotationContainer.height(
$('#MainContainer').height()
- smallPlayerHeight
- editBorder
- AnnotationTiles.height()
- playerMargin
);
} else {
AnnotationContainer.height(0);
}
if ( videolinksVisible ) {
VideolinkContainer.height(
$('#MainContainer').height()
- smallPlayerHeight
- editBorder
- VideolinkTiles.height()
- playerMargin
);
} else {
VideolinkContainer.height(0);
}
if ( slidePosition == 'top' ) {
if ( slidingMode == 'adjust' ) {
slideArea.css('marginTop',
- ((editMode != false && editMode != 'preview') ? playerMargin : 0)
+ 'px'
);
} else if ( slidingMode == 'overlay' ) {
slideArea.css('marginTop',
- ((videolinksVisible && annotationsPosition == 'bottom') ? VideolinkContainer.height() : 0)
- ((annotationsVisible && annotationsPosition == 'top') ? AnnotationContainer.height() : 0)
- (( annotationsVisible && annotationsPosition == 'top'
|| videolinksVisible && annotationsPosition == 'bottom') ? 0 : playerMargin)
+ 'px'
);
// slidingMode overlay top behaviour
var targetOffset = playerMargin + ( (PlayerContainer.height() - Controls.height())/2 ),
thisOffset;
if ( annotationsPosition == 'top' ) {
thisOffset = AnnotationContainer.height() + AnnotationTiles.height() + targetOffset - (AnnotationContainer.height() / 2);
AnnotationContainer.css({
marginTop: thisOffset + 'px'
});
AnnotationTiles.css({
marginTop: - thisOffset + 'px'
});
VideolinkContainer.css({
marginTop: ''
});
VideolinkTiles.css({
marginTop: ''
});
} else {
thisOffset = VideolinkContainer.height() + VideolinkTiles.height() + targetOffset - (VideolinkContainer.height() / 2);
VideolinkContainer.css({
marginTop: thisOffset + 'px'
});
VideolinkTiles.css({
marginTop: - thisOffset + 'px'
});
AnnotationContainer.css({
marginTop: ''
});
AnnotationTiles.css({
marginTop: ''
});
}
}
} else if ( slidePosition == 'bottom' ) {
if ( slidingMode == 'adjust' ) {
slideArea.css('marginTop',
- ((videolinksVisible && annotationsPosition == 'bottom') ? VideolinkContainer.height() + VideolinkTiles.height() : 0)
- ((annotationsVisible && annotationsPosition == 'top') ? AnnotationContainer.height() + AnnotationTiles.height() : 0)
- playerMargin
+ 'px'
);
} else if ( slidingMode == 'overlay' ) {
slideArea.css('marginTop',
- ((videolinksVisible && annotationsPosition == 'bottom') ? VideolinkContainer.height() : 0)
- ((annotationsVisible && annotationsPosition == 'top') ? AnnotationContainer.height() : 0)
- (( annotationsVisible && annotationsPosition == 'top'
|| videolinksVisible && annotationsPosition == 'bottom') ? 0 : playerMargin)
+ 'px'
);
var targetOffset = playerMargin + (PlayerContainer.height()/2) + (OverlayTimeline.height()*2);
// slidingMode overlay bottom behaviour
if ( annotationsPosition == 'bottom' ) {
AnnotationContainer.css({
marginTop: - (targetOffset + AnnotationTiles.height() + (AnnotationContainer.height() / 2)) + 'px'
});
VideolinkContainer.css({
marginTop: ''
});
VideolinkTiles.css({
marginTop: ''
});
} else {
VideolinkContainer.css({
marginTop: - (targetOffset + VideolinkTiles.height() + (VideolinkContainer.height() / 2)) + 'px'
});
AnnotationContainer.css({
marginTop: ''
});
AnnotationTiles.css({
marginTop: ''
});
}
}
} else {
slideArea.css('marginTop',
- ((videolinksVisible && annotationsPosition == 'bottom') ? VideolinkContainer.height() : 0)
- ((annotationsVisible && annotationsPosition == 'top') ? AnnotationContainer.height() : 0)
- (( annotationsVisible && annotationsPosition == 'top'
|| videolinksVisible && annotationsPosition == 'bottom') ? 0 : playerMargin)
+ 'px'
);
AnnotationContainer.css({
marginTop: ''
});
AnnotationTiles.css({
marginTop: ''
});
VideolinkContainer.css({
marginTop: ''
});
VideolinkTiles.css({
marginTop: ''
});
}
HypervideoContainer.height(
PlayerContainer.height()
- (videolinkTimelineVisible ? VideolinkTimeline.height() : 0)
- (annotationTimelineVisible ? AnnotationTimeline.height() : 0)
- OverlayTimeline.height()
- Controls.height()
);
if ( editMode != false && editMode != 'preview' ) {
EditingOptions.height( $('#MainContainer').height() - PlayerContainer.height() - editBorder );
EditingOptions.find('.ui-tabs').tabs('refresh');
}
domElement.find('#PlayerProgress .ui-slider-handle-circle').css( 'bottom',
Controls.height()
+ OverlayTimeline.height()
+ ((annotationTimelineVisible && annotationsPosition == 'bottom') ? AnnotationTimeline.height() : 0)
+ ((videolinkTimelineVisible && annotationsPosition == 'top') ? VideolinkTimeline.height() : 0)
);
slideArea.children("svg").css({
width: $(document).width() + "px",
height: $(document).height() + "px"
});
};
/**
* I re-adjust the CSS of the main video and its container (without surrounding elements).
* I try to fill the available space to fit the video elements.
*
* Also I try to animate the transition of the dimensions smoothly, when my single parameter is true.
*
* @method adjustHypervideo
* @param {Boolean} animate
*/
function adjustHypervideo(animate) {
var editBorder = (FrameTrail.getState('editMode') != false) ? (parseInt(domElement.css('borderTopWidth'))*2) : 0;
mainContainerWidth = $(window).width()
- ((FrameTrail.getState('sidebarOpen') && !FrameTrail.getState('fullscreen')) ? FrameTrail.module('Sidebar').width : 0)
- editBorder,
mainContainerHeight = $(window).height()
- $('#Titlebar').height()
- editBorder,
_video = $(Video);
if (animate) {
VideoContainer.css({
'transition-duration': '',
'-moz-transition-duration': '',
'-webkit-transition-duration': '',
'-o-transition-duration': ''
});
Hypervideo.css({
'transition-duration': '',
'-moz-transition-duration': '',
'-webkit-transition-duration': '',
'-o-transition-duration': ''
});
} else {
VideoContainer.css({
'transition-duration': '0ms',
'-moz-transition-duration': '0ms',
'-webkit-transition-duration': '0ms',
'-o-transition-duration': '0ms'
});
Hypervideo.css({
'transition-duration': '0ms',
'-moz-transition-duration': '0ms',
'-webkit-transition-duration': '0ms',
'-o-transition-duration': '0ms'
});
}
var widthAuto = (_video[0].style.width == 'auto');
var heightAuto = (_video[0].style.height == 'auto');
var videoContainerWidth;
if ( FrameTrail.getState('hv_config_annotationPreviewVisible') || ( FrameTrail.getState('editMode') != false && FrameTrail.getState('editMode') != 'preview' ) ) {
videoContainerWidth = mainContainerWidth - domElement.find('#InfoAreaRight').outerWidth();
} else {
videoContainerWidth = mainContainerWidth;
}
VideoContainer.width(videoContainerWidth);
if ( (_video.height() < VideoContainer.height() && widthAuto) || (_video.width() < videoContainerWidth && heightAuto) ) {
_video.css({
height: FrameTrail.getState('viewSize')[1] + 'px',
width: FrameTrail.getState('viewSize')[0] + 'px'
});
}
if (_video.height() >= VideoContainer.height()) {
_video.css({
height: VideoContainer.height() + 'px',
width: 'auto'
});
}
if (_video.width() >= videoContainerWidth) {
_video.css({
height: 'auto',
width: videoContainerWidth + 'px'
});
}
Hypervideo.css({
marginLeft: - _video.width()/2 + 'px',
marginTop: - _video.height()/2 + 'px',
height: _video.height()
});
};
/**
* I react to a change of the global state "fullscreen".
* @method toggleFullscreen
* @param {Boolean} aBoolean
*/
function toggleFullscreen(aBoolean) {
if (aBoolean) {
$('#FullscreenButton').addClass('active');
$('#MainContainer').addClass('inFullscreen');
} else {
$('#FullscreenButton').removeClass('active');
$('#MainContainer').removeClass('inFullscreen');
}
};
/**
* I react to a change of the global state "unsavedChanges".
* @method toogleUnsavedChanges
* @param {Boolean} aBoolean
*/
function toogleUnsavedChanges(aBoolean) {
};
/**
* I react to a change in the global state "viewMode".
* @method toggleViewMode
* @param {String} viewMode
*/
function toggleViewMode(viewMode) {
if (viewMode === 'video') {
domElement.addClass('active');
FrameTrail.module('Titlebar').title = FrameTrail.module('HypervideoModel').hypervideoName;
adjustLayout();
adjustHypervideo();
} else if (viewMode != 'resources') {
domElement.removeClass('active');
}
};
/**
* I react to a change in the global state "editMode".
* @method toggleEditMode
* @param {} editMode
* @return
*/
function toggleEditMode(editMode) {
resetEditMode();
switch (editMode) {
case false:
leaveEditMode();
break;
case 'preview':
leaveEditMode();
enterPreviewMode();
break;
case 'overlays':
enterOverlayMode();
break;
case 'audio':
enterAudioMode();
break;
case 'links':
enterLinkMode();
break;
case 'annotations':
enterAnnotationMode();
break;
}
window.setTimeout(function() {
slideArea.css({
'transition-duration': '0ms',
'-moz-transition-duration': '0ms',
'-webkit-transition-duration': '0ms',
'-o-transition-duration': '0ms'
});
adjustLayout();
adjustHypervideo();
slideArea.css({
'transition-duration': '',
'-moz-transition-duration': '',
'-webkit-transition-duration': '',
'-o-transition-duration': ''
});
}, 10);
};
/**
* I am called, whenever the editMode changes, to restore the default timeline.
* @method resetEditMode
*/
function resetEditMode() {
domElement.find('.timeline').removeClass('editable');
}
/**
* I prepare the several UI elements, when one of the editMode is started.
* @method initEditMode
*/
function initEditMode() {
ExpandButton.hide();
$(VideoContainer).css({
opacity: 1
});
domElement.find('#InfoAreaRight').css({
opacity: 1
});
AnnotationTiles.hide();
AnnotationContainer.hide();
VideolinkTiles.hide();
VideolinkContainer.hide();
domElement.find('.timeline').show();
EditingOptions.show();
if ( FrameTrail.getState('hv_config_annotationPreviewVisible') ) {
HypervideoContainer.find('#AnnotationPreviewContainer').hide();
}
domElement.find('#InfoAreaRight').show();
EditPropertiesContainer.show();
Controls.find('#RightControlPanel').hide();
}
/**
* I restore the UI elements to the view mode with no editing features activated.
* @method leaveEditMode
*/
function leaveEditMode() {
EditingOptions.hide();
EditPropertiesContainer.removeAttr('data-editmode').hide();
toggleConfig_annotationsVisible(FrameTrail.getState('hv_config_annotationsVisible'));
toggleConfig_videolinksVisible(FrameTrail.getState('hv_config_videolinksVisible'));
toggleConfig_annotationPreviewVisible(FrameTrail.getState('hv_config_annotationPreviewVisible'));
toggleConfig_overlaysVisible(FrameTrail.getState('hv_config_overlaysVisible'));
toggleConfig_slidingMode(FrameTrail.getState('hv_config_slidingMode'));
changeSlidePosition(FrameTrail.getState('slidePosition'));
Controls.find('#RightControlPanel').show();
}
/**
* I am called when the app enters the editMode "preview"
* @method enterPreviewMode
*/
function enterPreviewMode() {
}
/**
* I am called when the app enters the editMode "overlays"
* @method enterOverlayMode
*/
function enterOverlayMode() {
initEditMode();
OverlayTimeline.addClass('editable');
toggleConfig_overlaysVisible(true);
EditPropertiesContainer
.html('<div class="message active">Add overlays by dragging resources into the video area.</div>')
.attr('data-editmode', 'overlays');
}
/**
* I am called when the app enters the editMode "audio"
* @method enterAudioMode
*/
function enterAudioMode() {
initEditMode();
}
/**
* I am called when the app enters the editMode "links"
* @method enterLinkMode
*/
function enterLinkMode() {
initEditMode();
VideolinkTimeline.addClass('editable');
EditPropertiesContainer
.html('<div class="message active">Add video links by dragging hypervideos into the active timeline or entering a link manually.</div>')
.attr('data-editmode', 'links');
}
/**
* I am called when the app enters the editMode "annotations"
* @method enterAnnotationMode
*/
function enterAnnotationMode() {
initEditMode();
AnnotationTimeline.addClass('editable');
EditPropertiesContainer
.html('<div class="message active">Add annotations by dragging resources into the active timeline.</div>')
.attr('data-editmode', 'annotations');
}
/**
* I am called when the global state "hv_config_annotationPreviewVisible" changes.
*
* This is a configuration option (saved in the hypervideo's index.json entry).
*
* @method toggleConfig_annotationPreviewVisible
* @param {Boolean} newState
* @param {Boolean} oldState
*/
function toggleConfig_annotationPreviewVisible(newState, oldState) {
if (newState == true) {
HypervideoContainer.find('#InfoAreaRight').show();
HypervideoContainer.find('#AnnotationPreviewContainer').show();
Controls.find('[data-config="hv_config_annotationPreviewVisible"]').addClass('active');
} else {
domElement.find('#InfoAreaRight').hide();
HypervideoContainer.find('#AnnotationPreviewContainer').hide();
Controls.find('[data-config="hv_config_annotationPreviewVisible"]').removeClass('active');
}
};
/**
* I am called when the global state "hv_config_annotationsPosition" changes.
*
* This is a configuration option (saved in the hypervideo's index.json entry).
*
* @method toggleConfig_annotationsPosition
* @param {String} newState
* @param {String} oldState
*/
function toggleConfig_annotationsPosition(newState, oldState) {
if ( FrameTrail.getState('slidePosition') != 'middle' ) {
FrameTrail.changeState('slidePosition', 'middle');
}
if (newState == "top") {
PlayerContainer.before(AnnotationTiles);
AnnotationTiles.before(AnnotationContainer);
PlayerProgress.before(AnnotationTimeline);
PlayerContainer.after(VideolinkTiles);
VideolinkTiles.after(VideolinkContainer);
Controls.after(VideolinkTimeline);
Controls.find('#SettingsContainer #PlayerWrapper')
.after(Controls.find('div[data-config="hv_config_videolinksVisible"]'))
.before(Controls.find('div[data-config="hv_config_annotationsVisible"]'));
} else {
PlayerContainer.before(VideolinkTiles);
VideolinkTiles.before(VideolinkContainer);
PlayerProgress.before(VideolinkTimeline);
PlayerContainer.after(AnnotationTiles);
AnnotationTiles.after(AnnotationContainer);
Controls.after(AnnotationTimeline);
Controls.find('#SettingsContainer #PlayerWrapper')
.before(Controls.find('div[data-config="hv_config_videolinksVisible"]'))
.after(Controls.find('div[data-config="hv_config_annotationsVisible"]'));
}
};
/**
* I am called when the global state "hv_config_annotationsVisible" changes.
*
* This is a configuration option (saved in the hypervideo's index.json entry).
*
* @method toggleConfig_annotationsVisible
* @param {Boolean} newState
* @param {Boolean} oldState
*/
function toggleConfig_annotationsVisible(newState, oldState) {
if (newState == true) {
domElement.find('#AnnotationTiles, #AnnotationContainer, #AnnotationTimeline').show();
Controls.find('[data-config="hv_config_annotationsVisible"]').addClass('active');
} else {
domElement.find('#AnnotationTiles, #AnnotationContainer, #AnnotationTimeline').hide();
Controls.find('[data-config="hv_config_annotationsVisible"]').removeClass('active');
if ( FrameTrail.getState('slidePosition') != 'middle' ) {
FrameTrail.changeState('slidePosition', 'middle');
}
}
};
/**
* I am called when the global state "hv_config_autohideControls" changes.
*
* This is a configuration option (saved in the hypervideo's index.json entry).
*
* @method toggleConfig_autohideControls
* @param {Boolean} newState
* @param {Boolean} oldState
*/
function toggleConfig_autohideControls(newState, oldState) {
};
/**
* I am called when the global state "hv_config_overlaysVisible" changes.
*
* This is a configuration option (saved in the hypervideo's index.json entry).
*
* @method toggleConfig_overlaysVisible
* @param {Boolean} newState
* @param {Boolean} oldState
*/
function toggleConfig_overlaysVisible(newState, oldState) {
if (newState == true) {
OverlayContainer.show();
Controls.find('[data-config="hv_config_overlaysVisible"]').addClass('active');
} else {
OverlayContainer.hide();
Controls.find('[data-config="hv_config_overlaysVisible"]').removeClass('active');
}
};
/**
* I am called when the global state "hv_config_slidingMode" changes.
*
* This is a configuration option (saved in the hypervideo's index.json entry).
*
* @method toggleConfig_slidingMode
* @param {String} newState
* @param {String} oldState
*/
function toggleConfig_slidingMode(newState, oldState) {
if ( FrameTrail.getState('slidePosition') != 'middle' ) {
FrameTrail.changeState('slidePosition', 'middle');
}
adjustLayout();
adjustHypervideo();
if (newState == 'overlay') {
Controls.find('[data-config="hv_config_slidingMode"]').addClass('active');
} else {
Controls.find('[data-config="hv_config_slidingMode"]').removeClass('active');
}
};
/**
* I am called when the global state "hv_config_slidingTrigger" changes.
*
* This is a configuration option (saved in the hypervideo's index.json entry).
*
* @method toggleConfig_slidingTrigger
* @param {String} newState
* @param {String} oldState
*/
function toggleConfig_slidingTrigger(newState, oldState) {
};
/**
* I am called when the global state "hv_config_theme" changes.
*
* This is a configuration option (saved in the hypervideo's index.json entry).
*
* @method toggleConfig_theme
* @param {String} newState
* @param {String} oldState
*/
function toggleConfig_theme(newState, oldState) {
};
/**
* I am called when the global state "hv_config_videolinksVisible" changes.
*
* This is a configuration option (saved in the hypervideo's index.json entry).
*
* @method toggleConfig_videolinksVisible
* @param {Boolean} newState
* @param {Boolean} oldState
*/
function toggleConfig_videolinksVisible(newState, oldState) {
if (newState == true) {
domElement.find('#VideolinkTiles, #VideolinkContainer, #VideolinkTimeline').show();
Controls.find('[data-config="hv_config_videolinksVisible"]').addClass('active');
} else {
domElement.find('#VideolinkTiles, #VideolinkContainer, #VideolinkTimeline').hide();
Controls.find('[data-config="hv_config_videolinksVisible"]').removeClass('active');
if ( FrameTrail.getState('slidePosition') != 'middle' ) {
FrameTrail.changeState('slidePosition', 'middle');
}
}
};
/**
* I am called when the global state "hv_config_captionsVisible" changes.
*
* This is a configuration option (saved in the hypervideo's index.json entry).
*
* @method toggleConfig_captionsVisible
* @param {Boolean} newState
* @param {Boolean} oldState
*/
function toggleConfig_captionsVisible(newState, oldState) {
if (newState == true && Controls.find('#CaptionsButton').find('.captionSelect[data-lang="'+ FrameTrail.module('HypervideoModel').selectedLang +'"]').length > 0 ) {
Controls.find('#CaptionsButton').addClass('active');
Controls.find('#CaptionsButton').find('.captionSelect').removeClass('active');
Controls.find('#CaptionsButton').find('.captionSelect[data-lang="'+ FrameTrail.module('HypervideoModel').selectedLang +'"]').addClass('active');
CaptionContainer.show();
} else {
Controls.find('#CaptionsButton').removeClass('active');
Controls.find('#CaptionsButton').find('.captionSelect').removeClass('active');
Controls.find('#CaptionsButton').find('.captionSelect.none').addClass('active');
CaptionContainer.hide();
}
};
/**
* I am called when the global state "slidePosition" changes.
*
* This state is either "top", "middle" or "bottom", and indicates, which area has the most visual weight.
* The Hypervideocontainer is always displayed in the middle (in different sizes), while the annotations
* and the video links can change their position (top or bottom), as defined in the hypervideo's _index.json.
*
* @method changeSlidePosition
* @param {String} newState
* @param {String} oldState
*/
function changeSlidePosition(newState, oldState) {
adjustLayout();
adjustHypervideo();
if ( FrameTrail.getState('editMode') && FrameTrail.getState('editMode') != 'preview' ) return;
if ( newState != 'middle' ) {
ExpandButton.show();
} else {
ExpandButton.hide();
}
if ( ( FrameTrail.getState('hv_config_annotationsPosition') == 'bottom'
&& newState == 'bottom' ) ||
( FrameTrail.getState('hv_config_annotationsPosition') == 'top'
&& newState == 'top') ) {
shownDetails = 'annotations';
AnnotationContainer.find('.resourceDetail[data-type="location"]').each(function() {
if ( $(this).data('map') ) {
$(this).data('map').updateSize();
}
});
} else if ( newState != 'middle' ) {
shownDetails = 'videolinks';
} else {
shownDetails = null;
}
if ( newState != oldState && FrameTrail.getState('hv_config_slidingMode') == 'overlay' ) {
if ( shownDetails != null ) {
$(VideoContainer).animate({
opacity: 0.2
}, 500);
domElement.find('#InfoAreaRight').animate({
opacity: 0.2
}, 500);
wasPlaying = FrameTrail.module('HypervideoController').isPlaying;
window.setTimeout(function() {
FrameTrail.module('HypervideoController').pause();
}, 500);
} else if ( wasPlaying ) {
$(VideoContainer).animate({
opacity: 1
}, 500);
domElement.find('#InfoAreaRight').animate({
opacity: 1
}, 500);
window.setTimeout(function() {
FrameTrail.module('HypervideoController').play();
}, 500);
} else {
$(VideoContainer).animate({
opacity: 1
}, 500);
domElement.find('#InfoAreaRight').animate({
opacity: 1
}, 500);
}
}
};
/**
* This method changes the global state "slidePosition" from "bottom" to "middle"
* or from "middle" to "top".
* @method slidePositionUp
*/
function slidePositionUp() {
var slidePosition = FrameTrail.getState('slidePosition');
if ( slidePosition == 'middle' && (
( FrameTrail.getState('hv_config_annotationsPosition') == 'top' && FrameTrail.getState('hv_config_annotationsVisible') ) ||
( FrameTrail.getState('hv_config_annotationsPosition') == 'bottom' && FrameTrail.getState('hv_config_videolinksVisible') )
)) {
FrameTrail.changeState('slidePosition', 'top');
} else if ( slidePosition == 'bottom' ) {
FrameTrail.changeState('slidePosition', 'middle')
}
};
/**
* This method changes the global state "slidePosition" from "top" to "middle"
* or from "middle" to "bottom".
* @method slidePositionDown
*/
function slidePositionDown() {
var slidePosition = FrameTrail.getState('slidePosition');
if ( slidePosition == 'top' ) {
FrameTrail.changeState('slidePosition', 'middle');
} else if ( slidePosition == 'middle' && (
( FrameTrail.getState('hv_config_annotationsPosition') == 'bottom' && FrameTrail.getState('hv_config_annotationsVisible') ) ||
( FrameTrail.getState('hv_config_annotationsPosition') == 'top' && FrameTrail.getState('hv_config_videolinksVisible') )
)) {
FrameTrail.changeState('slidePosition', 'bottom');
}
};
/**
* This method is used to show the details (aka the content) of either the annotation or of the videolink.
* Because they can change their position ("top" or "bottom"), this method checks first were they are placed
* according to the current configuration, and slides the display area up or down respectively.
*
* @method showDetails
* @param {String} mode
*/
function showDetails(mode) {
shownDetails = mode;
if (!mode) {
FrameTrail.changeState('slidePosition', 'middle');
} else if ( mode == 'videolinks' ) {
if ( FrameTrail.getState('hv_config_videolinksVisible') && FrameTrail.getState('hv_config_annotationsPosition') == 'bottom' ) {
FrameTrail.changeState('slidePosition', 'top');
} else if ( FrameTrail.getState('hv_config_videolinksVisible') && FrameTrail.getState('hv_config_annotationsPosition') == 'top' ) {
FrameTrail.changeState('slidePosition', 'bottom');
}
} else if ( mode == 'annotations' ) {
if ( FrameTrail.getState('hv_config_annotationsVisible') && FrameTrail.getState('hv_config_annotationsPosition') == 'top' ) {
FrameTrail.changeState('slidePosition', 'top');
} else if ( FrameTrail.getState('hv_config_annotationsVisible') && FrameTrail.getState('hv_config_annotationsPosition') == 'bottom' ) {
FrameTrail.changeState('slidePosition', 'bottom');
}
}
};
/**
* Toggles the visibility of a vertical grid based on the positions of all timeline items
* in each category (Videolinks, Overlays, Annotations). This grid is used to allow snapping
* items to positions of other timeline items.
*
* @method toggleGrid
* @param {Boolean} visible
*/
function toggleGrid(visible) {
if ( !FrameTrail.getState('editMode') || FrameTrail.getState('editMode') == 'preview' ) {
return;
}
var timelineItems = $('.timelineElement').not('.ui-draggable'),
draggableElements = $('.timelineElement.ui-draggableable');
GridContainer = $('<div id="GridContainer" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 3"></div>');
if ( visible ) {
for (var i = 0; i < timelineItems.length; i++) { // vertical grid lines
var position = $(timelineItems[i]).position();
$('<div class="gridline"></div>').css({
'position': 'absolute',
'top': 0,
'left': position.left,
'width': 1,
'height': 100 + '%',
'background-color': '#ff9900'
}).appendTo(GridContainer);
$('<div class="gridline"></div>').css({
'position': 'absolute',
'top': 0,
'left': position.left + $(timelineItems[i]).width(),
'width': 1,
'height': 100 + '%',
'background-color': '#ff9900'
}).appendTo(GridContainer);
}
GridContainer.appendTo(PlayerProgress);
} else {
PlayerProgress.find('#GridContainer').remove();
}
};
/**
* Toggles the visibility of the working (loading) indicator.
*
* @method toggleVideoWorking
* @param {Boolean} working
*/
function toggleVideoWorking(working) {
var WorkingIndicator = VideoContainer.find('#WorkingIndicator');
if ( working ) {
WorkingIndicator.show();
} else {
WorkingIndicator.hide();
}
};
/**
* I return the closest element from a given position {top: XX, left: XX} in a collection.
*
* @method closestToOffset
* @param {Object} collection
* @param {Object} position
*/
function closestToOffset(collection, position) {
var el = null, elOffset, x = position.left, y = position.top, distance, dx, dy, minDistance;
collection.each(function() {
elOffset = $(this).position();
if (
(x >= elOffset.left) && (x <= elOffset.right) &&
(y >= elOffset.top) && (y <= elOffset.bottom)
) {
el = $(this);
return false;
}
var offsets = [[elOffset.left, elOffset.top], [elOffset.right, elOffset.top], [elOffset.left, elOffset.bottom], [elOffset.right, elOffset.bottom]];
for (off in offsets) {
dx = offsets[off][0] - x;
dy = offsets[off][1] - y;
distance = Math.sqrt((dx*dx) + (dy*dy));
if (minDistance === undefined || distance < minDistance) {
minDistance = distance;
el = $(this);
}
}
});
return el;
};
/**
* Toggle (Enter / Exit) native Fullscreen State
*
* @method toggleNativeFullscreenState
*/
function toggleNativeFullscreenState() {
var element = $('#MainContainer')[0];
if (element.requestFullScreen) {
if (!document.fullScreen) {
element.requestFullscreen();
} else {
document.exitFullScreen();
}
} else if (element.mozRequestFullScreen) {
if (!document.mozFullScreen) {
element.mozRequestFullScreen();
} else {
document.mozCancelFullScreen();
}
} else if (element.webkitRequestFullScreen) {
if (!document.webkitIsFullScreen) {
element.webkitRequestFullScreen();
} else {
document.webkitCancelFullScreen();
}
}
}
/**
* Toggle internal Fullscreen State
*
* @method toggleFullscreenState
*/
function toggleFullscreenState() {
var element = $('#MainContainer')[0];
if (element.requestFullScreen) {
if (!document.fullScreen) {
FrameTrail.changeState('fullscreen', false);
} else {
FrameTrail.changeState('fullscreen', true);
}
} else if (element.mozRequestFullScreen) {
if (!document.mozFullScreen) {
FrameTrail.changeState('fullscreen', false);
} else {
FrameTrail.changeState('fullscreen', true);
}
} else if (element.webkitRequestFullScreen) {
if (!document.webkitIsFullScreen) {
FrameTrail.changeState('fullscreen', false);
} else {
FrameTrail.changeState('fullscreen', true);
}
}
setTimeout(function() {
$(window).resize();
}, 1000);
}
/**
* Init Scroll Animations (using ScrollMagic & TweenMax)
*
* @method initScrollAnimations
*/
function initScrollAnimations() {
/*
$('#ViewVideo').css({
'overflow': 'hidden'
}).height( $(window).height() - 40 );
*/
$('#SlideArea').css({
marginRight: 10 + "px"
});
$('#AnnotationTiles').css({
position: 'fixed'
});
$('#PlayerContainer').css({
position: 'fixed'
}).addClass('scrollFix');
$('#PlayerContainer').after( $('<div id="PlayerContainerSpacer"></div>').height($('#PlayerContainer').height()) );
$('#AnnotationContainer').css({
zIndex: 2
});
scrollController = new ScrollMagic({container: '#ViewVideo'});
var annotationAnimation = TweenMax.fromTo('#AnnotationContainer', 0.5, { css: {
top: 100 + 'px'
}}, { css: {
top: -180 + 'px'
}});
var annotationTileAnimation = TweenMax.fromTo('#AnnotationTiles', 0.5, { css: {
bottom: '-40px'
}}, { css: {
bottom: '0px'
}});
var playerOpacityAnimation = TweenMax.fromTo('#PlayerContainer', 0.5, {
opacity: 1
}, {
opacity: 0.3
});
/*
var playerScene = new ScrollScene({
offset: 0
}).setPin('#PlayerContainer', {pinnedClass: 'scrollFix'}).addTo(scrollController);
*/
/*
var playerPin = TweenMax.fromTo('#PlayerContainer', 0.5, { css: {
top: 3 + "px"
}}, { css: {
top: 3 + "px"
}});
var playerScene = new ScrollScene({
offset: 35
}).setTween(playerPin).addTo(controller);
*/
var annotationTileScene = new ScrollScene({
offset: 200,
duration: '10%'
}).setTween(annotationTileAnimation).addTo(scrollController);
var annotationScene = new ScrollScene({
offset: 10,
duration: '80%'
}).setTween(annotationAnimation).addTo(scrollController);
var videoPaused = false;
var playerOpacityScene = new ScrollScene({
offset: 70,
duration: '80%'
}).setTween(playerOpacityAnimation).on('start', function(evt) {
if (evt.scrollDirection == 'FORWARD') {
videoPaused = !FrameTrail.module('HypervideoController').isPlaying;
} else if (!videoPaused) {
FrameTrail.module('HypervideoController').play();
}
}).on('end', function(evt) {
FrameTrail.module('HypervideoController').pause();
}).addTo(scrollController);
$('#ViewVideo').perfectScrollbar({
wheelSpeed: 4,
suppressScrollX: true,
wheelPropagation: true
});
}
/**
* Destroy Scroll Animations
*
* @method destroyScrollAnimations
* @param {Method} callback
*/
function destroyScrollAnimations(callback) {
var testInterval = setInterval(function() {
if ( $('.scrollmagic-pin-spacer').length > 0 ) {
scrollController.destroy(true);
$('#ViewVideo').perfectScrollbar('destroy');
$('#AnnotationContainer').css({
zIndex: ''
});
} else {
clearInterval(testInterval);
if (callback) {
callback.call();
}
}
}, 10);
}
return {
onChange: {
viewSize: changeViewSize,
viewSizeChanged: onViewSizeChanged,
fullscreen: toggleFullscreen,
viewMode: toggleViewMode,
editMode: toggleEditMode,
slidePosition: changeSlidePosition,
xKey: toggleGrid,
videoWorking: toggleVideoWorking,
hv_config_annotationPreviewVisible: toggleConfig_annotationPreviewVisible,
hv_config_annotationsPosition: toggleConfig_annotationsPosition,
hv_config_annotationsVisible: toggleConfig_annotationsVisible,
hv_config_autohideControls: toggleConfig_autohideControls,
hv_config_overlaysVisible: toggleConfig_overlaysVisible,
hv_config_slidingMode: toggleConfig_slidingMode,
hv_config_slidingTrigger: toggleConfig_slidingTrigger,
hv_config_theme: toggleConfig_theme,
hv_config_videolinksVisible: toggleConfig_videolinksVisible,
hv_config_captionsVisible: toggleConfig_captionsVisible
},
create: create,
toggleSidebarOpen: toggleSidebarOpen,
adjustLayout: adjustLayout,
adjustHypervideo: adjustHypervideo,
toggleFullscreenState: toggleFullscreenState,
slidePositionUp: slidePositionUp,
slidePositionDown: slidePositionDown,
closestToOffset: closestToOffset,
initScrollAnimations: initScrollAnimations,
destroyScrollAnimations: destroyScrollAnimations,
/**
* I display a (formated time) string in an area of the progress bar.
* @attribute currentTime
* @type String
*/
set currentTime(aString) { return CurrentTime.text(aString) },
/**
* I display a (formated time) string in an area of the progress bar.
* @attribute duration
* @type String
*/
set duration(aString) { return TotalDuration.text(aString) },
/**
* I contain the HypervideoContainer element.
* @attribute HypervideoContainer
* @type HTMLElement
*/
get HypervideoContainer() { return HypervideoContainer },
/**
* I contain the Video element.
* @attribute Video
* @type HTMLElement
*/
get Video() { return Video },
/**
* I contain the CaptionContainer element.
* @attribute CaptionContainer
* @type HTMLElement
*/
get CaptionContainer() { return CaptionContainer },
/**
* I contain the progress bar element.
* @attribute PlayerProgress
* @type HTMLElement
*/
get PlayerProgress() { return PlayerProgress },
/**
* I contain the play button element.
* @attribute PlayButton
* @type HTMLElement
*/
get PlayButton() { return PlayButton },
/**
* I contain the EditingOptions element (where e.g. the ResourcePicker is rendered).
* @attribute EditingOptions
* @type HTMLElement
*/
get EditingOptions() { return EditingOptions },
/**
* I contain the OverlayContainer element.
* @attribute OverlayContainer
* @type HTMLElement
*/
get OverlayContainer() { return OverlayContainer },
/**
* I contain the OverlayTimeline element.
* @attribute OverlayTimeline
* @type HTMLElement
*/
get OverlayTimeline() { return OverlayTimeline },
/**
* I contain the VideolinkContainer element.
* @attribute VideolinkContainer
* @type HTMLElement
*/
get VideolinkContainer() { return VideolinkContainer },
/**
* I contain the VideolinkTiles element.
* @attribute VideolinkTiles
* @type HTMLElement
*/
get VideolinkTiles() { return VideolinkTiles },
/**
* I contain the VideolinkTileSlider element.
* @attribute VideolinkTileSlider
* @type HTMLElement
*/
get VideolinkTileSlider() { return VideolinkTileSlider },
/**
* I contain the VideolinkTimeline element.
* @attribute VideolinkTimeline
* @type HTMLElement
*/
get VideolinkTimeline() { return VideolinkTimeline },
/**
* I contain the AnnotationContainer element.
* @attribute AnnotationContainer
* @type HTMLElement
*/
get AnnotationContainer() { return AnnotationSlider },
/**
* I contain the AnnotationTiles element.
* @attribute AnnotationTiles
* @type HTMLElement
*/
get AnnotationTiles() { return AnnotationTiles },
/**
* I contain the AnnotationTileSlider element.
* @attribute AnnotationTileSlider
* @type HTMLElement
*/
get AnnotationTileSlider() { return AnnotationTileSlider },
/**
* I contain the AnnotationTimeline element.
* @attribute AnnotationTimeline
* @type HTMLElement
*/
get AnnotationTimeline() { return AnnotationTimeline },
/**
* I contain the AnnotationPreviewContainer element.
* @attribute AnnotationPreviewContainer
* @type HTMLElement
*/
get AnnotationPreviewContainer() { return AnnotationPreviewContainer },
/**
* I contain the EditPropertiesContainer element (where properties of an overlay/annotation/videolink can be viewed and – in the case ov overlays – changed).
* @attribute EditPropertiesContainer
* @type HTMLElement
*/
get EditPropertiesContainer() { return EditPropertiesContainer },
/**
* This attribute controls wether the view places its visual weight on "annotations" or "videolinks", or none of the both (null).
* @attribute shownDetails
* @type String or null
*/
get shownDetails() { return shownDetails },
set shownDetails(mode) { return showDetails(mode) },
/**
* I contain the ExpandButton element.
* @attribute ExpandButton
* @type HTMLElement
*/
get ExpandButton() { return ExpandButton },
/**
* I contain the CaptionsButton element, including the list of available subtitles.
* @attribute CaptionsButton
* @type HTMLElement
*/
get CaptionsButton() { return Controls.find('#CaptionsButton') }
};
});