Commit 3fa636e0 by Calen Pennington

Move module javascript into common/lib/xmodule. Still TODO: run jasmine tests in that directory

parent 1395e5d5
...@@ -98,7 +98,7 @@ def edit_item(request): ...@@ -98,7 +98,7 @@ def edit_item(request):
item = modulestore().get_item(item_location) item = modulestore().get_item(item_location)
return render_to_response('unit.html', { return render_to_response('unit.html', {
'contents': item.get_html(), 'contents': item.get_html(),
'js_module': item.js_module_name(), 'js_module': item.__class__.__name__,
'category': item.category, 'category': item.category,
'name': item.name, 'name': item.name,
'previews': get_module_previews(item), 'previews': get_module_previews(item),
......
...@@ -177,6 +177,8 @@ PIPELINE_CSS = { ...@@ -177,6 +177,8 @@ PIPELINE_CSS = {
PIPELINE_ALWAYS_RECOMPILE = ['sass/base-style.scss'] PIPELINE_ALWAYS_RECOMPILE = ['sass/base-style.scss']
# Load javascript from all of the available descriptors, and
# prep it for use in pipeline js
from xmodule.x_module import XModuleDescriptor from xmodule.x_module import XModuleDescriptor
from xmodule.raw_module import RawDescriptor from xmodule.raw_module import RawDescriptor
js_file_dir = PROJECT_ROOT / "static" / "coffee" / "module" js_file_dir = PROJECT_ROOT / "static" / "coffee" / "module"
......
...@@ -8,6 +8,7 @@ import re ...@@ -8,6 +8,7 @@ import re
from datetime import timedelta from datetime import timedelta
from lxml import etree from lxml import etree
from pkg_resources import resource_string
from xmodule.x_module import XModule from xmodule.x_module import XModule
from xmodule.raw_module import RawDescriptor from xmodule.raw_module import RawDescriptor
...@@ -70,6 +71,8 @@ class CapaModule(XModule): ...@@ -70,6 +71,8 @@ class CapaModule(XModule):
''' '''
icon_class = 'problem' icon_class = 'problem'
js = {'coffee': [resource_string(__name__, 'js/src/capa/display.coffee')]}
def __init__(self, system, location, definition, instance_state=None, shared_state=None, **kwargs): def __init__(self, system, location, definition, instance_state=None, shared_state=None, **kwargs):
XModule.__init__(self, system, location, definition, instance_state, shared_state, **kwargs) XModule.__init__(self, system, location, definition, instance_state, shared_state, **kwargs)
......
[
{
"content": "Video 1",
"type": "video",
"title": "Video 1"
}, {
"content": "Video 2",
"type": "video",
"title": "Video 2"
}, {
"content": "Sample Problem",
"type": "problem",
"title": "Sample Problem"
}
]
class @Raw
constructor: (@element) ->
@edit_box = $(".edit-box", @element)
save: -> @edit_box.val()
class @HTML class @RawDescriptor
constructor: (@element) -> constructor: (@element) ->
@edit_box = $(".edit-box", @element) @edit_box = $(".edit-box", @element)
......
class @Video class @Video
constructor: (@id, videos) -> constructor: (@id, videos, @caption_url_base) ->
window.player = null window.player = null
@el = $("#video_#{@id}") @el = $("#video_#{@id}")
@parseVideos videos @parseVideos videos
......
...@@ -7,7 +7,7 @@ class @VideoCaption extends Subview ...@@ -7,7 +7,7 @@ class @VideoCaption extends Subview
.bind('DOMMouseScroll', @onMovement) .bind('DOMMouseScroll', @onMovement)
captionURL: -> captionURL: ->
"/static/subs/#{@youtubeId}.srt.sjson" "#{@captionURLBase}/#{@youtubeId}.srt.sjson"
render: -> render: ->
@$('.video-wrapper').after """ @$('.video-wrapper').after """
......
...@@ -25,7 +25,7 @@ class @VideoPlayer extends Subview ...@@ -25,7 +25,7 @@ class @VideoPlayer extends Subview
render: -> render: ->
@control = new VideoControl el: @$('.video-controls') @control = new VideoControl el: @$('.video-controls')
@caption = new VideoCaption el: @el, youtubeId: @video.youtubeId('1.0'), currentSpeed: @currentSpeed() @caption = new VideoCaption el: @el, youtubeId: @video.youtubeId('1.0'), currentSpeed: @currentSpeed(), captionURLBase: @video.caption_url_base
unless onTouchBasedDevice() unless onTouchBasedDevice()
@volumeControl = new VideoVolumeControl el: @$('.secondary-controls') @volumeControl = new VideoVolumeControl el: @$('.secondary-controls')
@speedControl = new VideoSpeedControl el: @$('.secondary-controls'), speeds: @video.speeds, currentSpeed: @currentSpeed() @speedControl = new VideoSpeedControl el: @$('.secondary-controls'), speeds: @video.speeds, currentSpeed: @currentSpeed()
......
...@@ -12,8 +12,7 @@ class RawDescriptor(MakoModuleDescriptor, XmlDescriptor): ...@@ -12,8 +12,7 @@ class RawDescriptor(MakoModuleDescriptor, XmlDescriptor):
""" """
mako_template = "widgets/raw-edit.html" mako_template = "widgets/raw-edit.html"
js = {'coffee': [resource_string(__name__, 'js/module/raw.coffee')]} js = {'coffee': [resource_string(__name__, 'js/src/raw/edit.coffee')]}
js_module = 'Raw'
def get_context(self): def get_context(self):
return { return {
......
...@@ -8,6 +8,7 @@ from xmodule.xml_module import XmlDescriptor ...@@ -8,6 +8,7 @@ from xmodule.xml_module import XmlDescriptor
from xmodule.x_module import XModule from xmodule.x_module import XModule
from xmodule.progress import Progress from xmodule.progress import Progress
from xmodule.exceptions import NotFoundError from xmodule.exceptions import NotFoundError
from pkg_resources import resource_string
log = logging.getLogger("mitx.common.lib.seq_module") log = logging.getLogger("mitx.common.lib.seq_module")
...@@ -19,6 +20,7 @@ class_priority = ['video', 'problem'] ...@@ -19,6 +20,7 @@ class_priority = ['video', 'problem']
class SequenceModule(XModule): class SequenceModule(XModule):
''' Layout module which lays out content in a temporal sequence ''' Layout module which lays out content in a temporal sequence
''' '''
js = {'coffee': [resource_string(__name__, 'js/src/sequence/display.coffee')]}
def __init__(self, system, location, definition, instance_state=None, shared_state=None, **kwargs): def __init__(self, system, location, definition, instance_state=None, shared_state=None, **kwargs):
XModule.__init__(self, system, location, definition, instance_state, shared_state, **kwargs) XModule.__init__(self, system, location, definition, instance_state, shared_state, **kwargs)
......
...@@ -2,6 +2,7 @@ import json ...@@ -2,6 +2,7 @@ import json
import logging import logging
from lxml import etree from lxml import etree
from pkg_resources import resource_string, resource_listdir
from xmodule.x_module import XModule from xmodule.x_module import XModule
from xmodule.raw_module import RawDescriptor from xmodule.raw_module import RawDescriptor
...@@ -13,6 +14,13 @@ class VideoModule(XModule): ...@@ -13,6 +14,13 @@ class VideoModule(XModule):
video_time = 0 video_time = 0
icon_class = 'video' icon_class = 'video'
js = {'coffee':
[resource_string(__name__, 'js/src/video/display.coffee')] +
[resource_string(__name__, 'js/src/video/display/' + filename)
for filename
in resource_listdir(__name__, 'js/src/video/display')
]}
def __init__(self, system, location, definition, instance_state=None, shared_state=None, **kwargs): def __init__(self, system, location, definition, instance_state=None, shared_state=None, **kwargs):
XModule.__init__(self, system, location, definition, instance_state, shared_state, **kwargs) XModule.__init__(self, system, location, definition, instance_state, shared_state, **kwargs)
xmltree = etree.fromstring(self.definition['data']) xmltree = etree.fromstring(self.definition['data'])
......
...@@ -77,6 +77,8 @@ class XModule(object): ...@@ -77,6 +77,8 @@ class XModule(object):
# if the icon class depends on the data in the module # if the icon class depends on the data in the module
icon_class = 'other' icon_class = 'other'
js = {}
def __init__(self, system, location, definition, instance_state=None, shared_state=None, **kwargs): def __init__(self, system, location, definition, instance_state=None, shared_state=None, **kwargs):
''' '''
Construct a new xmodule Construct a new xmodule
...@@ -179,11 +181,6 @@ class XModule(object): ...@@ -179,11 +181,6 @@ class XModule(object):
''' '''
return None return None
def get_html(self):
''' HTML, as shown in the browser. This is the only method that must be implemented
'''
raise NotImplementedError("get_html must be defined for all XModules that appear on the screen. Not defined in %s" % self.__class__.__name__)
def get_progress(self): def get_progress(self):
''' Return a progress.Progress object that represents how far the student has gone ''' Return a progress.Progress object that represents how far the student has gone
in this module. Must be implemented to get correct progress tracking behavior in in this module. Must be implemented to get correct progress tracking behavior in
...@@ -198,6 +195,25 @@ class XModule(object): ...@@ -198,6 +195,25 @@ class XModule(object):
get is a dictionary-like object ''' get is a dictionary-like object '''
return "" return ""
# ================================== HTML INTERFACE DEFINITIONS ======================
@classmethod
def get_javascript(cls):
"""
Return a dictionary containing some of the following keys:
coffee: A list of coffeescript fragments that should be compiled and
placed on the page
js: A list of javascript fragments that should be included on the page
All of these will be loaded onto the page in the LMS
"""
return cls.js
def get_html(self):
''' HTML, as shown in the browser. This is the only method that must be implemented
'''
raise NotImplementedError("get_html must be defined for all XModules that appear on the screen. Not defined in %s" % self.__class__.__name__)
class XModuleDescriptor(Plugin): class XModuleDescriptor(Plugin):
""" """
...@@ -211,7 +227,6 @@ class XModuleDescriptor(Plugin): ...@@ -211,7 +227,6 @@ class XModuleDescriptor(Plugin):
""" """
entry_point = "xmodule.v1" entry_point = "xmodule.v1"
js = {} js = {}
js_module = None
# A list of metadata that this module can inherit from its parent module # A list of metadata that this module can inherit from its parent module
inheritable_metadata = ( inheritable_metadata = (
...@@ -398,13 +413,6 @@ class XModuleDescriptor(Plugin): ...@@ -398,13 +413,6 @@ class XModuleDescriptor(Plugin):
""" """
return cls.js return cls.js
def js_module_name(self):
"""
Return the name of the javascript class to instantiate when
this module descriptor is loaded for editing
"""
return self.js_module
def get_html(self): def get_html(self):
""" """
Return the html used to edit this module Return the html used to edit this module
......
...@@ -22,6 +22,7 @@ import sys ...@@ -22,6 +22,7 @@ import sys
import os import os
import tempfile import tempfile
import glob2 import glob2
import errno
import djcelery import djcelery
from path import path from path import path
...@@ -327,6 +328,37 @@ main_vendor_js = [ ...@@ -327,6 +328,37 @@ main_vendor_js = [
'js/vendor/jquery.qtip.min.js', 'js/vendor/jquery.qtip.min.js',
] ]
# Load javascript from all of the available xmodules, and
# prep it for use in pipeline js
from xmodule.x_module import XModuleDescriptor
from xmodule.hidden_module import HiddenDescriptor
js_file_dir = PROJECT_ROOT / "static" / "coffee" / "module"
try:
os.makedirs(js_file_dir)
except OSError as exc:
if exc.errno == errno.EEXIST:
pass
else:
raise
module_js_sources = []
for descriptor in XModuleDescriptor.load_classes() + [HiddenDescriptor]:
module = getattr(descriptor, 'module_class', None)
if module is None:
continue
js = module.get_javascript()
for filetype in ('coffee', 'js'):
for idx, fragment in enumerate(js.get(filetype, [])):
path = os.path.join(js_file_dir, "{name}.{idx}.{type}".format(
name=module.__name__,
idx=idx,
type=filetype))
with open(path, 'w') as js_file:
js_file.write(fragment)
module_js_sources.append(path.replace(PROJECT_ROOT / "static/", ""))
PIPELINE_JS = { PIPELINE_JS = {
'application': { 'application': {
# Application will contain all paths not in courseware_only_js # Application will contain all paths not in courseware_only_js
...@@ -350,6 +382,10 @@ PIPELINE_JS = { ...@@ -350,6 +382,10 @@ PIPELINE_JS = {
'source_filenames': main_vendor_js, 'source_filenames': main_vendor_js,
'output_filename': 'js/main_vendor.js', 'output_filename': 'js/main_vendor.js',
}, },
'module-js': {
'source_filenames': module_js_sources,
'output_filename': 'js/modules.js',
},
'spec': { 'spec': {
'source_filenames': [pth.replace(PROJECT_ROOT / 'static/', '') for pth in glob2.glob(PROJECT_ROOT / 'static/coffee/spec/**/*.coffee')], 'source_filenames': [pth.replace(PROJECT_ROOT / 'static/', '') for pth in glob2.glob(PROJECT_ROOT / 'static/coffee/spec/**/*.coffee')],
'output_filename': 'js/spec.js' 'output_filename': 'js/spec.js'
......
...@@ -18,7 +18,7 @@ class @Courseware ...@@ -18,7 +18,7 @@ class @Courseware
render: -> render: ->
$('.course-content .video').each -> $('.course-content .video').each ->
id = $(this).attr('id').replace(/video_/, '') id = $(this).attr('id').replace(/video_/, '')
new Video id, $(this).data('streams') new Video id, $(this).data('streams'), $(this).data('caption-url-base')
$('.course-content .problems-wrapper').each -> $('.course-content .problems-wrapper').each ->
id = $(this).data('problem-id') id = $(this).data('problem-id')
new Problem id, $(this).attr('id'), $(this).data('url') new Problem id, $(this).attr('id'), $(this).data('url')
......
/**
* jQuery.ScrollTo - Easy element scrolling using jQuery.
* Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
* Dual licensed under MIT and GPL.
* Date: 5/25/2009
* @author Ariel Flesler
* @version 1.4.2
*
* http://flesler.blogspot.com/2007/10/jqueryscrollto.html
*/
;(function(d){var k=d.scrollTo=function(a,i,e){d(window).scrollTo(a,i,e)};k.defaults={axis:'xy',duration:parseFloat(d.fn.jquery)>=1.3?0:1};k.window=function(a){return d(window)._scrollable()};d.fn._scrollable=function(){return this.map(function(){var a=this,i=!a.nodeName||d.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!i)return a;var e=(a.contentWindow||a).document||a.ownerDocument||a;return d.browser.safari||e.compatMode=='BackCompat'?e.body:e.documentElement})};d.fn.scrollTo=function(n,j,b){if(typeof j=='object'){b=j;j=0}if(typeof b=='function')b={onAfter:b};if(n=='max')n=9e9;b=d.extend({},k.defaults,b);j=j||b.speed||b.duration;b.queue=b.queue&&b.axis.length>1;if(b.queue)j/=2;b.offset=p(b.offset);b.over=p(b.over);return this._scrollable().each(function(){var q=this,r=d(q),f=n,s,g={},u=r.is('html,body');switch(typeof f){case'number':case'string':if(/^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(f)){f=p(f);break}f=d(f,this);case'object':if(f.is||f.style)s=(f=d(f)).offset()}d.each(b.axis.split(''),function(a,i){var e=i=='x'?'Left':'Top',h=e.toLowerCase(),c='scroll'+e,l=q[c],m=k.max(q,i);if(s){g[c]=s[h]+(u?0:l-r.offset()[h]);if(b.margin){g[c]-=parseInt(f.css('margin'+e))||0;g[c]-=parseInt(f.css('border'+e+'Width'))||0}g[c]+=b.offset[h]||0;if(b.over[h])g[c]+=f[i=='x'?'width':'height']()*b.over[h]}else{var o=f[h];g[c]=o.slice&&o.slice(-1)=='%'?parseFloat(o)/100*m:o}if(/^\d+$/.test(g[c]))g[c]=g[c]<=0?0:Math.min(g[c],m);if(!a&&b.queue){if(l!=g[c])t(b.onAfterFirst);delete g[c]}});t(b.onAfter);function t(a){r.animate(g,j,b.easing,a&&function(){a.call(this,n,b)})}}).end()};k.max=function(a,i){var e=i=='x'?'Width':'Height',h='scroll'+e;if(!d(a).is('html,body'))return a[h]-d(a)[e.toLowerCase()]();var c='client'+e,l=a.ownerDocument.documentElement,m=a.ownerDocument.body;return Math.max(l[h],m[h])-Math.min(l[c],m[c])};function p(a){return typeof a=='object'?a:{top:a,left:a}}})(jQuery);
\ No newline at end of file
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
{% load compressed %} {% load compressed %}
{# static files #} {# static files #}
{% compressed_js 'application' %} {% compressed_js 'application' %}
{% compressed_js 'module-js' %}
{# spec files #} {# spec files #}
{% compressed_js 'spec' %} {% compressed_js 'spec' %}
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
<%include file="footer.html" /> <%include file="footer.html" />
<%static:js group='application'/> <%static:js group='application'/>
<%static:js group='module-js'/>
<%block name="js_extra"/> <%block name="js_extra"/>
</body> </body>
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<h1> ${name} </h1> <h1> ${name} </h1>
% endif % endif
<div id="video_${id}" class="video" data-streams="${streams}"> <div id="video_${id}" class="video" data-streams="${streams}" data-caption-url-base="/static/subs">
<div class="tc-wrapper"> <div class="tc-wrapper">
<article class="video-wrapper"> <article class="video-wrapper">
<section class="video-player"> <section class="video-player">
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
$('.course-content .video').each(function() { $('.course-content .video').each(function() {
var id; var id;
id = $(this).attr('id').replace(/video_/, ''); id = $(this).attr('id').replace(/video_/, '');
return new Video(id, $(this).data('streams')); return new Video(id, $(this).data('streams'), $(this).data('caption-url'));
}); });
$('.course-content .problems-wrapper').each(function() { $('.course-content .problems-wrapper').each(function() {
var id; var id;
......
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