- /**
- * @module Player
- */
-
-
- /**
- * I am the type definition of a Videolink.
- *
- * A video link is classical hyperlink (to either another video or any other resource locatable bei an URL)
- * and which has a start and a end time related to the time base of the main video.
- *
- * Videolinks are managed by the {{#crossLink "VideolinksController"}}VideolinksController{{/crossLink}}.
- *
- * @class Videolink
- * @category TypeDefinition
- */
-
-
-
- FrameTrail.defineType(
-
- 'Videolink',
-
- function(data){
-
- this.data = data;
-
- this.timelineElement = $('<div class="timelineElement"></div>');
- this.tileElement = $('<div class="tileElement"></div>');
- this.videolinkElement = $('<div class="videolinkElement"></div>');
-
-
- },
-
- {
-
- /**
- * I hold the data object of a Videolink, which is stored in the {{#crossLink "Database"}}Database{{/crossLink}} and saved in the hypervideos's links.json file.
- * @attribute data
- * @type {}
- */
- data: {},
-
- /**
- * I hold the timelineElement (a jquery-enabled HTMLElement), which indicates my start and end time.
- * @attribute timelineElement
- * @type HTMLElement
- */
- timelineElement: null,
-
- /**
- * I hold the tileElement (a jquery-enabled HTMLElement), which shows a icon for me close to my position in the timeline.
- * @attribute tileElement
- * @type HTMLElement
- */
- tileElement: null,
-
- /**
- * I hold the videolinkElement (a jquery-enabled HTMLElement), which shows the linked content in an iframe.
- * @attribute videolinkElement
- * @type HTMLElement
- */
- videolinkElement: null,
-
- /**
- * I store my state, wether I am "active" (this is, when my timelineElement and tileElements are highlighted) or not.
- * @attribute activeState
- * @type Boolean
- */
- activeState: false,
-
-
- /**
- * I store my state, wether I am "in focus" or not. See also:
- * * {{#crossLink "Videolink/gotInFocus:method"}}Videolink/gotInFocus(){{/crossLink}}
- * * {{#crossLink "Videolink/removedFromFocus:method"}}Videolink/removedFromFocus(){{/crossLink}}
- * * {{#crossLink "VideolinksController/videolinkInFocus:attribute"}}VideolinksController/videolinkInFocus{{/crossLink}}
- * @attribute permanentFocusState
- * @type Boolean
- */
- permanentFocusState: false,
-
-
- /**
- * I render my ({{#crossLink "Videolink/timelineElement:attribute"}}this.timelineElement{{/crossLink}}
- * into the DOM.
- *
- * I am called, when the Videolink is initialized.
- *
- * The tileElement and videolinkElement however are rendere separately into the DOM (see
- * {{#crossLink "Videolink/renderTilesAndContentInDOM:method"}}this.renderTilesAndContentInDOM{{/crossLink}}),
- * because their order in the DOM tree has to be sorted by this.data.start after each change.
- *
- * @method renderTimelineInDOM
- */
- renderTimelineInDOM: function () {
-
- var ViewVideo = FrameTrail.module('ViewVideo');
-
- this.timelineElement.unbind('hover');
- this.timelineElement.hover(this.brushIn.bind(this), this.brushOut.bind(this));
-
- ViewVideo.VideolinkTimeline.append(this.timelineElement);
- this.updateTimelineElement();
-
-
- },
-
- /**
- * I render my ({{#crossLink "Videolink/tileElement:attribute"}}this.tileElement{{/crossLink}}
- * and my {{#crossLink "Videolink/videolinkElement:attribute"}}this.videolinkElement{{/crossLink}} into the DOM.
- *
- * I am called, when the Videolink is initialized, and also every time, when the global state "editMode" leaves the state
- * "links". This is the case, when the user has finished his/her changes to the Videolinks. All tiles and content
- * containers have to be re-rendered into the DOM, because they need to sorted by ascending value of this.data.start.
- *
- * @method renderTilesAndContentInDOM
- */
- renderTilesAndContentInDOM: function () {
-
- var ViewVideo = FrameTrail.module('ViewVideo'),
- iFrame = $('<iframe frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>'),
- linkElem = $('<a class="videolinkAnchor" href="'+ this.data.href +'">Go to Hypervideo</a>');
-
- this.videolinkElement.empty();
- this.videolinkElement.append(iFrame, linkElem);
-
- ViewVideo.VideolinkTileSlider.append(this.tileElement);
- ViewVideo.VideolinkContainer.append(this.videolinkElement);
-
- this.tileElement.unbind('hover');
- this.tileElement.unbind('click')
- this.tileElement.hover(this.brushIn.bind(this), this.brushOut.bind(this));
-
- // self = this necessary as self can not be kept in anonymous handler function
- var self = this;
- this.tileElement.click(function() {
- if ( FrameTrail.module('VideolinksController').openedLink == self ) {
- self.closeVideolink();
- } else {
- self.openVideolink();
- }
- });
-
- },
-
-
- /**
- * I remove all my elements from the DOM. I am called when a Videolink is to be deleted.
- * @method removeFromDOM
- */
- removeFromDOM: function () {
-
- this.timelineElement.remove();
- this.tileElement.remove();
- this.videolinkElement.remove();
-
- },
-
- /**
- * I update the CSS of the {{#crossLink "Videolink/timelineElement:attribute"}}timelineElement{{/crossLink}}
- * to its correct position within the timeline.
- *
- * @method updateTimelineElement
- */
- updateTimelineElement: function () {
-
- var videoDuration = FrameTrail.module('HypervideoModel').duration,
- positionLeft = 100 * (this.data.start / videoDuration),
- width = 100 * ((this.data.end - this.data.start) / videoDuration);
-
- this.timelineElement.css({
- top: '',
- left: positionLeft + '%',
- right: '',
- width: width + '%'
- });
-
- },
-
-
-
- /**
- * When I am scheduled to be displayed, this is the method to be called.
- * @method setActive
- */
- setActive: function () {
-
- this.activeState = true;
-
- this.timelineElement.addClass('active');
- this.tileElement.addClass('active');
-
- },
-
- /**
- * When I am scheduled to disappear, this is the method to be called.
- * @method setInactive
- */
- setInactive: function () {
-
- this.activeState = false;
-
- this.timelineElement.removeClass('active');
- this.tileElement.removeClass('active');
-
- },
-
-
- /**
- * I am called when the mouse pointer is hovering over one of my tile or my timeline element
- * @method brushIn
- */
- brushIn: function () {
-
- this.timelineElement.addClass('brushed');
- this.tileElement.addClass('brushed');
-
- if ( FrameTrail.getState('editMode') == false || FrameTrail.getState('editMode') == 'preview' ) {
- clearRaphael();
- drawConnections( this.tileElement, this.timelineElement, 10, {stroke: "#6B7884"} );
- }
-
- },
-
- /**
- * I am called when the mouse pointer is leaving the hovering area over my tile or my timeline element.
- * @method brushOut
- */
- brushOut: function () {
-
- this.timelineElement.removeClass('brushed');
- this.tileElement.removeClass('brushed');
-
- if ( (FrameTrail.getState('editMode') == false || FrameTrail.getState('editMode') == 'preview') ) {
- clearRaphael();
- }
-
- },
-
-
- /**
- * A video link can be "opened" and "closed".
- *
- * When I am called, I open the video link, which means:
- * * I set the current play position to my data.start value
- * * I tell the {{#crossLink "VideolinksController/openedLink:attribute"}}VideolinksController{{/crossLink}} to set me as the "openedLink".
- *
- * @method openVideolink
- */
- openVideolink: function () {
-
- var ViewVideo = FrameTrail.module('ViewVideo');
-
-
- FrameTrail.module('HypervideoController').currentTime = this.data.start;
-
- FrameTrail.module('VideolinksController').openedLink = this;
-
- //ViewVideo.ExpandButton.one('click', this.closeVideolink.bind(this));
-
-
- },
-
- /**
- * I tell the {{#crossLink "VideolinksController/openedLink:attribute"}}VideolinksController{{/crossLink}} to set "openedLink" to null.
- * @method closeVideolink
- * @return
- */
- closeVideolink: function () {
-
- FrameTrail.module('VideolinksController').openedLink = null;
-
- },
-
-
-
- /**
- * I am called when the app switches to the editMode "links".
- *
- * I make sure
- * * that my {{#crossLink "Videolink/timelineElement:attribute"}}timelineElement{{/crossLink}} is resizable and draggable
- * * and that it has a click handler for putting myself into focus.
- *
- * @method startEditing
- */
- startEditing: function () {
-
-
- var self = this,
- VideolinksController = FrameTrail.module('VideolinksController');
-
- this.makeTimelineElementDraggable();
- this.makeTimelineElementResizeable();
-
- this.timelineElement.on('click', function(){
-
- if (VideolinksController.videolinkInFocus === self){
- return VideolinksController.videolinkInFocus = null;
- }
-
- self.permanentFocusState = true;
- VideolinksController.videolinkInFocus = self;
-
- FrameTrail.module('HypervideoController').currentTime = self.data.start;
-
- });
-
-
- },
-
- /**
- * When the global editMode leaves the state "links", I am called to
- * stop the editing features of the video link.
- *
- * @method stopEditing
- */
- stopEditing: function () {
-
- this.timelineElement.draggable('destroy');
- this.timelineElement.resizable('destroy');
-
- this.timelineElement.unbind('click');
-
-
- },
-
-
- /**
- * I make my {{#crossLink "Videolink/timelineElement:attribute"}}timelineElement{{/crossLink}} draggable.
- *
- * The event handling changes my this.data.start and this.data.end attributes
- * accordingly.
- *
- * @method makeTimelineElementDraggable
- */
- makeTimelineElementDraggable: function () {
-
- var self = this;
-
-
- this.timelineElement.draggable({
-
- axis: 'x',
- containment: 'parent',
- snapTolerance: 10,
-
- drag: function(event, ui) {
-
- var closestGridline = FrameTrail.module('ViewVideo').closestToOffset($('.gridline'), {
- left: ui.position.left,
- top: ui.position.top
- }),
- snapTolerance = $(this).draggable('option', 'snapTolerance');
-
- if (closestGridline) {
-
- $('.gridline').css('background-color', '#ff9900');
-
- if ( ui.position.left - snapTolerance < closestGridline.position().left &&
- ui.position.left + snapTolerance > closestGridline.position().left ) {
-
- ui.position.left = closestGridline.position().left;
-
- closestGridline.css('background-color', '#00ff00');
-
- }
- }
-
- var videoDuration = FrameTrail.module('HypervideoModel').duration,
- leftPercent = 100 * (ui.helper.position().left / ui.helper.parent().width()),
- newStartValue = leftPercent * (videoDuration / 100);
-
- FrameTrail.module('HypervideoController').currentTime = newStartValue;
-
- },
-
- start: function(event, ui) {
-
- if (!self.permanentFocusState) {
- FrameTrail.module('VideolinksController').videolinkInFocus = self;
- }
-
- },
-
- stop: function(event, ui) {
-
- if (!self.permanentFocusState) {
- FrameTrail.module('VideolinksController').videolinkInFocus = null;
- }
-
-
- var videoDuration = FrameTrail.module('HypervideoModel').duration,
- leftPercent = 100 * (ui.helper.position().left / ui.helper.parent().width()),
- widthPercent = 100 * (ui.helper.width() / ui.helper.parent().width());
-
- self.data.start = leftPercent * (videoDuration / 100);
- self.data.end = (leftPercent + widthPercent) * (videoDuration / 100);
-
- self.updateTimelineElement();
-
- FrameTrail.module('VideolinksController').stackTimelineView();
-
- FrameTrail.module('HypervideoModel').newUnsavedChange('links');
-
- }
- });
-
- },
-
- /**
- * I make my {{#crossLink "Videolink/timelineElement:attribute"}}timelineElement{{/crossLink}} resizable.
- *
- * The event handling changes my this.data.start and this.data.end attributes
- * accordingly.
- *
- * @method makeTimelineElementResizeable
- */
- makeTimelineElementResizeable: function () {
-
- var self = this,
- endHandleGrabbed;
-
-
- this.timelineElement.resizable({
-
- containment: 'parent',
- handles: 'e, w',
-
- resize: function(event, ui) {
-
- var closestGridline = FrameTrail.module('ViewVideo').closestToOffset($('.gridline'), {
- left: (endHandleGrabbed ? (ui.position.left + ui.helper.width()) : ui.position.left),
- top: ui.position.top
- }),
- snapTolerance = $(this).draggable('option', 'snapTolerance');
-
- if (closestGridline) {
-
- $('.gridline').css('background-color', '#ff9900');
-
- if ( !endHandleGrabbed &&
- ui.position.left - snapTolerance < closestGridline.position().left &&
- ui.position.left + snapTolerance > closestGridline.position().left ) {
-
- ui.position.left = closestGridline.position().left;
- ui.size.width = ( ui.helper.width() + ( ui.helper.position().left - ui.position.left ) );
-
- closestGridline.css('background-color', '#00ff00');
-
- } else if ( endHandleGrabbed &&
- ui.position.left + ui.helper.width() - snapTolerance < closestGridline.position().left &&
- ui.position.left + ui.helper.width() + snapTolerance > closestGridline.position().left ) {
-
- ui.helper.width(closestGridline.position().left - ui.position.left);
-
- closestGridline.css('background-color', '#00ff00');
-
- }
- }
-
-
- var videoDuration = FrameTrail.module('HypervideoModel').duration,
- leftPercent = 100 * (ui.position.left / ui.helper.parent().width()),
- widthPercent = 100 * (ui.helper.width() / ui.helper.parent().width()),
- newValue;
-
- if ( endHandleGrabbed ) {
-
- newValue = (leftPercent + widthPercent) * (videoDuration / 100);
- FrameTrail.module('HypervideoController').currentTime = newValue;
-
- } else {
-
- newValue = leftPercent * (videoDuration / 100);
- FrameTrail.module('HypervideoController').currentTime = newValue;
-
- }
-
- },
-
- start: function(event, ui) {
-
- if (!self.permanentFocusState) {
- FrameTrail.module('VideolinksController').videolinkInFocus = self;
- }
-
- endHandleGrabbed = $(event.originalEvent.target).hasClass('ui-resizable-e')
-
- },
-
- stop: function(event, ui) {
-
- if (!self.permanentFocusState) {
- FrameTrail.module('VideolinksController').videolinkInFocus = null;
- }
-
-
- var videoDuration = FrameTrail.module('HypervideoModel').duration,
- leftPercent = 100 * (ui.helper.position().left / ui.helper.parent().width()),
- widthPercent = 100 * (ui.helper.width() / ui.helper.parent().width());
-
-
- self.data.start = leftPercent * (videoDuration / 100);
- self.data.end = (leftPercent + widthPercent) * (videoDuration / 100);
-
- FrameTrail.module('VideolinksController').stackTimelineView();
-
- FrameTrail.module('HypervideoModel').newUnsavedChange('links');
-
- }
- });
-
- },
-
-
- /**
- * When I "got into focus" (which happens, when I become the referenced object in the VideolinksController's
- * {{#crossLink "VideolinksController/videolinkInFocus:attribute"}}videolinkInFocus attribute{{/crossLink}}),
- * then this method will be called.
- *
- * @method gotInFocus
- */
- gotInFocus: function () {
-
- var EditPropertiesContainer = FrameTrail.module('ViewVideo').EditPropertiesContainer,
- self = this;
-
- EditPropertiesContainer.empty();
-
- var propertiesControls = $('<div>'
- + ' <div class="propertiesTypeIcon" data-type="videolink"></div>'
- + ' <div>Title:</div>'
- + ' <div>' + this.data.name + '</div><br>'
- + ' <div>Link:</div>'
- + ' <div><input id="VideolinkHref" type="text" value="'+ this.data.href +'"></div><br>'
- + ' <button id="DeleteVideolink">Delete</button>'
- + '</div>');
-
- propertiesControls.find('#VideolinkHref').change(function() {
-
- self.data.href = $(this).val();
-
- FrameTrail.module('HypervideoModel').newUnsavedChange('links');
-
- });
-
- propertiesControls.find('#DeleteVideolink').click(function() {
-
- FrameTrail.module('VideolinksController').deleteVideolink(self);
-
- });
-
- EditPropertiesContainer.addClass('active').append(propertiesControls);
-
- this.timelineElement.addClass('highlighted');
-
-
- },
-
-
- /**
- * See also: {{#crossLink "Videolink/gotIntoFocus:method"}}this.gotIntoFocus(){{/crossLink}}
- *
- * When I was "removed from focus" (which happens, when the VideolinksController's
- * {{#crossLink "VideolinksController/videolinkInFocus:attribute"}}videolinkInFocus attribute{{/crossLink}}),
- * is set either to null or to an other Videolink than myself),
- * then this method will be called.
- *
- * @method removedFromFocus
- */
- removedFromFocus: function () {
-
- FrameTrail.module('ViewVideo').EditPropertiesContainer.removeClass('active').empty();
-
- this.timelineElement.removeClass('highlighted');
-
-
- }
-
-
-
- }
-
- );
-
-