Commit 6e91ac90 by Calen Pennington

Merge branch 'master' into cpennington/cms-github

parents 3baf4bcb e9d3f866
$fg-column: 70px;
$fg-gutter: 26px;
$fg-max-columns: 12;
$body-font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
$body-font-family: "Open Sans", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
$body-font-size: 14px;
$body-line-height: 20px;
$light-blue: #f0f8fa;
$dark-blue: #50545c;
$bright-blue: #3c8ebf;
$orange: #f96e5b;
$yellow: #fff8af;
// Base html styles
html {
height: 100%;
......@@ -13,23 +19,44 @@ html {
a {
text-decoration: none;
color: #888;
@include transition;
&:hover {
color: #ccc;
}
}
input {
font-family: $body-font-family;
}
input[type="submit"], .button {
border: 1px solid #ccc;
background: #efefef;
@include border-radius(3px);
padding: 6px;
button, input[type="submit"], .button {
background-color: $orange;
color: #fff;
-webkit-font-smoothing: antialiased;
padding: 8px 10px;
border: 0;
font-weight: bold;
&:hover {
background-color: shade($orange, 10%);
}
}
#{$all-text-inputs}, textarea {
border: 1px solid $dark-blue;
font: $body-font-size $body-font-family;
padding: 4px 6px;
@include box-shadow(inset 0 3px 6px $light-blue);
}
textarea {
@include box-sizing(border-box);
display: block;
line-height: lh();
padding: 15px;
width: 100%;
}
// Extends
......@@ -68,6 +95,15 @@ textarea {
float: right;
}
.editable {
&:hover {
background: lighten($yellow, 10%);
}
button {
padding: 4px 10px;
}
}
.wip {
outline: 1px solid #f00 !important;
position: relative;
......
......@@ -6,8 +6,12 @@ section.cal {
> header {
@include clearfix;
margin-bottom: 10px;
background: #efefef;
border: 1px solid #ddd;
opacity: .4;
@include transition;
&:hover {
opacity: 1;
}
h2 {
@include inline-block();
......@@ -15,7 +19,6 @@ section.cal {
letter-spacing: 1px;
font-size: 14px;
padding: 6px;
margin-left: 6px;
font-size: 12px;
}
......@@ -25,9 +28,8 @@ section.cal {
li {
@include inline-block;
margin-left: 6px;
padding-left: 6px;
border-left: 1px solid #ddd;
padding: 6px;
padding: 0 6px;
a {
@include inline-block();
......@@ -49,31 +51,35 @@ section.cal {
ol {
list-style: none;
@include clearfix;
border-left: 1px solid #333;
border-top: 1px solid #333;
border-left: 1px solid lighten($dark-blue, 40%);
border-top: 1px solid lighten($dark-blue, 40%);
width: 100%;
@include box-sizing(border-box);
> li {
border-right: 1px solid #333;
border-bottom: 1px solid;
border-right: 1px solid lighten($dark-blue, 40%);
border-bottom: 1px solid lighten($dark-blue, 40%);
@include box-sizing(border-box);
float: left;
width: flex-grid(3) + ((flex-gutter() * 3) / 4);
background-color: lighten($light-blue, 2%);
header {
border-bottom: 1px solid #000;
@include box-shadow(0 1px 2px #aaa);
border-bottom: 1px solid lighten($dark-blue, 40%);
@include box-shadow(0 2px 2px $light-blue);
display: block;
margin-bottom: 2px;
background: #FFF;
h1 {
font-size: 14px;
text-transform: uppercase;
border-bottom: 1px solid #ccc;
border-bottom: 1px solid lighten($dark-blue, 60%);
padding: 6px;
a {
color: #000;
color: $bright-blue;
display: block;
}
}
......@@ -84,6 +90,10 @@ section.cal {
color: #888;
border-bottom: 0;
font-size: 12px;
&:hover {
background: #fff;
}
}
}
}
......@@ -93,10 +103,17 @@ section.cal {
margin-bottom: 1px;
li {
background: #efefef;
border-bottom: 1px solid #666;
border-bottom: 1px solid darken($light-blue, 8%);
padding: 6px;
&:hover {
background: lighten($yellow, 10%);
}
a {
color: lighten($dark-blue, 10%);
}
&.create-module {
position: relative;
......
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 700;
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/k3k702ZOKiLJc3WVjuplzKRDOzjiPcYnFooOUGCOsRk.woff) format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
src: local('Open Sans Light'), local('OpenSans-Light'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/DXI1ORHCpsQm3Vp6mXoaTaRDOzjiPcYnFooOUGCOsRk.woff) format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 700;
src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/PRmiXeptR36kaC0GEAetxhbnBKKEOwRKgsHDreGcocg.woff) format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 300;
src: local('Open Sans Light Italic'), local('OpenSansLight-Italic'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/PRmiXeptR36kaC0GEAetxvR_54zmj3SbGZQh3vCOwvY.woff) format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 400;
src: local('Open Sans Italic'), local('OpenSans-Italic'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/xjAJXh38I15wypJXxuGMBrrIa-7acMAeDBVuclsi6Gc.woff) format('woff');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: local('Open Sans'), local('OpenSans'), url(http://themes.googleusercontent.com/static/fonts/opensans/v6/cJZKeOuBrn4kERxqtaUH3bO3LdcAZYWl9Si6vvxL-qU.woff) format('woff');
}
......@@ -9,13 +9,14 @@ body {
}
> header {
background: #000;
background: $dark-blue;
color: #fff;
display: block;
float: none;
padding: 6px 20px;
padding: 8px 25px;
width: 100%;
@include box-sizing(border-box);
-webkit-font-smoothing: antialiased;
nav {
@include clearfix;
......@@ -24,6 +25,23 @@ body {
font-size: 14px;
text-transform: uppercase;
float: left;
margin-right: 15px;
a {
color: #fff;
&:hover {
color: rgba(#fff, .6);
}
}
}
a {
color: rgba(#fff, .8);
&:hover {
color: rgba(#fff, .6);
}
}
ul {
......@@ -35,7 +53,16 @@ body {
li {
@include inline-block();
margin-left: 15px;
a {
padding: 8px 10px;
display: block;
margin: -8px 0;
&:hover {
background-color: darken($dark-blue, 15%);
}
}
}
}
}
......@@ -43,11 +70,11 @@ body {
&.content {
section.main-content {
border-left: 2px solid #000;
border-left: 2px solid $dark-blue;
@include box-sizing(border-box);
width: flex-grid(9);
float: left;
@include box-shadow( -2px 0 3px #ddd );
@include box-shadow( -2px 0 0 darken($light-blue, 3%));
}
}
}
......@@ -2,8 +2,12 @@ section#unit-wrapper {
section.filters {
@include clearfix;
margin-bottom: 10px;
background: #efefef;
border: 1px solid #ddd;
opacity: .4;
@include transition;
&:hover {
opacity: 1;
}
ul {
@include clearfix();
......@@ -22,14 +26,16 @@ section#unit-wrapper {
div.content {
display: table;
border: 1px solid;
border: 1px solid lighten($dark-blue, 40%);
width: 100%;
section {
header {
background: #eee;
background: #fff;
padding: 6px;
border-bottom: 1px solid #ccc;
border-bottom: 1px solid lighten($dark-blue, 60%);
border-top: 1px solid lighten($dark-blue, 60%);
margin-top: -1px;
@include clearfix;
h2 {
......@@ -37,6 +43,7 @@ section#unit-wrapper {
letter-spacing: 1px;
font-size: 12px;
float: left;
color: $bright-blue;
}
}
......@@ -44,7 +51,7 @@ section#unit-wrapper {
@include box-sizing(border-box);
display: table-cell;
width: flex-grid(6, 9);
border-right: 1px solid #333;
border-right: 1px solid lighten($dark-blue, 40%);
&.empty {
text-align: center;
......@@ -59,14 +66,9 @@ section#unit-wrapper {
ol {
list-style: none;
border-bottom: 1px solid #333;
li {
border-bottom: 1px solid #333;
&:last-child{
border-bottom: 0;
}
border-bottom: 1px solid lighten($dark-blue, 60%);
a {
color: #000;
......@@ -78,6 +80,10 @@ section#unit-wrapper {
li {
padding: 6px;
&:last-child {
border-bottom: 0;
}
&:hover {
a.draggable {
opacity: 1;
......@@ -127,11 +133,10 @@ section#unit-wrapper {
ol {
list-style: none;
border-bottom: 1px solid #999;
li {
border-bottom: 1px solid #999;
background: #f9f9f9;
border-bottom: 1px solid darken($light-blue, 8%);
background: lighten($light-blue, 2%);
&:last-child {
border-bottom: 0;
......
section#unit-wrapper {
> header {
border-bottom: 2px solid #333;
border-bottom: 2px solid $dark-blue;
@include clearfix();
@include box-shadow( 0 2px 0 darken($light-blue, 3%));
padding: 6px 20px;
section {
......@@ -12,6 +13,7 @@ section#unit-wrapper {
text-transform: uppercase;
letter-spacing: 1px;
@include inline-block();
color: $bright-blue;
}
p {
......@@ -28,11 +30,19 @@ section#unit-wrapper {
color: #666;
a {
display: block;
@include inline-block;
&.cancel {
margin-right: 20px;
font-style: italic;
font-size: 12px;
}
&.save-update {
@extend .button;
margin: -6px -25px -6px 0;
}
}
}
}
......@@ -45,16 +55,16 @@ section#unit-wrapper {
&.status-settings {
float: left;
margin-bottom: 10px;
color: $dark-blue;
ul {
list-style: none;
@include border-radius(2px);
border: 1px solid #999;
border: 1px solid lighten($dark-blue, 40%);
@include inline-block();
li {
@include inline-block();
border-right: 1px solid #999;
border-right: 1px solid lighten($dark-blue, 40%);
padding: 6px;
&:last-child {
......@@ -64,14 +74,19 @@ section#unit-wrapper {
&.current {
background: #eee;
}
a {
color: $dark-blue;
}
}
}
a.settings {
@include inline-block();
margin: 0 20px;
border: 1px solid #999;
padding: 6px;
border: 1px solid lighten($dark-blue, 40%);
color: $dark-blue;
}
select {
......@@ -81,6 +96,7 @@ section#unit-wrapper {
&.author {
float: right;
color: lighten($dark-blue, 6%);
dl {
dt {
......@@ -94,7 +110,8 @@ section#unit-wrapper {
}
&.tags {
background: #eee;
background: $light-blue;
color: lighten($dark-blue, 6%);
padding: 10px;
margin: 0 0 20px;
@include clearfix();
......@@ -118,9 +135,9 @@ section#unit-wrapper {
}
//general styles for main content
div.preview {
background: #eee;
.preview {
@include box-sizing(border-box);
border: 1px solid lighten($dark-blue, 40%);
min-height: 40px;
padding: 10px;
width: 100%;
......@@ -153,13 +170,16 @@ section#unit-wrapper {
//notes
section.notes {
margin-top: 20px;
padding: 20px 0 0;
border-top: 1px solid #ccc;
margin-top: 40px;
padding: 40px 0 0;
border-top: 2px solid lighten( $dark-blue, 60% );
h2 {
font-size: 14px;
margin-bottom: 6px;
font-size: $body-font-size;
text-transform: uppercase;
color: $bright-blue;
letter-spacing: 1px;
}
form {
......@@ -194,6 +214,12 @@ section#unit-wrapper {
@include inline-block();
margin-top: 20px;
}
a.cancel {
float: right;
font-style: italic;
margin-top: 20px;
}
}
}
}
......@@ -15,7 +15,7 @@
<form>
<h2>Add a note</h2>
<textarea name="" id= rows="8" cols="40"></textarea>
<input type="submit" name="" id="" value="post" />
<input type="submit" name="" id="" value="Post" />
</form>
</section>
......@@ -12,8 +12,8 @@ describe 'Calculator', ->
it 'bind the help button', ->
# These events are bind by $.hover()
expect($('div.help-wrapper a')).toHandleWith 'mouseenter', @calculator.helpToggle
expect($('div.help-wrapper a')).toHandleWith 'mouseleave', @calculator.helpToggle
expect($('div.help-wrapper a')).toHandleWith 'mouseover', @calculator.helpToggle
expect($('div.help-wrapper a')).toHandleWith 'mouseout', @calculator.helpToggle
it 'prevent default behavior on help button', ->
$('div.help-wrapper a').click (e) ->
......
......@@ -34,7 +34,7 @@ describe 'Courseware', ->
<div class="course-content">
<div id="video_1" class="video" data-streams="1.0:abc1234"></div>
<div id="video_2" class="video" data-streams="1.0:def5678"></div>
<div id="problem_3" class="problems-wrapper" data-url="/example/url/">
<div id="problem_3" class="problems-wrapper" data-problem-id="3" data-url="/example/url/">
<div id="histogram_3" class="histogram" data-histogram="[[0, 1]]" style="height: 20px; display: block;">
</div>
</div>
......@@ -46,7 +46,7 @@ describe 'Courseware', ->
expect(window.Video).toHaveBeenCalledWith('2', '1.0:def5678')
it 'detect the problem element and convert it', ->
expect(window.Problem).toHaveBeenCalledWith('3', '/example/url/')
expect(window.Problem).toHaveBeenCalledWith(3, 'problem_3', '/example/url/')
it 'detect the histrogram element and convert it', ->
expect(window.Histogram).toHaveBeenCalledWith('3', [[0, 1]])
jasmine.getFixtures().fixturesPath = "/_jasmine/fixtures/"
jasmine.stubbedMetadata =
abc123:
id: 'abc123'
duration: 100
def456:
id: 'def456'
slowerSpeedYoutubeId:
id: 'slowerSpeedYoutubeId'
duration: 300
normalSpeedYoutubeId:
id: 'normalSpeedYoutubeId'
duration: 200
bogus:
duration: 300
duration: 100
jasmine.stubbedCaption =
start: [0, 10000, 20000, 30000]
......@@ -20,12 +20,12 @@ jasmine.stubRequests = ->
settings.success data: jasmine.stubbedMetadata[match[1]]
else if match = settings.url.match /static\/subs\/(.+)\.srt\.sjson/
settings.success jasmine.stubbedCaption
else if settings.url.match /modx\/problem\/.+\/problem_get$/
else if settings.url.match /modx\/.+\/problem_get$/
settings.success html: readFixtures('problem_content.html')
else if settings.url == '/calculate' ||
settings.url == '/6002x/modx/sequence/1/goto_position' ||
settings.url.match(/modx\/.+\/goto_position$/) ||
settings.url.match(/event$/) ||
settings.url.match(/modx\/problem\/.+\/problem_(check|reset|show|save)$/)
settings.url.match(/modx\/.+\/problem_(check|reset|show|save)$/)
# do nothing
else
throw "External request attempted for #{settings.url}, which is not defined."
......@@ -49,10 +49,10 @@ jasmine.stubVideoPlayer = (context, enableParts, createPlayer=true) ->
loadFixtures 'video.html'
jasmine.stubRequests()
YT.Player = undefined
context.video = new Video 'example', '.75:abc123,1.0:def456'
context.video = new Video 'example', '.75:slowerSpeedYoutubeId,1.0:normalSpeedYoutubeId'
jasmine.stubYoutubePlayer()
if createPlayer
return new VideoPlayer context.video
return new VideoPlayer(video: context.video)
spyOn(window, 'onunload')
......
......@@ -25,7 +25,7 @@ describe 'Logger', ->
it 'send a request to log event', ->
spyOn($, 'ajax')
$(window).trigger('onunload')
window.onunload()
expect($.ajax).toHaveBeenCalledWith
url: "#{Courseware.prefix}/event",
data:
......
......@@ -17,16 +17,16 @@ describe 'Problem', ->
describe 'constructor', ->
beforeEach ->
@problem = new Problem 1, '/problem/url/'
@problem = new Problem 1, "problem_1", "/problem/url/"
it 'set the element', ->
expect(@problem.element).toBe '#problem_1'
expect(@problem.el).toBe '#problem_1'
describe 'bind', ->
beforeEach ->
spyOn window, 'update_schematics'
MathJax.Hub.getAllJax.andReturn [@stubbedJax]
@problem = new Problem 1, '/problem/url/'
@problem = new Problem 1, "problem_1", "/problem/url/"
it 'set mathjax typeset', ->
expect(MathJax.Hub.Queue).toHaveBeenCalled()
......@@ -60,7 +60,7 @@ describe 'Problem', ->
describe 'render', ->
beforeEach ->
@problem = new Problem 1, '/problem/url/'
@problem = new Problem 1, "problem_1", "/problem/url/"
@bind = @problem.bind
spyOn @problem, 'bind'
......@@ -69,7 +69,7 @@ describe 'Problem', ->
@problem.render 'Hello World'
it 'render the content', ->
expect(@problem.element.html()).toEqual 'Hello World'
expect(@problem.el.html()).toEqual 'Hello World'
it 're-bind the content', ->
expect(@problem.bind).toHaveBeenCalled()
......@@ -81,14 +81,14 @@ describe 'Problem', ->
@problem.render()
it 'load the content via ajax', ->
expect(@problem.element.html()).toEqual 'Hello World'
expect(@problem.el.html()).toEqual 'Hello World'
it 're-bind the content', ->
expect(@problem.bind).toHaveBeenCalled()
describe 'check', ->
beforeEach ->
@problem = new Problem 1, '/problem/url/'
@problem = new Problem 1, "problem_1", "/problem/url/"
@problem.answers = 'foo=1&bar=2'
it 'log the problem_check event', ->
......@@ -98,19 +98,19 @@ describe 'Problem', ->
it 'submit the answer for check', ->
spyOn $, 'postWithPrefix'
@problem.check()
expect($.postWithPrefix).toHaveBeenCalledWith '/modx/problem/1/problem_check', 'foo=1&bar=2', jasmine.any(Function)
expect($.postWithPrefix).toHaveBeenCalledWith '/modx/1/problem_check', 'foo=1&bar=2', jasmine.any(Function)
describe 'when the response is correct', ->
it 'call render with returned content', ->
spyOn($, 'postWithPrefix').andCallFake (url, answers, callback) -> callback(success: 'correct', contents: 'Correct!')
@problem.check()
expect(@problem.element.html()).toEqual 'Correct!'
expect(@problem.el.html()).toEqual 'Correct!'
describe 'when the response is incorrect', ->
it 'call render with returned content', ->
spyOn($, 'postWithPrefix').andCallFake (url, answers, callback) -> callback(success: 'incorrect', contents: 'Correct!')
@problem.check()
expect(@problem.element.html()).toEqual 'Correct!'
expect(@problem.el.html()).toEqual 'Correct!'
describe 'when the response is undetermined', ->
it 'alert the response', ->
......@@ -121,7 +121,7 @@ describe 'Problem', ->
describe 'reset', ->
beforeEach ->
@problem = new Problem 1, '/problem/url/'
@problem = new Problem 1, "problem_1", "/problem/url/"
it 'log the problem_reset event', ->
@problem.answers = 'foo=1&bar=2'
......@@ -131,22 +131,22 @@ describe 'Problem', ->
it 'POST to the problem reset page', ->
spyOn $, 'postWithPrefix'
@problem.reset()
expect($.postWithPrefix).toHaveBeenCalledWith '/modx/problem/1/problem_reset', { id: 1 }, jasmine.any(Function)
expect($.postWithPrefix).toHaveBeenCalledWith '/modx/1/problem_reset', { id: 1 }, jasmine.any(Function)
it 'render the returned content', ->
spyOn($, 'postWithPrefix').andCallFake (url, answers, callback) ->
callback html: "Reset!"
@problem.reset()
expect(@problem.element.html()).toEqual 'Reset!'
expect(@problem.el.html()).toEqual 'Reset!'
describe 'show', ->
beforeEach ->
@problem = new Problem 1, '/problem/url/'
@problem.element.prepend '<div id="answer_1_1" /><div id="answer_1_2" />'
@problem = new Problem 1, "problem_1", "/problem/url/"
@problem.el.prepend '<div id="answer_1_1" /><div id="answer_1_2" />'
describe 'when the answer has not yet shown', ->
beforeEach ->
@problem.element.removeClass 'showed'
@problem.el.removeClass 'showed'
it 'log the problem_show event', ->
@problem.show()
......@@ -155,7 +155,7 @@ describe 'Problem', ->
it 'fetch the answers', ->
spyOn $, 'postWithPrefix'
@problem.show()
expect($.postWithPrefix).toHaveBeenCalledWith '/modx/problem/1/problem_show', jasmine.any(Function)
expect($.postWithPrefix).toHaveBeenCalledWith '/modx/1/problem_show', jasmine.any(Function)
it 'show the answers', ->
spyOn($, 'postWithPrefix').andCallFake (url, callback) ->
......@@ -172,11 +172,11 @@ describe 'Problem', ->
it 'add the showed class to element', ->
spyOn($, 'postWithPrefix').andCallFake (url, callback) -> callback(answers: {})
@problem.show()
expect(@problem.element).toHaveClass 'showed'
expect(@problem.el).toHaveClass 'showed'
describe 'multiple choice question', ->
beforeEach ->
@problem.element.prepend '''
@problem.el.prepend '''
<label for="input_1_1_1"><input type="checkbox" name="input_1_1" id="input_1_1_1" value="1"> One</label>
<label for="input_1_1_2"><input type="checkbox" name="input_1_1" id="input_1_1_2" value="2"> Two</label>
<label for="input_1_1_3"><input type="checkbox" name="input_1_1" id="input_1_1_3" value="3"> Three</label>
......@@ -194,8 +194,8 @@ describe 'Problem', ->
describe 'when the answers are alreay shown', ->
beforeEach ->
@problem.element.addClass 'showed'
@problem.element.prepend '''
@problem.el.addClass 'showed'
@problem.el.prepend '''
<label for="input_1_1_1" correct_answer="true">
<input type="checkbox" name="input_1_1" id="input_1_1_1" value="1" />
One
......@@ -216,11 +216,11 @@ describe 'Problem', ->
it 'remove the showed class from element', ->
@problem.show()
expect(@problem.element).not.toHaveClass 'showed'
expect(@problem.el).not.toHaveClass 'showed'
describe 'save', ->
beforeEach ->
@problem = new Problem 1, '/problem/url/'
@problem = new Problem 1, "problem_1", "/problem/url/"
@problem.answers = 'foo=1&bar=2'
it 'log the problem_save event', ->
......@@ -230,7 +230,7 @@ describe 'Problem', ->
it 'POST to save problem', ->
spyOn $, 'postWithPrefix'
@problem.save()
expect($.postWithPrefix).toHaveBeenCalledWith '/modx/problem/1/problem_save', 'foo=1&bar=2', jasmine.any(Function)
expect($.postWithPrefix).toHaveBeenCalledWith '/modx/1/problem_save', 'foo=1&bar=2', jasmine.any(Function)
it 'alert to the user', ->
spyOn window, 'alert'
......@@ -240,7 +240,7 @@ describe 'Problem', ->
describe 'refreshMath', ->
beforeEach ->
@problem = new Problem 1, '/problem/url/'
@problem = new Problem 1, "problem_1", "/problem/url/"
$('#input_example_1').val 'E=mc^2'
@problem.refreshMath target: $('#input_example_1').get(0)
......@@ -250,7 +250,7 @@ describe 'Problem', ->
describe 'updateMathML', ->
beforeEach ->
@problem = new Problem 1, '/problem/url/'
@problem = new Problem 1, "problem_1", "/problem/url/"
@stubbedJax.root.toMathML.andReturn '<MathML>'
describe 'when there is no exception', ->
......@@ -270,8 +270,8 @@ describe 'Problem', ->
describe 'refreshAnswers', ->
beforeEach ->
@problem = new Problem 1, '/problem/url/'
@problem.element.html '''
@problem = new Problem 1, "problem_1", "/problem/url/"
@problem.el.html '''
<textarea class="CodeMirror" />
<input id="input_1_1" name="input_1_1" class="schematic" value="one" />
<input id="input_1_2" name="input_1_2" value="two" />
......
......@@ -9,10 +9,10 @@ describe 'Sequence', ->
describe 'constructor', ->
beforeEach ->
@sequence = new Sequence '1', @items, 'sequence', 1
@sequence = new Sequence '1', 'sequence_1', @items, 'sequence', 1
it 'set the element', ->
expect(@sequence.element).toEqual $('#sequence_1')
expect(@sequence.el).toEqual $('#sequence_1')
it 'build the navigation', ->
classes = $('#sequence-list li>a').map(-> $(this).attr('class')).get()
......@@ -31,7 +31,7 @@ describe 'Sequence', ->
describe 'toggleArrows', ->
beforeEach ->
@sequence = new Sequence '1', @items, 'sequence', 1
@sequence = new Sequence '1', 'sequence_1', @items, 'sequence', 1
describe 'when the first tab is active', ->
beforeEach ->
......@@ -73,8 +73,8 @@ describe 'Sequence', ->
describe 'render', ->
beforeEach ->
spyOn $, 'postWithPrefix'
@sequence = new Sequence '1', @items, 'sequence'
spyOnEvent @sequence.element, 'contentChanged'
@sequence = new Sequence '1', 'sequence_1', @items, 'sequence'
spyOnEvent @sequence.el, 'contentChanged'
spyOn(@sequence, 'toggleArrows').andCallThrough()
describe 'with a different position than the current one', ->
......@@ -94,7 +94,7 @@ describe 'Sequence', ->
expect($('[data-element="2"]')).toHaveClass 'seq_video_visited'
it 'save the new position', ->
expect($.postWithPrefix).toHaveBeenCalledWith '/modx/sequence/1/goto_position', position: 1
expect($.postWithPrefix).toHaveBeenCalledWith '/modx/1/goto_position', position: 1
it 'mark new tab as active', ->
expect($('[data-element="1"]')).toHaveClass 'seq_video_active'
......@@ -109,18 +109,18 @@ describe 'Sequence', ->
expect(@sequence.toggleArrows).toHaveBeenCalled()
it 'trigger contentChanged event', ->
expect('contentChanged').toHaveBeenTriggeredOn @sequence.element
expect('contentChanged').toHaveBeenTriggeredOn @sequence.el
describe 'with the same position as the current one', ->
it 'should not trigger contentChanged event', ->
@sequence.position = 2
@sequence.render 2
expect('contentChanged').not.toHaveBeenTriggeredOn @sequence.element
expect('contentChanged').not.toHaveBeenTriggeredOn @sequence.el
describe 'goto', ->
beforeEach ->
jasmine.stubRequests()
@sequence = new Sequence '1', @items, 'sequence', 2
@sequence = new Sequence '1', 'sequence_1', @items, 'sequence', 2
$('[data-element="3"]').click()
it 'log the sequence goto event', ->
......@@ -132,7 +132,7 @@ describe 'Sequence', ->
describe 'next', ->
beforeEach ->
jasmine.stubRequests()
@sequence = new Sequence '1', @items, 'sequence', 2
@sequence = new Sequence '1', 'sequence_1', @items, 'sequence', 2
$('.sequence-nav-buttons .next a').click()
it 'log the next sequence event', ->
......@@ -144,7 +144,7 @@ describe 'Sequence', ->
describe 'previous', ->
beforeEach ->
jasmine.stubRequests()
@sequence = new Sequence '1', @items, 'sequence', 2
@sequence = new Sequence '1', 'sequence_1', @items, 'sequence', 2
$('.sequence-nav-buttons .prev a').click()
it 'log the previous sequence event', ->
......@@ -155,5 +155,5 @@ describe 'Sequence', ->
describe 'link_for', ->
it 'return a link for specific position', ->
sequence = new Sequence '1', @items, 2
sequence = new Sequence '1', 'sequence_1', @items, 2
expect(sequence.link_for(2)).toBe '[data-element="2"]'
......@@ -9,7 +9,7 @@ describe 'Tab', ->
@tab = new Tab 1, @items
it 'set the element', ->
expect(@tab.element).toEqual $('#tab_1')
expect(@tab.el).toEqual $('#tab_1')
it 'build the tabs', ->
links = $('.navigation li>a').map(-> $(this).attr('href')).get()
......@@ -34,6 +34,6 @@ describe 'Tab', ->
expect($('#tab-1-2').html()).toEqual ''
it 'trigger contentChanged event on the element', ->
spyOnEvent @tab.element, 'contentChanged'
spyOnEvent @tab.el, 'contentChanged'
$('[href="#tab-1-1"]').click()
expect('contentChanged').toHaveBeenTriggeredOn @tab.element
expect('contentChanged').toHaveBeenTriggeredOn @tab.el
describe 'VideoControl', ->
beforeEach ->
@player = jasmine.stubVideoPlayer @
jasmine.stubVideoPlayer @
$('.video-controls').html ''
describe 'constructor', ->
it 'render the video controls', ->
new VideoControl @player
new VideoControl(el: $('.video-controls'))
expect($('.video-controls').html()).toContain '''
<div class="slider"></div>
<div>
......@@ -21,14 +21,8 @@ describe 'VideoControl', ->
</div>
'''
it 'bind player events', ->
control = new VideoControl @player
expect($(@player)).toHandleWith 'play', control.onPlay
expect($(@player)).toHandleWith 'pause', control.onPause
expect($(@player)).toHandleWith 'ended', control.onPause
it 'bind the playback button', ->
control = new VideoControl @player
control = new VideoControl(el: $('.video-controls'))
expect($('.video_control')).toHandleWith 'click', control.togglePlayback
describe 'when on a touch based device', ->
......@@ -36,7 +30,7 @@ describe 'VideoControl', ->
spyOn(window, 'onTouchBasedDevice').andReturn true
it 'does not add the play class to video control', ->
new VideoControl @player
new VideoControl(el: $('.video-controls'))
expect($('.video_control')).not.toHaveClass 'play'
expect($('.video_control')).not.toHaveHtml 'Play'
......@@ -46,24 +40,24 @@ describe 'VideoControl', ->
spyOn(window, 'onTouchBasedDevice').andReturn false
it 'add the play class to video control', ->
new VideoControl @player
new VideoControl(el: $('.video-controls'))
expect($('.video_control')).toHaveClass 'play'
expect($('.video_control')).toHaveHtml 'Play'
describe 'onPlay', ->
describe 'play', ->
beforeEach ->
@control = new VideoControl @player
@control.onPlay()
@control = new VideoControl(el: $('.video-controls'))
@control.play()
it 'switch playback button to play state', ->
expect($('.video_control')).not.toHaveClass 'play'
expect($('.video_control')).toHaveClass 'pause'
expect($('.video_control')).toHaveHtml 'Pause'
describe 'onPause', ->
describe 'pause', ->
beforeEach ->
@control = new VideoControl @player
@control.onPause()
@control = new VideoControl(el: $('.video-controls'))
@control.pause()
it 'switch playback button to pause state', ->
expect($('.video_control')).not.toHaveClass 'pause'
......@@ -72,7 +66,7 @@ describe 'VideoControl', ->
describe 'togglePlayback', ->
beforeEach ->
@control = new VideoControl @player
@control = new VideoControl(el: $('.video-controls'))
describe 'when the control does not have play or pause class', ->
beforeEach ->
......@@ -80,41 +74,36 @@ describe 'VideoControl', ->
describe 'when the video is playing', ->
beforeEach ->
spyOn(@player, 'isPlaying').andReturn true
spyOnEvent @player, 'pause'
$('.video_control').addClass('play')
spyOnEvent @control, 'pause'
@control.togglePlayback jQuery.Event('click')
it 'does not trigger the pause event', ->
expect('pause').not.toHaveBeenTriggeredOn @player
expect('pause').not.toHaveBeenTriggeredOn @control
describe 'when the video is paused', ->
beforeEach ->
spyOn(@player, 'isPlaying').andReturn false
spyOnEvent @player, 'play'
$('.video_control').addClass('pause')
spyOnEvent @control, 'play'
@control.togglePlayback jQuery.Event('click')
it 'does not trigger the play event', ->
expect('play').not.toHaveBeenTriggeredOn @player
expect('play').not.toHaveBeenTriggeredOn @control
for className in ['play', 'pause']
describe "when the control has #{className} class", ->
describe 'when the video is playing', ->
beforeEach ->
$('.video_control').addClass className
describe 'when the video is playing', ->
beforeEach ->
spyOn(@player, 'isPlaying').andReturn true
spyOnEvent @player, 'pause'
@control.togglePlayback jQuery.Event('click')
spyOnEvent @control, 'pause'
$('.video_control').addClass 'pause'
@control.togglePlayback jQuery.Event('click')
it 'trigger the pause event', ->
expect('pause').toHaveBeenTriggeredOn @player
it 'trigger the pause event', ->
expect('pause').toHaveBeenTriggeredOn @control
describe 'when the video is paused', ->
beforeEach ->
spyOn(@player, 'isPlaying').andReturn false
spyOnEvent @player, 'play'
@control.togglePlayback jQuery.Event('click')
describe 'when the video is paused', ->
beforeEach ->
spyOnEvent @control, 'play'
$('.video_control').addClass 'play'
@control.togglePlayback jQuery.Event('click')
it 'trigger the play event', ->
expect('play').toHaveBeenTriggeredOn @player
it 'trigger the play event', ->
expect('play').toHaveBeenTriggeredOn @control
describe 'VideoProgressSlider', ->
beforeEach ->
@player = jasmine.stubVideoPlayer @
jasmine.stubVideoPlayer @
describe 'constructor', ->
describe 'on a non-touch based device', ->
beforeEach ->
spyOn($.fn, 'slider').andCallThrough()
spyOn(window, 'onTouchBasedDevice').andReturn false
@slider = new VideoProgressSlider @player
@slider = new VideoProgressSlider el: $('.slider')
it 'build the slider', ->
expect(@slider.slider).toBe '.slider'
......@@ -18,7 +18,7 @@ describe 'VideoProgressSlider', ->
stop: @slider.onStop
it 'build the seek handle', ->
expect(@slider.handle).toBe '.slider .ui-slider-handle'
expect(@slider.handle).toBe '.ui-slider-handle'
expect($.fn.qtip).toHaveBeenCalledWith
content: "0:00"
position:
......@@ -31,41 +31,24 @@ describe 'VideoProgressSlider', ->
classes: 'ui-tooltip-slider'
widget: true
it 'bind player events', ->
expect($(@player)).toHandleWith 'updatePlayTime', @slider.onUpdatePlayTime
expect($(@player)).toHandleWith 'ready', @slider.onReady
expect($(@player)).toHandleWith 'play', @slider.onPlay
describe 'on a touch-based device', ->
beforeEach ->
spyOn($.fn, 'slider').andCallThrough()
spyOn(window, 'onTouchBasedDevice').andReturn true
@slider = new VideoProgressSlider @player
@slider = new VideoProgressSlider el: $('.slider')
it 'does not build the slider', ->
expect(@slider.slider).toBeUndefined
expect($.fn.slider).not.toHaveBeenCalled()
it 'bind player events', ->
expect($(@player)).toHandleWith 'updatePlayTime', @slider.onUpdatePlayTime
describe 'onReady', ->
beforeEach ->
spyOn(@player, 'duration').andReturn 120
@slider = new VideoProgressSlider @player
@slider.onReady()
it 'set the max value to the length of video', ->
expect(@slider.slider.slider('option', 'max')).toEqual 120
describe 'onPlay', ->
describe 'play', ->
beforeEach ->
@slider = new VideoProgressSlider @player
@slider = new VideoProgressSlider el: $('.slider')
spyOn($.fn, 'slider').andCallThrough()
describe 'when the slider was already built', ->
beforeEach ->
@slider.onPlay()
@slider.play()
it 'does not build the slider', ->
expect($.fn.slider).not.toHaveBeenCalled
......@@ -73,7 +56,7 @@ describe 'VideoProgressSlider', ->
describe 'when the slider was not already built', ->
beforeEach ->
@slider.slider = null
@slider.onPlay()
@slider.play()
it 'build the slider', ->
expect(@slider.slider).toBe '.slider'
......@@ -97,16 +80,15 @@ describe 'VideoProgressSlider', ->
classes: 'ui-tooltip-slider'
widget: true
describe 'onUpdatePlayTime', ->
describe 'updatePlayTime', ->
beforeEach ->
@slider = new VideoProgressSlider @player
spyOn(@player, 'duration').andReturn 120
@slider = new VideoProgressSlider el: $('.slider')
spyOn($.fn, 'slider').andCallThrough()
describe 'when frozen', ->
beforeEach ->
@slider.frozen = true
@slider.onUpdatePlayTime {}, 20
@slider.updatePlayTime 20, 120
it 'does not update the slider', ->
expect($.fn.slider).not.toHaveBeenCalled()
......@@ -114,7 +96,7 @@ describe 'VideoProgressSlider', ->
describe 'when not frozen', ->
beforeEach ->
@slider.frozen = false
@slider.onUpdatePlayTime {}, 20
@slider.updatePlayTime 20, 120
it 'update the max value of the slider', ->
expect($.fn.slider).toHaveBeenCalledWith 'option', 'max', 120
......@@ -124,10 +106,10 @@ describe 'VideoProgressSlider', ->
describe 'onSlide', ->
beforeEach ->
@slider = new VideoProgressSlider @player
@slider = new VideoProgressSlider el: $('.slider')
@time = null
$(@player).bind 'seek', (event, time) => @time = time
spyOnEvent @player, 'seek'
$(@slider).bind 'seek', (event, time) => @time = time
spyOnEvent @slider, 'seek'
@slider.onSlide {}, value: 20
it 'freeze the slider', ->
......@@ -137,12 +119,12 @@ describe 'VideoProgressSlider', ->
expect($.fn.qtip).toHaveBeenCalled()
it 'trigger seek event', ->
expect('seek').toHaveBeenTriggeredOn @player
expect('seek').toHaveBeenTriggeredOn @slider
expect(@time).toEqual 20
describe 'onChange', ->
beforeEach ->
@slider = new VideoProgressSlider @player
@slider = new VideoProgressSlider el: $('.slider')
@slider.onChange {}, value: 20
it 'update the tooltip', ->
......@@ -150,10 +132,10 @@ describe 'VideoProgressSlider', ->
describe 'onStop', ->
beforeEach ->
@slider = new VideoProgressSlider @player
@slider = new VideoProgressSlider el: $('.slider')
@time = null
$(@player).bind 'seek', (event, time) => @time = time
spyOnEvent @player, 'seek'
$(@slider).bind 'seek', (event, time) => @time = time
spyOnEvent @slider, 'seek'
spyOn(window, 'setTimeout')
@slider.onStop {}, value: 20
......@@ -161,7 +143,7 @@ describe 'VideoProgressSlider', ->
expect(@slider.frozen).toBeTruthy()
it 'trigger seek event', ->
expect('seek').toHaveBeenTriggeredOn @player
expect('seek').toHaveBeenTriggeredOn @slider
expect(@time).toEqual 20
it 'set timeout to unfreeze the slider', ->
......@@ -171,7 +153,7 @@ describe 'VideoProgressSlider', ->
describe 'updateTooltip', ->
beforeEach ->
@slider = new VideoProgressSlider @player
@slider = new VideoProgressSlider el: $('.slider')
@slider.updateTooltip 90
it 'set the tooltip value', ->
......
describe 'VideoSpeedControl', ->
beforeEach ->
@player = jasmine.stubVideoPlayer @
jasmine.stubVideoPlayer @
$('.speeds').remove()
describe 'constructor', ->
describe 'always', ->
beforeEach ->
@speedControl = new VideoSpeedControl @player, @video.speeds
@speedControl = new VideoSpeedControl el: $('.secondary-controls'), speeds: @video.speeds, currentSpeed: '1.0'
it 'add the video speed control to player', ->
expect($('.secondary-controls').html()).toContain '''
......@@ -19,9 +19,6 @@ describe 'VideoSpeedControl', ->
</div>
'''
it 'bind to player speedChange event', ->
expect($(@player)).toHandleWith 'speedChange', @speedControl.onSpeedChange
it 'bind to change video speed link', ->
expect($('.video_speeds a')).toHandleWith 'click', @speedControl.changeVideoSpeed
......@@ -29,7 +26,7 @@ describe 'VideoSpeedControl', ->
beforeEach ->
spyOn(window, 'onTouchBasedDevice').andReturn true
$('.speeds').removeClass 'open'
@speedControl = new VideoSpeedControl @player, @video.speeds
@speedControl = new VideoSpeedControl el: $('.secondary-controls'), speeds: @video.speeds, currentSpeed: '1.0'
it 'open the speed toggle on click', ->
$('.speeds').click()
......@@ -41,7 +38,7 @@ describe 'VideoSpeedControl', ->
beforeEach ->
spyOn(window, 'onTouchBasedDevice').andReturn false
$('.speeds').removeClass 'open'
@speedControl = new VideoSpeedControl @player, @video.speeds
@speedControl = new VideoSpeedControl el: $('.secondary-controls'), speeds: @video.speeds, currentSpeed: '1.0'
it 'open the speed toggle on hover', ->
$('.speeds').mouseenter()
......@@ -59,31 +56,31 @@ describe 'VideoSpeedControl', ->
describe 'changeVideoSpeed', ->
beforeEach ->
@speedControl = new VideoSpeedControl @player, @video.speeds
@speedControl = new VideoSpeedControl el: $('.secondary-controls'), speeds: @video.speeds, currentSpeed: '1.0'
@video.setSpeed '1.0'
describe 'when new speed is the same', ->
beforeEach ->
spyOnEvent @player, 'speedChange'
spyOnEvent @speedControl, 'speedChange'
$('li[data-speed="1.0"] a').click()
it 'does not trigger speedChange event', ->
expect('speedChange').not.toHaveBeenTriggeredOn @player
expect('speedChange').not.toHaveBeenTriggeredOn @speedControl
describe 'when new speed is not the same', ->
beforeEach ->
@newSpeed = null
$(@player).bind 'speedChange', (event, newSpeed) => @newSpeed = newSpeed
spyOnEvent @player, 'speedChange'
$(@speedControl).bind 'speedChange', (event, newSpeed) => @newSpeed = newSpeed
spyOnEvent @speedControl, 'speedChange'
$('li[data-speed="0.75"] a').click()
it 'trigger player speedChange event', ->
expect('speedChange').toHaveBeenTriggeredOn @player
it 'trigger speedChange event', ->
expect('speedChange').toHaveBeenTriggeredOn @speedControl
expect(@newSpeed).toEqual 0.75
describe 'onSpeedChange', ->
beforeEach ->
@speedControl = new VideoSpeedControl @player, @video.speeds
@speedControl = new VideoSpeedControl el: $('.secondary-controls'), speeds: @video.speeds, currentSpeed: '1.0'
$('li[data-speed="1.0"] a').addClass 'active'
@speedControl.setSpeed '0.75'
......
describe 'VideoVolumeControl', ->
beforeEach ->
@player = jasmine.stubVideoPlayer @
jasmine.stubVideoPlayer @
$('.volume').remove()
describe 'constructor', ->
beforeEach ->
spyOn($.fn, 'slider')
@volumeControl = new VideoVolumeControl @player
@volumeControl = new VideoVolumeControl el: $('.secondary-controls')
it 'initialize previousVolume to 100', ->
expect(@volumeControl.previousVolume).toEqual 100
it 'initialize currentVolume to 100', ->
expect(@volumeControl.currentVolume).toEqual 100
it 'render the volume control', ->
expect($('.secondary-controls').html()).toContain """
......@@ -32,7 +32,6 @@ describe 'VideoVolumeControl', ->
slide: @volumeControl.onChange
it 'bind the volume control', ->
expect($(@player)).toHandleWith 'ready', @volumeControl.onReady
expect($('.volume>a')).toHandleWith 'click', @volumeControl.toggleMute
expect($('.volume')).not.toHaveClass 'open'
......@@ -41,27 +40,19 @@ describe 'VideoVolumeControl', ->
$('.volume').mouseleave()
expect($('.volume')).not.toHaveClass 'open'
describe 'onReady', ->
beforeEach ->
@volumeControl = new VideoVolumeControl @player
spyOn $.fn, 'slider'
spyOn(@player, 'volume').andReturn 60
@volumeControl.onReady()
it 'set the max value of the slider', ->
expect($.fn.slider).toHaveBeenCalledWith 'option', 'max', 60
describe 'onChange', ->
beforeEach ->
spyOn @player, 'volume'
@volumeControl = new VideoVolumeControl @player
spyOnEvent @volumeControl, 'volumeChange'
@newVolume = undefined
@volumeControl = new VideoVolumeControl el: $('.secondary-controls')
$(@volumeControl).bind 'volumeChange', (event, volume) => @newVolume = volume
describe 'when the new volume is more than 0', ->
beforeEach ->
@volumeControl.onChange undefined, value: 60
it 'set the player volume', ->
expect(@player.volume).toHaveBeenCalledWith 60
expect(@newVolume).toEqual 60
it 'remote muted class', ->
expect($('.volume')).not.toHaveClass 'muted'
......@@ -71,32 +62,33 @@ describe 'VideoVolumeControl', ->
@volumeControl.onChange undefined, value: 0
it 'set the player volume', ->
expect(@player.volume).toHaveBeenCalledWith 0
expect(@newVolume).toEqual 0
it 'add muted class', ->
expect($('.volume')).toHaveClass 'muted'
describe 'toggleMute', ->
beforeEach ->
spyOn @player, 'volume'
@volumeControl = new VideoVolumeControl @player
@newVolume = undefined
@volumeControl = new VideoVolumeControl el: $('.secondary-controls')
$(@volumeControl).bind 'volumeChange', (event, volume) => @newVolume = volume
describe 'when the current volume is more than 0', ->
beforeEach ->
@player.volume.andReturn 60
@volumeControl.currentVolume = 60
@volumeControl.toggleMute()
it 'save the previous volume', ->
expect(@volumeControl.previousVolume).toEqual 60
it 'set the player volume', ->
expect(@player.volume).toHaveBeenCalledWith 0
expect(@newVolume).toEqual 0
describe 'when the current volume is 0', ->
beforeEach ->
@player.volume.andReturn 0
@volumeControl.currentVolume = 0
@volumeControl.previousVolume = 60
@volumeControl.toggleMute()
it 'set the player volume to previous volume', ->
expect(@player.volume).toHaveBeenCalledWith 60
expect(@newVolume).toEqual 60
......@@ -3,6 +3,10 @@ describe 'Video', ->
loadFixtures 'video.html'
jasmine.stubRequests()
@videosDefinition = '.75:slowerSpeedYoutubeId,1.0:normalSpeedYoutubeId'
@slowerSpeedYoutubeId = 'slowerSpeedYoutubeId'
@normalSpeedYoutubeId = 'normalSpeedYoutubeId'
afterEach ->
window.player = undefined
window.onYouTubePlayerAPIReady = undefined
......@@ -15,26 +19,26 @@ describe 'Video', ->
describe 'by default', ->
beforeEach ->
@video = new Video 'example', '.75:abc123,1.0:def456'
@video = new Video 'example', @videosDefinition
it 'reset the current video player', ->
expect(window.player).toBeNull()
it 'set the elements', ->
expect(@video.element).toBe '#video_example'
expect(@video.el).toBe '#video_example'
it 'parse the videos', ->
expect(@video.videos).toEqual
'0.75': 'abc123'
'1.0': 'def456'
'0.75': @slowerSpeedYoutubeId
'1.0': @normalSpeedYoutubeId
it 'fetch the video metadata', ->
expect(@video.metadata).toEqual
abc123:
id: 'abc123'
duration: 100
def456:
id: 'def456'
slowerSpeedYoutubeId:
id: @slowerSpeedYoutubeId
duration: 300
normalSpeedYoutubeId:
id: @normalSpeedYoutubeId
duration: 200
it 'parse available video speeds', ->
......@@ -51,20 +55,20 @@ describe 'Video', ->
@originalYT = window.YT
window.YT = { Player: true }
spyOn(window, 'VideoPlayer').andReturn(@stubVideoPlayer)
@video = new Video 'example', '.75:abc123,1.0:def456'
@video = new Video 'example', @videosDefinition
afterEach ->
window.YT = @originalYT
it 'create the Video Player', ->
expect(window.VideoPlayer).toHaveBeenCalledWith @video
expect(window.VideoPlayer).toHaveBeenCalledWith(video: @video)
expect(@video.player).toEqual @stubVideoPlayer
describe 'when the Youtube API is not ready', ->
beforeEach ->
@originalYT = window.YT
window.YT = {}
@video = new Video 'example', '.75:abc123,1.0:def456'
@video = new Video 'example', @videosDefinition
afterEach ->
window.YT = @originalYT
......@@ -77,33 +81,33 @@ describe 'Video', ->
@originalYT = window.YT
window.YT = {}
spyOn(window, 'VideoPlayer').andReturn(@stubVideoPlayer)
@video = new Video 'example', '.75:abc123,1.0:def456'
@video = new Video 'example', @videosDefinition
window.onYouTubePlayerAPIReady()
afterEach ->
window.YT = @originalYT
it 'create the Video Player for all video elements', ->
expect(window.VideoPlayer).toHaveBeenCalledWith @video
expect(window.VideoPlayer).toHaveBeenCalledWith(video: @video)
expect(@video.player).toEqual @stubVideoPlayer
describe 'youtubeId', ->
beforeEach ->
$.cookie.andReturn '1.0'
@video = new Video 'example', '.75:abc123,1.0:def456'
@video = new Video 'example', @videosDefinition
describe 'with speed', ->
it 'return the video id for given speed', ->
expect(@video.youtubeId('0.75')).toEqual 'abc123'
expect(@video.youtubeId('1.0')).toEqual 'def456'
expect(@video.youtubeId('0.75')).toEqual @slowerSpeedYoutubeId
expect(@video.youtubeId('1.0')).toEqual @normalSpeedYoutubeId
describe 'without speed', ->
it 'return the video id for current speed', ->
expect(@video.youtubeId()).toEqual 'def456'
expect(@video.youtubeId()).toEqual @normalSpeedYoutubeId
describe 'setSpeed', ->
beforeEach ->
@video = new Video 'example', '.75:abc123,1.0:def456'
@video = new Video 'example', @videosDefinition
describe 'when new speed is available', ->
beforeEach ->
......@@ -124,7 +128,22 @@ describe 'Video', ->
describe 'getDuration', ->
beforeEach ->
@video = new Video 'example', '.75:abc123,1.0:def456'
@video = new Video 'example', @videosDefinition
it 'return duration for current video', ->
expect(@video.getDuration()).toEqual 200
describe 'log', ->
beforeEach ->
@video = new Video 'example', @videosDefinition
@video.setSpeed '1.0'
spyOn Logger, 'log'
@video.player = { currentTime: 25 }
@video.log 'someEvent'
it 'call the logger with valid parameters', ->
expect(Logger.log).toHaveBeenCalledWith 'someEvent',
id: 'example'
code: @normalSpeedYoutubeId
currentTime: 25
speed: '1.0'
class @Subview
constructor: (options) ->
$.each options, (key, value) =>
@[key] = value
@initialize()
@render()
@bind()
$: (selector) ->
$(selector, @el)
initialize: ->
render: ->
bind: ->
......@@ -20,7 +20,7 @@ class @Courseware
id = $(this).attr('id').replace(/video_/, '')
new Video id, $(this).data('streams')
$('.course-content .problems-wrapper').each ->
id = $(this).attr('problem-id')
id = $(this).data('problem-id')
new Problem id, $(this).attr('id'), $(this).data('url')
$('.course-content .histogram').each ->
id = $(this).attr('id').replace(/histogram_/, '')
......
......@@ -24,8 +24,8 @@ $ ->
# Preserved for backward compatibility
window.submit_circuit = (circuit_id) ->
$("input.schematic").each (index, element) ->
element.schematic.update_value()
$("input.schematic").each (index, el) ->
el.schematic.update_value()
schematic_value $("#schematic_#{circuit_id}").attr("value")
$.postWithPrefix "/save_circuit/#{circuit_id}", schematic: schematic_value, (data) ->
......
class @Problem
constructor: (@id, @element_id, url) ->
@element = $("##{element_id}")
@el = $("##{@element_id}")
@render()
$: (selector) ->
$(selector, @element)
$(selector, @el)
bind: =>
MathJax.Hub.Queue ["Typeset", MathJax.Hub]
......@@ -18,16 +18,16 @@ class @Problem
updateProgress: (response) =>
if response.progress_changed
@element.attr progress: response.progress_status
@element.trigger('progressChanged')
@el.attr progress: response.progress_status
@el.trigger('progressChanged')
render: (content) ->
if content
@element.html(content)
@el.html(content)
@bind()
else
$.postWithPrefix "/modx/#{@id}/problem_get", (response) =>
@element.html(response.html)
@el.html(response.html)
@bind()
check: =>
......@@ -47,7 +47,7 @@ class @Problem
@updateProgress response
show: =>
if !@element.hasClass 'showed'
if !@el.hasClass 'showed'
Logger.log 'problem_show', problem: @id
$.postWithPrefix "/modx/#{@id}/problem_show", (response) =>
answers = response.answers
......@@ -59,12 +59,12 @@ class @Problem
@$("#answer_#{key}, #solution_#{key}").html(value)
MathJax.Hub.Queue ["Typeset", MathJax.Hub]
@$('.show').val 'Hide Answer'
@element.addClass 'showed'
@el.addClass 'showed'
@updateProgress response
else
@$('[id^=answer_], [id^=solution_]').text ''
@$('[correct_answer]').attr correct_answer: null
@element.removeClass 'showed'
@el.removeClass 'showed'
@$('.show').val 'Show Answer'
save: =>
......
class @Sequence
constructor: (@id, @element_id, @elements, @tag, position) ->
@element = $("#sequence_#{@element_id}")
@el = $("##{@element_id}")
@buildNavigation()
@initProgress()
@bind()
@render position
$: (selector) ->
$(selector, @element)
$(selector, @el)
bind: ->
@$('#sequence-list a').click @goto
......@@ -57,7 +57,7 @@ class @Sequence
when 'none' then element.addClass('progress-none')
when 'in_progress' then element.addClass('progress-some')
when 'done' then element.addClass('progress-done')
buildNavigation: ->
$.each @elements, (index, item) =>
link = $('<a>').attr class: "seq_#{item.type}_inactive", 'data-element': index + 1
......@@ -65,10 +65,10 @@ class @Sequence
# TODO (vshnayder): add item.progress_detail either to the title or somewhere else.
# Make sure it gets updated after ajax calls.
# implementation note: will need to figure out how to handle combining detail
# statuses of multiple modules in js.
# statuses of multiple modules in js.
list_item = $('<li>').append(link.append(title))
@setProgress item.progress_status, link
@$('#sequence-list').append list_item
toggleArrows: =>
......@@ -89,7 +89,7 @@ class @Sequence
if @position != undefined
@mark_visited @position
$.postWithPrefix "/modx/#{@id}/goto_position", position: new_position
@mark_active new_position
@$('#seq_content').html @elements[new_position - 1].content
......@@ -97,7 +97,7 @@ class @Sequence
@position = new_position
@toggleArrows()
@hookUpProgressEvent()
@element.trigger 'contentChanged'
@el.trigger 'contentChanged'
goto: (event) =>
event.preventDefault()
......
class @Tab
constructor: (@id, @items) ->
@element = $("#tab_#{id}")
@el = $("#tab_#{id}")
@render()
$: (selector) ->
$(selector, @element)
$(selector, @el)
render: ->
$.each @items, (index, item) =>
tab = $('<a>').attr(href: "##{@tabId(index)}").html(item.title)
@$('.navigation').append($('<li>').append(tab))
@element.append($('<section>').attr(id: @tabId(index)))
@element.tabs
@el.append($('<section>').attr(id: @tabId(index)))
@el.tabs
show: @onShow
onShow: (element, ui) =>
@$('section.ui-tabs-hide').html('')
@$("##{@tabId(ui.index)}").html(@items[ui.index]['content'])
@element.trigger 'contentChanged'
@el.trigger 'contentChanged'
tabId: (index) ->
"tab-#{@id}-#{index}"
class @Video
constructor: (@id, videos) ->
window.player = null
@element = $("#video_#{@id}")
@el = $("#video_#{@id}")
@parseVideos videos
@fetchMetadata()
@parseSpeed()
......@@ -36,7 +36,7 @@ class @Video
@speed = '1.0'
embed: ->
@player = new VideoPlayer(this)
@player = new VideoPlayer video: this
fetchMetadata: (url) ->
@metadata = {}
......@@ -45,3 +45,10 @@ class @Video
getDuration: ->
@metadata[@youtubeId()].duration
log: (eventName) ->
Logger.log eventName,
id: @id
code: @youtubeId()
currentTime: @player.currentTime
speed: @speed
class @VideoCaption
constructor: (@player, @youtubeId) ->
@render()
@bind()
$: (selector) ->
@player.$(selector)
class @VideoCaption extends Subview
bind: ->
$(window).bind('resize', @onWindowResize)
$(@player).bind('resize', @onWindowResize)
$(@player).bind('updatePlayTime', @onUpdatePlayTime)
$(@player).bind('seek', @onUpdatePlayTime)
$(@player).bind('play', @onPlay)
$(window).bind('resize', @resize)
@$('.hide-subtitles').click @toggle
@$('.subtitles').mouseenter(@onMouseEnter).mouseleave(@onMouseLeave)
.mousemove(@onMovement).bind('mousewheel', @onMovement)
......@@ -70,12 +59,16 @@ class @VideoCaption
return min
onPlay: =>
play: ->
@renderCaption() unless @rendered
@playing = true
pause: ->
@playing = false
onUpdatePlayTime: (event, time) =>
updatePlayTime: (time) ->
# This 250ms offset is required to match the video speed
time = Math.round(Time.convert(time, @player.currentSpeed(), '1.0') * 1000 + 250)
time = Math.round(Time.convert(time, @currentSpeed, '1.0') * 1000 + 250)
newIndex = @search time
if newIndex != undefined && @currentIndex != newIndex
......@@ -86,7 +79,7 @@ class @VideoCaption
@currentIndex = newIndex
@scrollCaption()
onWindowResize: =>
resize: =>
@$('.subtitles').css maxHeight: @captionHeight()
@$('.subtitles .spacing:first').height(@topSpacingHeight())
@$('.subtitles .spacing:last').height(@bottomSpacingHeight())
......@@ -102,7 +95,7 @@ class @VideoCaption
onMouseLeave: =>
clearTimeout @frozen if @frozen
@frozen = null
@scrollCaption() if @player.isPlaying()
@scrollCaption() if @playing
scrollCaption: ->
if !@frozen && @$('.subtitles .current:first').length
......@@ -111,8 +104,8 @@ class @VideoCaption
seekPlayer: (event) =>
event.preventDefault()
time = Math.round(Time.convert($(event.target).data('start'), '1.0', @player.currentSpeed()) / 1000)
$(@player).trigger('seek', time)
time = Math.round(Time.convert($(event.target).data('start'), '1.0', @currentSpeed) / 1000)
$(@).trigger('seek', time)
calculateOffset: (element) ->
@captionHeight() / 2 - element.height() / 2
......@@ -125,16 +118,16 @@ class @VideoCaption
toggle: (event) =>
event.preventDefault()
if @player.element.hasClass('closed')
if @el.hasClass('closed')
@$('.hide-subtitles').attr('title', 'Turn off captions')
@player.element.removeClass('closed')
@el.removeClass('closed')
@scrollCaption()
else
@$('.hide-subtitles').attr('title', 'Turn on captions')
@player.element.addClass('closed')
@el.addClass('closed')
captionHeight: ->
if @player.element.hasClass('fullscreen')
if @el.hasClass('fullscreen')
$(window).height() - @$('.video-controls').height()
else
@$('.video-wrapper').height()
class @VideoControl
constructor: (@player) ->
@render()
@bind()
$: (selector) ->
@player.$(selector)
class @VideoControl extends Subview
bind: ->
$(@player).bind('play', @onPlay)
.bind('pause', @onPause)
.bind('ended', @onPause)
@$('.video_control').click @togglePlayback
render: ->
@$('.video-controls').append """
@el.append """
<div class="slider"></div>
<div>
<ul class="vcr">
......@@ -31,16 +21,15 @@ class @VideoControl
unless onTouchBasedDevice()
@$('.video_control').addClass('play').html('Play')
onPlay: =>
play: ->
@$('.video_control').removeClass('play').addClass('pause').html('Pause')
onPause: =>
pause: ->
@$('.video_control').removeClass('pause').addClass('play').html('Play')
togglePlayback: (event) =>
event.preventDefault()
if $('.video_control').hasClass('play') || $('.video_control').hasClass('pause')
if @player.isPlaying()
$(@player).trigger('pause')
else
$(@player).trigger('play')
if @$('.video_control').hasClass('play')
$(@).trigger('play')
else if @$('.video_control').hasClass('pause')
$(@).trigger('pause')
class @VideoPlayer
constructor: (@video) ->
class @VideoPlayer extends Subview
initialize: ->
# Define a missing constant of Youtube API
YT.PlayerState.UNSTARTED = -1
@currentTime = 0
@element = $("#video_#{@video.id}")
@render()
@bind()
$: (selector) ->
$(selector, @element)
@el = $("#video_#{@video.id}")
bind: ->
$(@).bind('seek', @onSeek)
.bind('updatePlayTime', @onUpdatePlayTime)
.bind('speedChange', @onSpeedChange)
.bind('play', @onPlay)
.bind('pause', @onPause)
.bind('ended', @onPause)
$(@control).bind('play', @play)
.bind('pause', @pause)
$(@caption).bind('seek', @onSeek)
$(@speedControl).bind('speedChange', @onSpeedChange)
$(@progressSlider).bind('seek', @onSeek)
if @volumeControl
$(@volumeControl).bind('volumeChange', @onVolumeChange)
$(document).keyup @bindExitFullScreen
@$('.add-fullscreen').click @toggleFullScreen
@addToolTip() unless onTouchBasedDevice()
bindExitFullScreen: (event) =>
if @element.hasClass('fullscreen') && event.keyCode == 27
if @el.hasClass('fullscreen') && event.keyCode == 27
@toggleFullScreen(event)
render: ->
new VideoControl @
new VideoCaption @, @video.youtubeId('1.0')
new VideoVolumeControl @ unless onTouchBasedDevice()
new VideoSpeedControl @, @video.speeds
new VideoProgressSlider @
@control = new VideoControl el: @$('.video-controls')
@caption = new VideoCaption el: @el, youtubeId: @video.youtubeId('1.0'), currentSpeed: @currentSpeed()
unless onTouchBasedDevice()
@volumeControl = new VideoVolumeControl el: @$('.secondary-controls')
@speedControl = new VideoSpeedControl el: @$('.secondary-controls'), speeds: @video.speeds, currentSpeed: @currentSpeed()
@progressSlider = new VideoProgressSlider el: @$('.slider')
@player = new YT.Player @video.id,
playerVars:
controls: 0
......@@ -52,90 +49,104 @@ class @VideoPlayer
at: 'top center'
onReady: =>
$(@).trigger('ready')
$(@).trigger('updatePlayTime', 0)
unless onTouchBasedDevice()
$('.course-content .video:first').data('video').player.play()
onStateChange: (event) =>
switch event.data
when YT.PlayerState.UNSTARTED
@onUnstarted()
when YT.PlayerState.PLAYING
$(@).trigger('play')
when YT.PlayerState.PAUSED, YT.PlayerState.UNSTARTED
$(@).trigger('pause')
@onPlay()
when YT.PlayerState.PAUSED
@onPause()
when YT.PlayerState.ENDED
$(@).trigger('ended')
@onEnded()
onUnstarted: =>
@control.pause()
@caption.pause()
onPlay: =>
Logger.log 'play_video', id: @currentTime, code: @player.getVideoEmbedCode()
@video.log 'play_video'
window.player.pauseVideo() if window.player && window.player != @player
window.player = @player
unless @player.interval
@player.interval = setInterval(@update, 200)
@caption.play()
@control.play()
@progressSlider.play()
onPause: =>
Logger.log 'pause_video', id: @currentTime, code: @player.getVideoEmbedCode()
@video.log 'pause_video'
window.player = null if window.player == @player
clearInterval(@player.interval)
@player.interval = null
@caption.pause()
@control.pause()
onEnded: =>
@control.pause()
@caption.pause()
onSeek: (event, time) ->
onSeek: (event, time) =>
@player.seekTo(time, true)
if @isPlaying()
clearInterval(@player.interval)
@player.interval = setInterval(@update, 200)
else
@currentTime = time
$(@).trigger('updatePlayTime', time)
@updatePlayTime time
onSpeedChange: (event, newSpeed) =>
@currentTime = Time.convert(@currentTime, parseFloat(@currentSpeed()), newSpeed)
@video.setSpeed(parseFloat(newSpeed).toFixed(2).replace /\.00$/, '.0')
newSpeed = parseFloat(newSpeed).toFixed(2).replace /\.00$/, '.0'
@video.setSpeed(newSpeed)
@caption.currentSpeed = newSpeed
if @isPlaying()
@player.loadVideoById(@video.youtubeId(), @currentTime)
else
@player.cueVideoById(@video.youtubeId(), @currentTime)
$(@).trigger('updatePlayTime', @currentTime)
@updatePlayTime @currentTime
onVolumeChange: (event, volume) =>
@player.setVolume volume
update: =>
if @currentTime = @player.getCurrentTime()
$(@).trigger('updatePlayTime', @currentTime)
@updatePlayTime @currentTime
onUpdatePlayTime: (event, time) =>
updatePlayTime: (time) ->
progress = Time.format(time) + ' / ' + Time.format(@duration())
@$(".vidtime").html(progress)
@caption.updatePlayTime(time)
@progressSlider.updatePlayTime(time, @duration())
toggleFullScreen: (event) =>
event.preventDefault()
if @element.hasClass('fullscreen')
if @el.hasClass('fullscreen')
@$('.exit').remove()
@$('.add-fullscreen').attr('title', 'Fill browser')
@element.removeClass('fullscreen')
@el.removeClass('fullscreen')
else
@element.append('<a href="#" class="exit">Exit</a>').addClass('fullscreen')
@el.append('<a href="#" class="exit">Exit</a>').addClass('fullscreen')
@$('.add-fullscreen').attr('title', 'Exit fill browser')
@$('.exit').click @toggleFullScreen
$(@).trigger('resize')
@caption.resize()
# Delegates
play: ->
play: =>
@player.playVideo() if @player.playVideo
isPlaying: ->
@player.getPlayerState() == YT.PlayerState.PLAYING
pause: ->
@player.pauseVideo()
pause: =>
@player.pauseVideo() if @player.pauseVideo
duration: ->
@video.getDuration()
currentSpeed: ->
@video.speed
volume: (value) ->
if value?
@player.setVolume value
else
@player.getVolume()
class @VideoProgressSlider
constructor: (@player) ->
class @VideoProgressSlider extends Subview
initialize: ->
@buildSlider() unless onTouchBasedDevice()
$(@player).bind('updatePlayTime', @onUpdatePlayTime)
$(@player).bind('ready', @onReady)
$(@player).bind('play', @onPlay)
$: (selector) ->
@player.$(selector)
buildSlider: ->
@slider = @$('.slider').slider
@slider = @el.slider
range: 'min'
change: @onChange
slide: @onSlide
......@@ -17,7 +11,7 @@ class @VideoProgressSlider
@buildHandle()
buildHandle: ->
@handle = @$('.slider .ui-slider-handle')
@handle = @$('.ui-slider-handle')
@handle.qtip
content: "#{Time.format(@slider.slider('value'))}"
position:
......@@ -30,28 +24,25 @@ class @VideoProgressSlider
classes: 'ui-tooltip-slider'
widget: true
onReady: =>
@slider.slider('option', 'max', @player.duration()) if @slider
onPlay: =>
play: =>
@buildSlider() unless @slider
onUpdatePlayTime: (event, currentTime) =>
updatePlayTime: (currentTime, duration) ->
if @slider && !@frozen
@slider.slider('option', 'max', @player.duration())
@slider.slider('option', 'max', duration)
@slider.slider('value', currentTime)
onSlide: (event, ui) =>
@frozen = true
@updateTooltip(ui.value)
$(@player).trigger('seek', ui.value)
$(@).trigger('seek', ui.value)
onChange: (event, ui) =>
@updateTooltip(ui.value)
onStop: (event, ui) =>
@frozen = true
$(@player).trigger('seek', ui.value)
$(@).trigger('seek', ui.value)
setTimeout (=> @frozen = false), 200
updateTooltip: (value)->
......
class @VideoSpeedControl
constructor: (@player, @speeds) ->
@render()
@bind()
$: (selector) ->
@player.$(selector)
class @VideoSpeedControl extends Subview
bind: ->
$(@player).bind('speedChange', @onSpeedChange)
@$('.video_speeds a').click @changeVideoSpeed
if onTouchBasedDevice()
@$('.speeds').click (event) ->
......@@ -23,7 +15,7 @@ class @VideoSpeedControl
$(this).removeClass('open')
render: ->
@$('.secondary-controls').prepend """
@el.prepend """
<div class="speeds">
<a href="#">
<h3>Speed</h3>
......@@ -36,15 +28,14 @@ class @VideoSpeedControl
$.each @speeds, (index, speed) =>
link = $('<a>').attr(href: "#").html("#{speed}x")
@$('.video_speeds').prepend($('<li>').attr('data-speed', speed).html(link))
@setSpeed(@player.currentSpeed())
@setSpeed(@currentSpeed)
changeVideoSpeed: (event) =>
event.preventDefault()
unless $(event.target).parent().hasClass('active')
$(@player).trigger 'speedChange', $(event.target).parent().data('speed')
onSpeedChange: (event, speed) =>
@setSpeed(parseFloat(speed).toFixed(2).replace /\.00$/, '.0')
@currentSpeed = $(event.target).parent().data('speed')
$(@).trigger 'speedChange', $(event.target).parent().data('speed')
@setSpeed(parseFloat(@currentSpeed).toFixed(2).replace /\.00$/, '.0')
setSpeed: (speed) ->
@$('.video_speeds li').removeClass('active')
......
class @VideoVolumeControl
constructor: (@player) ->
@previousVolume = 100
@render()
@bind()
$: (selector) ->
@player.$(selector)
class @VideoVolumeControl extends Subview
initialize: ->
@currentVolume = 100
bind: ->
$(@player).bind('ready', @onReady)
@$('.volume').mouseenter ->
$(this).addClass('open')
@$('.volume').mouseleave ->
......@@ -16,7 +10,7 @@ class @VideoVolumeControl
@$('.volume>a').click(@toggleMute)
render: ->
@$('.secondary-controls').prepend """
@el.prepend """
<div class="volume">
<a href="#"></a>
<div class="volume-slider-container">
......@@ -33,16 +27,14 @@ class @VideoVolumeControl
change: @onChange
slide: @onChange
onReady: =>
@slider.slider 'option', 'max', @player.volume()
onChange: (event, ui) =>
@player.volume ui.value
@$('.secondary-controls .volume').toggleClass 'muted', ui.value == 0
@currentVolume = ui.value
$(@).trigger 'volumeChange', @currentVolume
@$('.volume').toggleClass 'muted', @currentVolume == 0
toggleMute: =>
if @player.volume() > 0
@previousVolume = @player.volume()
if @currentVolume > 0
@previousVolume = @currentVolume
@slider.slider 'option', 'value', 0
else
@slider.slider 'option', 'value', @previousVolume
......@@ -137,7 +137,6 @@ section.course-content {
float: left;
margin-bottom: 0;
a {
border-bottom: none;
border-right: 1px solid #000;
......@@ -199,6 +198,8 @@ section.course-content {
ol.video_speeds {
display: block;
opacity: 1;
list-style: none;
padding-left: 0;
}
}
......@@ -476,6 +477,7 @@ section.course-content {
ol.subtitles {
width: 0px;
display: none;
}
}
......@@ -498,6 +500,7 @@ section.course-content {
ol.subtitles {
right: -(flex-grid(4));
width: auto;
display: block;
}
}
......
function ${ id }_content_updated() {
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
update_schematics();
// dynamic math display: generate MathML on click
$.each($("[id^=display_${ id }_]"), function(index,value){
theid = value.id.replace("display_",""); // ID of the response
if (document.getElementById("input_" + theid)){
MathJax.Hub.queue.Push(function () {
math = MathJax.Hub.getAllJax(value.id)[0];
if (math){
math.Text(document.getElementById("input_" + theid).value);
}
});
};
});
$('#check_${ id }').unbind('click').click(function() {
$("input.schematic").each(function(index,element){ element.schematic.update_value(); });
$(".CodeMirror").each(function(index,element){ if (element.CodeMirror.save) element.CodeMirror.save(); });
// dynamic math display: generate MathML on click
$.each($("[id^=input_${ id }_]"), function(index,value){
theid = value.id.replace("input_",""); // ID of the response
if (document.getElementById("display_" + theid)){
MathJax.Hub.queue.Push(function () {
math = MathJax.Hub.getAllJax("display_" + theid)[0];
if (math){
math.Text(document.getElementById("input_" + theid).value);
UpdateMathML(math,theid);
}
});
};
});
var submit_data={};
$.each($("[id^=input_${ id }_]"), function(index,value){
if (value.type==="checkbox"){
if (value.checked) {
if (typeof submit_data[value.name] == 'undefined'){
submit_data[value.name]=[];
}
submit_data[value.name].push(value.value);
}
}
if (value.type==="radio"){
if (value.checked) {
submit_data[value.name]= value.value;
}
}
else{
submit_data[value.id]=value.value;
}
});
postJSON('${ MITX_ROOT_URL }/modx/problem/${ id }/problem_check',
submit_data,
function(json) {
switch(json.success) {
case 'incorrect': // Worked, but answer not
case 'correct':
$('#main_${ id }').html(json.contents);
${ id }_content_updated();
break;
default:
alert(json.success);
}
}
);
log_event('problem_check', submit_data);
});
$('#reset_${ id }').unbind('click').click(function() {
var submit_data={};
$.each($("[id^=input_${ id }_]"), function(index,value){
submit_data[value.id]=value.value;
});
postJSON('${ MITX_ROOT_URL }/modx/problem/${ id }/problem_reset', {'id':'${ id }'}, function(html_as_json) {
$('#main_${ id }').html(html_as_json);
${ id }_content_updated();
});
log_event('problem_reset', submit_data);
});
// show answer button
// TODO: the button should turn into "hide answer" afterwards
$('#show_${ id }').unbind('click').click(function() {
postJSON('${ MITX_ROOT_URL }/modx/problem/${ id }/problem_show', {}, function(data) {
for (var key in data) {
if ($.isArray(data[key])){
for (var ans_index in data[key]){
var choice_id = 'input_'+key+'_'+data[key][ans_index];
$("label[for="+choice_id+"]").attr("correct_answer", "true");
}
}
// $("#answer_"+key).text(data[key]);
$("#answer_"+key).html(data[key]);
}
});
for (var key in codemirror_set) {
codemirror_set[key].refresh();
}
log_event('problem_show', {'problem':'${ id }'});
});
$('#save_${ id }').unbind('click').click(function() {
$("input.schematic").each(function(index,element){ element.schematic.update_value(); });
var submit_data={};
$.each($("[id^=input_${ id }_]"), function(index,value) {
submit_data[value.id]=value.value;
});
postJSON('${ MITX_ROOT_URL }/modx/problem/${ id }/problem_save',
submit_data,
function(data) {
if(data.success) {
alert('Saved');
}});
log_event('problem_save', submit_data);
});
}
function ${ id }_load() {
$('#main_${ id }').load('${ ajax_url }problem_get?id=${ id }', ${ id }_content_updated);
}
$(function() {
${ id }_load();
});
<section id="problem_${element_id}" class="problems-wrapper" problem-id="${id}" data-url="${ajax_url}"></section>
<section id="problem_${element_id}" class="problems-wrapper" data-problem-id="${id}" data-url="${ajax_url}"></section>
......@@ -22,7 +22,7 @@
<%block name="js_extra">
<script type="text/javascript">
$(function(){
new Sequence('${item_id}', '${element_id}', ${items}, '${tag}', ${position});
new Sequence('${item_id}', 'sequence_${element_id}', ${items}, '${tag}', ${position});
});
</script>
</%block>
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