Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
9b3bc84c
Commit
9b3bc84c
authored
Oct 31, 2013
by
Valera Rozuvan
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1480 from edx/valera/bugfix_start_end_time_correct_navigation
Bug fix: video end time proper seek beyond.
parents
2628e4f1
5cd4bae3
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
735 additions
and
402 deletions
+735
-402
CHANGELOG.rst
+4
-0
common/lib/xmodule/xmodule/js/spec/video/general_spec.js
+62
-21
common/lib/xmodule/xmodule/js/spec/video/html5_video_spec.js
+29
-31
common/lib/xmodule/xmodule/js/spec/video/resizer_spec.js
+16
-5
common/lib/xmodule/xmodule/js/spec/video/video_caption_spec.js
+21
-2
common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js
+250
-197
common/lib/xmodule/xmodule/js/spec/video/video_progress_slider_spec.js
+95
-34
common/lib/xmodule/xmodule/js/src/video/00_resizer.js
+4
-2
common/lib/xmodule/xmodule/js/src/video/01_initialize.js
+13
-9
common/lib/xmodule/xmodule/js/src/video/025_focus_grabber.js
+6
-2
common/lib/xmodule/xmodule/js/src/video/02_html5_video.js
+76
-74
common/lib/xmodule/xmodule/js/src/video/03_video_player.js
+141
-17
common/lib/xmodule/xmodule/js/src/video/06_video_progress_slider.js
+15
-6
common/lib/xmodule/xmodule/js/src/video/09_video_caption.js
+3
-2
No files found.
CHANGELOG.rst
View file @
9b3bc84c
...
...
@@ -5,6 +5,10 @@ These are notable changes in edx-platform. This is a rolling list of changes,
in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected.
Blades: Video start and end times now function the same for both YouTube and
HTML5 videos. If end time is set, the video can still play until the end, after
it pauses on the end time.
Blades: Disallow users to enter video url's in http.
Blades: Fix bug when the speed can only be changed when the video is playing.
...
...
common/lib/xmodule/xmodule/js/spec/video/general_spec.js
View file @
9b3bc84c
...
...
@@ -96,7 +96,10 @@
});
});
it
(
'parse the videos if subtitles do not exist'
,
function
()
{
it
(
'parse the videos if subtitles do not exist'
,
function
()
{
var
sub
=
''
;
$
(
'#example'
).
find
(
'.video'
).
data
(
'sub'
,
''
);
...
...
@@ -117,16 +120,41 @@
ogg
:
null
},
v
=
document
.
createElement
(
'video'
);
if
(
!!
(
v
.
canPlayType
&&
v
.
canPlayType
(
'video/webm; codecs="vp8, vorbis"'
).
replace
(
/no/
,
''
)))
{
html5Sources
[
'webm'
]
=
'xmodule/include/fixtures/test.webm'
;
if
(
!!
(
v
.
canPlayType
&&
v
.
canPlayType
(
'video/webm; codecs="vp8, vorbis"'
).
replace
(
/no/
,
''
)
)
)
{
html5Sources
[
'webm'
]
=
'xmodule/include/fixtures/test.webm'
;
}
if
(
!!
(
v
.
canPlayType
&&
v
.
canPlayType
(
'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'
).
replace
(
/no/
,
''
)))
{
html5Sources
[
'mp4'
]
=
'xmodule/include/fixtures/test.mp4'
;
if
(
!!
(
v
.
canPlayType
&&
v
.
canPlayType
(
'video/mp4; codecs="avc1.42E01E, '
+
'mp4a.40.2"'
).
replace
(
/no/
,
''
)
)
)
{
html5Sources
[
'mp4'
]
=
'xmodule/include/fixtures/test.mp4'
;
}
if
(
!!
(
v
.
canPlayType
&&
v
.
canPlayType
(
'video/ogg; codecs="theora"'
).
replace
(
/no/
,
''
)))
{
html5Sources
[
'ogg'
]
=
'xmodule/include/fixtures/test.ogv'
;
if
(
!!
(
v
.
canPlayType
&&
v
.
canPlayType
(
'video/ogg; codecs="theora"'
).
replace
(
/no/
,
''
)
)
)
{
html5Sources
[
'ogg'
]
=
'xmodule/include/fixtures/test.ogv'
;
}
expect
(
state
.
html5Sources
).
toEqual
(
html5Sources
);
...
...
@@ -143,10 +171,10 @@
});
});
// Note that the loading of stand alone HTML5 player API is
handled by
//
Require JS. When state.videoPlayer is created, the stand alone HTML5
//
player object is already loaded, so no further testing in that case
// is required.
// Note that the loading of stand alone HTML5 player API is
//
handled by Require JS. When state.videoPlayer is created,
//
the stand alone HTML5 player object is already loaded, so no
//
further testing in that case
is required.
describe
(
'HTML5 API is available'
,
function
()
{
beforeEach
(
function
()
{
state
=
new
Video
(
'#example'
);
...
...
@@ -172,8 +200,10 @@
describe
(
'with speed'
,
function
()
{
it
(
'return the video id for given speed'
,
function
()
{
expect
(
state
.
youtubeId
(
'0.75'
)).
toEqual
(
this
[
'7tqY6eQzVhE'
]);
expect
(
state
.
youtubeId
(
'1.0'
)).
toEqual
(
this
[
'cogebirgzzM'
]);
expect
(
state
.
youtubeId
(
'0.75'
))
.
toEqual
(
this
[
'7tqY6eQzVhE'
]);
expect
(
state
.
youtubeId
(
'1.0'
))
.
toEqual
(
this
[
'cogebirgzzM'
]);
});
});
...
...
@@ -210,7 +240,7 @@
itDescription
:
'start time is greater than end time'
,
data
:
{
start
:
42
,
end
:
24
},
expectData
:
{
start
:
42
,
end
:
null
}
}
,
}
];
beforeEach
(
function
()
{
...
...
@@ -234,8 +264,8 @@
state
=
new
Video
(
'#example'
);
expect
(
state
.
config
.
start
).
toBe
(
expectData
.
start
);
expect
(
state
.
config
.
end
).
toBe
(
expectData
.
end
);
expect
(
state
.
config
.
start
Time
).
toBe
(
expectData
.
start
);
expect
(
state
.
config
.
end
Time
).
toBe
(
expectData
.
end
);
});
}
});
...
...
@@ -263,7 +293,10 @@
state3
=
new
Video
(
'#example3'
);
});
it
(
'check for YT availability is performed only once'
,
function
()
{
it
(
'check for YT availability is performed only once'
,
function
()
{
var
numAjaxCalls
=
0
;
// Total ajax calls made.
...
...
@@ -307,10 +340,14 @@
});
it
(
'save setting for new speed'
,
function
()
{
expect
(
$
.
cookie
).
toHaveBeenCalledWith
(
'video_speed'
,
'0.75'
,
{
expect
(
$
.
cookie
).
toHaveBeenCalledWith
(
'video_speed'
,
'0.75'
,
{
expires
:
3650
,
path
:
'/'
});
}
);
});
});
...
...
@@ -341,10 +378,14 @@
});
it
(
'save setting for new speed'
,
function
()
{
expect
(
$
.
cookie
).
toHaveBeenCalledWith
(
'video_speed'
,
'0.75'
,
{
expect
(
$
.
cookie
).
toHaveBeenCalledWith
(
'video_speed'
,
'0.75'
,
{
expires
:
3650
,
path
:
'/'
});
}
);
});
});
...
...
common/lib/xmodule/xmodule/js/spec/video/html5_video_spec.js
View file @
9b3bc84c
...
...
@@ -10,7 +10,8 @@
beforeEach
(
function
()
{
oldOTBD
=
window
.
onTouchBasedDevice
;
window
.
onTouchBasedDevice
=
jasmine
.
createSpy
(
'onTouchBasedDevice'
).
andReturn
(
false
);
window
.
onTouchBasedDevice
=
jasmine
.
createSpy
(
'onTouchBasedDevice'
).
andReturn
(
false
);
initialize
();
player
.
config
.
events
.
onReady
=
jasmine
.
createSpy
(
'onReady'
);
});
...
...
@@ -46,17 +47,22 @@
},
'Player state should be changed'
,
WAIT_TIMEOUT
);
runs
(
function
()
{
expect
(
player
.
getPlayerState
()).
toBe
(
STATUS
.
PLAYING
);
expect
(
player
.
getPlayerState
())
.
toBe
(
STATUS
.
PLAYING
);
});
});
it
(
'callback was called'
,
function
()
{
waitsFor
(
function
()
{
return
state
.
videoPlayer
.
player
.
getPlayerState
()
!==
STATUS
.
PAUSED
;
var
stateStatus
=
state
.
videoPlayer
.
player
.
getPlayerState
();
return
stateStatus
!==
STATUS
.
PAUSED
;
},
'Player state should be changed'
,
WAIT_TIMEOUT
);
runs
(
function
()
{
expect
(
player
.
callStateChangeCallback
).
toHaveBeenCalled
();
expect
(
player
.
callStateChangeCallback
)
.
toHaveBeenCalled
();
});
});
});
...
...
@@ -78,7 +84,8 @@
},
'Player state should be changed'
,
WAIT_TIMEOUT
);
runs
(
function
()
{
expect
(
player
.
getPlayerState
()).
toBe
(
STATUS
.
PAUSED
);
expect
(
player
.
getPlayerState
())
.
toBe
(
STATUS
.
PAUSED
);
});
});
...
...
@@ -88,7 +95,8 @@
},
'Player state should be changed'
,
WAIT_TIMEOUT
);
runs
(
function
()
{
expect
(
player
.
callStateChangeCallback
).
toHaveBeenCalled
();
expect
(
player
.
callStateChangeCallback
)
.
toHaveBeenCalled
();
});
});
});
...
...
@@ -121,7 +129,8 @@
},
'Player state should be changed'
,
WAIT_TIMEOUT
);
runs
(
function
()
{
expect
(
player
.
callStateChangeCallback
).
toHaveBeenCalled
();
expect
(
player
.
callStateChangeCallback
)
.
toHaveBeenCalled
();
});
});
});
...
...
@@ -156,39 +165,26 @@
return
player
.
getPlayerState
()
!==
STATUS
.
PLAYING
;
},
'Player state should be changed'
,
WAIT_TIMEOUT
);
runs
(
function
()
{
expect
(
player
.
callStateChangeCallback
).
toHaveBeenCalled
();
expect
(
player
.
callStateChangeCallback
)
.
toHaveBeenCalled
();
});
});
});
describe
(
'[canplay]'
,
function
()
{
beforeEach
(
function
()
{
it
(
'player state was changed, start/end was defined, '
+
'onReady called'
,
function
()
{
waitsFor
(
function
()
{
return
player
.
getPlayerState
()
!==
STATUS
.
UNSTARTED
;
},
'Video cannot be played'
,
WAIT_TIMEOUT
);
});
it
(
'player state was changed'
,
function
()
{
runs
(
function
()
{
expect
(
player
.
getPlayerState
()).
toBe
(
STATUS
.
PAUSED
);
});
});
it
(
'end property was defined'
,
function
()
{
runs
(
function
()
{
expect
(
player
.
end
).
not
.
toBeNull
();
});
});
it
(
'start position was defined'
,
function
()
{
runs
(
function
()
{
expect
(
player
.
video
.
currentTime
).
toBe
(
player
.
start
);
});
});
it
(
'onReady callback was called'
,
function
()
{
runs
(
function
()
{
expect
(
player
.
config
.
events
.
onReady
).
toHaveBeenCalled
();
expect
(
player
.
video
.
currentTime
).
toBe
(
0
);
expect
(
player
.
config
.
events
.
onReady
)
.
toHaveBeenCalled
();
});
});
});
...
...
@@ -276,7 +272,8 @@
it
(
'getCurrentTime'
,
function
()
{
runs
(
function
()
{
player
.
video
.
currentTime
=
3
;
expect
(
player
.
getCurrentTime
()).
toBe
(
player
.
video
.
currentTime
);
expect
(
player
.
getCurrentTime
())
.
toBe
(
player
.
video
.
currentTime
);
});
});
...
...
@@ -330,7 +327,8 @@
});
it
(
'getAvailablePlaybackRates'
,
function
()
{
expect
(
player
.
getAvailablePlaybackRates
()).
toEqual
(
playbackRates
);
expect
(
player
.
getAvailablePlaybackRates
())
.
toEqual
(
playbackRates
);
});
});
});
...
...
common/lib/xmodule/xmodule/js/spec/video/resizer_spec.js
View file @
9b3bc84c
...
...
@@ -6,13 +6,19 @@ function (Resizer) {
describe
(
'Resizer'
,
function
()
{
var
html
=
[
'<div class="rszr-wrapper" style="width:200px; height: 200px;">'
,
'<div class="rszr-el" style="width:100px; height: 150px;">'
,
'<div '
+
'class="rszr-wrapper" '
+
'style="width:200px; height: 200px;"'
+
'>'
,
'<div '
+
'class="rszr-el" '
+
'style="width:100px; height: 150px;"'
+
'>'
,
'Content'
,
'</div>'
,
'</div>'
].
join
(
''
),
config
,
container
,
element
;
config
,
container
,
element
,
originalConsoleLog
;
beforeEach
(
function
()
{
setFixtures
(
html
);
...
...
@@ -23,12 +29,17 @@ function (Resizer) {
container
:
container
,
element
:
element
};
originalConsoleLog
=
window
.
console
.
log
;
spyOn
(
console
,
'log'
);
});
afterEach
(
function
()
{
window
.
console
.
log
=
originalConsoleLog
;
});
it
(
'When Initialize without required parameters, log message is shown'
,
function
()
{
spyOn
(
console
,
'log'
);
new
Resizer
({
});
expect
(
console
.
log
).
toHaveBeenCalled
();
}
...
...
common/lib/xmodule/xmodule/js/spec/video/video_caption_spec.js
View file @
9b3bc84c
...
...
@@ -22,7 +22,12 @@
afterEach
(
function
()
{
YT
.
Player
=
undefined
;
$
(
'.subtitles'
).
remove
();
// `source` tags should be removed to avoid memory leak bug that we
// had before. Removing of `source` tag, not `video` tag, stops
// loading video source and clears the memory.
$
(
'source'
).
remove
();
window
.
onTouchBasedDevice
=
oldOTBD
;
});
...
...
@@ -442,7 +447,8 @@
expect
(
videoCaption
.
currentIndex
).
toEqual
(
5
);
});
it
(
'scroll caption to new position'
,
function
()
{
// Disabled 10/25/13 due to flakiness in master
xit
(
'scroll caption to new position'
,
function
()
{
expect
(
$
.
fn
.
scrollTo
).
toHaveBeenCalled
();
});
});
...
...
@@ -640,6 +646,8 @@
beforeEach
(
function
()
{
state
.
el
.
addClass
(
'closed'
);
videoCaption
.
toggle
(
jQuery
.
Event
(
'click'
));
jasmine
.
Clock
.
useMock
();
});
it
(
'log the show_transcript event'
,
function
()
{
...
...
@@ -655,11 +663,22 @@
expect
(
state
.
el
).
not
.
toHaveClass
(
'closed'
);
});
it
(
'scroll the caption'
,
function
()
{
// Test turned off due to flakiness (30.10.2013).
xit
(
'scroll the caption'
,
function
()
{
// After transcripts are shown, and the video plays for a
// bit.
jasmine
.
Clock
.
tick
(
1000
);
// The transcripts should have advanced by at least one
// position. When they advance, the list scrolls. The
// current transcript position should be constantly
// visible.
runs
(
function
()
{
expect
(
$
.
fn
.
scrollTo
).
toHaveBeenCalled
();
});
});
});
});
describe
(
'caption accessibility'
,
function
()
{
beforeEach
(
function
()
{
...
...
common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js
View file @
9b3bc84c
(
function
()
{
describe
(
'VideoPlayer'
,
function
()
{
var
state
,
videoPlayer
,
player
,
videoControl
,
videoCaption
,
videoProgressSlider
,
videoSpeedControl
,
videoVolumeControl
,
oldOTBD
;
(
function
()
{
describe
(
'VideoPlayer'
,
function
()
{
var
state
,
videoPlayer
,
player
,
videoControl
,
videoCaption
,
videoProgressSlider
,
videoSpeedControl
,
videoVolumeControl
,
oldOTBD
;
function
initialize
(
fixture
)
{
if
(
typeof
fixture
===
'undefined'
)
{
...
...
@@ -22,16 +24,20 @@
state
.
resizer
=
(
function
()
{
var
methods
=
[
'align'
,
'alignByWidthOnly'
,
'alignByHeightOnly'
,
'setParams'
,
'setMode'
'align'
,
'alignByWidthOnly'
,
'alignByHeightOnly'
,
'setParams'
,
'setMode'
],
obj
=
{};
$
.
each
(
methods
,
function
(
index
,
method
)
{
$
.
each
(
methods
,
function
(
index
,
method
)
{
obj
[
method
]
=
jasmine
.
createSpy
(
method
).
andReturn
(
obj
);
});
return
obj
;
})(
);
}()
);
}
function
initializeYouTube
()
{
...
...
@@ -40,72 +46,85 @@
beforeEach
(
function
()
{
oldOTBD
=
window
.
onTouchBasedDevice
;
window
.
onTouchBasedDevice
=
jasmine
.
createSpy
(
'onTouchBasedDevice'
).
andReturn
(
false
);
window
.
onTouchBasedDevice
=
jasmine
.
createSpy
(
'onTouchBasedDevice'
)
.
andReturn
(
false
);
});
afterEach
(
function
()
{
afterEach
(
function
()
{
$
(
'source'
).
remove
();
window
.
onTouchBasedDevice
=
oldOTBD
;
});
describe
(
'constructor'
,
function
()
{
describe
(
'always'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'constructor'
,
function
()
{
describe
(
'always'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
});
it
(
'instanticate current time to zero'
,
function
()
{
it
(
'instanticate current time to zero'
,
function
()
{
expect
(
videoPlayer
.
currentTime
).
toEqual
(
0
);
});
it
(
'set the element'
,
function
()
{
it
(
'set the element'
,
function
()
{
expect
(
state
.
el
).
toHaveId
(
'video_id'
);
});
it
(
'create video control'
,
function
()
{
it
(
'create video control'
,
function
()
{
expect
(
videoControl
).
toBeDefined
();
expect
(
videoControl
.
el
).
toHaveClass
(
'video-controls'
);
});
it
(
'create video caption'
,
function
()
{
it
(
'create video caption'
,
function
()
{
expect
(
videoCaption
).
toBeDefined
();
expect
(
state
.
youtubeId
()).
toEqual
(
'Z5KLxerq05Y'
);
expect
(
state
.
speed
).
toEqual
(
'1.0'
);
expect
(
state
.
config
.
caption_asset_path
).
toEqual
(
'/static/subs/'
);
expect
(
state
.
config
.
caption_asset_path
)
.
toEqual
(
'/static/subs/'
);
});
it
(
'create video speed control'
,
function
()
{
it
(
'create video speed control'
,
function
()
{
expect
(
videoSpeedControl
).
toBeDefined
();
expect
(
videoSpeedControl
.
el
).
toHaveClass
(
'speeds'
);
expect
(
videoSpeedControl
.
speeds
).
toEqual
([
'0.75'
,
'1.0'
,
'1.25'
,
'1.50'
]);
expect
(
videoSpeedControl
.
speeds
)
.
toEqual
([
'0.75'
,
'1.0'
,
'1.25'
,
'1.50'
]);
expect
(
state
.
speed
).
toEqual
(
'1.0'
);
});
it
(
'create video progress slider'
,
function
()
{
it
(
'create video progress slider'
,
function
()
{
expect
(
videoProgressSlider
).
toBeDefined
();
expect
(
videoProgressSlider
.
el
).
toHaveClass
(
'slider'
);
});
// All the toHandleWith() expect tests are not necessary for this version of Video.
// jQuery event system is not used to trigger and invoke methods. This is an artifact from
// All the toHandleWith() expect tests are not necessary for
// this version of Video. jQuery event system is not used to
// trigger and invoke methods. This is an artifact from
// previous version of Video.
});
it
(
'create Youtube player'
,
function
()
{
var
oldYT
=
window
.
YT
;
it
(
'create Youtube player'
,
function
()
{
var
oldYT
=
window
.
YT
,
events
;
jasmine
.
stubRequests
();
window
.
YT
=
{
Player
:
function
()
{
},
PlayerState
:
oldYT
.
PlayerState
,
ready
:
function
(
f
){
f
();}
ready
:
function
(
f
)
{
f
();
}
};
spyOn
(
window
.
YT
,
'Player'
);
initializeYouTube
();
events
=
{
onReady
:
videoPlayer
.
onReady
,
onStateChange
:
videoPlayer
.
onStateChange
,
onPlaybackQualityChange
:
videoPlayer
.
onPlaybackQualityChange
};
expect
(
YT
.
Player
).
toHaveBeenCalledWith
(
'id'
,
{
playerVars
:
{
controls
:
0
,
...
...
@@ -114,51 +133,46 @@
showinfo
:
0
,
enablejsapi
:
1
,
modestbranding
:
1
,
html5
:
1
,
start
:
0
,
end
:
null
html5
:
1
},
videoId
:
'cogebirgzzM'
,
events
:
{
onReady
:
videoPlayer
.
onReady
,
onStateChange
:
videoPlayer
.
onStateChange
,
onPlaybackQualityChange
:
videoPlayer
.
onPlaybackQualityChange
}
events
:
events
});
window
.
YT
=
oldYT
;
});
// We can't test the invocation of HTML5Video because it is not available
// globally. It is defined within the scope of Require JS.
// We can't test the invocation of HTML5Video because it is not
// available globally. It is defined within the scope of Require
// JS.
describe
(
'when not on a touch based device'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'when not on a touch based device'
,
function
()
{
beforeEach
(
function
()
{
window
.
onTouchBasedDevice
.
andReturn
(
true
);
initialize
();
});
it
(
'create video volume control'
,
function
()
{
it
(
'create video volume control'
,
function
()
{
expect
(
videoVolumeControl
).
toBeDefined
();
expect
(
videoVolumeControl
.
el
).
toHaveClass
(
'volume'
);
});
});
describe
(
'when on a touch based device'
,
function
()
{
describe
(
'when on a touch based device'
,
function
()
{
var
oldOTBD
;
beforeEach
(
function
()
{
beforeEach
(
function
()
{
initialize
();
});
it
(
'controls are in paused state'
,
function
()
{
it
(
'controls are in paused state'
,
function
()
{
expect
(
videoControl
.
isPlaying
).
toBe
(
false
);
});
});
});
describe
(
'onReady'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'onReady'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
spyOn
(
videoPlayer
,
'log'
).
andCallThrough
();
...
...
@@ -166,18 +180,18 @@
videoPlayer
.
onReady
();
});
it
(
'log the load_video event'
,
function
()
{
it
(
'log the load_video event'
,
function
()
{
expect
(
videoPlayer
.
log
).
toHaveBeenCalledWith
(
'load_video'
);
});
it
(
'autoplay the first video'
,
function
()
{
it
(
'autoplay the first video'
,
function
()
{
expect
(
videoPlayer
.
play
).
not
.
toHaveBeenCalled
();
});
});
describe
(
'onStateChange'
,
function
()
{
describe
(
'when the video is unstarted'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'onStateChange'
,
function
()
{
describe
(
'when the video is unstarted'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
spyOn
(
videoControl
,
'pause'
).
andCallThrough
();
...
...
@@ -188,19 +202,19 @@
});
});
it
(
'pause the video control'
,
function
()
{
it
(
'pause the video control'
,
function
()
{
expect
(
videoControl
.
pause
).
toHaveBeenCalled
();
});
it
(
'pause the video caption'
,
function
()
{
it
(
'pause the video caption'
,
function
()
{
expect
(
videoCaption
.
pause
).
toHaveBeenCalled
();
});
});
describe
(
'when the video is playing'
,
function
()
{
describe
(
'when the video is playing'
,
function
()
{
var
oldState
;
beforeEach
(
function
()
{
beforeEach
(
function
()
{
// Create the first instance of the player.
initialize
();
oldState
=
state
;
...
...
@@ -220,38 +234,39 @@
});
});
it
(
'log the play_video event'
,
function
()
{
expect
(
videoPlayer
.
log
).
toHaveBeenCalledWith
(
'play_video'
,
{
currentTime
:
0
}
);
it
(
'log the play_video event'
,
function
()
{
expect
(
videoPlayer
.
log
).
toHaveBeenCalledWith
(
'play_video'
,
{
currentTime
:
0
}
);
});
it
(
'pause other video player'
,
function
()
{
it
(
'pause other video player'
,
function
()
{
expect
(
oldState
.
videoPlayer
.
onPause
).
toHaveBeenCalled
();
});
it
(
'set update interval'
,
function
()
{
expect
(
window
.
setInterval
).
toHaveBeenCalledWith
(
videoPlayer
.
update
,
200
);
it
(
'set update interval'
,
function
()
{
expect
(
window
.
setInterval
).
toHaveBeenCalledWith
(
videoPlayer
.
update
,
200
);
expect
(
videoPlayer
.
updateInterval
).
toEqual
(
100
);
});
it
(
'play the video control'
,
function
()
{
it
(
'play the video control'
,
function
()
{
expect
(
videoControl
.
play
).
toHaveBeenCalled
();
});
it
(
'play the video caption'
,
function
()
{
it
(
'play the video caption'
,
function
()
{
expect
(
videoCaption
.
play
).
toHaveBeenCalled
();
});
});
describe
(
'when the video is paused'
,
function
()
{
describe
(
'when the video is paused'
,
function
()
{
var
currentUpdateIntrval
;
beforeEach
(
function
()
{
beforeEach
(
function
()
{
initialize
();
spyOn
(
videoPlayer
,
'log'
).
andCallThrough
();
spyOn
(
window
,
'clearInterval'
).
andCallThrough
();
spyOn
(
videoControl
,
'pause'
).
andCallThrough
();
spyOn
(
videoCaption
,
'pause'
).
andCallThrough
();
...
...
@@ -266,28 +281,27 @@
});
});
it
(
'log the pause_video event'
,
function
()
{
expect
(
videoPlayer
.
log
).
toHaveBeenCalledWith
(
'pause_video'
,
{
currentTime
:
0
}
);
it
(
'log the pause_video event'
,
function
()
{
expect
(
videoPlayer
.
log
).
toHaveBeenCalledWith
(
'pause_video'
,
{
currentTime
:
0
}
);
});
it
(
'clear update interval'
,
function
()
{
expect
(
window
.
clearInterval
).
toHaveBeenCalledWith
(
currentUpdateIntrval
);
it
(
'clear update interval'
,
function
()
{
expect
(
videoPlayer
.
updateInterval
).
toBeUndefined
();
});
it
(
'pause the video control'
,
function
()
{
it
(
'pause the video control'
,
function
()
{
expect
(
videoControl
.
pause
).
toHaveBeenCalled
();
});
it
(
'pause the video caption'
,
function
()
{
it
(
'pause the video caption'
,
function
()
{
expect
(
videoCaption
.
pause
).
toHaveBeenCalled
();
});
});
describe
(
'when the video is ended'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'when the video is ended'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
spyOn
(
videoControl
,
'pause'
).
andCallThrough
();
...
...
@@ -298,70 +312,103 @@
});
});
it
(
'pause the video control'
,
function
()
{
it
(
'pause the video control'
,
function
()
{
expect
(
videoControl
.
pause
).
toHaveBeenCalled
();
});
it
(
'pause the video caption'
,
function
()
{
it
(
'pause the video caption'
,
function
()
{
expect
(
videoCaption
.
pause
).
toHaveBeenCalled
();
});
});
});
describe
(
'onSeek'
,
function
()
{
beforeEach
(
function
()
{
spyOn
(
window
,
'clearInterval'
).
andCallThrough
();
describe
(
'onSeek'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
videoPlayer
.
updateInterval
=
100
;
spyOn
(
videoPlayer
,
'updatePlayTime'
);
spyOn
(
videoPlayer
,
'log'
);
spyOn
(
videoPlayer
.
player
,
'seekTo'
);
state
.
videoPlayer
.
play
();
waitsFor
(
function
()
{
var
duration
=
videoPlayer
.
duration
(),
currentTime
=
videoPlayer
.
currentTime
;
return
(
isFinite
(
currentTime
)
&&
currentTime
>
0
&&
isFinite
(
duration
)
&&
duration
>
0
);
},
'video begins playing'
,
10000
);
});
it
(
'Slider event causes log update'
,
function
()
{
videoProgressSlider
.
onSlide
(
jQuery
.
Event
(
'slide'
),
{
value
:
60
});
runs
(
function
()
{
var
currentTime
=
videoPlayer
.
currentTime
;
videoProgressSlider
.
onSlide
(
jQuery
.
Event
(
'slide'
),
{
value
:
2
}
);
expect
(
videoPlayer
.
log
).
toHaveBeenCalledWith
(
'seek_video'
,
{
old_time
:
0
,
new_time
:
60
,
old_time
:
currentTime
,
new_time
:
2
,
type
:
'onSlideSeek'
}
);
});
it
(
'seek the player'
,
function
()
{
videoProgressSlider
.
onSlide
(
jQuery
.
Event
(
'slide'
),
{
value
:
60
});
expect
(
videoPlayer
.
player
.
seekTo
).
toHaveBeenCalledWith
(
60
,
true
);
});
it
(
'call updatePlayTime on player'
,
function
()
{
videoProgressSlider
.
onSlide
(
jQuery
.
Event
(
'slide'
),
{
value
:
60
});
it
(
'seek the player'
,
function
()
{
runs
(
function
()
{
videoProgressSlider
.
onSlide
(
jQuery
.
Event
(
'slide'
),
{
value
:
60
}
);
expect
(
videoPlayer
.
updatePlayTime
).
toHaveBeenCalledWith
(
60
);
expect
(
videoPlayer
.
player
.
seekTo
)
.
toHaveBeenCalledWith
(
60
,
true
);
});
});
it
(
'when the player is playing: reset the update interval'
,
function
()
{
videoProgressSlider
.
onSlide
(
jQuery
.
Event
(
'slide'
),
{
value
:
60
});
it
(
'call updatePlayTime on player'
,
function
()
{
runs
(
function
()
{
videoProgressSlider
.
onSlide
(
jQuery
.
Event
(
'slide'
),
{
value
:
60
}
);
expect
(
window
.
clearInterval
).
toHaveBeenCalledWith
(
100
);
expect
(
videoPlayer
.
updatePlayTime
)
.
toHaveBeenCalledWith
(
60
);
});
});
it
(
'when the player is not playing: set the current time'
,
function
()
{
videoProgressSlider
.
onSlide
(
jQuery
.
Event
(
'slide'
),
{
value
:
60
});
// Disabled 10/25/13 due to flakiness in master
xit
(
'when the player is not playing: set the current time'
,
function
()
{
runs
(
function
()
{
videoProgressSlider
.
onSlide
(
jQuery
.
Event
(
'slide'
),
{
value
:
20
}
);
videoPlayer
.
pause
();
videoProgressSlider
.
onSlide
(
jQuery
.
Event
(
'slide'
),
{
value
:
10
}
);
expect
(
videoPlayer
.
currentTime
).
toEqual
(
60
);
waitsFor
(
function
()
{
return
Math
.
round
(
videoPlayer
.
currentTime
)
===
10
;
},
'currentTime got updated'
,
10000
);
});
});
});
describe
(
'onSpeedChange'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'onSpeedChange'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
videoPlayer
.
currentTime
=
60
;
...
...
@@ -372,113 +419,122 @@
spyOn
(
videoPlayer
.
player
,
'setPlaybackRate'
).
andCallThrough
();
});
describe
(
'always'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'always'
,
function
()
{
beforeEach
(
function
()
{
videoPlayer
.
onSpeedChange
(
'0.75'
,
false
);
});
it
(
'check if speed_change_video is logged'
,
function
()
{
expect
(
videoPlayer
.
log
).
toHaveBeenCalledWith
(
'speed_change_video'
,
{
it
(
'check if speed_change_video is logged'
,
function
()
{
expect
(
videoPlayer
.
log
).
toHaveBeenCalledWith
(
'speed_change_video'
,
{
current_time
:
videoPlayer
.
currentTime
,
old_speed
:
'1.0'
,
new_speed
:
'0.75'
});
}
);
});
it
(
'convert the current time to the new speed'
,
function
()
{
it
(
'convert the current time to the new speed'
,
function
()
{
expect
(
videoPlayer
.
currentTime
).
toEqual
(
60
);
});
it
(
'set video speed to the new speed'
,
function
()
{
it
(
'set video speed to the new speed'
,
function
()
{
expect
(
state
.
setSpeed
).
toHaveBeenCalledWith
(
'0.75'
,
false
);
});
// Not relevant any more:
//
// expect( "tell video caption that the speed has changed" ) ...
});
describe
(
'when the video is playing'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'when the video is playing'
,
function
()
{
beforeEach
(
function
()
{
videoPlayer
.
play
();
videoPlayer
.
onSpeedChange
(
'0.75'
,
false
);
});
it
(
'trigger updatePlayTime event'
,
function
()
{
expect
(
videoPlayer
.
player
.
setPlaybackRate
).
toHaveBeenCalledWith
(
'0.75'
);
it
(
'trigger updatePlayTime event'
,
function
()
{
expect
(
videoPlayer
.
player
.
setPlaybackRate
)
.
toHaveBeenCalledWith
(
'0.75'
);
});
});
describe
(
'when the video is not playing'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'when the video is not playing'
,
function
()
{
beforeEach
(
function
()
{
videoPlayer
.
pause
();
videoPlayer
.
onSpeedChange
(
'0.75'
,
false
);
});
it
(
'trigger updatePlayTime event'
,
function
()
{
expect
(
videoPlayer
.
player
.
setPlaybackRate
).
toHaveBeenCalledWith
(
'0.75'
);
it
(
'trigger updatePlayTime event'
,
function
()
{
expect
(
videoPlayer
.
player
.
setPlaybackRate
)
.
toHaveBeenCalledWith
(
'0.75'
);
});
});
});
describe
(
'onVolumeChange'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'onVolumeChange'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
spyOn
(
videoPlayer
.
player
,
'setVolume'
);
videoPlayer
.
onVolumeChange
(
60
);
});
it
(
'set the volume on player'
,
function
()
{
it
(
'set the volume on player'
,
function
()
{
expect
(
videoPlayer
.
player
.
setVolume
).
toHaveBeenCalledWith
(
60
);
});
});
describe
(
'update'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'update'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
spyOn
(
videoPlayer
,
'updatePlayTime'
).
andCallThrough
();
});
describe
(
'when the current time is unavailable from the player'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'when the current time is unavailable from the player'
,
function
()
{
beforeEach
(
function
()
{
videoPlayer
.
player
.
getCurrentTime
=
function
()
{
return
NaN
;
};
videoPlayer
.
update
();
});
it
(
'does not trigger updatePlayTime event'
,
function
()
{
it
(
'does not trigger updatePlayTime event'
,
function
()
{
expect
(
videoPlayer
.
updatePlayTime
).
not
.
toHaveBeenCalled
();
});
});
describe
(
'when the current time is available from the player'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'when the current time is available from the player'
,
function
()
{
beforeEach
(
function
()
{
videoPlayer
.
player
.
getCurrentTime
=
function
()
{
return
60
;
};
videoPlayer
.
update
();
});
it
(
'trigger updatePlayTime event'
,
function
()
{
expect
(
videoPlayer
.
updatePlayTime
).
toHaveBeenCalledWith
(
60
);
it
(
'trigger updatePlayTime event'
,
function
()
{
expect
(
videoPlayer
.
updatePlayTime
)
.
toHaveBeenCalledWith
(
60
);
});
});
});
describe
(
'updatePlayTime'
,
function
()
{
beforeEach
(
function
()
{
// Disabled 10/24/13 due to flakiness in master
xdescribe
(
'updatePlayTime'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
spyOn
(
videoCaption
,
'updatePlayTime'
).
andCallThrough
();
spyOn
(
videoProgressSlider
,
'updatePlayTime'
).
andCallThrough
();
});
it
(
'update the video playback time'
,
function
()
{
it
(
'update the video playback time'
,
function
()
{
var
duration
=
0
;
waitsFor
(
function
()
{
...
...
@@ -500,7 +556,10 @@
// We resort to this trickery because Firefox and Chrome
// round the total time a bit differently.
if
(
htmlStr
.
match
(
'1:00 / 1:01'
)
||
htmlStr
.
match
(
'1:00 / 1:00'
))
{
if
(
htmlStr
.
match
(
'1:00 / 1:01'
)
||
htmlStr
.
match
(
'1:00 / 1:00'
)
)
{
expect
(
true
).
toBe
(
true
);
}
else
{
expect
(
true
).
toBe
(
false
);
...
...
@@ -512,43 +571,33 @@
});
});
it
(
'update the playback time on caption'
,
function
()
{
var
duration
=
0
;
it
(
'update the playback time on caption'
,
function
()
{
waitsFor
(
function
()
{
duration
=
videoPlayer
.
duration
();
if
(
duration
>
0
)
{
return
true
;
}
return
false
;
return
videoPlayer
.
duration
()
>
0
;
},
'Video is fully loaded.'
,
1000
);
runs
(
function
()
{
videoPlayer
.
updatePlayTime
(
60
);
expect
(
videoCaption
.
updatePlayTime
).
toHaveBeenCalledWith
(
60
);
expect
(
videoCaption
.
updatePlayTime
)
.
toHaveBeenCalledWith
(
60
);
});
});
it
(
'update the playback time on progress slider'
,
function
()
{
it
(
'update the playback time on progress slider'
,
function
()
{
var
duration
=
0
;
waitsFor
(
function
()
{
duration
=
videoPlayer
.
duration
();
if
(
duration
>
0
)
{
return
true
;
}
return
false
;
return
duration
>
0
;
},
'Video is fully loaded.'
,
1000
);
runs
(
function
()
{
videoPlayer
.
updatePlayTime
(
60
);
expect
(
videoProgressSlider
.
updatePlayTime
).
toHaveBeenCalledWith
({
expect
(
videoProgressSlider
.
updatePlayTime
)
.
toHaveBeenCalledWith
({
time
:
60
,
duration
:
duration
});
...
...
@@ -556,30 +605,31 @@
});
});
describe
(
'toggleFullScreen'
,
function
()
{
describe
(
'when the video player is not full screen'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'toggleFullScreen'
,
function
()
{
describe
(
'when the video player is not full screen'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
spyOn
(
videoCaption
,
'resize'
).
andCallThrough
();
videoControl
.
toggleFullScreen
(
jQuery
.
Event
(
"click"
));
videoControl
.
toggleFullScreen
(
jQuery
.
Event
(
'click'
));
});
it
(
'replace the full screen button tooltip'
,
function
()
{
expect
(
$
(
'.add-fullscreen'
)).
toHaveAttr
(
'title'
,
'Exit full browser'
);
it
(
'replace the full screen button tooltip'
,
function
()
{
expect
(
$
(
'.add-fullscreen'
))
.
toHaveAttr
(
'title'
,
'Exit full browser'
);
});
it
(
'add the video-fullscreen class'
,
function
()
{
it
(
'add the video-fullscreen class'
,
function
()
{
expect
(
state
.
el
).
toHaveClass
(
'video-fullscreen'
);
});
it
(
'tell VideoCaption to resize'
,
function
()
{
it
(
'tell VideoCaption to resize'
,
function
()
{
expect
(
videoCaption
.
resize
).
toHaveBeenCalled
();
expect
(
state
.
resizer
.
setMode
).
toHaveBeenCalled
();
});
});
describe
(
'when the video player already full screen'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'when the video player already full screen'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
spyOn
(
videoCaption
,
'resize'
).
andCallThrough
();
...
...
@@ -588,121 +638,124 @@
isFullScreen
=
true
;
videoControl
.
fullScreenEl
.
attr
(
'title'
,
'Exit-fullscreen'
);
videoControl
.
toggleFullScreen
(
jQuery
.
Event
(
"click"
));
videoControl
.
toggleFullScreen
(
jQuery
.
Event
(
'click'
));
});
it
(
'replace the full screen button tooltip'
,
function
()
{
expect
(
$
(
'.add-fullscreen'
)).
toHaveAttr
(
'title'
,
'Fill browser'
);
it
(
'replace the full screen button tooltip'
,
function
()
{
expect
(
$
(
'.add-fullscreen'
))
.
toHaveAttr
(
'title'
,
'Fill browser'
);
});
it
(
'remove the video-fullscreen class'
,
function
()
{
it
(
'remove the video-fullscreen class'
,
function
()
{
expect
(
state
.
el
).
not
.
toHaveClass
(
'video-fullscreen'
);
});
it
(
'tell VideoCaption to resize'
,
function
()
{
it
(
'tell VideoCaption to resize'
,
function
()
{
expect
(
videoCaption
.
resize
).
toHaveBeenCalled
();
expect
(
state
.
resizer
.
setMode
).
toHaveBeenCalledWith
(
'width'
);
expect
(
state
.
resizer
.
setMode
)
.
toHaveBeenCalledWith
(
'width'
);
});
});
});
describe
(
'play'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'play'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
spyOn
(
player
,
'playVideo'
).
andCallThrough
();
});
describe
(
'when the player is not ready'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'when the player is not ready'
,
function
()
{
beforeEach
(
function
()
{
player
.
playVideo
=
void
0
;
videoPlayer
.
play
();
});
it
(
'does nothing'
,
function
()
{
it
(
'does nothing'
,
function
()
{
expect
(
player
.
playVideo
).
toBeUndefined
();
});
});
describe
(
'when the player is ready'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'when the player is ready'
,
function
()
{
beforeEach
(
function
()
{
player
.
playVideo
.
andReturn
(
true
);
videoPlayer
.
play
();
});
it
(
'delegate to the player'
,
function
()
{
it
(
'delegate to the player'
,
function
()
{
expect
(
player
.
playVideo
).
toHaveBeenCalled
();
});
});
});
describe
(
'isPlaying'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'isPlaying'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
spyOn
(
player
,
'getPlayerState'
).
andCallThrough
();
});
describe
(
'when the video is playing'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'when the video is playing'
,
function
()
{
beforeEach
(
function
()
{
player
.
getPlayerState
.
andReturn
(
YT
.
PlayerState
.
PLAYING
);
});
it
(
'return true'
,
function
()
{
it
(
'return true'
,
function
()
{
expect
(
videoPlayer
.
isPlaying
()).
toBeTruthy
();
});
});
describe
(
'when the video is not playing'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'when the video is not playing'
,
function
()
{
beforeEach
(
function
()
{
player
.
getPlayerState
.
andReturn
(
YT
.
PlayerState
.
PAUSED
);
});
it
(
'return false'
,
function
()
{
it
(
'return false'
,
function
()
{
expect
(
videoPlayer
.
isPlaying
()).
toBeFalsy
();
});
});
});
describe
(
'pause'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'pause'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
spyOn
(
player
,
'pauseVideo'
).
andCallThrough
();
videoPlayer
.
pause
();
});
it
(
'delegate to the player'
,
function
()
{
it
(
'delegate to the player'
,
function
()
{
expect
(
player
.
pauseVideo
).
toHaveBeenCalled
();
});
});
describe
(
'duration'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'duration'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
spyOn
(
player
,
'getDuration'
).
andCallThrough
();
videoPlayer
.
duration
();
});
it
(
'delegate to the player'
,
function
()
{
it
(
'delegate to the player'
,
function
()
{
expect
(
player
.
getDuration
).
toHaveBeenCalled
();
});
});
describe
(
'playback rate'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'playback rate'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
player
.
setPlaybackRate
(
1.5
);
});
it
(
'set the player playback rate'
,
function
()
{
it
(
'set the player playback rate'
,
function
()
{
expect
(
player
.
video
.
playbackRate
).
toEqual
(
1.5
);
});
});
describe
(
'volume'
,
function
()
{
beforeEach
(
function
()
{
describe
(
'volume'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
spyOn
(
player
,
'getVolume'
).
andCallThrough
();
});
it
(
'set the player volume'
,
function
()
{
it
(
'set the player volume'
,
function
()
{
var
expectedValue
=
60
,
realValue
;
...
...
common/lib/xmodule/xmodule/js/spec/video/video_progress_slider_spec.js
View file @
9b3bc84c
...
...
@@ -11,10 +11,10 @@
beforeEach
(
function
()
{
oldOTBD
=
window
.
onTouchBasedDevice
;
window
.
onTouchBasedDevice
=
jasmine
.
createSpy
(
'onTouchBasedDevice'
).
andReturn
(
false
);
window
.
onTouchBasedDevice
=
jasmine
.
createSpy
(
'onTouchBasedDevice'
)
.
andReturn
(
false
);
});
afterEach
(
function
()
{
$
(
'source'
).
remove
();
window
.
onTouchBasedDevice
=
oldOTBD
;
...
...
@@ -38,7 +38,8 @@
});
it
(
'build the seek handle'
,
function
()
{
expect
(
videoProgressSlider
.
handle
).
toBe
(
'.slider .ui-slider-handle'
);
expect
(
videoProgressSlider
.
handle
)
.
toBe
(
'.slider .ui-slider-handle'
);
});
});
...
...
@@ -101,15 +102,22 @@
beforeEach
(
function
()
{
spyOn
(
$
.
fn
,
'slider'
).
andCallThrough
();
videoProgressSlider
.
frozen
=
false
;
videoProgressSlider
.
updatePlayTime
({
time
:
20
,
duration
:
120
});
videoProgressSlider
.
updatePlayTime
({
time
:
20
,
duration
:
120
});
});
it
(
'update the max value of the slider'
,
function
()
{
expect
(
$
.
fn
.
slider
).
toHaveBeenCalledWith
(
'option'
,
'max'
,
120
);
expect
(
$
.
fn
.
slider
).
toHaveBeenCalledWith
(
'option'
,
'max'
,
120
);
});
it
(
'update current value of the slider'
,
function
()
{
expect
(
$
.
fn
.
slider
).
toHaveBeenCalledWith
(
'option'
,
'value'
,
20
);
expect
(
$
.
fn
.
slider
).
toHaveBeenCalledWith
(
'option'
,
'value'
,
20
);
});
});
});
...
...
@@ -119,27 +127,44 @@
initialize
();
spyOn
(
$
.
fn
,
'slider'
).
andCallThrough
();
spyOn
(
videoPlayer
,
'onSlideSeek'
).
andCallThrough
();
videoProgressSlider
.
onSlide
({},
{
value
:
20
});
state
.
videoPlayer
.
play
();
waitsFor
(
function
()
{
var
duration
=
videoPlayer
.
duration
(),
currentTime
=
videoPlayer
.
currentTime
;
return
(
isFinite
(
currentTime
)
&&
currentTime
>
0
&&
isFinite
(
duration
)
&&
duration
>
0
);
},
'video begins playing'
,
10000
);
});
it
(
'freeze the slider'
,
function
()
{
runs
(
function
()
{
videoProgressSlider
.
onSlide
(
jQuery
.
Event
(
'slide'
),
{
value
:
20
}
);
expect
(
videoProgressSlider
.
frozen
).
toBeTruthy
();
});
});
// Turned off test due to flakiness (30.10.2013).
xit
(
'trigger seek event'
,
function
()
{
runs
(
function
()
{
videoProgressSlider
.
onSlide
(
jQuery
.
Event
(
'slide'
),
{
value
:
20
}
);
it
(
'trigger seek event'
,
function
()
{
expect
(
videoPlayer
.
onSlideSeek
).
toHaveBeenCalled
();
expect
(
videoPlayer
.
currentTime
).
toEqual
(
20
);
});
});
describe
(
'onChange'
,
function
()
{
beforeEach
(
function
()
{
initialize
();
spyOn
(
$
.
fn
,
'slider'
).
andCallThrough
();
videoProgressSlider
.
onChange
({},
{
value
:
20
waitsFor
(
function
()
{
return
Math
.
round
(
videoPlayer
.
currentTime
)
===
20
;
},
'currentTime got updated'
,
10000
);
});
});
});
...
...
@@ -149,44 +174,80 @@
var
oldSetTimeout
=
null
;
beforeEach
(
function
()
{
// Store original window.setTimeout() function. If we do not do this, then
// all other tests that rely on code which uses window.setTimeout()
//
function might (and probably will) fail.
// Store original window.setTimeout() function. If we do not do
// this, then all other tests that rely on code which uses
// window.setTimeout()
function might (and probably will) fail.
oldSetTimeout
=
window
.
setTimeout
;
// Redefine window.setTimeout() function as a spy.
window
.
setTimeout
=
jasmine
.
createSpy
().
andCallFake
(
function
(
callback
,
timeout
)
{
return
5
;
});
window
.
setTimeout
=
jasmine
.
createSpy
()
.
andCallFake
(
function
(
callback
,
timeout
)
{
return
5
;
});
window
.
setTimeout
.
andReturn
(
100
);
initialize
();
spyOn
(
videoPlayer
,
'onSlideSeek'
).
andCallThrough
();
videoProgressSlider
.
onStop
({},
{
value
:
20
});
videoPlayer
.
play
();
waitsFor
(
function
()
{
var
duration
=
videoPlayer
.
duration
(),
currentTime
=
videoPlayer
.
currentTime
;
return
(
isFinite
(
currentTime
)
&&
currentTime
>
0
&&
isFinite
(
duration
)
&&
duration
>
0
);
},
'video begins playing'
,
10000
);
});
afterEach
(
function
()
{
// Reset the default window.setTimeout() function. If we do not do this,
// then all other tests that rely on code which uses window.setTimeout()
//
function might (and probably will) fail.
// Reset the default window.setTimeout() function. If we do not
// do this, then all other tests that rely on code which uses
// window.setTimeout()
function might (and probably will) fail.
window
.
setTimeout
=
oldSetTimeout
;
});
it
(
'freeze the slider'
,
function
()
{
runs
(
function
()
{
videoProgressSlider
.
onStop
(
jQuery
.
Event
(
'stop'
),
{
value
:
20
}
);
expect
(
videoProgressSlider
.
frozen
).
toBeTruthy
();
});
});
// Turned off test due to flakiness (30.10.2013).
xit
(
'trigger seek event'
,
function
()
{
runs
(
function
()
{
videoProgressSlider
.
onStop
(
jQuery
.
Event
(
'stop'
),
{
value
:
20
}
);
it
(
'trigger seek event'
,
function
()
{
expect
(
videoPlayer
.
onSlideSeek
).
toHaveBeenCalled
();
expect
(
videoPlayer
.
currentTime
).
toEqual
(
20
);
waitsFor
(
function
()
{
return
Math
.
round
(
videoPlayer
.
currentTime
)
===
20
;
},
'currentTime got updated'
,
10000
);
});
});
it
(
'set timeout to unfreeze the slider'
,
function
()
{
runs
(
function
()
{
videoProgressSlider
.
onStop
(
jQuery
.
Event
(
'stop'
),
{
value
:
20
}
);
// Disabled 10/9/13 after failing in master
xit
(
'set timeout to unfreeze the slider'
,
function
()
{
expect
(
window
.
setTimeout
).
toHaveBeenCalledWith
(
jasmine
.
any
(
Function
),
200
);
expect
(
window
.
setTimeout
).
toHaveBeenCalledWith
(
jasmine
.
any
(
Function
),
200
);
window
.
setTimeout
.
mostRecentCall
.
args
[
0
]();
expect
(
videoProgressSlider
.
frozen
).
toBeFalsy
();
});
});
});
});
}).
call
(
this
);
common/lib/xmodule/xmodule/js/src/video/00_resizer.js
View file @
9b3bc84c
...
...
@@ -23,7 +23,9 @@ function () {
}
if
(
!
config
.
element
)
{
console
.
log
(
'Required parameter `element` is not passed.'
);
console
.
log
(
'[Video info]: Required parameter `element` is not passed.'
);
}
return
this
;
...
...
@@ -55,7 +57,7 @@ function () {
};
};
var
align
=
function
()
{
var
align
=
function
()
{
var
data
=
getData
();
switch
(
mode
)
{
...
...
common/lib/xmodule/xmodule/js/src/video/01_initialize.js
View file @
9b3bc84c
...
...
@@ -262,8 +262,8 @@ function (VideoPlayer) {
this
.
config
=
{
element
:
element
,
start
:
data
[
'start'
],
end
:
data
[
'end'
],
start
Time
:
data
[
'start'
],
end
Time
:
data
[
'end'
],
caption_data_dir
:
data
[
'captionDataDir'
],
caption_asset_path
:
data
[
'captionAssetPath'
],
show_captions
:
regExp
.
test
(
data
[
'showCaptions'
].
toString
()),
...
...
@@ -369,7 +369,7 @@ function (VideoPlayer) {
/*
* function checkStartEndTimes()
*
* Validate config.start
and config.end
times.
* Validate config.start
Time and config.endTime
times.
*
* We can check at this time if the times are proper integers, and if they
* make general sense. I.e. if start time is => 0 and <= end time.
...
...
@@ -379,14 +379,18 @@ function (VideoPlayer) {
* if start time and/or end time are greater than the length of the video.
*/
function
checkStartEndTimes
()
{
this
.
config
.
start
=
parseInt
(
this
.
config
.
start
,
10
);
if
(
(
!
isFinite
(
this
.
config
.
start
))
||
(
this
.
config
.
start
<
0
)
)
{
this
.
config
.
start
=
0
;
this
.
config
.
start
Time
=
parseInt
(
this
.
config
.
startTime
,
10
);
if
(
!
isFinite
(
this
.
config
.
startTime
)
||
this
.
config
.
startTime
<
0
)
{
this
.
config
.
start
Time
=
0
;
}
this
.
config
.
end
=
parseInt
(
this
.
config
.
end
,
10
);
if
((
!
isFinite
(
this
.
config
.
end
))
||
(
this
.
config
.
end
<
this
.
config
.
start
))
{
this
.
config
.
end
=
null
;
this
.
config
.
endTime
=
parseInt
(
this
.
config
.
endTime
,
10
);
if
(
!
isFinite
(
this
.
config
.
endTime
)
||
this
.
config
.
endTime
<
this
.
config
.
startTime
||
this
.
config
.
endTime
===
0
)
{
this
.
config
.
endTime
=
null
;
}
}
...
...
common/lib/xmodule/xmodule/js/src/video/025_focus_grabber.js
View file @
9b3bc84c
...
...
@@ -44,8 +44,12 @@ function () {
// Private functions.
function
_makeFunctionsPublic
(
state
)
{
state
.
focusGrabber
.
enableFocusGrabber
=
_
.
bind
(
enableFocusGrabber
,
state
);
state
.
focusGrabber
.
disableFocusGrabber
=
_
.
bind
(
disableFocusGrabber
,
state
);
state
.
focusGrabber
.
enableFocusGrabber
=
_
.
bind
(
enableFocusGrabber
,
state
);
state
.
focusGrabber
.
disableFocusGrabber
=
_
.
bind
(
disableFocusGrabber
,
state
);
state
.
focusGrabber
.
onFocus
=
_
.
bind
(
onFocus
,
state
);
}
...
...
common/lib/xmodule/xmodule/js/src/video/02_html5_video.js
View file @
9b3bc84c
/**
* @file HTML5 video player module. Provides methods to control the in-browser HTML5 video player.
* @file HTML5 video player module. Provides methods to control the in-browser
* HTML5 video player.
*
* The goal was to write this module so that it closely resembles the YouTube API. The main reason
* for this is because initially the edX video player supported only YouTube videos. When HTML5
* support was added, for greater compatibility, and to reduce the amount of code that needed to
* be modified, it was decided to write a similar API as the one provided by YouTube.
* The goal was to write this module so that it closely resembles the YouTube
* API. The main reason for this is because initially the edX video player
* supported only YouTube videos. When HTML5 support was added, for greater
* compatibility, and to reduce the amount of code that needed to be modified,
* it was decided to write a similar API as the one provided by YouTube.
*
* @external RequireJS
*
...
...
@@ -33,16 +35,17 @@ function () {
};
Player
.
prototype
.
seekTo
=
function
(
value
)
{
if
((
typeof
value
===
'number'
)
&&
(
value
<=
this
.
video
.
duration
)
&&
(
value
>=
0
))
{
this
.
start
=
0
;
this
.
end
=
this
.
video
.
duration
;
if
(
typeof
value
===
'number'
&&
value
<=
this
.
video
.
duration
&&
value
>=
0
)
{
this
.
video
.
currentTime
=
value
;
}
};
Player
.
prototype
.
setVolume
=
function
(
value
)
{
if
(
(
typeof
value
===
'number'
)
&&
(
value
<=
100
)
&&
(
value
>=
0
)
)
{
if
(
typeof
value
===
'number'
&&
value
<=
100
&&
value
>=
0
)
{
this
.
video
.
volume
=
value
*
0.01
;
}
};
...
...
@@ -92,34 +95,32 @@ function () {
/*
* Constructor function for HTML5 Video player.
*
* @param {String|Object} el A DOM element where the HTML5 player will be inserted (as returned by jQuery(selector) function),
* or a selector string which will be used to select an element. This is a required parameter.
* @param {String|Object} el A DOM element where the HTML5 player will
* be inserted (as returned by jQuery(selector) function), or a
* selector string which will be used to select an element. This is a
* required parameter.
*
* @param config - An object whose properties will be used as configuration options for the HTML5 video
* player. This is an optional parameter. In the case if this parameter is missing, or some of the config
* object's properties are missing, defaults will be used. The available options (and their defaults) are as
* @param config - An object whose properties will be used as
* configuration options for the HTML5 video player. This is an
* optional parameter. In the case if this parameter is missing, or
* some of the config object's properties are missing, defaults will be
* used. The available options (and their defaults) are as
* follows:
*
* config = {
*
* videoSources: {}, // An object with properties being video sources. The property name is the
* // video format of the source. Supported video formats are: 'mp4', 'webm', and
* videoSources: {}, // An object with properties being video
* // sources. The property name is the
* // video format of the source. Supported
* // video formats are: 'mp4', 'webm', and
* // 'ogg'.
*
* playerVars: { // Object's properties identify player parameters.
* start: 0, // Possible values: positive integer. Position from which to start playing the
* // video. Measured in seconds. If value is non-numeric, or 'start' property is
* // not specified, the video will start playing from the beginning.
*
* end: null // Possible values: positive integer. Position when to stop playing the
* // video. Measured in seconds. If value is null, or 'end' property is not
* // specified, the video will end playing at the end.
*
* },
*
* events: { // Object's properties identify the events that the API fires, and the
* // functions (event listeners) that the API will call when those events occur.
* // If value is null, or property is not specified, then no callback will be
* events: { // Object's properties identify the
* // events that the API fires, and the
* // functions (event listeners) that the
* // API will call when those events occur.
* // If value is null, or property is not
* // specified, then no callback will be
* // called for that event.
*
* onReady: null,
...
...
@@ -130,16 +131,19 @@ function () {
function
Player
(
el
,
config
)
{
var
sourceStr
,
_this
,
errorMessage
;
// Initially we assume that el is a DOM element. If jQuery selector fails to select something, we
// assume that el is an ID of a DOM element. We try to select by ID. If jQuery fails this time,
// we return. Nothing breaks because the player 'onReady' event will never be fired.
// Initially we assume that el is a DOM element. If jQuery selector
// fails to select something, we assume that el is an ID of a DOM
// element. We try to select by ID. If jQuery fails this time, we
// return. Nothing breaks because the player 'onReady' event will
// never be fired.
this
.
el
=
$
(
el
);
if
(
this
.
el
.
length
===
0
)
{
this
.
el
=
$
(
'#'
+
el
);
if
(
this
.
el
.
length
===
0
)
{
errorMessage
=
'VideoPlayer: Element corresponding to the given selector does not found.'
;
errorMessage
=
'VideoPlayer: Element corresponding to '
+
'the given selector does not found.'
;
if
(
window
.
console
&&
console
.
log
)
{
console
.
log
(
errorMessage
);
}
else
{
...
...
@@ -156,12 +160,14 @@ function () {
return
;
}
// We should have at least one video source. Otherwise there is no point to continue.
// We should have at least one video source. Otherwise there is no
// point to continue.
if
(
!
config
.
videoSources
)
{
return
;
}
// From the start, all sources are empty. We will populate this object below.
// From the start, all sources are empty. We will populate this
// object below.
sourceStr
=
{
mp4
:
' '
,
webm
:
' '
,
...
...
@@ -171,7 +177,8 @@ function () {
// Will be used in inner functions to point to the current object.
_this
=
this
;
// Create HTML markup for individual sources of the HTML5 <video> element.
// Create HTML markup for individual sources of the HTML5 <video>
// element.
$
.
each
(
sourceStr
,
function
(
videoType
,
videoSource
)
{
if
(
(
_this
.
config
.
videoSources
[
videoType
])
&&
...
...
@@ -179,58 +186,60 @@ function () {
)
{
sourceStr
[
videoType
]
=
'<source '
+
'src="'
+
_this
.
config
.
videoSources
[
videoType
]
+
'" '
+
'type="video/'
+
videoType
+
'" '
+
'src="'
+
_this
.
config
.
videoSources
[
videoType
]
+
'
" '
+
'
type="video/'
+
videoType
+
'" '
+
'/> '
;
}
});
// We should have at least one video source. Otherwise there is no point to continue.
if
((
sourceStr
.
mp4
===
' '
)
&&
(
sourceStr
.
webm
===
' '
)
&&
(
sourceStr
.
ogg
===
' '
))
{
// We should have at least one video source. Otherwise there is no
// point to continue.
if
(
sourceStr
.
mp4
===
' '
&&
sourceStr
.
webm
===
' '
&&
sourceStr
.
ogg
===
' '
)
{
return
;
}
// Determine the starting and ending time for the video.
this
.
start
=
config
.
playerVars
.
start
;
this
.
end
=
config
.
playerVars
.
end
;
// Create HTML markup for the <video> element, populating it with sources from previous step.
// Because of problems with creating video element via jquery
// (http://bugs.jquery.com/ticket/9174) we create it using native JS.
// Create HTML markup for the <video> element, populating it with
// sources from previous step. Because of problems with creating
// video element via jquery (http://bugs.jquery.com/ticket/9174) we
// create it using native JS.
this
.
video
=
document
.
createElement
(
'video'
);
this
.
video
.
innerHTML
=
_
.
values
(
sourceStr
).
join
(
''
);
// Get the jQuery object, and set the player state to UNSTARTED.
// The player state is used by other parts of the VideoPlayer to
detrermine what the video is
// currently doing.
// The player state is used by other parts of the VideoPlayer to
//
determine what the video is
currently doing.
this
.
videoEl
=
$
(
this
.
video
);
this
.
playerState
=
HTML5Video
.
PlayerState
.
UNSTARTED
;
// Attach a 'click' event on the <video> element. It will cause the video to pause/play.
// Attach a 'click' event on the <video> element. It will cause the
// video to pause/play.
this
.
videoEl
.
on
(
'click'
,
function
(
event
)
{
if
(
_this
.
playerState
===
HTML5Video
.
PlayerState
.
PAUSED
)
{
_this
.
playVideo
();
_this
.
playerState
=
HTML5Video
.
PlayerState
.
PLAYING
;
_this
.
callStateChangeCallback
();
}
else
if
(
_this
.
playerState
===
HTML5Video
.
PlayerState
.
PLAYING
)
{
}
else
if
(
_this
.
playerState
===
HTML5Video
.
PlayerState
.
PLAYING
)
{
_this
.
pauseVideo
();
_this
.
playerState
=
HTML5Video
.
PlayerState
.
PAUSED
;
_this
.
callStateChangeCallback
();
}
});
// When the <video> tag has been processed by the browser, and it is ready for playback,
// notify other parts of the VideoPlayer, and initially pause the video.
//
// Also, at this time we can get the real duration of the video. Update the starting end ending
// points of the video. Note that first time, the video will start playing at the specified start time,
// and end playing at the specified end time. After it was paused, or when a seek operation happeded,
// the starting time and ending time will reset to the beginning and the end of the video respectively.
// When the <video> tag has been processed by the browser, and it
// is ready for playback, notify other parts of the VideoPlayer,
// and initially pause the video.
this
.
video
.
addEventListener
(
'canplay'
,
function
()
{
// Because firefox triggers 'canplay' event every time when 'currentTime' property
// changes, we must make sure that this block of code runs only once. Otherwise,
// this will be an endless loop ('currentTime' property is changed below).
// Because Firefox triggers 'canplay' event every time when
// 'currentTime' property changes, we must make sure that this
// block of code runs only once. Otherwise, this will be an
// endless loop ('currentTime' property is changed below).
//
// Chrome is immune to this behavior.
if
(
_this
.
playerState
!==
HTML5Video
.
PlayerState
.
UNSTARTED
)
{
...
...
@@ -239,14 +248,6 @@ function () {
_this
.
playerState
=
HTML5Video
.
PlayerState
.
PAUSED
;
if
(
_this
.
start
>
_this
.
video
.
duration
)
{
_this
.
start
=
0
;
}
if
((
_this
.
end
===
null
)
||
(
_this
.
end
>
_this
.
video
.
duration
))
{
_this
.
end
=
_this
.
video
.
duration
;
}
_this
.
video
.
currentTime
=
_this
.
start
;
if
(
$
.
isFunction
(
_this
.
config
.
events
.
onReady
))
{
_this
.
config
.
events
.
onReady
(
null
);
}
...
...
@@ -275,9 +276,10 @@ function () {
}
}());
// The YouTube API presents several constants which describe the player's state at a given moment.
// HTML5Video API will copy these constats so that code which uses both the YouTube API and this API
// doesn't have to change.
// The YouTube API presents several constants which describe the player's
// state at a given moment. HTML5Video API will copy these constants so
// that code which uses both the YouTube API and this API doesn't have to
// change.
HTML5Video
.
PlayerState
=
{
UNSTARTED
:
-
1
,
ENDED
:
0
,
...
...
common/lib/xmodule/xmodule/js/src/video/03_video_player.js
View file @
9b3bc84c
...
...
@@ -63,9 +63,9 @@ function (HTML5Video, Resizer) {
var
youTubeId
;
// The function is called just once to apply pre-defined configurations
// by student before video starts playing. Waits until the video's
metadata
//
is loaded, which normally happens just after the video starts playing.
// Just after that configurations can be applied.
// by student before video starts playing. Waits until the video's
//
metadata is loaded, which normally happens just after the video
//
starts playing.
Just after that configurations can be applied.
state
.
videoPlayer
.
ready
=
_
.
once
(
function
()
{
state
.
videoPlayer
.
onSpeedChange
(
state
.
speed
);
});
...
...
@@ -79,6 +79,15 @@ function (HTML5Video, Resizer) {
state
.
videoPlayer
.
currentTime
=
0
;
state
.
videoPlayer
.
initialSeekToStartTime
=
true
;
state
.
videoPlayer
.
oneTimePauseAtEndTime
=
true
;
// The initial value of the variable `seekToStartTimeOldSpeed`
// should always differ from the value returned by the duration
// function.
state
.
videoPlayer
.
seekToStartTimeOldSpeed
=
'void'
;
state
.
videoPlayer
.
playerVars
=
{
controls
:
0
,
wmode
:
'transparent'
,
...
...
@@ -92,9 +101,6 @@ function (HTML5Video, Resizer) {
state
.
videoPlayer
.
playerVars
.
html5
=
1
;
}
state
.
videoPlayer
.
playerVars
.
start
=
state
.
config
.
start
;
state
.
videoPlayer
.
playerVars
.
end
=
state
.
config
.
end
;
// There is a bug which prevents YouTube API to correctly set the speed
// to 1.0 from another speed in Firefox when in HTML5 mode. There is a
// fix which basically reloads the video at speed 1.0 when this change
...
...
@@ -196,6 +202,24 @@ function (HTML5Video, Resizer) {
if
(
isFinite
(
this
.
videoPlayer
.
currentTime
))
{
this
.
videoPlayer
.
updatePlayTime
(
this
.
videoPlayer
.
currentTime
);
// We need to pause the video is current time is smaller (or equal)
// than end time. Also, we must make sure that the end time is the
// one that was set in the configuration parameter. If it differs,
// this means that it was either reset to the end, or the duration
// changed it's value.
//
// In the case of YouTube Flash mode, we must remember that the
// start and end times are rescaled based on the current speed of
// the video.
if
(
this
.
videoPlayer
.
endTime
<=
this
.
videoPlayer
.
currentTime
&&
this
.
videoPlayer
.
oneTimePauseAtEndTime
)
{
this
.
videoPlayer
.
oneTimePauseAtEndTime
=
false
;
this
.
videoPlayer
.
pause
();
this
.
videoPlayer
.
endTime
=
this
.
videoPlayer
.
duration
();
}
}
}
...
...
@@ -254,27 +278,43 @@ function (HTML5Video, Resizer) {
// It is created on a onPlay event. Cleared on a onPause event.
// Reinitialized on a onSeek event.
function
onSeek
(
params
)
{
var
duration
=
this
.
videoPlayer
.
duration
(),
newTime
=
params
.
time
;
if
(
(
typeof
newTime
!==
'number'
)
||
(
newTime
>
duration
)
||
(
newTime
<
0
)
)
{
return
;
}
this
.
videoPlayer
.
log
(
'seek_video'
,
{
old_time
:
this
.
videoPlayer
.
currentTime
,
new_time
:
params
.
t
ime
,
new_time
:
newT
ime
,
type
:
params
.
type
}
);
this
.
videoPlayer
.
player
.
seekTo
(
params
.
time
,
true
);
this
.
videoPlayer
.
startTime
=
0
;
this
.
videoPlayer
.
endTime
=
duration
;
this
.
videoPlayer
.
player
.
seekTo
(
newTime
,
true
);
if
(
this
.
videoPlayer
.
isPlaying
())
{
clearInterval
(
this
.
videoPlayer
.
updateInterval
);
this
.
videoPlayer
.
updateInterval
=
setInterval
(
this
.
videoPlayer
.
update
,
200
);
setTimeout
(
this
.
videoPlayer
.
update
,
0
);
}
else
{
this
.
videoPlayer
.
currentTime
=
params
.
t
ime
;
this
.
videoPlayer
.
currentTime
=
newT
ime
;
}
this
.
videoPlayer
.
updatePlayTime
(
params
.
t
ime
);
this
.
videoPlayer
.
updatePlayTime
(
newT
ime
);
}
function
onEnded
()
{
...
...
@@ -469,21 +509,89 @@ function (HTML5Video, Resizer) {
}
function
updatePlayTime
(
time
)
{
var
duration
;
var
duration
,
durationChange
;
duration
=
this
.
videoPlayer
.
duration
();
if
(
duration
>
0
&&
(
this
.
videoPlayer
.
seekToStartTimeOldSpeed
!==
this
.
speed
||
this
.
videoPlayer
.
initialSeekToStartTime
)
)
{
if
(
this
.
videoPlayer
.
seekToStartTimeOldSpeed
!==
this
.
speed
&&
this
.
videoPlayer
.
initialSeekToStartTime
===
false
)
{
durationChange
=
true
;
}
else
{
durationChange
=
false
;
}
this
.
videoPlayer
.
initialSeekToStartTime
=
false
;
this
.
videoPlayer
.
seekToStartTimeOldSpeed
=
this
.
speed
;
// We retrieve the original times. They could have been changed due
// to the fact of speed change (duration change). This happens when
// in YouTube Flash mode. There each speed is a different video,
// with a different length.
this
.
videoPlayer
.
startTime
=
this
.
config
.
startTime
;
this
.
videoPlayer
.
endTime
=
this
.
config
.
endTime
;
if
(
this
.
videoPlayer
.
startTime
>
duration
)
{
this
.
videoPlayer
.
startTime
=
0
;
}
else
{
if
(
this
.
currentPlayerMode
===
'flash'
)
{
this
.
videoPlayer
.
startTime
/=
Number
(
this
.
speed
);
}
}
if
(
this
.
videoPlayer
.
endTime
===
null
||
this
.
videoPlayer
.
endTime
>
duration
)
{
this
.
videoPlayer
.
endTime
=
duration
;
}
else
{
if
(
this
.
currentPlayerMode
===
'flash'
)
{
this
.
videoPlayer
.
endTime
/=
Number
(
this
.
speed
);
}
}
// If this is not a duration change (if it is, we continue playing
// from current time), then we need to seek the video to the start
// time.
//
// We seek only if start time differs from zero.
if
(
durationChange
===
false
&&
this
.
videoPlayer
.
startTime
>
0
)
{
if
(
this
.
videoType
===
'html5'
)
{
this
.
videoPlayer
.
player
.
seekTo
(
this
.
videoPlayer
.
startTime
);
}
else
{
this
.
videoPlayer
.
player
.
loadVideoById
({
videoId
:
this
.
youtubeId
(),
startSeconds
:
this
.
videoPlayer
.
startTime
});
}
}
// Rebuild the slider start-end range (if it doesn't take up the
// whole slider).
if
(
!
(
this
.
videoPlayer
.
startTime
===
0
&&
this
.
videoPlayer
.
endTime
===
duration
))
{
this
.
trigger
(
'videoProgressSlider.updatePlayTime
'
,
'videoProgressSlider.updateStartEndTimeRegion
'
,
{
time
:
time
,
duration
:
duration
}
);
}
}
this
.
trigger
(
'videoProgressSlider.update
StartEndTimeRegion
'
,
'videoProgressSlider.update
PlayTime
'
,
{
time
:
time
,
duration
:
duration
}
);
...
...
@@ -506,10 +614,26 @@ function (HTML5Video, Resizer) {
return
playerState
===
PLAYING
;
}
/*
* Return the duration of the video in seconds.
*
* First, try to use the native player API call to get the duration.
* If the value returned by the native function is not valid, resort to
* the value stored in the metadata for the video. Note that the metadata
* is available only for YouTube videos.
*
* IMPORTANT! It has been observed that sometimes, after initial playback
* of the video, when operations "pause" and "play" are performed (in that
* sequence), the function will start returning a slightly different value.
*
* For example: While playing for the first time, the function returns 31.
* After pausing the video and then resuming once more, the function will
* start returning 31.950656.
*
* This instability is internal to the player API (or browser internals).
*/
function
duration
()
{
var
dur
;
dur
=
this
.
videoPlayer
.
player
.
getDuration
();
var
dur
=
this
.
videoPlayer
.
player
.
getDuration
();
if
(
!
isFinite
(
dur
))
{
dur
=
this
.
getDuration
();
...
...
common/lib/xmodule/xmodule/js/src/video/06_video_progress_slider.js
View file @
9b3bc84c
...
...
@@ -99,7 +99,7 @@ function () {
}
function
updateStartEndTimeRegion
(
params
)
{
var
left
,
width
,
start
,
end
;
var
left
,
width
,
start
,
end
,
step
;
// We must have a duration in order to determine the area of range.
// It also must be non-zero.
...
...
@@ -108,19 +108,28 @@ function () {
}
// If the range spans the entire length of video, we don't do anything.
if
(
!
this
.
config
.
start
&&
!
this
.
config
.
end
)
{
if
(
!
this
.
videoPlayer
.
startTime
&&
!
this
.
videoPlayer
.
endTime
)
{
return
;
}
start
=
this
.
config
.
start
;
start
=
this
.
videoPlayer
.
startTime
;
// If end is set to null, then we set it to the end of the video. We
// know that start is not a the beginning, therefore we must build a
// range.
end
=
this
.
config
.
end
||
params
.
duration
;
end
=
this
.
videoPlayer
.
endTime
||
params
.
duration
;
left
=
(
100
*
(
start
/
params
.
duration
)).
toFixed
(
1
);
width
=
(
100
*
((
end
-
start
)
/
params
.
duration
)).
toFixed
(
1
);
// Because JavaScript has weird rounding rules when a series of
// mathematical operations are performed in a single statement, we will
// split everything up into smaller statements.
//
// This will ensure that visually, the start-end range aligns nicely
// with actual starting and ending point of the video.
step
=
100.0
/
params
.
duration
;
left
=
start
*
step
;
width
=
end
*
step
-
left
;
left
=
left
.
toFixed
(
1
);
width
=
width
.
toFixed
(
1
);
if
(
!
this
.
videoProgressSlider
.
sliderRange
)
{
this
.
videoProgressSlider
.
sliderRange
=
$
(
'<div />'
,
{
...
...
common/lib/xmodule/xmodule/js/src/video/09_video_caption.js
View file @
9b3bc84c
...
...
@@ -241,9 +241,10 @@ function () {
}
},
error
:
function
(
jqXHR
,
textStatus
,
errorThrown
)
{
console
.
log
(
'ERROR while fetching captions.'
);
console
.
log
(
'
[Video info]:
ERROR while fetching captions.'
);
console
.
log
(
'STATUS:'
,
textStatus
+
', MESSAGE:'
,
''
+
errorThrown
'[Video info]: STATUS:'
,
textStatus
+
', MESSAGE:'
,
''
+
errorThrown
);
_this
.
videoCaption
.
hideCaptions
(
true
,
false
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment