Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
386029be
Commit
386029be
authored
Jun 30, 2013
by
Julian Arni
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Adding jasmine tests; code cleanup.
parent
b03d9390
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
220 additions
and
162 deletions
+220
-162
common/lib/capa/capa/templates/jsinput.html
+7
-7
common/lib/xmodule/xmodule/css/capa/display.scss
+9
-1
common/lib/xmodule/xmodule/js/src/capa/display.coffee
+1
-1
common/static/js/capa/jsinput.js
+132
-137
common/static/js/capa/spec/jsinput/jsinput.js
+71
-0
common/static/js/capa/spec/jsinput/mainfixture.html
+0
-0
common/static/js/test/jsinput/jsinput.js
+0
-16
No files found.
common/lib/capa/capa/templates/jsinput.html
View file @
386029be
<section
id=
"inputtype_${id}"
class=
"jsinput"
<section
id=
"inputtype_${id}"
class=
"jsinput"
data=
"${gradefn}"
data=
"${gradefn}"
%
if
saved_state:
%
if
saved_state:
data-stored=
"${saved_state|x}"
data-stored=
"${saved_state|x}"
%
endif
%
endif
...
@@ -11,7 +11,7 @@
...
@@ -11,7 +11,7 @@
%
endif
%
endif
>
>
<div
class=
"script_placeholder"
data-src=
"${applet_loader}"
/>
<div
class=
"script_placeholder"
data-src=
"${applet_loader}"
/>
% if status == 'unsubmitted':
% if status == 'unsubmitted':
<div
class=
"unanswered"
id=
"status_${id}"
>
<div
class=
"unanswered"
id=
"status_${id}"
>
...
@@ -23,8 +23,8 @@
...
@@ -23,8 +23,8 @@
<div
class=
"incorrect"
id=
"status_${id}"
>
<div
class=
"incorrect"
id=
"status_${id}"
>
% endif
% endif
<iframe
name=
"iframe_${id}"
<iframe
name=
"iframe_${id}"
id=
"iframe_${id}"
id=
"iframe_${id}"
sandbox=
"allow-scripts allow-popups allow-same-origin allow-forms allow-pointer-lock"
sandbox=
"allow-scripts allow-popups allow-same-origin allow-forms allow-pointer-lock"
seamless=
"seamless"
seamless=
"seamless"
frameborder=
"0"
frameborder=
"0"
...
@@ -32,7 +32,7 @@
...
@@ -32,7 +32,7 @@
height=
"${height}"
height=
"${height}"
width=
"${width}"
width=
"${width}"
/>
/>
<input
type=
"hidden"
name=
"input_${id}"
id=
"input_${id}"
<input
type=
"hidden"
name=
"input_${id}"
id=
"input_${id}"
waitfor=
""
waitfor=
""
value=
"${value|h}"
/>
value=
"${value|h}"
/>
...
@@ -57,7 +57,7 @@
...
@@ -57,7 +57,7 @@
% if status in ['unsubmitted', 'correct', 'incorrect', 'incomplete']:
% if status in ['unsubmitted', 'correct', 'incorrect', 'incomplete']:
</div>
</div>
% endif
% endif
% if msg:
% if msg:
<span
class=
"message"
>
${msg|n}
</span>
<span
class=
"message"
>
${msg|n}
</span>
% endif
% endif
...
...
common/lib/xmodule/xmodule/css/capa/display.scss
View file @
386029be
...
@@ -16,8 +16,16 @@ h2 {
...
@@ -16,8 +16,16 @@ h2 {
}
}
}
}
iframe
[
seamless
]
{
background-color
:
transparent
;
border
:
0px
none
transparent
;
padding
:
0px
;
overflow
:
hidden
;
}
.inline-error
{
.inline-error
{
color
:
darken
(
$error-red
,
1
0
%
);
color
:
darken
(
$error-red
,
1
1
%
);
}
}
...
...
common/lib/xmodule/xmodule/js/src/capa/display.coffee
View file @
386029be
...
@@ -142,7 +142,7 @@ class @Problem
...
@@ -142,7 +142,7 @@ class @Problem
# off @answers
# off @answers
check_waitfor
:
=>
check_waitfor
:
=>
for
inp
in
@
inputs
for
inp
in
@
inputs
if
(
$
(
inp
).
attr
(
"waitfor"
)
?
)
if
(
$
(
inp
).
is
(
"input[waitfor]"
)
)
try
try
$
(
inp
).
data
(
"waitfor"
)()
$
(
inp
).
data
(
"waitfor"
)()
catch
e
catch
e
...
...
common/static/js/capa/jsinput.js
View file @
386029be
...
@@ -4,45 +4,66 @@
...
@@ -4,45 +4,66 @@
// most relevantly, jquery). Keep in mind what happens in which context
// most relevantly, jquery). Keep in mind what happens in which context
// when modifying this file.
// when modifying this file.
/* Check whether there is anything to be done */
// When all the problems are first loaded, we want to make sure the
// constructor only runs once for each iframe; but we also want to make
// sure that if part of the page is reloaded (e.g., a problem is
// submitted), the constructor is called again.
if
(
!
jsinput
)
{
console
.
log
(
"hi"
);
jsinput
=
{
runs
:
1
,
arr
:
[],
exists
:
function
(
id
)
{
jsinput
.
arr
.
filter
(
function
(
e
,
i
,
a
)
{
return
e
.
id
=
id
;
});
}
};
}
jsinput
.
runs
++
;
if
(
$
(
document
).
find
(
'section[class="jsinput"]'
).
length
>
jsinput
.
runs
)
{
return
;
}
/* Utils */
jsinput
.
_DEBUG
=
jsinput
.
_DEBUG
||
true
;
var
debuglog
=
function
(
text
)
{
if
(
jsinput
.
_DEBUG
)
{
console
.
log
(
text
);}};
var
eqTimeout
=
function
(
fn
,
pred
,
time
,
max
)
{
var
i
=
0
;
while
(
pred
(
fn
)
&&
i
<
max
)
{
setTimeout
(
fn
,
time
);
}
return
fn
;
};
var
isUndef
=
function
(
e
)
{
return
(
typeof
(
e
)
===
'undefined'
);
};
// _deepKey and _ctxCall are helper functions used to ensure that gradefn
// etc. can be nested objects (e.g., "firepad.getText") and that when
// called they receive the appropriate objects as "this" (e.g., "firepad").
// Take a string and find the nested object that corresponds to it. E.g.:
// Take a string and find the nested object that corresponds to it. E.g.:
// deepKey(obj, "an.example") -> obj["an"]["example"]
// deepKey(obj, "an.example") -> obj["an"]["example"]
var
_deepKey
=
function
(
obj
,
path
){
var
_deepKey
=
function
(
obj
,
path
){
for
(
var
i
=
0
,
p
ath
=
path
.
split
(
'.'
),
len
=
path
.
length
;
i
<
len
;
i
++
){
for
(
var
i
=
0
,
p
=
path
.
split
(
'.'
),
len
=
p
.
length
;
i
<
len
;
i
++
){
obj
=
obj
[
p
ath
[
i
]];
obj
=
obj
[
p
[
i
]];
}
}
return
obj
;
return
obj
;
};
};
var
_ctxCall
=
function
(
obj
,
fn
)
{
var
func
=
_deepKey
(
obj
,
fn
);
var
oldthis
=
fn
.
split
(
'.'
);
oldthis
.
pop
();
oldthis
=
oldthis
.
join
();
var
newthis
=
_deepKey
(
obj
,
oldthis
);
var
args
=
Array
.
prototype
.
slice
.
call
(
arguments
);
/* END Utils */
args
=
args
.
slice
(
2
,
args
.
length
);
return
func
.
apply
(
newthis
,
args
);
};
// First time this function was called?
var
isFirst
=
typeof
(
jsinput
.
jsinputarr
)
!=
'undefined'
;
// Use this array to keep track of the elements that have already been
// initialized.
jsinput
.
jsinputarr
=
jsinput
.
jsinputarr
||
[];
jsinput
.
jsinputarr
.
exists
=
function
(
id
)
{
this
.
filter
(
function
(
e
,
i
,
a
)
{
return
e
.
id
=
id
;
});
};
function
jsinputConstructor
(
spec
)
{
function
jsinputConstructor
(
spec
)
{
// Define an class that will be instantiated for each jsinput element
// Define an class that will be instantiated for each jsinput element
...
@@ -55,41 +76,29 @@
...
@@ -55,41 +76,29 @@
/* Private methods */
/* Private methods */
var
sect
=
$
(
spec
.
elem
).
parent
().
find
(
'section[class="jsinput"]'
);
var
sect
=
$
(
spec
.
elem
).
parent
().
find
(
'section[class="jsinput"]'
);
var
sectattr
=
function
(
e
)
{
return
$
(
sect
).
attr
(
e
);
};
var
thisIFrame
=
$
(
spec
.
elem
).
find
(
'iframe[name^="iframe_"]'
).
get
(
0
);
var
cWindow
=
thisIFrame
.
contentWindow
;
// Get the hidden input field to pass to customresponse
// Get the hidden input field to pass to customresponse
function
inputfield
()
{
function
_
inputfield
()
{
var
parent
=
$
(
spec
.
elem
).
parent
();
var
parent
=
$
(
spec
.
elem
).
parent
();
return
parent
.
find
(
'input[id^="input_"]'
);
return
parent
.
find
(
'input[id^="input_"]'
);
}
}
var
inputfield
=
_inputfield
();
// For the state and grade functions below, use functions instead of
// storing their return values since we might need to call them
// repeatedly, and they might change (e.g., they might not be defined
// when we first try calling them).
// Get the grade function name
// Get the grade function name
function
getgradefn
()
{
var
getgradefn
=
sectattr
(
"data"
);
return
$
(
sect
).
attr
(
"data"
);
}
// Get state getter
// Get state getter
function
getgetstate
()
{
var
getgetstate
=
sectattr
(
"data-getstate"
);
return
$
(
sect
).
attr
(
"data-getstate"
);
}
// Get state setter
// Get state setter
function
getsetstate
()
{
var
getsetstate
=
sectattr
(
"data-setstate"
);
var
gss
=
$
(
sect
).
attr
(
"data-setstate"
);
return
gss
;
}
// Get stored state
// Get stored state
function
getstoredstate
()
{
var
getstoredstate
=
sectattr
(
"data-stored"
);
return
$
(
sect
).
attr
(
"data-stored"
);
}
var
thisIFrame
=
$
(
spec
.
elem
).
find
(
'iframe[name^="iframe_"]'
).
get
(
0
);
var
cWindow
=
thisIFrame
.
contentWindow
;
// Put the return value of gradefn in the hidden inputfield.
// Put the return value of gradefn in the hidden inputfield.
// If passed an argument, does not call gradefn, and instead directly
// If passed an argument, does not call gradefn, and instead directly
...
@@ -97,28 +106,32 @@
...
@@ -97,28 +106,32 @@
var
update
=
function
(
answer
)
{
var
update
=
function
(
answer
)
{
var
ans
;
var
ans
;
ans
=
_
ctxCall
(
cWindow
,
gradefn
);
ans
=
_
deepKey
(
cWindow
,
gradefn
);
// setstate presumes getstate, so don't getstate unless setstate is
// setstate presumes getstate, so don't getstate unless setstate is
// defined.
// defined.
if
(
getgetstate
()
&&
getsetstate
()
)
{
if
(
getgetstate
&&
getsetstate
)
{
var
state
,
store
;
var
state
,
store
;
state
=
_
ctxCall
(
cWindow
,
getgetstate
()
);
state
=
_
deepKey
(
cWindow
,
getgetstate
);
store
=
{
store
=
{
answer
:
ans
,
answer
:
ans
,
state
:
state
state
:
state
};
};
inputfield
().
val
(
JSON
.
stringify
(
store
));
debuglog
(
"Store: "
+
store
);
inputfield
.
val
(
JSON
.
stringify
(
store
));
}
else
{
}
else
{
inputfield
().
val
(
ans
);
inputfield
.
val
(
ans
);
debuglog
(
"Answer: "
+
ans
);
}
}
return
;
return
;
};
};
// Find the update button, and bind the update function to its click
// Find the update button, and bind the update function to its click
// event.
// event.
function
updateHandler
()
{
function
bindUpdate
()
{
var
updatebutton
=
$
(
spec
.
elem
).
var
updatebutton
=
$
(
spec
.
elem
).
find
(
'button[class="update"]'
).
get
(
0
);
find
(
'button[class="update"]'
).
get
(
0
);
$
(
updatebutton
).
click
(
update
);
$
(
updatebutton
).
click
(
update
);
}
}
...
@@ -130,111 +143,93 @@
...
@@ -130,111 +143,93 @@
/* Initialization */
/* Initialization */
jsinput
.
jsinput
arr
.
push
(
that
);
jsinput
.
arr
.
push
(
that
);
// Put the update function as the value of the inputfield's "waitfor"
// Put the update function as the value of the inputfield's "waitfor"
// attribute so that it is called when the check button is clicked.
// attribute so that it is called when the check button is clicked.
function
bindCheck
()
{
function
bindCheck
()
{
inputfield
().
data
(
'waitfor'
,
that
.
update
);
debuglog
(
"Update function: "
+
that
.
update
);
inputfield
.
data
(
'waitfor'
,
that
.
update
);
return
;
return
;
}
}
var
gradefn
=
getgradefn
();
var
gradefn
=
getgradefn
;
debuglog
(
"Gradefn: "
+
gradefn
);
if
(
spec
.
passive
===
false
)
{
if
(
spec
.
passive
===
false
)
{
updateHandler
();
// If there is a separate "Update" button, bind update to it.
bindUpdate
();
}
else
{
// Otherwise, bind update to the check button.
bindCheck
();
bindCheck
();
// Check whether application takes in state and there is a saved
}
// state to give it. If getsetstate is specified but calling it
// fails, wait and try again, since the iframe might still be
bindCheck
();
// loading.
if
(
getsetstate
()
&&
getstoredstate
())
{
// Check whether application takes in state and there is a saved
var
sval
;
// state to give it. If getsetstate is specified but calling it
if
(
typeof
(
getstoredstate
())
===
"object"
)
{
// fails, wait and try again, since the iframe might still be
sval
=
getstoredstate
()[
"state"
];
// loading.
}
else
{
if
(
getsetstate
&&
getstoredstate
)
{
sval
=
getstoredstate
();
var
sval
;
}
if
(
typeof
(
getstoredstate
)
===
"object"
)
{
function
whileloop
(
n
)
{
sval
=
getstoredstate
[
"state"
];
if
(
n
<
10
){
}
else
{
try
{
sval
=
getstoredstate
;
_ctxCall
(
cWindow
,
getsetstate
(),
sval
);
}
}
catch
(
err
)
{
setTimeout
(
whileloop
(
n
+
1
),
200
);
debuglog
(
"Stored state: "
+
sval
);
}
debuglog
(
"Set_statefn: "
+
getsetstate
);
}
else
{
function
whileloop
(
n
)
{
console
.
log
(
"Error: could not set state"
);
if
(
n
<
10
){
try
{
_deepKey
(
cWindow
,
getsetstate
)(
sval
);
}
catch
(
err
)
{
setTimeout
(
whileloop
(
n
+
1
),
200
);
}
}
}
}
whileloop
(
0
);
else
{
debuglog
(
"Error: could not set state"
);
_deepKey
(
cWindow
,
getsetstate
)(
sval
);
}
}
}
}
else
{
whileloop
(
0
);
// NOT CURRENTLY SUPPORTED
// If set up to passively receive updates (intercept a function's
// return value whenever the function is called) add an event
// listener that listens to messages that match "that"'s id.
// Decorate the iframe gradefn with updateDecorator.
iframe
.
contentWindow
[
gradefn
]
=
updateDecorator
(
iframe
.
contentWindow
[
gradefn
]);
iframe
.
contentWindow
.
addEventListener
(
'message'
,
function
(
e
)
{
var
id
=
e
.
data
[
0
],
msg
=
e
.
data
[
1
];
if
(
id
===
spec
.
id
)
{
update
(
msg
);
}
});
}
}
return
that
;
return
that
;
}
}
function
updateDecorator
(
fn
,
id
)
{
// NOT CURRENTLY SUPPORTED
// Simple function decorator that posts the output of a function to the
// parent iframe before returning the original function's value.
// Can be used to decorate one or more gradefn (instead of using an
// explicit "Update" button) when gradefn is automatically called as part
// of an application's natural behavior.
// The id argument is used to specify which of the instances of jsinput on
// the parent page the message is being posted to.
return
function
()
{
var
result
=
fn
.
apply
(
null
,
arguments
);
window
.
parent
.
contentWindow
.
postMessage
([
id
,
result
],
document
.
referrer
);
return
result
;
};
}
function
walkDOM
()
{
function
walkDOM
()
{
// Find all jsinput elements, and create a jsinput object for each one
var
all
=
$
(
document
).
find
(
'section[class="jsinput"]'
);
var
newid
;
var
newid
;
all
.
each
(
function
()
{
// Find all jsinput elements, and create a jsinput object for each one
var
all
=
$
(
document
).
find
(
'section[class="jsinput"]'
);
all
.
each
(
function
(
index
,
value
)
{
// Get just the mako variable 'id' from the id attribute
// Get just the mako variable 'id' from the id attribute
newid
=
$
(
this
).
attr
(
"id"
).
replace
(
/^inputtype_/
,
""
);
newid
=
$
(
value
).
attr
(
"id"
).
replace
(
/^inputtype_/
,
""
);
if
(
!
jsinput
.
jsinputarr
.
exists
(
newid
)){
if
(
!
jsinput
.
exists
(
newid
)){
var
newJsElem
=
jsinputConstructor
({
var
newJsElem
=
jsinputConstructor
({
id
:
newid
,
id
:
newid
,
elem
:
this
,
elem
:
value
,
passive
:
fals
e
passive
:
tru
e
});
});
}
}
});
});
}
}
// TODO: Inject css into, and retrieve frame size from, the iframe (for non
// This is ugly, but without a timeout pages with multiple/heavy jsinputs
// "seamless"-supporting browsers).
// don't load properly.
//var iframeInjection = {
if
(
$
.
isReady
)
{
//injectStyles : function (style) {
setTimeout
(
walkDOM
,
1000
);
//$(document.body).css(style);
}
else
{
//},
$
(
document
).
ready
(
setTimeout
(
walkDOM
,
1000
));
//sendMySize : function () {
}
//var height = html.height,
//width = html.width;
})(
window
.
jsinput
=
window
.
jsinput
||
false
);
//window.parent.postMessage(['height', height], '*');
//window.parent.postMessage(['width', width], '*');
//}
//};
setTimeout
(
walkDOM
,
100
);
})(
window
.
jsinput
=
window
.
jsinput
||
{})
common/static/js/capa/spec/jsinput/jsinput.js
0 → 100644
View file @
386029be
describe
(
"A jsinput has:"
,
function
()
{
beforeEach
(
function
()
{
$
(
'#fixture'
).
remove
();
$
.
ajax
({
async
:
false
,
url
:
'mainfixture.html'
,
success
:
function
(
data
)
{
$
(
'body'
).
append
(
$
(
data
));
}
});
});
describe
(
"The ctxCall function"
,
function
()
{
it
(
"Evaluatates nested-object functions"
,
function
()
{
var
ctxTest
=
{
ctxFn
:
function
()
{
return
this
.
name
;
}
};
var
fnString
=
"nest.ctxFn"
;
var
holder
=
{};
holder
.
nest
=
ctxTest
;
var
fn
=
_ctxCall
(
holder
,
fnString
);
expect
(
fnString
).
toBe
(
holder
.
nest
.
ctxFn
());
});
it
(
"Throws an exception when the object does not exits"
,
function
()
{
var
notObj
=
_ctxCall
(
"twas"
,
"brilling"
);
expect
(
notObj
).
toThrow
();
});
it
(
"Throws an exception when the function does not exist"
,
function
()
{
var
anobj
=
{};
var
notFn
=
_ctxCall
(
"anobj"
,
"brillig"
);
expect
(
notFn
).
toThrow
();
});
});
describe
(
"The jsinput constructor"
,
function
(){
var
testJsElem
=
jsinputConstructor
({
id
:
3781
,
elem
:
"<div id='abc'> a div </div>"
,
passive
:
false
});
it
(
"Returns an object"
,
function
(){
expect
(
typeof
(
testJsElem
)).
toEqual
(
'object'
);
});
it
(
"Adds the object to the jsinput array"
,
function
()
{
expect
(
jsinput
.
jsinputarr
.
exists
(
3781
)).
toBe
(
true
);
});
describe
(
"The returned object"
,
function
()
{
it
(
"Has a public 'update' method"
,
function
(){
expect
(
testJsElem
.
update
).
toBeDefined
();
});
it
(
"Changes the parent's inputfield"
,
function
()
{
})
});
});
}
)
common/static/js/
test
/jsinput/mainfixture.html
→
common/static/js/
capa/spec
/jsinput/mainfixture.html
View file @
386029be
File moved
common/static/js/test/jsinput/jsinput.js
deleted
100644 → 0
View file @
b03d9390
describe
(
"jsinput test"
,
function
()
{
beforeEach
(
function
()
{
$
(
'#fixture'
).
remove
();
$
.
ajax
({
async
:
false
,
url
:
'mainfixture.html'
,
success
:
function
(
data
)
{
$
(
'body'
).
append
(
$
(
data
));
}
});
});
it
(
""
)
}
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment