upgrade backbone-associations, fix tests
Merge pull request #17559 from edx/ri/EDUCATOR-2231-remove-enrollment-dates-references EDUCATOR-2231 remove course run enrollment dates references from studio add NoTextbooks component make backbone-associations work webpackify context course test fixup
This commit is contained in:
@@ -29,8 +29,8 @@ class CourseAccessRoleSerializer(serializers.ModelSerializer):
|
||||
class CourseRunScheduleSerializer(serializers.Serializer):
|
||||
start = serializers.DateTimeField()
|
||||
end = serializers.DateTimeField()
|
||||
enrollment_start = serializers.DateTimeField(allow_null=True)
|
||||
enrollment_end = serializers.DateTimeField(allow_null=True)
|
||||
enrollment_start = serializers.DateTimeField(allow_null=True, required=False)
|
||||
enrollment_end = serializers.DateTimeField(allow_null=True, required=False)
|
||||
|
||||
|
||||
class CourseRunTeamSerializer(serializers.Serializer):
|
||||
|
||||
@@ -16,39 +16,46 @@ from ...serializers.course_runs import CourseRunSerializer
|
||||
@ddt.ddt
|
||||
class CourseRunSerializerTests(ModuleStoreTestCase):
|
||||
|
||||
@ddt.data(
|
||||
('instructor_paced', False),
|
||||
('self_paced', True),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_data(self, expected_pacing_type, self_paced):
|
||||
start = datetime.datetime.now(pytz.UTC)
|
||||
end = start + datetime.timedelta(days=30)
|
||||
enrollment_start = start - datetime.timedelta(days=7)
|
||||
enrollment_end = end - datetime.timedelta(days=14)
|
||||
def setUp(self):
|
||||
super(CourseRunSerializerTests, self).setUp()
|
||||
|
||||
course = CourseFactory(
|
||||
start=start,
|
||||
end=end,
|
||||
self.course_start = datetime.datetime.now(pytz.UTC)
|
||||
self.course_end = self.course_start + datetime.timedelta(days=30)
|
||||
|
||||
self.request = RequestFactory().get('')
|
||||
|
||||
def setup_course(self, self_paced, enrollment_start=None, enrollment_end=None):
|
||||
return CourseFactory(
|
||||
start=self.course_start,
|
||||
end=self.course_end,
|
||||
self_paced=self_paced,
|
||||
enrollment_start=enrollment_start,
|
||||
enrollment_end=enrollment_end,
|
||||
self_paced=self_paced
|
||||
enrollment_end=enrollment_end
|
||||
)
|
||||
|
||||
def setup_course_user_roles(self, course):
|
||||
"""
|
||||
get course staff and instructor roles user
|
||||
"""
|
||||
instructor = UserFactory()
|
||||
CourseInstructorRole(course.id).add_users(instructor)
|
||||
staff = UserFactory()
|
||||
CourseStaffRole(course.id).add_users(staff)
|
||||
|
||||
request = RequestFactory().get('')
|
||||
serializer = CourseRunSerializer(course, context={'request': request})
|
||||
expected = {
|
||||
return instructor, staff
|
||||
|
||||
def get_expected_course_data(
|
||||
self, course, enrollment_start, enrollment_end,
|
||||
instructor, staff, expected_pacing_type
|
||||
):
|
||||
return {
|
||||
'id': str(course.id),
|
||||
'title': course.display_name,
|
||||
'schedule': {
|
||||
'start': serialize_datetime(start),
|
||||
'end': serialize_datetime(end),
|
||||
'enrollment_start': serialize_datetime(enrollment_start),
|
||||
'enrollment_end': serialize_datetime(enrollment_end),
|
||||
'start': serialize_datetime(self.course_start),
|
||||
'end': serialize_datetime(self.course_end),
|
||||
'enrollment_start': enrollment_start,
|
||||
'enrollment_end': enrollment_end,
|
||||
},
|
||||
'team': [
|
||||
{
|
||||
@@ -61,8 +68,50 @@ class CourseRunSerializerTests(ModuleStoreTestCase):
|
||||
},
|
||||
],
|
||||
'images': {
|
||||
'card_image': request.build_absolute_uri(course_image_url(course)),
|
||||
'card_image': self.request.build_absolute_uri(course_image_url(course)),
|
||||
},
|
||||
'pacing_type': expected_pacing_type,
|
||||
}
|
||||
assert serializer.data == expected
|
||||
|
||||
@ddt.data(
|
||||
('instructor_paced', False),
|
||||
('self_paced', True),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_data_with_enrollment_dates(self, expected_pacing_type, self_paced):
|
||||
"""
|
||||
Verify that CourseRunSerializer serializes the course object.
|
||||
"""
|
||||
|
||||
enrollment_start = self.course_start - datetime.timedelta(days=7)
|
||||
enrollment_end = self.course_end - datetime.timedelta(days=14)
|
||||
course = self.setup_course(self_paced, enrollment_start, enrollment_end)
|
||||
instructor, staff = self.setup_course_user_roles(course)
|
||||
serializer = CourseRunSerializer(course, context={'request': self.request})
|
||||
|
||||
expected_course_data = self.get_expected_course_data(
|
||||
course, serialize_datetime(enrollment_start), serialize_datetime(enrollment_end),
|
||||
instructor, staff, expected_pacing_type
|
||||
)
|
||||
|
||||
assert serializer.data == expected_course_data
|
||||
|
||||
@ddt.data(
|
||||
('instructor_paced', False),
|
||||
('self_paced', True),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_data_without_enrollment_dates(self, expected_pacing_type, self_paced):
|
||||
"""
|
||||
Verify that CourseRunSerializer serializes the course object without enrollment
|
||||
start and end dates.
|
||||
"""
|
||||
course = self.setup_course(self_paced)
|
||||
instructor, staff = self.setup_course_user_roles(course)
|
||||
serializer = CourseRunSerializer(course, context={'request': self.request})
|
||||
|
||||
expected_course_data = self.get_expected_course_data(
|
||||
course, None, None, instructor, staff, expected_pacing_type
|
||||
)
|
||||
|
||||
assert serializer.data == expected_course_data
|
||||
|
||||
@@ -34,7 +34,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase):
|
||||
user = AdminFactory()
|
||||
self.client.login(username=user.username, password=TEST_PASSWORD)
|
||||
|
||||
def get_course_run_data(self, user, start, end, enrollment_start, enrollment_end, pacing_type, role='instructor'):
|
||||
def get_course_run_data(self, user, start, end, pacing_type, role='instructor'):
|
||||
return {
|
||||
'title': 'Testing 101',
|
||||
'org': 'TestingX',
|
||||
@@ -43,8 +43,6 @@ class CourseRunViewSetTests(ModuleStoreTestCase):
|
||||
'schedule': {
|
||||
'start': serialize_datetime(start),
|
||||
'end': serialize_datetime(end),
|
||||
'enrollment_start': serialize_datetime(enrollment_start),
|
||||
'enrollment_end': serialize_datetime(enrollment_end),
|
||||
},
|
||||
'team': [
|
||||
{
|
||||
@@ -55,11 +53,9 @@ class CourseRunViewSetTests(ModuleStoreTestCase):
|
||||
'pacing_type': pacing_type,
|
||||
}
|
||||
|
||||
def assert_course_run_schedule(self, course_run, start, end, enrollment_start, enrollment_end):
|
||||
def assert_course_run_schedule(self, course_run, start, end):
|
||||
assert course_run.start == start
|
||||
assert course_run.end == end
|
||||
assert course_run.enrollment_start == enrollment_start
|
||||
assert course_run.enrollment_end == enrollment_end
|
||||
|
||||
def assert_access_role(self, course_run, user, role):
|
||||
# An error will be raised if the endpoint did not create the role
|
||||
@@ -111,14 +107,12 @@ class CourseRunViewSetTests(ModuleStoreTestCase):
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_update(self):
|
||||
course_run = CourseFactory(start=None, end=None, enrollment_start=None, enrollment_end=None)
|
||||
course_run = CourseFactory(start=None, end=None)
|
||||
assert CourseAccessRole.objects.filter(course_id=course_run.id).count() == 0
|
||||
|
||||
url = reverse('api:v1:course_run-detail', kwargs={'pk': str(course_run.id)})
|
||||
start = datetime.datetime.now(pytz.UTC).replace(microsecond=0)
|
||||
end = start + datetime.timedelta(days=30)
|
||||
enrollment_start = start - datetime.timedelta(days=7)
|
||||
enrollment_end = end - datetime.timedelta(days=14)
|
||||
title = 'A New Testing Strategy'
|
||||
user = UserFactory()
|
||||
role = 'staff'
|
||||
@@ -127,8 +121,6 @@ class CourseRunViewSetTests(ModuleStoreTestCase):
|
||||
'schedule': {
|
||||
'start': serialize_datetime(start),
|
||||
'end': serialize_datetime(end),
|
||||
'enrollment_start': serialize_datetime(enrollment_start),
|
||||
'enrollment_end': serialize_datetime(enrollment_end),
|
||||
},
|
||||
'team': [
|
||||
{
|
||||
@@ -145,7 +137,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase):
|
||||
course_run = modulestore().get_course(course_run.id)
|
||||
assert response.data == CourseRunSerializer(course_run, context=self.get_serializer_context()).data
|
||||
assert course_run.display_name == title
|
||||
self.assert_course_run_schedule(course_run, start, end, enrollment_start, enrollment_end)
|
||||
self.assert_course_run_schedule(course_run, start, end)
|
||||
|
||||
def test_update_with_invalid_user(self):
|
||||
course_run = CourseFactory()
|
||||
@@ -168,7 +160,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase):
|
||||
Test that update run updates the pacing type
|
||||
"""
|
||||
start = datetime.datetime.now(pytz.UTC).replace(microsecond=0)
|
||||
course_run = CourseFactory(start=start, end=None, enrollment_start=None, enrollment_end=None, self_paced=False)
|
||||
course_run = CourseFactory(start=start, end=None, self_paced=False)
|
||||
data = {
|
||||
'pacing_type': 'self_paced',
|
||||
}
|
||||
@@ -178,7 +170,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase):
|
||||
|
||||
course_run = modulestore().get_course(course_run.id)
|
||||
assert course_run.self_paced is True
|
||||
self.assert_course_run_schedule(course_run, start, None, None, None)
|
||||
self.assert_course_run_schedule(course_run, start, None)
|
||||
|
||||
def test_update_with_instructor_role(self):
|
||||
"""
|
||||
@@ -187,7 +179,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase):
|
||||
instructor_role = 'instructor'
|
||||
start = datetime.datetime.now(pytz.UTC).replace(microsecond=0)
|
||||
new_user = UserFactory()
|
||||
course_run = CourseFactory(start=start, end=None, enrollment_start=None, enrollment_end=None, self_paced=False)
|
||||
course_run = CourseFactory(start=start, end=None, self_paced=False)
|
||||
assert CourseAccessRole.objects.filter(course_id=course_run.id).count() == 0
|
||||
data = {
|
||||
'team': [
|
||||
@@ -217,7 +209,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase):
|
||||
staff_role = 'staff'
|
||||
instructor_role = 'instructor'
|
||||
start = datetime.datetime.now(pytz.UTC).replace(microsecond=0)
|
||||
course_run = CourseFactory(start=start, end=None, enrollment_start=None, enrollment_end=None, self_paced=False)
|
||||
course_run = CourseFactory(start=start, end=None, self_paced=False)
|
||||
|
||||
existing_user = UserFactory()
|
||||
CourseAccessRole.objects.create(
|
||||
@@ -259,10 +251,8 @@ class CourseRunViewSetTests(ModuleStoreTestCase):
|
||||
user = UserFactory()
|
||||
start = datetime.datetime.now(pytz.UTC).replace(microsecond=0)
|
||||
end = start + datetime.timedelta(days=30)
|
||||
enrollment_start = start - datetime.timedelta(days=7)
|
||||
enrollment_end = end - datetime.timedelta(days=14)
|
||||
role = 'staff'
|
||||
data = self.get_course_run_data(user, start, end, enrollment_start, enrollment_end, pacing_type, role)
|
||||
data = self.get_course_run_data(user, start, end, pacing_type, role)
|
||||
|
||||
response = self.client.post(self.list_url, data, format='json')
|
||||
self.assertEqual(response.status_code, 201)
|
||||
@@ -274,7 +264,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase):
|
||||
self.assertEqual(course_run.id.course, data['number'])
|
||||
self.assertEqual(course_run.id.run, data['run'])
|
||||
self.assertEqual(course_run.self_paced, expected_self_paced_value)
|
||||
self.assert_course_run_schedule(course_run, start, end, enrollment_start, enrollment_end)
|
||||
self.assert_course_run_schedule(course_run, start, end)
|
||||
self.assert_access_role(course_run, user, role)
|
||||
self.assert_course_access_role_count(course_run, 1)
|
||||
|
||||
@@ -286,9 +276,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase):
|
||||
user = UserFactory()
|
||||
start = datetime.datetime.now(pytz.UTC).replace(microsecond=0)
|
||||
end = start + datetime.timedelta(days=30)
|
||||
enrollment_start = start - datetime.timedelta(days=7)
|
||||
enrollment_end = end - datetime.timedelta(days=14)
|
||||
data = self.get_course_run_data(user, start, end, enrollment_start, enrollment_end, 'self-paced')
|
||||
data = self.get_course_run_data(user, start, end, 'self-paced')
|
||||
data['team'] = [{'user': 'invalid-username'}]
|
||||
response = self.client.post(self.list_url, data, format='json')
|
||||
self.assertEqual(response.status_code, 400)
|
||||
@@ -345,8 +333,6 @@ class CourseRunViewSetTests(ModuleStoreTestCase):
|
||||
'schedule': {
|
||||
'start': serialize_datetime(start),
|
||||
'end': serialize_datetime(end),
|
||||
'enrollment_start': None,
|
||||
'enrollment_end': None,
|
||||
},
|
||||
'team': [
|
||||
{
|
||||
@@ -363,7 +349,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase):
|
||||
course_run = modulestore().get_course(course_run_key)
|
||||
assert course_run.id.run == run
|
||||
assert course_run.self_paced is expected_self_paced_value
|
||||
self.assert_course_run_schedule(course_run, start, end, None, None)
|
||||
self.assert_course_run_schedule(course_run, start, end)
|
||||
self.assert_access_role(course_run, user, role)
|
||||
self.assert_course_access_role_count(course_run, 1)
|
||||
|
||||
|
||||
24
cms/static/js/features_jsx/studio/NoTextbooks.jsx
Normal file
24
cms/static/js/features_jsx/studio/NoTextbooks.jsx
Normal file
@@ -0,0 +1,24 @@
|
||||
/* global gettext */
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
export class NoTextbooks extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="no-textbook-content">
|
||||
<p>
|
||||
{gettext("You haven't added any textbooks to this course yet.")}
|
||||
<a href="#" className="button new-button">
|
||||
<span className="icon fa fa-plus" aria-hidden="true"></span>
|
||||
{gettext("Add your first textbook")}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
NoTextbooks.propTypes = {
|
||||
|
||||
};
|
||||
6
cms/static/js/pages/course.js
Normal file
6
cms/static/js/pages/course.js
Normal file
@@ -0,0 +1,6 @@
|
||||
define(
|
||||
['js/models/course'],
|
||||
function(ContextCourse) {
|
||||
window.course = new ContextCourse(window.pageFactoryArguments.ContextCourse[0]);
|
||||
}
|
||||
);
|
||||
7
cms/static/js/pages/textbooks.js
Normal file
7
cms/static/js/pages/textbooks.js
Normal file
@@ -0,0 +1,7 @@
|
||||
define(
|
||||
['js/factories/textbooks', 'common/js/utils/page_factory', 'js/pages/course'],
|
||||
function(TextbooksFactory, invokePageFactory) {
|
||||
'use strict';
|
||||
invokePageFactory('TextbooksFactory', TextbooksFactory);
|
||||
}
|
||||
);
|
||||
@@ -123,19 +123,29 @@ from openedx.core.djangolib.markup import HTML
|
||||
<%block name="jsextra"></%block>
|
||||
<%block name="page_bundle">
|
||||
<script type="text/javascript">
|
||||
if (typeof window.pageFactoryArguments == "undefined") {
|
||||
window.pageFactoryArguments = {};
|
||||
}
|
||||
% if context_course:
|
||||
window.pageFactoryArguments['ContextCourse'] = {
|
||||
id: "${context_course.id | n, js_escaped_string}",
|
||||
name: "${context_course.display_name_with_default | n, js_escaped_string}",
|
||||
url_name: "${context_course.location.block_id | n, js_escaped_string}",
|
||||
org: "${context_course.location.org | n, js_escaped_string}",
|
||||
num: "${context_course.location.course | n, js_escaped_string}",
|
||||
display_course_number: "${context_course.display_coursenumber | n, js_escaped_string}",
|
||||
revision: "${context_course.location.branch | n, js_escaped_string}",
|
||||
self_paced: ${ context_course.self_paced | n, dump_js_escaped_json }
|
||||
}
|
||||
% endif
|
||||
|
||||
|
||||
require(['js/factories/base'], function () {
|
||||
require(['js/models/course'], function(Course) {
|
||||
% if context_course:
|
||||
window.course = new Course({
|
||||
id: "${context_course.id | n, js_escaped_string}",
|
||||
name: "${context_course.display_name_with_default | n, js_escaped_string}",
|
||||
url_name: "${context_course.location.block_id | n, js_escaped_string}",
|
||||
org: "${context_course.location.org | n, js_escaped_string}",
|
||||
num: "${context_course.location.course | n, js_escaped_string}",
|
||||
display_course_number: "${context_course.display_coursenumber | n, js_escaped_string}",
|
||||
revision: "${context_course.location.branch | n, js_escaped_string}",
|
||||
self_paced: ${context_course.self_paced | n, dump_js_escaped_json}
|
||||
});
|
||||
window.course = new Course(
|
||||
window.pageFactoryArguments['ContextCourse']
|
||||
);
|
||||
% endif
|
||||
% if user.is_authenticated():
|
||||
require(['js/sock']);
|
||||
@@ -145,6 +155,7 @@ from openedx.core.djangolib.markup import HTML
|
||||
});
|
||||
</script>
|
||||
</%block>
|
||||
<%block name="page_bundle_extra"></%block>
|
||||
<%include file="widgets/segment-io-footer.html" />
|
||||
<div class="modal-cover"></div>
|
||||
</body>
|
||||
|
||||
@@ -26,10 +26,11 @@ CMS.URL.TEXTBOOKS = "${textbook_url}"
|
||||
CMS.URL.LMS_BASE = "${settings.LMS_BASE}"
|
||||
</script>
|
||||
</%block>
|
||||
<%block name="requirejs">
|
||||
require(["js/factories/textbooks"], function(TextbooksFactory) {
|
||||
TextbooksFactory(${textbooks | n, dump_js_escaped_json});
|
||||
});
|
||||
|
||||
<%block name="page_bundle_extra">
|
||||
<%static:invoke_page_bundle page_name="js/pages/textbooks" class_name="TextbooksFactory">
|
||||
${textbooks | n, dump_js_escaped_json}
|
||||
</%static:invoke_page_bundle>
|
||||
</%block>
|
||||
|
||||
<%block name="content">
|
||||
@@ -54,7 +55,10 @@ CMS.URL.LMS_BASE = "${settings.LMS_BASE}"
|
||||
<div class="wrapper-content wrapper">
|
||||
<section class="content">
|
||||
<article class="content-primary" role="main">
|
||||
|
||||
${static.renderReact(
|
||||
component="NoTextbooks",
|
||||
id="no-textbooks-container"
|
||||
)}
|
||||
</article>
|
||||
<aside class="content-supplementary" role="complementary">
|
||||
<div class="bit">
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
(function(){var v=this,g,h,w,m,r,s,z,o,A,B;"undefined"===typeof window?(g=require("underscore"),h=require("backbone"),"undefined"!==typeof exports&&(exports=module.exports=h)):(g=v._,h=v.Backbone);w=h.Model;m=h.Collection;r=w.prototype;s=m.prototype;A=/[\.\[\]]+/g;z="change add remove reset sort destroy".split(" ");B=["reset","sort"];h.Associations={VERSION:"0.5.0"};h.Associations.Many=h.Many="Many";h.Associations.One=h.One="One";o=h.AssociatedModel=h.Associations.AssociatedModel=w.extend({relations:void 0,
|
||||
_proxyCalls:void 0,get:function(a){var c=r.get.call(this,a);return c?c:this._getAttr.apply(this,arguments)},set:function(a,c,d){var b;if(g.isObject(a)||a==null){b=a;d=c}else{b={};b[a]=c}a=this._set(b,d);this._processPendingEvents();return a},_set:function(a,c){var d,b,n,f,j=this;if(!a)return this;for(d in a){b||(b={});if(d.match(A)){var k=x(d);f=g.initial(k);k=k[k.length-1];f=this.get(f);if(f instanceof o){f=b[f.cid]||(b[f.cid]={model:f,data:{}});f.data[k]=a[d]}}else{f=b[this.cid]||(b[this.cid]={model:this,
|
||||
data:{}});f.data[d]=a[d]}}if(b)for(n in b){f=b[n];this._setAttr.call(f.model,f.data,c)||(j=false)}else j=this._setAttr.call(this,a,c);return j},_setAttr:function(a,c){var d;c||(c={});if(c.unset)for(d in a)a[d]=void 0;this.parents=this.parents||[];this.relations&&g.each(this.relations,function(b){var d=b.key,f=b.relatedModel,j=b.collectionType,k=b.map,i=this.attributes[d],y=i&&i.idAttribute,e,q,l,p;f&&g.isString(f)&&(f=t(f));j&&g.isString(j)&&(j=t(j));k&&g.isString(k)&&(k=t(k));q=b.options?g.extend({},
|
||||
b.options,c):c;if(a[d]){e=g.result(a,d);e=k?k(e):e;if(b.type===h.Many){if(j&&!j.prototype instanceof m)throw Error("collectionType must inherit from Backbone.Collection");if(e instanceof m)l=e;else if(i){i._deferEvents=true;i.set(e,c);l=i}else{l=j?new j:this._createCollection(f);l.add(e,q)}}else if(b.type===h.One&&f)if(e instanceof o)l=e;else if(i)if(i&&e[y]&&i.get(y)===e[y]){i._deferEvents=true;i._set(e,c);l=i}else l=new f(e,q);else l=new f(e,q);if((p=a[d]=l)&&!p._proxyCallback){p._proxyCallback=
|
||||
function(){return this._bubbleEvent.call(this,d,p,arguments)};p.on("all",p._proxyCallback,this)}}if(a.hasOwnProperty(d)){b=a[d];f=this.attributes[d];if(b){b.parents=b.parents||[];g.indexOf(b.parents,this)==-1&&b.parents.push(this)}else if(f&&f.parents.length>0)f.parents=g.difference(f.parents,[this])}},this);return r.set.call(this,a,c)},_bubbleEvent:function(a,c,d){var b=d[0].split(":"),n=b[0],f=d[0]=="nested-change",j=d[1],k=d[2],i=-1,h=c._proxyCalls,e,q=g.indexOf(z,n)!==-1;if(!f){g.size(b)>1&&(e=
|
||||
b[1]);g.indexOf(B,n)!==-1&&(k=j);if(c instanceof m&&q&&j){var l=x(e),p=g.initial(l);(b=c.find(function(a){if(j===a)return true;if(!a)return false;var b=a.get(p);if((b instanceof o||b instanceof m)&&j===b)return true;b=a.get(l);if((b instanceof o||b instanceof m)&&j===b||b instanceof m&&k&&k===b)return true}))&&(i=c.indexOf(b))}e=a+(i!==-1&&(n==="change"||e)?"["+i+"]":"")+(e?"."+e:"");if(/\[\*\]/g.test(e))return this;b=e.replace(/\[\d+\]/g,"[*]");i=[];i.push.apply(i,d);i[0]=n+":"+e;h=c._proxyCalls=
|
||||
h||{};if(this._isEventAvailable.call(this,h,e))return this;h[e]=true;if("change"===n){this._previousAttributes[a]=c._previousAttributes;this.changed[a]=c}this.trigger.apply(this,i);"change"===n&&this.get(e)!=d[2]&&this.trigger.apply(this,["nested-change",e,d[1]]);h&&e&&delete h[e];if(e!==b){i[0]=n+":"+b;this.trigger.apply(this,i)}return this}},_isEventAvailable:function(a,c){return g.find(a,function(a,b){return c.indexOf(b,c.length-b.length)!==-1})},_createCollection:function(a){var c=a;g.isString(c)&&
|
||||
(c=t(c));if(c&&c.prototype instanceof o){a=new m;a.model=c}else throw Error("type must inherit from Backbone.AssociatedModel");return a},_processPendingEvents:function(){if(!this.visited){this.visited=true;this._deferEvents=false;g.each(this._pendingEvents,function(a){a.c.trigger.apply(a.c,a.a)});this._pendingEvents=[];g.each(this.relations,function(a){(a=this.attributes[a.key])&&a._processPendingEvents()},this);delete this.visited}},trigger:function(a){if(this._deferEvents){this._pendingEvents=this._pendingEvents||
|
||||
[];this._pendingEvents.push({c:this,a:arguments})}else r.trigger.apply(this,arguments)},toJSON:function(a){var c,d;if(!this.visited){this.visited=true;c=r.toJSON.apply(this,arguments);this.relations&&g.each(this.relations,function(b){var h=this.attributes[b.key];if(h){d=h.toJSON(a);c[b.key]=g.isArray(d)?g.compact(d):d}},this);delete this.visited}return c},clone:function(){return new this.constructor(this.toJSON())},_getAttr:function(a){var c=this,a=x(a),d,b;if(!(g.size(a)<1)){for(b=0;b<a.length;b++){d=
|
||||
a[b];if(!c)break;c=c instanceof m?isNaN(d)?void 0:c.at(d):c.attributes[d]}return c}}});var C=/[^\.\[\]]+/g,x=function(a){return a===""?[""]:g.isString(a)?a.match(C):a||[]},t=function(a){return g.reduce(a.split("."),function(a,d){return a[d]},v)},D=function(a,c,d){var b;g.find(a,function(a){if(b=g.find(a.relations,function(b){return a.get(b.key)===c},this))return true},this);return b&&b.map?b.map(d):d},u={};g.each(["set","remove","reset"],function(a){u[a]=m.prototype[a];s[a]=function(c,d){this.model.prototype instanceof
|
||||
o&&this.parents&&(arguments[0]=D(this.parents,this,c));return u[a].apply(this,arguments)}});u.trigger=s.trigger;s.trigger=function(a){if(this._deferEvents){this._pendingEvents=this._pendingEvents||[];this._pendingEvents.push({c:this,a:arguments})}else u.trigger.apply(this,arguments)};s._processPendingEvents=o.prototype._processPendingEvents}).call(this);
|
||||
(function(q,f){if("function"===typeof define&&define.amd)define(["underscore","backbone"],function(g,i){return f(q,i,g)});else if("undefined"!==typeof exports){var g=require("underscore"),i=require("backbone");f(q,i,g);"undefined"!==typeof module&&module.exports&&(module.exports=i);exports=i}else f(q,q.Backbone,q._)})(this,function(q,f,g){var i,p,t,w,n,v,D,E,k,z,F,s={};i=f.Model;p=f.Collection;t=i.prototype;n=p.prototype;w=f.Events;f.Associations={VERSION:"0.6.2"};f.Associations.scopes=[];var G=function(){return k},
|
||||
A=function(a){if(!g.isString(a)||1>g.size(a))a=".";k=a;D=RegExp("[\\"+k+"\\[\\]]+","g");E=RegExp("[^\\"+k+"\\[\\]]+","g")};try{Object.defineProperty(f.Associations,"SEPARATOR",{enumerable:!0,get:G,set:A})}catch(J){}f.Associations.Many=f.Many="Many";f.Associations.One=f.One="One";f.Associations.Self=f.Self="Self";f.Associations.SEPARATOR=".";f.Associations.getSeparator=G;f.Associations.setSeparator=A;f.Associations.EVENTS_BUBBLE=!0;f.Associations.EVENTS_WILDCARD=!0;f.Associations.EVENTS_NC=!1;A();
|
||||
v=f.AssociatedModel=f.Associations.AssociatedModel=i.extend({relations:void 0,_proxyCalls:void 0,constructor:function(a,c){c&&c.__parents__&&(this.parents=[c.__parents__]);i.apply(this,arguments)},on:function(a,c,d){var b=w.on.apply(this,arguments);if(f.Associations.EVENTS_NC)return b;var l=/\s+/;g.isString(a)&&a&&!l.test(a)&&c&&(l=B(a))&&(s[l]="undefined"===typeof s[l]?1:s[l]+1);return b},off:function(a,c,d){if(f.Associations.EVENTS_NC)return w.off.apply(this,arguments);var b=/\s+/,l=this._events,
|
||||
e={},h=l?g.keys(l):[],m=!a&&!c&&!d,i=g.isString(a)&&!b.test(a);if(m||i)for(var b=0,j=h.length;b<j;b++)e[h[b]]=l[h[b]]?l[h[b]].length:0;var p=w.off.apply(this,arguments);if(m||i){b=0;for(j=h.length;b<j;b++)(m=B(h[b]))&&(s[m]=l[h[b]]?s[m]-(e[h[b]]-l[h[b]].length):s[m]-e[h[b]])}return p},get:function(a){var c=this.__attributes__,d=t.get.call(this,a),c=c?x(d)?d:c[a]:d;return x(c)?c:this._getAttr.apply(this,arguments)},set:function(a,c,d){var b;g.isObject(a)||null==a?(b=a,d=c):(b={},b[a]=c);a=this._set(b,
|
||||
d);this._processPendingEvents();return a},_set:function(a,c){var d,b,l,e,h=this;if(!a)return this;this.__attributes__=a;for(d in a)if(b||(b={}),d.match(D)){var f=H(d);e=g.initial(f);f=f[f.length-1];e=this.get(e);e instanceof i&&(e=b[e.cid]||(b[e.cid]={model:e,data:{}}),e.data[f]=a[d])}else e=b[this.cid]||(b[this.cid]={model:this,data:{}}),e.data[d]=a[d];if(b)for(l in b)e=b[l],this._setAttr.call(e.model,e.data,c)||(h=!1);else h=this._setAttr.call(this,a,c);delete this.__attributes__;return h},_setAttr:function(a,
|
||||
c){var d;c||(c={});if(c.unset)for(d in a)a[d]=void 0;this.parents=this.parents||[];this.relations&&g.each(this.relations,function(b){var d=b.key,e=b.scope||q,h=this._transformRelatedModel(b,a),m=this._transformCollectionType(b,h,a),u=g.isString(b.map)?C(b.map,e):b.map,j=this.attributes[d],k=j&&j.idAttribute,o,r,n=!1;o=b.options?g.extend({},b.options,c):c;if(a[d]){e=g.result(a,d);e=u?u.call(this,e,m?m:h):e;if(x(e))if(b.type===f.Many)j?(j._deferEvents=!0,j[o.reset?"reset":"set"](e instanceof p?e.models:
|
||||
e,o),h=j):(n=!0,e instanceof p?h=e:(h=this._createCollection(m||p,b.collectionOptions||(h?{model:h}:{})),h[o.reset?"reset":"set"](e,o)));else if(b.type===f.One)b=e instanceof i?e.attributes.hasOwnProperty(k):e.hasOwnProperty(k),m=e instanceof i?e.attributes[k]:e[k],j&&b&&j.attributes[k]===m?(j._deferEvents=!0,j._set(e instanceof i?e.attributes:e,o),h=j):(n=!0,e instanceof i?h=e:(o.__parents__=this,h=new h(e,o),delete o.__parents__));else throw Error("type attribute must be specified and have the values Backbone.One or Backbone.Many");
|
||||
else h=e;r=a[d]=h;if(n||r&&!r._proxyCallback)r._proxyCallback||(r._proxyCallback=function(){return f.Associations.EVENTS_BUBBLE&&this._bubbleEvent.call(this,d,r,arguments)}),r.on("all",r._proxyCallback,this)}a.hasOwnProperty(d)&&this._setupParents(a[d],this.attributes[d])},this);return t.set.call(this,a,c)},_bubbleEvent:function(a,c,d){var b=d[0].split(":"),g=b[0],e="nested-change"==d[0],h="change"===g,m=d[1],u=-1,j=c._proxyCalls,b=b[1],n=!b||-1==b.indexOf(k),o;if(!e&&(n&&(F=B(d[0])||a),f.Associations.EVENTS_NC||
|
||||
s[F])){if(f.Associations.EVENTS_WILDCARD&&/\[\*\]/g.test(b))return this;if(c instanceof p&&(h||b))u=c.indexOf(z||m);this instanceof i&&(z=this);b=a+(-1!==u&&(h||b)?"["+u+"]":"")+(b?k+b:"");f.Associations.EVENTS_WILDCARD&&(o=b.replace(/\[\d+\]/g,"[*]"));e=[];e.push.apply(e,d);e[0]=g+":"+b;f.Associations.EVENTS_WILDCARD&&b!==o&&(e[0]=e[0]+" "+g+":"+o);j=c._proxyCalls=j||{};if(this._isEventAvailable.call(this,j,b))return this;j[b]=!0;h&&(this._previousAttributes[a]=c._previousAttributes,this.changed[a]=
|
||||
c);this.trigger.apply(this,e);f.Associations.EVENTS_NC&&(h&&this.get(b)!=d[2])&&(a=["nested-change",b,d[1]],d[2]&&a.push(d[2]),this.trigger.apply(this,a));j&&b&&delete j[b];z=void 0;return this}},_isEventAvailable:function(a,c){return g.find(a,function(a,b){return-1!==c.indexOf(b,c.length-b.length)})},_setupParents:function(a,c){a&&(a.parents=a.parents||[],-1==g.indexOf(a.parents,this)&&a.parents.push(this));c&&(0<c.parents.length&&c!=a)&&(c.parents=g.difference(c.parents,[this]),c._proxyCallback&&
|
||||
c.off("all",c._proxyCallback,this))},_createCollection:function(a,c){var c=g.defaults(c,{model:a.model}),d=new a([],g.isFunction(c)?c.call(this):c);d.parents=[this];return d},_processPendingEvents:function(){this._processedEvents||(this._processedEvents=!0,this._deferEvents=!1,g.each(this._pendingEvents,function(a){a.c.trigger.apply(a.c,a.a)}),this._pendingEvents=[],g.each(this.relations,function(a){(a=this.attributes[a.key])&&a._processPendingEvents&&a._processPendingEvents()},this),delete this._processedEvents)},
|
||||
_transformRelatedModel:function(a,c){var d=a.relatedModel,b=a.scope||q;d&&!(d.prototype instanceof i)&&(d=g.isFunction(d)?d.call(this,a,c):d);d&&g.isString(d)&&(d=d===f.Self?this.constructor:C(d,b));if(a.type===f.One){if(!d)throw Error("specify a relatedModel for Backbone.One type");if(!(d.prototype instanceof f.Model))throw Error("specify an AssociatedModel or Backbone.Model for Backbone.One type");}return d},_transformCollectionType:function(a,c,d){var b=a.collectionType,l=a.scope||q;if(b&&g.isFunction(b)&&
|
||||
b.prototype instanceof i)throw Error("type is of Backbone.Model. Specify derivatives of Backbone.Collection");b&&!(b.prototype instanceof p)&&(b=g.isFunction(b)?b.call(this,a,d):b);b&&g.isString(b)&&(b=C(b,l));if(b&&!b.prototype instanceof p)throw Error("collectionType must inherit from Backbone.Collection");if(a.type===f.Many&&!c&&!b)throw Error("specify either a relatedModel or collectionType");return b},trigger:function(a){this._deferEvents?(this._pendingEvents=this._pendingEvents||[],this._pendingEvents.push({c:this,
|
||||
a:arguments})):t.trigger.apply(this,arguments)},toJSON:function(a){var c={},d;c[this.idAttribute]=this.id;this.visited||(this.visited=!0,c=t.toJSON.apply(this,arguments),a&&a.serialize_keys&&(c=g.pick(c,a.serialize_keys)),this.relations&&g.each(this.relations,function(b){var f=b.key,e=b.remoteKey,h=this.attributes[f],i=!b.isTransient,b=b.serialize||[],k=g.clone(a);delete c[f];i&&(b.length&&(k?k.serialize_keys=b:k={serialize_keys:b}),d=h&&h.toJSON?h.toJSON(k):h,c[e||f]=g.isArray(d)?g.compact(d):d)},
|
||||
this),delete this.visited);return c},clone:function(a){return new this.constructor(this.toJSON(a))},cleanup:function(a){a=a||{};g.each(this.relations,function(a){if(a=this.attributes[a.key])a._proxyCallback&&a.off("all",a._proxyCallback,this),a.parents=g.difference(a.parents,[this])},this);!a.listen&&this.off()},destroy:function(a){var a=a?g.clone(a):{},a=g.defaults(a,{remove_references:!0,listen:!0}),c=this;if(a.remove_references&&a.wait){var d=a.success;a.success=function(b){d&&d(c,b,a);c.cleanup(a)}}var b=
|
||||
t.destroy.apply(this,[a]);a.remove_references&&!a.wait&&c.cleanup(a);return b},_getAttr:function(a){var c=this,d=this.__attributes__,a=H(a),b,f;if(!(1>g.size(a))){for(f=0;f<a.length;f++){b=a[f];if(!c)break;c=c instanceof p?isNaN(b)?void 0:c.at(b):d?x(c.attributes[b])?c.attributes[b]:d[b]:c.attributes[b]}return c}}});var H=function(a){return""===a?[""]:g.isString(a)?a.match(E):a||[]},B=function(a){if(!a)return a;a=a.split(":");return 1<a.length?(a=a[a.length-1],a=a.split(k),1<a.length?a[a.length-1].split("[")[0]:
|
||||
a[0].split("[")[0]):""},C=function(a,c){var d,b=[c];b.push.apply(b,f.Associations.scopes);for(var i,e=0,h=b.length;e<h;++e)if(i=b[e])if(d=g.reduce(a.split(k),function(a,b){return a[b]},i))break;return d},I=function(a,c,d){var b,f;g.find(a,function(a){if(b=g.find(a.relations,function(b){return a.get(b.key)===c},this))return f=a,!0},this);return b&&b.map?b.map.call(f,d,c):d},x=function(a){return!g.isUndefined(a)&&!g.isNull(a)},y={};g.each(["set","remove","reset"],function(a){y[a]=p.prototype[a];n[a]=
|
||||
function(c,d){this.model.prototype instanceof v&&this.parents&&(arguments[0]=I(this.parents,this,c));return y[a].apply(this,arguments)}});y.trigger=n.trigger;n.trigger=function(a){this._deferEvents?(this._pendingEvents=this._pendingEvents||[],this._pendingEvents.push({c:this,a:arguments})):y.trigger.apply(this,arguments)};n._processPendingEvents=v.prototype._processPendingEvents;n.on=v.prototype.on;n.off=v.prototype.off;return f});
|
||||
@@ -25,6 +25,8 @@ module.exports = {
|
||||
Import: './cms/static/js/features/import/factories/import.js',
|
||||
CourseOrLibraryListing: './cms/static/js/features_jsx/studio/CourseOrLibraryListing.jsx',
|
||||
'js/pages/login': './cms/static/js/pages/login.js',
|
||||
'js/pages/textbooks': './cms/static/js/pages/textbooks.js',
|
||||
NoTextbooks: './cms/static/js/features_jsx/studio/NoTextbooks.jsx',
|
||||
|
||||
// LMS
|
||||
SingleSupportForm: './lms/static/support/jsx/single_support_form.jsx',
|
||||
@@ -171,6 +173,8 @@ module.exports = {
|
||||
'edx-ui-toolkit': 'edx-ui-toolkit/src/', // @TODO: some paths in toolkit are not valid relative paths
|
||||
'jquery.ui': 'jQuery-File-Upload/js/vendor/jquery.ui.widget.js',
|
||||
jquery: 'jquery/src/jquery', // Use the non-dist form of jQuery for better debugging + optimization
|
||||
// 'backbone': 'backbone',
|
||||
'backbone.associations': 'backbone-associations-min',
|
||||
|
||||
// See sinon/webpack interaction weirdness:
|
||||
// https://github.com/webpack/webpack/issues/304#issuecomment-272150177
|
||||
|
||||
Reference in New Issue
Block a user