Commit 2612fc2b by Vik Paruchuri

Merge remote-tracking branch 'origin/master' into feature/vik/oe-ui

parents f929b493 0f9d7230
...@@ -617,8 +617,26 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase): ...@@ -617,8 +617,26 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
} }
module_store = modulestore('direct') module_store = modulestore('direct')
draft_store = modulestore('draft')
import_from_xml(module_store, 'common/test/data/', ['toy']) import_from_xml(module_store, 'common/test/data/', ['toy'])
source_course_id = 'edX/toy/2012_Fall'
dest_course_id = 'MITx/999/2013_Spring'
source_location = CourseDescriptor.id_to_location(source_course_id)
dest_location = CourseDescriptor.id_to_location(dest_course_id)
# get a vertical (and components in it) to put into 'draft'
# this is to assert that draft content is also cloned over
vertical = module_store.get_instance(source_course_id, Location([
source_location.tag, source_location.org, source_location.course, 'vertical', 'vertical_test', None]), depth=1)
draft_store.convert_to_draft(vertical.location)
for child in vertical.get_children():
draft_store.convert_to_draft(child.location)
items = module_store.get_items(Location([source_location.tag, source_location.org, source_location.course, None, None, 'draft']))
self.assertGreater(len(items), 0)
resp = self.client.post(reverse('create_new_course'), course_data) resp = self.client.post(reverse('create_new_course'), course_data)
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
data = parse_json(resp) data = parse_json(resp)
...@@ -626,22 +644,54 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase): ...@@ -626,22 +644,54 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
content_store = contentstore() content_store = contentstore()
source_location = CourseDescriptor.id_to_location('edX/toy/2012_Fall')
dest_location = CourseDescriptor.id_to_location('MITx/999/2013_Spring')
clone_course(module_store, content_store, source_location, dest_location) clone_course(module_store, content_store, source_location, dest_location)
# first assert that all draft content got cloned as well
items = module_store.get_items(Location([source_location.tag, source_location.org, source_location.course, None, None, 'draft']))
self.assertGreater(len(items), 0)
clone_items = module_store.get_items(Location([dest_location.tag, dest_location.org, dest_location.course, None, None, 'draft']))
self.assertGreater(len(clone_items), 0)
self.assertEqual(len(items), len(clone_items))
# now loop through all the units in the course and verify that the clone can render them, which # now loop through all the units in the course and verify that the clone can render them, which
# means the objects are at least present # means the objects are at least present
items = module_store.get_items(Location(['i4x', 'edX', 'toy', 'poll_question', None])) items = module_store.get_items(Location([source_location.tag, source_location.org, source_location.course, None, None]))
self.assertGreater(len(items), 0) self.assertGreater(len(items), 0)
clone_items = module_store.get_items(Location(['i4x', 'MITx', '999', 'poll_question', None])) clone_items = module_store.get_items(Location([dest_location.tag, dest_location.org, dest_location.course, None, None]))
self.assertGreater(len(clone_items), 0) self.assertGreater(len(clone_items), 0)
for descriptor in items: for descriptor in items:
new_loc = descriptor.location.replace(org='MITx', course='999') source_item = module_store.get_instance(source_course_id, descriptor.location)
if descriptor.location.category == 'course':
new_loc = descriptor.location.replace(org=dest_location.org, course=dest_location.course, name='2013_Spring')
else:
new_loc = descriptor.location.replace(org=dest_location.org, course=dest_location.course)
print "Checking {0} should now also be at {1}".format(descriptor.location.url(), new_loc.url()) print "Checking {0} should now also be at {1}".format(descriptor.location.url(), new_loc.url())
resp = self.client.get(reverse('edit_unit', kwargs={'location': new_loc.url()})) lookup_item = module_store.get_item(new_loc)
self.assertEqual(resp.status_code, 200)
# we want to assert equality between the objects, but we know the locations
# differ, so just make them equal for testing purposes
source_item.location = new_loc
if hasattr(source_item, 'data') and hasattr(lookup_item, 'data'):
self.assertEqual(source_item.data, lookup_item.data)
# also make sure that metadata was cloned over and filtered with own_metadata, i.e. inherited
# values were not explicitly set
self.assertEqual(own_metadata(source_item), own_metadata(lookup_item))
# check that the children are as expected
self.assertEqual(source_item.has_children, lookup_item.has_children)
if source_item.has_children:
expected_children = []
for child_loc_url in source_item.children:
child_loc = Location(child_loc_url)
child_loc = child_loc._replace(
tag=dest_location.tag,
org=dest_location.org,
course=dest_location.course
)
expected_children.append(child_loc.url())
self.assertEqual(expected_children, lookup_item.children)
def test_illegal_draft_crud_ops(self): def test_illegal_draft_crud_ops(self):
draft_store = modulestore('draft') draft_store = modulestore('draft')
......
...@@ -3,6 +3,7 @@ import json ...@@ -3,6 +3,7 @@ import json
import os import os
import tarfile import tarfile
import shutil import shutil
import cgi
from tempfile import mkdtemp from tempfile import mkdtemp
from path import path from path import path
...@@ -27,7 +28,7 @@ from xmodule.modulestore import Location ...@@ -27,7 +28,7 @@ from xmodule.modulestore import Location
from xmodule.contentstore.content import StaticContent from xmodule.contentstore.content import StaticContent
from xmodule.util.date_utils import get_default_time_display from xmodule.util.date_utils import get_default_time_display
from xmodule.modulestore import InvalidLocationError from xmodule.modulestore import InvalidLocationError
from xmodule.exceptions import NotFoundError from xmodule.exceptions import NotFoundError, SerializationError
from .access import get_location_and_verify_access from .access import get_location_and_verify_access
from util.json_request import JsonResponse from util.json_request import JsonResponse
...@@ -336,16 +337,59 @@ def generate_export_course(request, org, course, name): ...@@ -336,16 +337,59 @@ def generate_export_course(request, org, course, name):
the course the course
""" """
location = get_location_and_verify_access(request, org, course, name) location = get_location_and_verify_access(request, org, course, name)
course_module = modulestore().get_instance(location.course_id, location)
loc = Location(location) loc = Location(location)
export_file = NamedTemporaryFile(prefix=name + '.', suffix=".tar.gz") export_file = NamedTemporaryFile(prefix=name + '.', suffix=".tar.gz")
root_dir = path(mkdtemp()) root_dir = path(mkdtemp())
# export out to a tempdir try:
logging.debug('root = {0}'.format(root_dir)) export_to_xml(modulestore('direct'), contentstore(), loc, root_dir, name, modulestore())
except SerializationError, e:
unit = None
failed_item = None
parent = None
try:
failed_item = modulestore().get_instance(course_module.location.course_id, e.location)
parent_locs = modulestore().get_parent_locations(failed_item.location, course_module.location.course_id)
if len(parent_locs) > 0:
parent = modulestore().get_item(parent_locs[0])
if parent.location.category == 'vertical':
unit = parent
except:
# if we have a nested exception, then we'll show the more generic error message
pass
export_to_xml(modulestore('direct'), contentstore(), loc, root_dir, name, modulestore()) return render_to_response('export.html', {
'context_course': course_module,
'successful_import_redirect_url': '',
'in_err': True,
'raw_err_msg': str(e),
'failed_module': failed_item,
'unit': unit,
'edit_unit_url': reverse('edit_unit', kwargs={
'location': parent.location
}) if parent else '',
'course_home_url': reverse('course_index', kwargs={
'org': org,
'course': course,
'name': name
})
})
except Exception, e:
return render_to_response('export.html', {
'context_course': course_module,
'successful_import_redirect_url': '',
'in_err': True,
'unit': None,
'raw_err_msg': str(e),
'course_home_url': reverse('course_index', kwargs={
'org': org,
'course': course,
'name': name
})
})
logging.debug('tar file being generated at {0}'.format(export_file.name)) logging.debug('tar file being generated at {0}'.format(export_file.name))
tar_file = tarfile.open(name=export_file.name, mode='w:gz') tar_file = tarfile.open(name=export_file.name, mode='w:gz')
......
...@@ -6,6 +6,62 @@ ...@@ -6,6 +6,62 @@
<%block name="title">${_("Course Export")}</%block> <%block name="title">${_("Course Export")}</%block>
<%block name="bodyclass">is-signedin course tools export</%block> <%block name="bodyclass">is-signedin course tools export</%block>
<%block name="jsextra">
% if in_err:
<script type='text/javascript'>
$(document).ready(function() {
%if unit:
var dialog = new CMS.Views.Prompt({
title: gettext('There has been an error while exporting.'),
message: gettext("There has been a failure to export to XML at least one component. It is recommended that you go to the edit page and repair the error before attempting another export. Please check that all components on the page are valid and do not display any error messages."),
intent: "error",
actions: {
primary: {
text: gettext('Correct failed component'),
click: function(view) {
view.hide();
document.location = "${edit_unit_url}"
}
},
secondary: {
text: gettext('Return to Export'),
click: function(view) {
view.hide();
}
}
}
});
% else:
var msg = "<p>" + gettext("There has been a failure to export your course to XML. Unfortunately, we do not have specific enough information to assist you in identifying the failed component. It is recommended that you inspect your courseware to identify any components in error and try again.") + "</p><p>" + gettext("The raw error message is:") + "</p>";
msg = msg + "${raw_err_msg}";
var dialog = new CMS.Views.Prompt({
title: gettext('There has been an error with your export.'),
message: msg,
intent: "error",
actions: {
primary: {
text: gettext('Yes, take me to the main course page'),
click: function(view) {
view.hide();
document.location = "${course_home_url}"
}
},
secondary: {
text: gettext('Cancel'),
click: function(view) {
view.hide();
}
}
}
});
%endif
dialog.show();
})
</script>
%endif
</%block>
<%block name="content"> <%block name="content">
<div class="wrapper-mast wrapper"> <div class="wrapper-mast wrapper">
<header class="mast has-subtitle"> <header class="mast has-subtitle">
...@@ -18,6 +74,7 @@ ...@@ -18,6 +74,7 @@
<div class="main-wrapper"> <div class="main-wrapper">
<div class="inner-wrapper"> <div class="inner-wrapper">
<article class="export-overview"> <article class="export-overview">
<div class="description"> <div class="description">
<h2>${_("About Exporting Courses")}</h2> <h2>${_("About Exporting Courses")}</h2>
......
...@@ -133,7 +133,6 @@ div.videoalpha { ...@@ -133,7 +133,6 @@ div.videoalpha {
line-height: 46px; line-height: 46px;
padding: 0 lh(.75); padding: 0 lh(.75);
text-indent: -9999px; text-indent: -9999px;
@include transition(background-color 0.75s linear 0s, opacity 0.75s linear 0s);
width: 14px; width: 14px;
background: url('../images/vcr.png') 15px 15px no-repeat; background: url('../images/vcr.png') 15px 15px no-repeat;
outline: 0; outline: 0;
...@@ -150,7 +149,7 @@ div.videoalpha { ...@@ -150,7 +149,7 @@ div.videoalpha {
&.play { &.play {
background-position: 17px -114px; background-position: 17px -114px;
&:hover { &:hover, &:focus {
background-color: #444; background-color: #444;
} }
} }
...@@ -158,7 +157,7 @@ div.videoalpha { ...@@ -158,7 +157,7 @@ div.videoalpha {
&.pause { &.pause {
background-position: 16px -50px; background-position: 16px -50px;
&:hover { &:hover, &:focus {
background-color: #444; background-color: #444;
} }
} }
...@@ -300,12 +299,15 @@ div.videoalpha { ...@@ -300,12 +299,15 @@ div.videoalpha {
&.muted { &.muted {
&>a { &>a {
background: url('../images/mute.png') 10px center no-repeat; background-image: url('../images/mute.png');
} }
} }
> a { > a {
background: url('../images/volume.png') 10px center no-repeat; background-image: url('../images/volume.png');
background-position: 10px center;
background-repeat: no-repeat;
border-right: 1px solid #000; border-right: 1px solid #000;
box-shadow: 1px 0 0 #555, inset 1px 0 0 #555; box-shadow: 1px 0 0 #555, inset 1px 0 0 #555;
@include clearfix(); @include clearfix();
...@@ -382,7 +384,7 @@ div.videoalpha { ...@@ -382,7 +384,7 @@ div.videoalpha {
@include transition(none); @include transition(none);
width: 30px; width: 30px;
&:hover { &:hover, &:active, &:focus {
background-color: #444; background-color: #444;
color: #fff; color: #fff;
text-decoration: none; text-decoration: none;
...@@ -403,7 +405,7 @@ div.videoalpha { ...@@ -403,7 +405,7 @@ div.videoalpha {
@include transition(none); @include transition(none);
width: 30px; width: 30px;
&:hover { &:hover, &:focus {
background-color: #444; background-color: #444;
color: #fff; color: #fff;
text-decoration: none; text-decoration: none;
...@@ -419,7 +421,6 @@ div.videoalpha { ...@@ -419,7 +421,6 @@ div.videoalpha {
a.hide-subtitles { a.hide-subtitles {
background: url('../images/cc.png') center no-repeat; background: url('../images/cc.png') center no-repeat;
display: block;
float: left; float: left;
font-weight: 800; font-weight: 800;
line-height: 46px; //height of play pause buttons line-height: 46px; //height of play pause buttons
...@@ -432,7 +433,7 @@ div.videoalpha { ...@@ -432,7 +433,7 @@ div.videoalpha {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
width: 30px; width: 30px;
&:hover { &:hover, &:focus {
background-color: #444; background-color: #444;
color: #fff; color: #fff;
text-decoration: none; text-decoration: none;
...@@ -442,9 +443,7 @@ div.videoalpha { ...@@ -442,9 +443,7 @@ div.videoalpha {
opacity: 0.7; opacity: 0.7;
} }
background-color: #444; color: #797979;
color: #fff;
text-decoration: none;
} }
} }
} }
...@@ -513,12 +512,6 @@ div.videoalpha { ...@@ -513,12 +512,6 @@ div.videoalpha {
z-index: 1; z-index: 1;
} }
article.video-wrapper section.video-controls div.secondary-controls a.hide-subtitles {
background-color: inherit;
color: #797979;
text-decoration: inherit;
}
article.video-wrapper div.video-player-pre, article.video-wrapper div.video-player-post { article.video-wrapper div.video-player-pre, article.video-wrapper div.video-player-post {
height: 0px; height: 0px;
} }
......
...@@ -13,6 +13,7 @@ class ProcessingError(Exception): ...@@ -13,6 +13,7 @@ class ProcessingError(Exception):
''' '''
pass pass
class InvalidVersionError(Exception): class InvalidVersionError(Exception):
""" """
Tried to save an item with a location that a store cannot support (e.g., draft version Tried to save an item with a location that a store cannot support (e.g., draft version
...@@ -21,3 +22,12 @@ class InvalidVersionError(Exception): ...@@ -21,3 +22,12 @@ class InvalidVersionError(Exception):
def __init__(self, location): def __init__(self, location):
super(InvalidVersionError, self).__init__() super(InvalidVersionError, self).__init__()
self.location = location self.location = location
class SerializationError(Exception):
"""
Thrown when a module cannot be exported to XML
"""
def __init__(self, location, msg):
super(SerializationError, self).__init__(msg)
self.location = location
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
<div id="video_example"> <div id="video_example">
<div id="example"> <div id="example">
<div id="video_id" class="video" <div id="video_id" class="video"
data-youtube-id-0-75="slowerSpeedYoutubeId" data-youtube-id-0-75="7tqY6eQzVhE"
data-youtube-id-1-0="normalSpeedYoutubeId" data-youtube-id-1-0="cogebirgzzM"
data-show-captions="true" data-show-captions="true"
data-start="" data-start=""
data-end="" data-end=""
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<div <div
id="video_id" id="video_id"
class="videoalpha" class="videoalpha"
data-streams="0.75:slowerSpeedYoutubeId,1.0:normalSpeedYoutubeId" data-streams="0.75:7tqY6eQzVhE,1.0:cogebirgzzM"
data-show-captions="true" data-show-captions="true"
data-start="" data-start=""
data-end="" data-end=""
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
data-start="" data-start=""
data-end="" data-end=""
data-caption-asset-path="/static/subs/" data-caption-asset-path="/static/subs/"
data-sub="test_name_of_the_subtitles" data-sub="Z5KLxerq05Y"
data-mp4-source="test_files/test.mp4" data-mp4-source="test_files/test.mp4"
data-webm-source="test_files/test.webm" data-webm-source="test_files/test.webm"
data-ogg-source="test_files/test.ogv" data-ogg-source="test_files/test.ogv"
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
data-start="" data-start=""
data-end="" data-end=""
data-caption-asset-path="/static/subs/" data-caption-asset-path="/static/subs/"
data-sub="test_name_of_the_subtitles" data-sub="Z5KLxerq05Y"
data-mp4-source="test_files/test.mp4" data-mp4-source="test_files/test.mp4"
data-webm-source="test_files/test.webm" data-webm-source="test_files/test.webm"
data-ogg-source="test_files/test.ogv" data-ogg-source="test_files/test.ogv"
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<div <div
id="video_id" id="video_id"
class="videoalpha" class="videoalpha"
data-streams="0.75:slowerSpeedYoutubeId,1.0:normalSpeedYoutubeId" data-streams="0.75:7tqY6eQzVhE,1.0:cogebirgzzM"
data-show-captions="false" data-show-captions="false"
data-start="" data-start=""
data-end="" data-end=""
......
...@@ -12,6 +12,9 @@ window.STATUS = window.YT.PlayerState ...@@ -12,6 +12,9 @@ window.STATUS = window.YT.PlayerState
oldAjaxWithPrefix = window.jQuery.ajaxWithPrefix oldAjaxWithPrefix = window.jQuery.ajaxWithPrefix
window.onTouchBasedDevice = ->
navigator.userAgent.match /iPhone|iPod|iPad/i
jasmine.stubbedCaption = jasmine.stubbedCaption =
end: [3120, 6270, 8490, 21620, 24920, 25750, 27900, 34380, 35550, 40250] end: [3120, 6270, 8490, 21620, 24920, 25750, 27900, 34380, 35550, 40250]
start: [1180, 3120, 6270, 14910, 21620, 24920, 25750, 27900, 34380, 35550] start: [1180, 3120, 6270, 14910, 21620, 24920, 25750, 27900, 34380, 35550]
...@@ -36,7 +39,7 @@ jasmine.stubbedCaption = ...@@ -36,7 +39,7 @@ jasmine.stubbedCaption =
# #
# We will replace it with a function that does: # We will replace it with a function that does:
# #
# 1.) Return a hard coded captions object if the file name contains 'test_name_of_the_subtitles'. # 1.) Return a hard coded captions object if the file name contains 'Z5KLxerq05Y'.
# 2.) Behaves the same a as the origianl in all other cases. # 2.) Behaves the same a as the origianl in all other cases.
window.jQuery.ajaxWithPrefix = (url, settings) -> window.jQuery.ajaxWithPrefix = (url, settings) ->
...@@ -46,7 +49,7 @@ window.jQuery.ajaxWithPrefix = (url, settings) -> ...@@ -46,7 +49,7 @@ window.jQuery.ajaxWithPrefix = (url, settings) ->
success = settings.success success = settings.success
data = settings.data data = settings.data
if url.match(/test_name_of_the_subtitles/g) isnt null or url.match(/slowerSpeedYoutubeId/g) isnt null or url.match(/normalSpeedYoutubeId/g) isnt null if url.match(/Z5KLxerq05Y/g) isnt null or url.match(/7tqY6eQzVhE/g) isnt null or url.match(/cogebirgzzM/g) isnt null
if window.jQuery.isFunction(success) is true if window.jQuery.isFunction(success) is true
success jasmine.stubbedCaption success jasmine.stubbedCaption
else if window.jQuery.isFunction(data) is true else if window.jQuery.isFunction(data) is true
...@@ -60,11 +63,11 @@ window.WAIT_TIMEOUT = 1000 ...@@ -60,11 +63,11 @@ window.WAIT_TIMEOUT = 1000
jasmine.getFixtures().fixturesPath = 'xmodule/js/fixtures' jasmine.getFixtures().fixturesPath = 'xmodule/js/fixtures'
jasmine.stubbedMetadata = jasmine.stubbedMetadata =
slowerSpeedYoutubeId: '7tqY6eQzVhE':
id: 'slowerSpeedYoutubeId' id: '7tqY6eQzVhE'
duration: 300 duration: 300
normalSpeedYoutubeId: 'cogebirgzzM':
id: 'normalSpeedYoutubeId' id: 'cogebirgzzM'
duration: 200 duration: 200
bogus: bogus:
duration: 100 duration: 100
...@@ -117,7 +120,7 @@ jasmine.stubVideoPlayer = (context, enableParts, createPlayer=true) -> ...@@ -117,7 +120,7 @@ jasmine.stubVideoPlayer = (context, enableParts, createPlayer=true) ->
loadFixtures 'video.html' loadFixtures 'video.html'
jasmine.stubRequests() jasmine.stubRequests()
YT.Player = undefined YT.Player = undefined
videosDefinition = '0.75:slowerSpeedYoutubeId,1.0:normalSpeedYoutubeId' videosDefinition = '0.75:7tqY6eQzVhE,1.0:cogebirgzzM'
context.video = new Video '#example', videosDefinition context.video = new Video '#example', videosDefinition
jasmine.stubYoutubePlayer() jasmine.stubYoutubePlayer()
if createPlayer if createPlayer
...@@ -135,7 +138,7 @@ jasmine.stubVideoPlayerAlpha = (context, enableParts, html5=false) -> ...@@ -135,7 +138,7 @@ jasmine.stubVideoPlayerAlpha = (context, enableParts, html5=false) ->
YT.Player = undefined YT.Player = undefined
window.OldVideoPlayerAlpha = undefined window.OldVideoPlayerAlpha = undefined
jasmine.stubYoutubePlayer() jasmine.stubYoutubePlayer()
return new VideoAlpha '#example', '.75:slowerSpeedYoutubeId,1.0:normalSpeedYoutubeId' return new VideoAlpha '#example', '.75:7tqY6eQzVhE,1.0:cogebirgzzM'
# Stub jQuery.cookie # Stub jQuery.cookie
......
...@@ -19,7 +19,7 @@ describe 'VideoCaption', -> ...@@ -19,7 +19,7 @@ describe 'VideoCaption', ->
@caption = @player.caption @caption = @player.caption
it 'set the youtube id', -> it 'set the youtube id', ->
expect(@caption.youtubeId).toEqual 'normalSpeedYoutubeId' expect(@caption.youtubeId).toEqual 'cogebirgzzM'
it 'create the caption element', -> it 'create the caption element', ->
expect($('.video')).toContain 'ol.subtitles' expect($('.video')).toContain 'ol.subtitles'
......
...@@ -35,7 +35,7 @@ describe 'VideoPlayer', -> ...@@ -35,7 +35,7 @@ describe 'VideoPlayer', ->
expect(window.VideoCaption.prototype.initialize).toHaveBeenCalled() expect(window.VideoCaption.prototype.initialize).toHaveBeenCalled()
expect(@player.caption).toBeDefined() expect(@player.caption).toBeDefined()
expect(@player.caption.el).toBe @player.el expect(@player.caption.el).toBe @player.el
expect(@player.caption.youtubeId).toEqual 'normalSpeedYoutubeId' expect(@player.caption.youtubeId).toEqual 'cogebirgzzM'
expect(@player.caption.currentSpeed).toEqual '1.0' expect(@player.caption.currentSpeed).toEqual '1.0'
expect(@player.caption.captionAssetPath).toEqual '/static/subs/' expect(@player.caption.captionAssetPath).toEqual '/static/subs/'
...@@ -60,7 +60,7 @@ describe 'VideoPlayer', -> ...@@ -60,7 +60,7 @@ describe 'VideoPlayer', ->
showinfo: 0 showinfo: 0
enablejsapi: 1 enablejsapi: 1
modestbranding: 1 modestbranding: 1
videoId: 'normalSpeedYoutubeId' videoId: 'cogebirgzzM'
events: events:
onReady: @player.onReady onReady: @player.onReady
onStateChange: @player.onStateChange onStateChange: @player.onStateChange
...@@ -290,7 +290,7 @@ describe 'VideoPlayer', -> ...@@ -290,7 +290,7 @@ describe 'VideoPlayer', ->
@player.onSpeedChange {}, '0.75' @player.onSpeedChange {}, '0.75'
it 'load the video', -> it 'load the video', ->
expect(@player.player.loadVideoById).toHaveBeenCalledWith 'slowerSpeedYoutubeId', '80.000' expect(@player.player.loadVideoById).toHaveBeenCalledWith '7tqY6eQzVhE', '80.000'
it 'trigger updatePlayTime event', -> it 'trigger updatePlayTime event', ->
expect(@player.updatePlayTime).toHaveBeenCalledWith '80.000' expect(@player.updatePlayTime).toHaveBeenCalledWith '80.000'
...@@ -301,7 +301,7 @@ describe 'VideoPlayer', -> ...@@ -301,7 +301,7 @@ describe 'VideoPlayer', ->
@player.onSpeedChange {}, '0.75' @player.onSpeedChange {}, '0.75'
it 'cue the video', -> it 'cue the video', ->
expect(@player.player.cueVideoById).toHaveBeenCalledWith 'slowerSpeedYoutubeId', '80.000' expect(@player.player.cueVideoById).toHaveBeenCalledWith '7tqY6eQzVhE', '80.000'
it 'trigger updatePlayTime event', -> it 'trigger updatePlayTime event', ->
expect(@player.updatePlayTime).toHaveBeenCalledWith '80.000' expect(@player.updatePlayTime).toHaveBeenCalledWith '80.000'
......
...@@ -5,14 +5,14 @@ describe 'Video', -> ...@@ -5,14 +5,14 @@ describe 'Video', ->
loadFixtures 'video.html' loadFixtures 'video.html'
jasmine.stubRequests() jasmine.stubRequests()
@slowerSpeedYoutubeId = 'slowerSpeedYoutubeId' @['7tqY6eQzVhE'] = '7tqY6eQzVhE'
@normalSpeedYoutubeId = 'normalSpeedYoutubeId' @['cogebirgzzM'] = 'cogebirgzzM'
metadata = metadata =
slowerSpeedYoutubeId: '7tqY6eQzVhE':
id: @slowerSpeedYoutubeId id: @['7tqY6eQzVhE']
duration: 300 duration: 300
normalSpeedYoutubeId: 'cogebirgzzM':
id: @normalSpeedYoutubeId id: @['cogebirgzzM']
duration: 200 duration: 200
afterEach -> afterEach ->
...@@ -38,8 +38,8 @@ describe 'Video', -> ...@@ -38,8 +38,8 @@ describe 'Video', ->
it 'parse the videos', -> it 'parse the videos', ->
expect(@video.videos).toEqual expect(@video.videos).toEqual
'0.75': @slowerSpeedYoutubeId '0.75': @['7tqY6eQzVhE']
'1.0': @normalSpeedYoutubeId '1.0': @['cogebirgzzM']
it 'fetch the video metadata', -> it 'fetch the video metadata', ->
expect(@video.fetchMetadata).toHaveBeenCalled expect(@video.fetchMetadata).toHaveBeenCalled
...@@ -102,12 +102,12 @@ describe 'Video', -> ...@@ -102,12 +102,12 @@ describe 'Video', ->
describe 'with speed', -> describe 'with speed', ->
it 'return the video id for given speed', -> it 'return the video id for given speed', ->
expect(@video.youtubeId('0.75')).toEqual @slowerSpeedYoutubeId expect(@video.youtubeId('0.75')).toEqual @['7tqY6eQzVhE']
expect(@video.youtubeId('1.0')).toEqual @normalSpeedYoutubeId expect(@video.youtubeId('1.0')).toEqual @['cogebirgzzM']
describe 'without speed', -> describe 'without speed', ->
it 'return the video id for current speed', -> it 'return the video id for current speed', ->
expect(@video.youtubeId()).toEqual @normalSpeedYoutubeId expect(@video.youtubeId()).toEqual @cogebirgzzM
describe 'setSpeed', -> describe 'setSpeed', ->
beforeEach -> beforeEach ->
...@@ -148,6 +148,6 @@ describe 'Video', -> ...@@ -148,6 +148,6 @@ describe 'Video', ->
it 'call the logger with valid parameters', -> it 'call the logger with valid parameters', ->
expect(Logger.log).toHaveBeenCalledWith 'someEvent', expect(Logger.log).toHaveBeenCalledWith 'someEvent',
id: 'id' id: 'id'
code: @normalSpeedYoutubeId code: @cogebirgzzM
currentTime: 25 currentTime: 25
speed: '1.0' speed: '1.0'
...@@ -6,9 +6,9 @@ ...@@ -6,9 +6,9 @@
jasmine.stubRequests(); jasmine.stubRequests();
oldOTBD = window.onTouchBasedDevice; oldOTBD = window.onTouchBasedDevice;
window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn(false); window.onTouchBasedDevice = jasmine.createSpy('onTouchBasedDevice').andReturn(false);
this.videosDefinition = '0.75:slowerSpeedYoutubeId,1.0:normalSpeedYoutubeId'; this.videosDefinition = '0.75:7tqY6eQzVhE,1.0:cogebirgzzM';
this.slowerSpeedYoutubeId = 'slowerSpeedYoutubeId'; this['7tqY6eQzVhE'] = '7tqY6eQzVhE';
this.normalSpeedYoutubeId = 'normalSpeedYoutubeId'; this['cogebirgzzM'] = 'cogebirgzzM';
}); });
afterEach(function () { afterEach(function () {
...@@ -45,8 +45,8 @@ ...@@ -45,8 +45,8 @@
it('parse the videos', function () { it('parse the videos', function () {
expect(this.state.videos).toEqual({ expect(this.state.videos).toEqual({
'0.75': this.slowerSpeedYoutubeId, '0.75': this['7tqY6eQzVhE'],
'1.0': this.normalSpeedYoutubeId '1.0': this['cogebirgzzM']
}); });
}); });
...@@ -91,7 +91,7 @@ ...@@ -91,7 +91,7 @@
}); });
it('parse the videos if subtitles exist', function () { it('parse the videos if subtitles exist', function () {
var sub = 'test_name_of_the_subtitles'; var sub = 'Z5KLxerq05Y';
expect(state.videos).toEqual({ expect(state.videos).toEqual({
'0.75': sub, '0.75': sub,
...@@ -165,14 +165,14 @@ ...@@ -165,14 +165,14 @@
describe('with speed', function () { describe('with speed', function () {
it('return the video id for given speed', function () { it('return the video id for given speed', function () {
expect(state.youtubeId('0.75')).toEqual(this.slowerSpeedYoutubeId); expect(state.youtubeId('0.75')).toEqual(this['7tqY6eQzVhE']);
expect(state.youtubeId('1.0')).toEqual(this.normalSpeedYoutubeId); expect(state.youtubeId('1.0')).toEqual(this['cogebirgzzM']);
}); });
}); });
describe('without speed', function () { describe('without speed', function () {
it('return the video id for current speed', function () { it('return the video id for current speed', function () {
expect(state.youtubeId()).toEqual(this.normalSpeedYoutubeId); expect(state.youtubeId()).toEqual(this.cogebirgzzM);
}); });
}); });
}); });
......
Jasmine JavaScript tests status Jasmine JavaScript tests status
------------------------------- -------------------------------
As of 22.07.2013, all the tests in this directory pass. To disable each of them, change the top level "describe(" to "xdescribe(". As of 22.07.2013, all the tests in this directory pass. To enable a test file, change
the top level "xdescribe(" to "describe(".
PS: When you are running the tests in chrome locally, make sure that chrome is started PS: When you are running the tests in chrome locally, make sure that chrome is started
with the option "--allow-file-access-from-files". with the option "--allow-file-access-from-files".
...@@ -130,7 +130,6 @@ ...@@ -130,7 +130,6 @@
describe('mouse movement', function() { describe('mouse movement', function() {
beforeEach(function() { beforeEach(function() {
//initialize();
window.setTimeout.andReturn(100); window.setTimeout.andReturn(100);
spyOn(window, 'clearTimeout'); spyOn(window, 'clearTimeout');
}); });
...@@ -221,10 +220,6 @@ ...@@ -221,10 +220,6 @@
}); });
describe('search', function() { describe('search', function() {
beforeEach(function() {
//initialize();
});
it('return a correct caption index', function() { it('return a correct caption index', function() {
expect(videoCaption.search(0)).toEqual(0); expect(videoCaption.search(0)).toEqual(0);
expect(videoCaption.search(3120)).toEqual(1); expect(videoCaption.search(3120)).toEqual(1);
...@@ -277,7 +272,6 @@ ...@@ -277,7 +272,6 @@
describe('pause', function() { describe('pause', function() {
beforeEach(function() { beforeEach(function() {
//initialize();
videoCaption.playing = true; videoCaption.playing = true;
videoCaption.pause(); videoCaption.pause();
}); });
...@@ -288,10 +282,6 @@ ...@@ -288,10 +282,6 @@
}); });
describe('updatePlayTime', function() { describe('updatePlayTime', function() {
/*beforeEach(function() {
initialize();
});*/
describe('when the video speed is 1.0x', function() { describe('when the video speed is 1.0x', function() {
beforeEach(function() { beforeEach(function() {
videoSpeedControl.currentSpeed = '1.0'; videoSpeedControl.currentSpeed = '1.0';
...@@ -369,16 +359,21 @@ ...@@ -369,16 +359,21 @@
}); });
it('when CC button is disabled ', function() { it('when CC button is disabled ', function() {
var realHeight = parseInt($('.subtitles').css('maxHeight'), 10), var realHeight, videoWrapperHeight, progressSliderHeight,
videoWrapperHeight = $('.video-wrapper').height(), controlHeight, shouldBeHeight;
controlsHeight = videoControl.el.height(),
progressSliderHeight = videoControl.sliderEl.height(),
shouldBeHeight = videoWrapperHeight - controlsHeight \
- 0.5 * controlsHeight;
state.captionsHidden = true; state.captionsHidden = true;
videoCaption.setSubtitlesHeight(); videoCaption.setSubtitlesHeight();
expect(realHeight).toBeCloseTo($('.video-wrapper').height(shouldBeHeight, 2));
realHeight = parseInt($('.subtitles').css('maxHeight'), 10);
videoWrapperHeight = $('.video-wrapper').height();
progressSliderHeight = videoControl.sliderEl.height();
controlHeight = videoControl.el.height();
shouldBeHeight = videoWrapperHeight -
0.5 * progressSliderHeight -
controlHeight;
expect(realHeight).toBe(shouldBeHeight);
}); });
}); });
...@@ -434,17 +429,6 @@ ...@@ -434,17 +429,6 @@
}); });
it('scroll to current caption', function() { it('scroll to current caption', function() {
// Check for calledWith(parameters) for some reason fails...
//
// var offset = -0.5 * ($('.video-wrapper').height() - $('.subtitles .current:first').height());
//
// expect($.fn.scrollTo).toHaveBeenCalledWith(
// $('.subtitles .current:first', videoCaption.el),
// {
// offset: offset
// }
// );
expect($.fn.scrollTo).toHaveBeenCalled(); expect($.fn.scrollTo).toHaveBeenCalled();
}); });
}); });
...@@ -454,7 +438,6 @@ ...@@ -454,7 +438,6 @@
describe('seekPlayer', function() { describe('seekPlayer', function() {
describe('when the video speed is 1.0x', function() { describe('when the video speed is 1.0x', function() {
beforeEach(function() { beforeEach(function() {
//initialize();
videoSpeedControl.currentSpeed = '1.0'; videoSpeedControl.currentSpeed = '1.0';
$('.subtitles li[data-start="14910"]').trigger('click'); $('.subtitles li[data-start="14910"]').trigger('click');
}); });
......
...@@ -34,12 +34,6 @@ ...@@ -34,12 +34,6 @@
}); });
describe('constructor', function() { describe('constructor', function() {
beforeEach(function() {
$.fn.qtip.andCallFake(function() {
$(this).data('qtip', true);
});
});
describe('always', function() { describe('always', function() {
beforeEach(function() { beforeEach(function() {
initialize(); initialize();
...@@ -60,7 +54,7 @@ ...@@ -60,7 +54,7 @@
it('create video caption', function() { it('create video caption', function() {
expect(videoCaption).toBeDefined(); expect(videoCaption).toBeDefined();
expect(state.youtubeId()).toEqual('test_name_of_the_subtitles'); expect(state.youtubeId()).toEqual('Z5KLxerq05Y');
expect(state.speed).toEqual('1.0'); expect(state.speed).toEqual('1.0');
expect(state.config.caption_asset_path).toEqual('/static/subs/'); expect(state.config.caption_asset_path).toEqual('/static/subs/');
}); });
...@@ -80,38 +74,6 @@ ...@@ -80,38 +74,6 @@
// All the toHandleWith() expect tests are not necessary for this version of Video Alpha. // All the toHandleWith() expect tests are not necessary for this version of Video Alpha.
// jQuery event system is not used to trigger and invoke methods. This is an artifact from // jQuery event system is not used to trigger and invoke methods. This is an artifact from
// previous version of Video Alpha. // previous version of Video Alpha.
//
// xit('bind to video control play event', function() {
// expect($(videoControl)).toHandleWith('play', player.play);
// });
//
// xit('bind to video control pause event', function() {
// expect($(videoControl)).toHandleWith('pause', player.pause);
// });
//
// xit('bind to video caption seek event', function() {
// expect($(videoCaption)).toHandleWith('caption_seek', player.onSeek);
// });
//
// xit('bind to video speed control speedChange event', function() {
// expect($(videoSpeedControl)).toHandleWith('speedChange', player.onSpeedChange);
// });
//
// xit('bind to video progress slider seek event', function() {
// expect($(videoProgressSlider)).toHandleWith('slide_seek', player.onSeek);
// });
//
// xit('bind to video volume control volumeChange event', function() {
// expect($(videoVolumeControl)).toHandleWith('volumeChange', player.onVolumeChange);
// });
//
// xit('bind to key press', function() {
// expect($(document.documentElement)).toHandleWith('keyup', player.bindExitFullScreen);
// });
//
// xit('bind to fullscreen switching button', function() {
// expect($('.add-fullscreen')).toHandleWith('click', player.toggleFullScreen);
// });
}); });
it('create Youtube player', function() { it('create Youtube player', function() {
...@@ -136,7 +98,7 @@ ...@@ -136,7 +98,7 @@
modestbranding: 1, modestbranding: 1,
html5: 1 html5: 1
}, },
videoId: 'normalSpeedYoutubeId', videoId: 'cogebirgzzM',
events: { events: {
onReady: videoPlayer.onReady, onReady: videoPlayer.onReady,
onStateChange: videoPlayer.onStateChange, onStateChange: videoPlayer.onStateChange,
...@@ -149,20 +111,6 @@ ...@@ -149,20 +111,6 @@
// We can't test the invocation of HTML5Video because it is not available // We can't test the invocation of HTML5Video because it is not available
// globally. It is defined within the scope of Require JS. // globally. It is defined within the scope of Require JS.
//
// xit('create HTML5 player', function() {
// spyOn(state.HTML5Video, 'Player').andCallThrough();
// initialize();
//
// expect(window.HTML5Video.Player).toHaveBeenCalledWith(this.video.el, {
// playerVars: playerVars,
// videoSources: this.video.html5Sources,
// events: {
// onReady: player.onReady,
// onStateChange: player.onStateChange
// }
// });
// });
describe('when not on a touch based device', function() { describe('when not on a touch based device', function() {
beforeEach(function() { beforeEach(function() {
...@@ -170,10 +118,6 @@ ...@@ -170,10 +118,6 @@
initialize(); initialize();
}); });
it('does not add the tooltip to fullscreen button', function() {
expect($('.add-fullscreen')).not.toHaveData('qtip');
});
it('create video volume control', function() { it('create video volume control', function() {
expect(videoVolumeControl).toBeDefined(); expect(videoVolumeControl).toBeDefined();
expect(videoVolumeControl.el).toHaveClass('volume'); expect(videoVolumeControl.el).toHaveClass('volume');
...@@ -187,10 +131,6 @@ ...@@ -187,10 +131,6 @@
initialize(); initialize();
}); });
it('add the tooltip to fullscreen button', function() {
expect($('.add-fullscreen')).toHaveData('qtip');
});
it('controls are in paused state', function() { it('controls are in paused state', function() {
expect(videoControl.isPlaying).toBe(false); expect(videoControl.isPlaying).toBe(false);
}); });
...@@ -433,11 +373,9 @@ ...@@ -433,11 +373,9 @@
expect(state.setSpeed).toHaveBeenCalledWith('0.75', false); expect(state.setSpeed).toHaveBeenCalledWith('0.75', false);
}); });
// Not relevant any more. // Not relevant any more:
// //
// it('tell video caption that the speed has changed', function() { // expect( "tell video caption that the speed has changed" ) ...
// expect(this.player.caption.currentSpeed).toEqual('0.75');
// });
}); });
describe('when the video is playing', function() { describe('when the video is playing', function() {
...@@ -548,8 +486,9 @@ ...@@ -548,8 +486,9 @@
expect(true).toBe(false); expect(true).toBe(false);
} }
// The below test has been replaced by above trickery. // The below test has been replaced by above trickery:
// expect($('.vidtime')).toHaveHtml('1:00 / 1:01'); //
// expect($('.vidtime')).toHaveHtml('1:00 / 1:01');
}); });
}); });
......
...@@ -39,21 +39,6 @@ ...@@ -39,21 +39,6 @@
it('build the seek handle', function() { it('build the seek handle', function() {
expect(videoProgressSlider.handle).toBe('.slider .ui-slider-handle'); expect(videoProgressSlider.handle).toBe('.slider .ui-slider-handle');
expect($.fn.qtip).toHaveBeenCalledWith({
content: "0:00",
position: {
my: 'bottom center',
at: 'top center',
container: videoProgressSlider.handle
},
hide: {
delay: 700
},
style: {
classes: 'ui-tooltip-slider',
widget: true
}
});
}); });
}); });
...@@ -69,7 +54,6 @@ ...@@ -69,7 +54,6 @@
// We can't expect $.fn.slider not to have been called, // We can't expect $.fn.slider not to have been called,
// because sliders are used in other parts of VideoAlpha. // because sliders are used in other parts of VideoAlpha.
// expect($.fn.slider).not.toHaveBeenCalled();
}); });
}); });
}); });
...@@ -94,43 +78,6 @@ ...@@ -94,43 +78,6 @@
}); });
// Currently, the slider is not rebuilt if it does not exist. // Currently, the slider is not rebuilt if it does not exist.
//
// describe('when the slider was not already built', function() {
// beforeEach(function() {
// spyOn($.fn, 'slider').andCallThrough();
// videoProgressSlider.slider = null;
// videoPlayer.play();
// });
//
// it('build the slider', function() {
// expect(videoProgressSlider.slider).toBe('.slider');
// expect($.fn.slider).toHaveBeenCalledWith({
// range: 'min',
// change: videoProgressSlider.onChange,
// slide: videoProgressSlider.onSlide,
// stop: videoProgressSlider.onStop
// });
// });
//
// it('build the seek handle', function() {
// expect(videoProgressSlider.handle).toBe('.ui-slider-handle');
// expect($.fn.qtip).toHaveBeenCalledWith({
// content: "0:00",
// position: {
// my: 'bottom center',
// at: 'top center',
// container: videoProgressSlider.handle
// },
// hide: {
// delay: 700
// },
// style: {
// classes: 'ui-tooltip-slider',
// widget: true
// }
// });
// });
// });
}); });
describe('updatePlayTime', function() { describe('updatePlayTime', function() {
...@@ -181,10 +128,6 @@ ...@@ -181,10 +128,6 @@
expect(videoProgressSlider.frozen).toBeTruthy(); expect(videoProgressSlider.frozen).toBeTruthy();
}); });
it('update the tooltip', function() {
expect($.fn.qtip).toHaveBeenCalled();
});
it('trigger seek event', function() { it('trigger seek event', function() {
expect(videoPlayer.onSlideSeek).toHaveBeenCalled(); expect(videoPlayer.onSlideSeek).toHaveBeenCalled();
expect(videoPlayer.currentTime).toEqual(20); expect(videoPlayer.currentTime).toEqual(20);
...@@ -199,9 +142,6 @@ ...@@ -199,9 +142,6 @@
value: 20 value: 20
}); });
}); });
it('update the tooltip', function() {
expect($.fn.qtip).toHaveBeenCalled();
});
}); });
describe('onStop', function() { describe('onStop', function() {
...@@ -228,18 +168,6 @@ ...@@ -228,18 +168,6 @@
expect(videoProgressSlider.frozen).toBeFalsy(); expect(videoProgressSlider.frozen).toBeFalsy();
}); });
}); });
describe('updateTooltip', function() {
beforeEach(function() {
initialize();
spyOn($.fn, 'slider').andCallThrough();
videoProgressSlider.updateTooltip(90);
});
it('set the tooltip value', function() {
expect($.fn.qtip).toHaveBeenCalledWith('option', 'content.text', '1:30');
});
});
}); });
}).call(this); }).call(this);
...@@ -90,19 +90,7 @@ ...@@ -90,19 +90,7 @@
// detect (and do not do anything) if there is a request for a speed that // detect (and do not do anything) if there is a request for a speed that
// is already set. // is already set.
// //
// describe('when new speed is the same', function() { // describe("when new speed is the same") ...
// beforeEach(function() {
// initialize();
// videoSpeedControl.setSpeed(1.0);
// spyOn(videoPlayer, 'onSpeedChange').andCallThrough();
//
// $('li[data-speed="1.0"] a').click();
// });
//
// it('does not trigger speedChange event', function() {
// expect(videoPlayer.onSpeedChange).not.toHaveBeenCalled();
// });
// });
describe('when new speed is not the same', function() { describe('when new speed is not the same', function() {
beforeEach(function() { beforeEach(function() {
......
...@@ -39,7 +39,6 @@ ...@@ -39,7 +39,6 @@
range: "min", range: "min",
min: 0, min: 0,
max: 100, max: 100,
/* value: 100, */
value: videoVolumeControl.currentVolume, value: videoVolumeControl.currentVolume,
change: videoVolumeControl.onChange, change: videoVolumeControl.onChange,
slide: videoVolumeControl.onChange slide: videoVolumeControl.onChange
......
...@@ -93,14 +93,7 @@ function (VideoPlayer) { ...@@ -93,14 +93,7 @@ function (VideoPlayer) {
fadeOutTimeout: 1400, fadeOutTimeout: 1400,
availableQualities: ['hd720', 'hd1080', 'highres'], availableQualities: ['hd720', 'hd1080', 'highres']
qTipConfig: {
position: {
my: 'top right',
at: 'top center'
}
}
}; };
if (!(_parseYouTubeIDs(state))) { if (!(_parseYouTubeIDs(state))) {
......
...@@ -53,9 +53,6 @@ function () { ...@@ -53,9 +53,6 @@ function () {
if (!onTouchBasedDevice()) { if (!onTouchBasedDevice()) {
state.videoControl.pause(); state.videoControl.pause();
state.videoControl.playPauseEl.qtip(state.config.qTipConfig);
state.videoControl.fullScreenEl.qtip(state.config.qTipConfig);
} else { } else {
state.videoControl.play(); state.videoControl.play();
} }
...@@ -77,7 +74,8 @@ function () { ...@@ -77,7 +74,8 @@ function () {
$(document).on('keyup', state.videoControl.exitFullScreen); $(document).on('keyup', state.videoControl.exitFullScreen);
if (state.videoType === 'html5') { if (state.videoType === 'html5') {
state.el.on('mousemove', state.videoControl.showControls) state.el.on('mousemove', state.videoControl.showControls);
state.el.on('keydown', state.videoControl.showControls);
} }
} }
......
...@@ -43,10 +43,6 @@ function () { ...@@ -43,10 +43,6 @@ function () {
state.videoQualityControl.el.show(); state.videoQualityControl.el.show();
state.videoQualityControl.quality = null; state.videoQualityControl.quality = null;
if (!onTouchBasedDevice()) {
state.videoQualityControl.el.qtip(state.config.qTipConfig);
}
} }
// function _bindHandlers(state) // function _bindHandlers(state)
......
...@@ -32,9 +32,7 @@ function () { ...@@ -32,9 +32,7 @@ function () {
// get the 'state' object as a context. // get the 'state' object as a context.
function _makeFunctionsPublic(state) { function _makeFunctionsPublic(state) {
state.videoProgressSlider.onSlide = _.bind(onSlide, state); state.videoProgressSlider.onSlide = _.bind(onSlide, state);
state.videoProgressSlider.onChange = _.bind(onChange, state);
state.videoProgressSlider.onStop = _.bind(onStop, state); state.videoProgressSlider.onStop = _.bind(onStop, state);
state.videoProgressSlider.updateTooltip = _.bind(updateTooltip, state);
state.videoProgressSlider.updatePlayTime = _.bind(updatePlayTime, state); state.videoProgressSlider.updatePlayTime = _.bind(updatePlayTime, state);
//Added for tests -- JM //Added for tests -- JM
state.videoProgressSlider.buildSlider = _.bind(buildSlider, state); state.videoProgressSlider.buildSlider = _.bind(buildSlider, state);
...@@ -56,22 +54,6 @@ function () { ...@@ -56,22 +54,6 @@ function () {
function _buildHandle(state) { function _buildHandle(state) {
state.videoProgressSlider.handle = state.videoProgressSlider.el.find('.ui-slider-handle'); state.videoProgressSlider.handle = state.videoProgressSlider.el.find('.ui-slider-handle');
state.videoProgressSlider.handle.qtip({
content: '' + Time.format(state.videoProgressSlider.slider.slider('value')),
position: {
my: 'bottom center',
at: 'top center',
container: state.videoProgressSlider.handle
},
hide: {
delay: 700
},
style: {
classes: 'ui-tooltip-slider',
widget: true
}
});
} }
// *************************************************************** // ***************************************************************
...@@ -83,7 +65,6 @@ function () { ...@@ -83,7 +65,6 @@ function () {
function buildSlider(state) { function buildSlider(state) {
state.videoProgressSlider.slider = state.videoProgressSlider.el.slider({ state.videoProgressSlider.slider = state.videoProgressSlider.el.slider({
range: 'min', range: 'min',
change: state.videoProgressSlider.onChange,
slide: state.videoProgressSlider.onSlide, slide: state.videoProgressSlider.onSlide,
stop: state.videoProgressSlider.onStop stop: state.videoProgressSlider.onStop
}); });
...@@ -91,15 +72,10 @@ function () { ...@@ -91,15 +72,10 @@ function () {
function onSlide(event, ui) { function onSlide(event, ui) {
this.videoProgressSlider.frozen = true; this.videoProgressSlider.frozen = true;
this.videoProgressSlider.updateTooltip(ui.value);
this.trigger('videoPlayer.onSlideSeek', {'type': 'onSlideSeek', 'time': ui.value}); this.trigger('videoPlayer.onSlideSeek', {'type': 'onSlideSeek', 'time': ui.value});
} }
function onChange(event, ui) {
this.videoProgressSlider.updateTooltip(ui.value);
}
function onStop(event, ui) { function onStop(event, ui) {
var _this = this; var _this = this;
...@@ -112,10 +88,6 @@ function () { ...@@ -112,10 +88,6 @@ function () {
}, 200); }, 200);
} }
function updateTooltip(value) {
this.videoProgressSlider.handle.qtip('option', 'content.text', '' + Time.format(value));
}
//Changed for tests -- JM: Check if it is the cause of Chrome Bug Valera noticed //Changed for tests -- JM: Check if it is the cause of Chrome Bug Valera noticed
function updatePlayTime(params) { function updatePlayTime(params) {
if ((this.videoProgressSlider.slider) && (!this.videoProgressSlider.frozen)) { if ((this.videoProgressSlider.slider) && (!this.videoProgressSlider.frozen)) {
......
...@@ -61,6 +61,9 @@ function () { ...@@ -61,6 +61,9 @@ function () {
slide: state.videoVolumeControl.onChange slide: state.videoVolumeControl.onChange
}); });
// Make sure that we can focus the actual volume slider while Tabing.
state.videoVolumeControl.volumeSliderEl.find('a').attr('tabindex', '0');
state.videoVolumeControl.el.toggleClass('muted', state.videoVolumeControl.currentVolume === 0); state.videoVolumeControl.el.toggleClass('muted', state.videoVolumeControl.currentVolume === 0);
} }
...@@ -74,9 +77,21 @@ function () { ...@@ -74,9 +77,21 @@ function () {
$(this).addClass('open'); $(this).addClass('open');
}); });
state.videoVolumeControl.buttonEl.on('focus', function() {
$(this).parent().addClass('open');
});
state.videoVolumeControl.el.on('mouseleave', function() { state.videoVolumeControl.el.on('mouseleave', function() {
$(this).removeClass('open'); $(this).removeClass('open');
}); });
state.videoVolumeControl.buttonEl.on('blur', function() {
state.videoVolumeControl.volumeSliderEl.find('a').focus();
});
state.videoVolumeControl.volumeSliderEl.find('a').on('blur', function () {
state.videoVolumeControl.el.removeClass('open');
});
} }
// *************************************************************** // ***************************************************************
......
...@@ -21,34 +21,41 @@ function () { ...@@ -21,34 +21,41 @@ function () {
// function _makeFunctionsPublic(state) // function _makeFunctionsPublic(state)
// //
// Functions which will be accessible via 'state' object. When called, these functions will // Functions which will be accessible via 'state' object. When called,
// get the 'state' object as a context. // these functions will get the 'state' object as a context.
function _makeFunctionsPublic(state) { function _makeFunctionsPublic(state) {
state.videoSpeedControl.changeVideoSpeed = _.bind(changeVideoSpeed, state); state.videoSpeedControl.changeVideoSpeed = _.bind(
changeVideoSpeed, state
);
state.videoSpeedControl.setSpeed = _.bind(setSpeed, state); state.videoSpeedControl.setSpeed = _.bind(setSpeed, state);
state.videoSpeedControl.reRender = _.bind(reRender, state); state.videoSpeedControl.reRender = _.bind(reRender, state);
} }
// function _renderElements(state) // function _renderElements(state)
// //
// Create any necessary DOM elements, attach them, and set their initial configuration. Also // Create any necessary DOM elements, attach them, and set their
// make the created DOM elements available via the 'state' object. Much easier to work this // initial configuration. Also make the created DOM elements available
// way - you don't have to do repeated jQuery element selects. // via the 'state' object. Much easier to work this way - you don't
// have to do repeated jQuery element selects.
function _renderElements(state) { function _renderElements(state) {
state.videoSpeedControl.speeds = state.speeds; state.videoSpeedControl.speeds = state.speeds;
state.videoSpeedControl.el = state.el.find('div.speeds'); state.videoSpeedControl.el = state.el.find('div.speeds');
state.videoSpeedControl.videoSpeedsEl = state.videoSpeedControl.el.find('.video_speeds'); state.videoSpeedControl.videoSpeedsEl = state.videoSpeedControl.el
.find('.video_speeds');
state.videoControl.secondaryControlsEl.prepend(state.videoSpeedControl.el); state.videoControl.secondaryControlsEl.prepend(
state.videoSpeedControl.el
);
$.each(state.videoSpeedControl.speeds, function(index, speed) { $.each(state.videoSpeedControl.speeds, function(index, speed) {
var link = '<a class="speed_link" href="#">' + speed + 'x</a>';
//var link = $('<a href="#">' + speed + 'x</a>'); state.videoSpeedControl.videoSpeedsEl
var link = '<a href="#">' + speed + 'x</a>'; .prepend(
$('<li data-speed="' + speed + '">' + link + '</li>')
state.videoSpeedControl.videoSpeedsEl.prepend($('<li data-speed="' + speed + '">' + link + '</li>')); );
}); });
state.videoSpeedControl.setSpeed(state.speed); state.videoSpeedControl.setSpeed(state.speed);
...@@ -56,9 +63,11 @@ function () { ...@@ -56,9 +63,11 @@ function () {
// function _bindHandlers(state) // function _bindHandlers(state)
// //
// Bind any necessary function callbacks to DOM events (click, mousemove, etc.). // Bind any necessary function callbacks to DOM events (click,
// mousemove, etc.).
function _bindHandlers(state) { function _bindHandlers(state) {
state.videoSpeedControl.videoSpeedsEl.find('a').on('click', state.videoSpeedControl.changeVideoSpeed); state.videoSpeedControl.videoSpeedsEl.find('a')
.on('click', state.videoSpeedControl.changeVideoSpeed);
if (onTouchBasedDevice()) { if (onTouchBasedDevice()) {
state.videoSpeedControl.el.on('click', function(event) { state.videoSpeedControl.el.on('click', function(event) {
...@@ -77,18 +86,36 @@ function () { ...@@ -77,18 +86,36 @@ function () {
event.preventDefault(); event.preventDefault();
$(this).removeClass('open'); $(this).removeClass('open');
}); });
state.videoSpeedControl.el.children('a')
.on('focus', function () {
$(this).parent().addClass('open');
})
.on('blur', function () {
state.videoSpeedControl.videoSpeedsEl
.find('a.speed_link:first')
.focus();
});
state.videoSpeedControl.videoSpeedsEl.find('a.speed_link:last')
.on('blur', function () {
state.videoSpeedControl.el.removeClass('open');
});
} }
} }
// *************************************************************** // ***************************************************************
// Public functions start here. // Public functions start here.
// These are available via the 'state' object. Their context ('this' keyword) is the 'state' object. // These are available via the 'state' object. Their context ('this'
// The magic private function that makes them available and sets up their context is makeFunctionsPublic(). // keyword) is the 'state' object. The magic private function that makes
// them available and sets up their context is makeFunctionsPublic().
// *************************************************************** // ***************************************************************
function setSpeed(speed) { function setSpeed(speed) {
this.videoSpeedControl.videoSpeedsEl.find('li').removeClass('active'); this.videoSpeedControl.videoSpeedsEl.find('li').removeClass('active');
this.videoSpeedControl.videoSpeedsEl.find("li[data-speed='" + speed + "']").addClass('active'); this.videoSpeedControl.videoSpeedsEl
.find("li[data-speed='" + speed + "']")
.addClass('active');
this.videoSpeedControl.el.find('p.active').html('' + speed + 'x'); this.videoSpeedControl.el.find('p.active').html('' + speed + 'x');
} }
...@@ -102,10 +129,15 @@ function () { ...@@ -102,10 +129,15 @@ function () {
this.videoSpeedControl.setSpeed( this.videoSpeedControl.setSpeed(
// To meet the API expected format. // To meet the API expected format.
parseFloat(this.videoSpeedControl.currentSpeed).toFixed(2).replace(/\.00$/, '.0') parseFloat(this.videoSpeedControl.currentSpeed)
.toFixed(2)
.replace(/\.00$/, '.0')
); );
this.trigger('videoPlayer.onSpeedChange', this.videoSpeedControl.currentSpeed); this.trigger(
'videoPlayer.onSpeedChange',
this.videoSpeedControl.currentSpeed
);
} }
} }
...@@ -119,7 +151,6 @@ function () { ...@@ -119,7 +151,6 @@ function () {
$.each(this.videoSpeedControl.speeds, function(index, speed) { $.each(this.videoSpeedControl.speeds, function(index, speed) {
var link, listItem; var link, listItem;
//link = $('<a href="#">' + speed + 'x</a>');
link = '<a href="#">' + speed + 'x</a>'; link = '<a href="#">' + speed + 'x</a>';
listItem = $('<li data-speed="' + speed + '">' + link + '</li>'); listItem = $('<li data-speed="' + speed + '">' + link + '</li>');
...@@ -131,7 +162,11 @@ function () { ...@@ -131,7 +162,11 @@ function () {
_this.videoSpeedControl.videoSpeedsEl.prepend(listItem); _this.videoSpeedControl.videoSpeedsEl.prepend(listItem);
}); });
this.videoSpeedControl.videoSpeedsEl.find('a').on('click', this.videoSpeedControl.changeVideoSpeed); this.videoSpeedControl.videoSpeedsEl.find('a')
.on('click', this.videoSpeedControl.changeVideoSpeed);
// TODO: After the control was re-rendered, we should attach 'focus'
// and 'blur' events once more.
} }
}); });
......
...@@ -109,6 +109,7 @@ function () { ...@@ -109,6 +109,7 @@ function () {
if (this.videoType === 'html5') { if (this.videoType === 'html5') {
this.el.on('mousemove', this.videoCaption.autoShowCaptions); this.el.on('mousemove', this.videoCaption.autoShowCaptions);
this.el.on('keydown', this.videoCaption.autoShowCaptions);
// Moving slider on subtitles is not a mouse move, // Moving slider on subtitles is not a mouse move,
// but captions and controls should be showed. // but captions and controls should be showed.
...@@ -122,6 +123,10 @@ function () { ...@@ -122,6 +123,10 @@ function () {
this.videoCaption.hideCaptions(this.hide_captions); this.videoCaption.hideCaptions(this.hide_captions);
if (!this.youtubeId('1.0')) {
return;
}
$.ajaxWithPrefix({ $.ajaxWithPrefix({
url: _this.videoCaption.captionURL(), url: _this.videoCaption.captionURL(),
notifyOnError: false, notifyOnError: false,
......
from xmodule.contentstore.content import StaticContent from xmodule.contentstore.content import StaticContent
from xmodule.modulestore import Location from xmodule.modulestore import Location
from xmodule.modulestore.mongo import MongoModuleStore from xmodule.modulestore.mongo import MongoModuleStore
from xmodule.modulestore.inheritance import own_metadata
import logging import logging
def _clone_modules(modulestore, modules, dest_location):
for module in modules:
original_loc = Location(module.location)
if original_loc.category != 'course':
module.location = module.location._replace(
tag=dest_location.tag, org=dest_location.org, course=dest_location.course)
else:
# on the course module we also have to update the module name
module.location = module.location._replace(
tag=dest_location.tag, org=dest_location.org, course=dest_location.course, name=dest_location.name)
print "Cloning module {0} to {1}....".format(original_loc, module.location)
# NOTE: usage of the the internal module.xblock_kvs._data does not include any 'default' values for the fields
modulestore.update_item(module.location, module.xblock_kvs._data)
# repoint children
if module.has_children:
new_children = []
for child_loc_url in module.children:
child_loc = Location(child_loc_url)
child_loc = child_loc._replace(
tag=dest_location.tag,
org=dest_location.org,
course=dest_location.course
)
new_children.append(child_loc.url())
modulestore.update_children(module.location, new_children)
# save metadata
modulestore.update_metadata(module.location, own_metadata(module))
def clone_course(modulestore, contentstore, source_location, dest_location, delete_original=False): def clone_course(modulestore, contentstore, source_location, dest_location, delete_original=False):
# first check to see if the modulestore is Mongo backed # first check to see if the modulestore is Mongo backed
if not isinstance(modulestore, MongoModuleStore): if not isinstance(modulestore, MongoModuleStore):
...@@ -37,38 +73,10 @@ def clone_course(modulestore, contentstore, source_location, dest_location, dele ...@@ -37,38 +73,10 @@ def clone_course(modulestore, contentstore, source_location, dest_location, dele
# Get all modules under this namespace which is (tag, org, course) tuple # Get all modules under this namespace which is (tag, org, course) tuple
modules = modulestore.get_items([source_location.tag, source_location.org, source_location.course, None, None, None]) modules = modulestore.get_items([source_location.tag, source_location.org, source_location.course, None, None, None])
_clone_modules(modulestore, modules, dest_location)
for module in modules: modules = modulestore.get_items([source_location.tag, source_location.org, source_location.course, None, None, 'draft'])
original_loc = Location(module.location) _clone_modules(modulestore, modules, dest_location)
if original_loc.category != 'course':
module.location = module.location._replace(tag=dest_location.tag, org=dest_location.org,
course=dest_location.course)
else:
# on the course module we also have to update the module name
module.location = module.location._replace(tag=dest_location.tag, org=dest_location.org,
course=dest_location.course, name=dest_location.name)
print "Cloning module {0} to {1}....".format(original_loc, module.location)
modulestore.update_item(module.location, module._model_data._kvs._data)
# repoint children
if module.has_children:
new_children = []
for child_loc_url in module.children:
child_loc = Location(child_loc_url)
child_loc = child_loc._replace(
tag=dest_location.tag,
org=dest_location.org,
course=dest_location.course
)
new_children.append(child_loc.url())
modulestore.update_children(module.location, new_children)
# save metadata
modulestore.update_metadata(module.location, module._model_data._kvs._metadata)
# now iterate through all of the assets and clone them # now iterate through all of the assets and clone them
# first the thumbnails # first the thumbnails
......
...@@ -4,6 +4,7 @@ from xmodule.xml_module import XmlDescriptor ...@@ -4,6 +4,7 @@ from xmodule.xml_module import XmlDescriptor
import logging import logging
import sys import sys
from xblock.core import String, Scope from xblock.core import String, Scope
from exceptions import SerializationError
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -27,11 +28,11 @@ class RawDescriptor(XmlDescriptor, XMLEditingDescriptor): ...@@ -27,11 +28,11 @@ class RawDescriptor(XmlDescriptor, XMLEditingDescriptor):
# re-raise # re-raise
lines = self.data.split('\n') lines = self.data.split('\n')
line, offset = err.position line, offset = err.position
msg = ("Unable to create xml for problem {loc}. " msg = ("Unable to create xml for module {loc}. "
"Context: '{context}'".format( "Context: '{context}'".format(
context=lines[line - 1][offset - 40:offset + 40], context=lines[line - 1][offset - 40:offset + 40],
loc=self.location)) loc=self.location))
raise Exception, msg, sys.exc_info()[2] raise SerializationError(self.location, msg)
class EmptyDataRawDescriptor(XmlDescriptor, XMLEditingDescriptor): class EmptyDataRawDescriptor(XmlDescriptor, XMLEditingDescriptor):
......
<%! from django.utils.translation import ugettext as _ %> <%! from django.utils.translation import ugettext as _ %>
% if display_name is not UNDEFINED and display_name is not None: % if display_name is not UNDEFINED and display_name is not None:
<h2> ${display_name} </h2> <h2>${display_name}</h2>
% endif % endif
<div <div
id="video_${id}" id="video_${id}"
class="videoalpha" class="videoalpha"
% if not settings.MITX_FEATURES['STUB_VIDEO_FOR_TESTING']: % if not settings.MITX_FEATURES['STUB_VIDEO_FOR_TESTING']:
data-streams="${youtube_streams}" data-streams="${youtube_streams}"
% endif % endif
${'data-sub="{}"'.format(sub) if sub else ''} ${'data-sub="{}"'.format(sub) if sub else ''}
${'data-autoplay="{}"'.format(autoplay) if autoplay else ''} ${'data-autoplay="{}"'.format(autoplay) if autoplay else ''}
% if not settings.MITX_FEATURES['STUB_VIDEO_FOR_TESTING']:
${'data-mp4-source="{}"'.format(sources.get('mp4')) if sources.get('mp4') else ''}
${'data-webm-source="{}"'.format(sources.get('webm')) if sources.get('webm') else ''}
${'data-ogg-source="{}"'.format(sources.get('ogv')) if sources.get('ogv') else ''}
% endif
data-caption-data-dir="${data_dir}" % if not settings.MITX_FEATURES['STUB_VIDEO_FOR_TESTING']:
data-show-captions="${show_captions}" ${'data-mp4-source="{}"'.format(sources.get('mp4')) if sources.get('mp4') else ''}
data-start="${start}" ${'data-webm-source="{}"'.format(sources.get('webm')) if sources.get('webm') else ''}
data-end="${end}" ${'data-ogg-source="{}"'.format(sources.get('ogv')) if sources.get('ogv') else ''}
data-caption-asset-path="${caption_asset_path}" % endif
data-autoplay="${autoplay}"
data-caption-data-dir="${data_dir}"
data-show-captions="${show_captions}"
data-start="${start}"
data-end="${end}"
data-caption-asset-path="${caption_asset_path}"
data-autoplay="${autoplay}"
> >
<div class="tc-wrapper"> <div class="tc-wrapper">
<article class="video-wrapper"> <article class="video-wrapper">
<div class="video-player-pre"></div> <div class="video-player-pre"></div>
<section class="video-player">
<div id="${id}"></div> <section class="video-player">
</section> <div id="${id}"></div>
<div class="video-player-post"></div> </section>
<section class="video-controls">
<div class="slider"></div> <div class="video-player-post"></div>
<div>
<ul class="vcr"> <section class="video-controls">
<li><a class="video_control" href="#" title="${_('Play')}"></a></li> <div class="slider" tabindex="0" title="Video position"></div>
<li><div class="vidtime">0:00 / 0:00</div></li>
</ul> <div>
<div class="secondary-controls"> <ul class="vcr">
<div class="speeds"> <li><a class="video_control" href="#" tabindex="0" title="${_('Play')}"></a></li>
<a href="#"> <li><div class="vidtime">0:00 / 0:00</div></li>
<h3>${_('Speed')}</h3> </ul>
<p class="active"></p> <div class="secondary-controls">
</a> <div class="speeds">
<ol class="video_speeds"></ol> <a href="#" tabindex="0" title="Speeds">
</div> <h3 tabindex="-1">${_('Speed')}</h3>
<div class="volume"> <p tabindex="-1" class="active"></p>
<a href="#"></a> </a>
<div class="volume-slider-container"> <ol tabindex="-1" class="video_speeds"></ol>
<div class="volume-slider"></div>
</div> </div>
</div> <div class="volume">
<a href="#" class="add-fullscreen" title="${_('Fill browser')}">${_('Fill browser')}</a> <a href="#" tabindex="0" title="Volume"></a>
<a href="#" class="quality_control" title="${_('HD')}">${_('HD')}</a> <div tabindex="-1" class="volume-slider-container">
<div tabindex="-1" class="volume-slider"></div>
</div>
</div>
<a href="#" class="add-fullscreen" tabindex="0" title="${_('Fill browser')}">${_('Fill browser')}</a>
<a href="#" class="quality_control" tabindex="0" title="${_('HD')}">${_('HD')}</a>
<a href="#" class="hide-subtitles" title="${_('Turn off captions')}">Captions</a> <a href="#" class="hide-subtitles" title="${_('Turn off captions')}">${_('Captions')}</a>
</div>
</div> </div>
</div> </section>
</section> </article>
</article>
<ol class="subtitles"><li></li></ol>
</div> <ol class="subtitles" tabindex="0" title="Captions"><li></li></ol>
</div>
</div> </div>
% if sources.get('main'): % if sources.get('main'):
<div class="video-sources"> <div class="video-sources">
<p>${_('Download video <a href="%s">here</a>.') % sources.get('main')}</p> <p>${(_('Download video') + ' <a href="%s">' + _('here') + '</a>.') % sources.get('main')}</p>
</div> </div>
% endif % endif
% if track: % if track:
<div class="video-tracks"> <div class="video-tracks">
<p>${_('Download subtitles <a href="%s">here</a>.') % track}</p> <p>${(_('Download subtitles') + ' <a href="%s">' + _('here') + '</a>.') % track}</p>
</div> </div>
% endif % endif
...@@ -8,6 +8,6 @@ ...@@ -8,6 +8,6 @@
-e git+https://github.com/eventbrite/zendesk.git@d53fe0e81b623f084e91776bcf6369f8b7b63879#egg=zendesk -e git+https://github.com/eventbrite/zendesk.git@d53fe0e81b623f084e91776bcf6369f8b7b63879#egg=zendesk
# Our libraries: # Our libraries:
-e git+https://github.com/edx/XBlock.git@f8c10cb0d16122ba66f7e94a32ec3774c1dee13e#egg=XBlock -e git+https://github.com/edx/XBlock.git@446668fddc75b78512eef4e9425cbc9a3327606f#egg=XBlock
-e git+https://github.com/edx/codejail.git@0a1b468#egg=codejail -e git+https://github.com/edx/codejail.git@0a1b468#egg=codejail
-e git+https://github.com/edx/diff-cover.git@v0.2.0#egg=diff_cover -e git+https://github.com/edx/diff-cover.git@v0.2.0#egg=diff_cover
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment