Commit 8554c3f0 by Anton Stupak

Merge pull request #1914 from edx/anton/show-answer-imageresponse

Show answer for imageresponse
parents 9c8e90a6 335a36f1
...@@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes, ...@@ -5,6 +5,8 @@ These are notable changes in edx-platform. This is a rolling list of changes,
in roughly chronological order, most recent first. Add your entries at or near in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected. the top. Include a label indicating the component affected.
Blades: Show answer for imageresponse. BLD-21.
Blades: LTI additional Python tests. LTI must use HTTPS for Blades: LTI additional Python tests. LTI must use HTTPS for
lis_outcome_service_url. BLD-564. lis_outcome_service_url. BLD-564.
......
...@@ -34,6 +34,7 @@ requirejs.config({ ...@@ -34,6 +34,7 @@ requirejs.config({
"sinon": "xmodule_js/common_static/js/vendor/sinon-1.7.1", "sinon": "xmodule_js/common_static/js/vendor/sinon-1.7.1",
"squire": "xmodule_js/common_static/js/vendor/Squire", "squire": "xmodule_js/common_static/js/vendor/Squire",
"jasmine-jquery": "xmodule_js/common_static/js/vendor/jasmine-jquery", "jasmine-jquery": "xmodule_js/common_static/js/vendor/jasmine-jquery",
"jasmine-imagediff": "xmodule_js/common_static/js/vendor/jasmine-imagediff",
"jasmine-stealth": "xmodule_js/common_static/js/vendor/jasmine-stealth", "jasmine-stealth": "xmodule_js/common_static/js/vendor/jasmine-stealth",
"jasmine.async": "xmodule_js/common_static/js/vendor/jasmine.async", "jasmine.async": "xmodule_js/common_static/js/vendor/jasmine.async",
"draggabilly": "xmodule_js/common_static/js/vendor/draggabilly.pkgd", "draggabilly": "xmodule_js/common_static/js/vendor/draggabilly.pkgd",
...@@ -151,6 +152,9 @@ requirejs.config({ ...@@ -151,6 +152,9 @@ requirejs.config({
"jasmine-jquery": { "jasmine-jquery": {
deps: ["jasmine"] deps: ["jasmine"]
}, },
"jasmine-imagediff": {
deps: ["jasmine"]
},
"jasmine-stealth": { "jasmine-stealth": {
deps: ["jasmine"] deps: ["jasmine"]
}, },
......
...@@ -47,6 +47,7 @@ lib_paths: ...@@ -47,6 +47,7 @@ lib_paths:
- xmodule_js/common_static/js/vendor/Squire.js - xmodule_js/common_static/js/vendor/Squire.js
- xmodule_js/common_static/js/vendor/jasmine-jquery.js - xmodule_js/common_static/js/vendor/jasmine-jquery.js
- xmodule_js/common_static/js/vendor/jasmine-stealth.js - xmodule_js/common_static/js/vendor/jasmine-stealth.js
- xmodule_js/common_static/js/vendor/jasmine-imagediff.js
- xmodule_js/common_static/js/vendor/jasmine.async.js - xmodule_js/common_static/js/vendor/jasmine.async.js
- xmodule_js/common_static/js/vendor/jquery.maskedinput.min.js - xmodule_js/common_static/js/vendor/jquery.maskedinput.min.js
- xmodule_js/common_static/js/vendor/CodeMirror/codemirror.js - xmodule_js/common_static/js/vendor/CodeMirror/codemirror.js
......
...@@ -46,6 +46,7 @@ lib_paths: ...@@ -46,6 +46,7 @@ lib_paths:
- xmodule_js/common_static/js/vendor/Squire.js - xmodule_js/common_static/js/vendor/Squire.js
- xmodule_js/common_static/js/vendor/jasmine-jquery.js - xmodule_js/common_static/js/vendor/jasmine-jquery.js
- xmodule_js/common_static/js/vendor/jasmine-stealth.js - xmodule_js/common_static/js/vendor/jasmine-stealth.js
- xmodule_js/common_static/js/vendor/jasmine-imagediff.js
- xmodule_js/common_static/js/vendor/jasmine.async.js - xmodule_js/common_static/js/vendor/jasmine.async.js
- xmodule_js/common_static/js/vendor/CodeMirror/codemirror.js - xmodule_js/common_static/js/vendor/CodeMirror/codemirror.js
- xmodule_js/src/xmodule.js - xmodule_js/src/xmodule.js
......
...@@ -2167,7 +2167,7 @@ class ImageResponse(LoncapaResponse): ...@@ -2167,7 +2167,7 @@ class ImageResponse(LoncapaResponse):
answers = {} answers = {}
for ielt in self.ielements: for ielt in self.ielements:
ie_id = ielt.get('id') ie_id = ielt.get('id')
answers[ie_id] = (ielt.get('rectangle'), ielt.get('regions')) answers[ie_id] = {'rectangle': ielt.get('rectangle'), 'regions': ielt.get('regions')}
return answers return answers
......
<span> <div class="imageinput capa_inputtype" id="inputtype_${id}">
<input <input
type="hidden" type="hidden"
class="imageinput" class="imageinput"
...@@ -7,10 +7,16 @@ ...@@ -7,10 +7,16 @@
id="input_${id}" id="input_${id}"
value="${value}" value="${value}"
/> />
<div style="position:relative;">
<div <div
id="imageinput_${id}" id="imageinput_${id}"
style="background-image: url('${src}'); width: ${width}px; height: ${height}px; position: relative; left: 0; top: 0;" style="
background-image: url('${src}');
width: ${width}px;
height: ${height}px;
position: relative;
left: 0;
top: 0;"
> >
<img <img
src="${STATIC_URL}green-pointer.png" src="${STATIC_URL}green-pointer.png"
...@@ -18,6 +24,16 @@ ...@@ -18,6 +24,16 @@
style="position: absolute; top: ${gy}px; left: ${gx}px;" style="position: absolute; top: ${gy}px; left: ${gx}px;"
/> />
</div> </div>
<div
data-width="${width}"
data-height="${height}"
id="answer_${id}"
style="
position: absolute;
left: 0;
top: 0;"
></div>
</div>
<script type="text/javascript" charset="utf-8"> <script type="text/javascript" charset="utf-8">
(new ImageInput('${id}')); (new ImageInput('${id}'));
...@@ -57,4 +73,4 @@ ...@@ -57,4 +73,4 @@
<span class="sr">Status: incorrect</span> <span class="sr">Status: incorrect</span>
</span> </span>
% endif % endif
</span> </div>
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<!-- ${width} = 300 --> <!-- ${width} = 300 -->
<!-- ${height} = 400 --> <!-- ${height} = 400 -->
<span> <div class="imageinput capa_inputtype" id="inputtype_12345">
<input <input
type="hidden" type="hidden"
class="imageinput" class="imageinput"
...@@ -12,12 +12,16 @@ ...@@ -12,12 +12,16 @@
value="" value=""
/> />
<div style="position:relative;">
<div <div
id="imageinput_12345" id="imageinput_12345"
style="width: 300px; height: 400px; position: relative; left: 0; top: 0; visibility: hidden;" style="width: 300px; height: 400px; position: relative; left: 0; top: 0; visibility: hidden;"
> >
<!-- image will go here --> <!-- image will go here -->
</div> </div>
<div id="answer_12345" data-width="100" data-height="100"></div>
</div>
<!-- status == 'unsubmitted' --> <!-- status == 'unsubmitted' -->
<span <span
...@@ -28,4 +32,4 @@ ...@@ -28,4 +32,4 @@
> >
<span class="sr">Status: unanswered</span> <span class="sr">Status: unanswered</span>
</span> </span>
</span> </div>
...@@ -36,6 +36,7 @@ lib_paths: ...@@ -36,6 +36,7 @@ lib_paths:
- common_static/coffee/src/ajax_prefix.js - common_static/coffee/src/ajax_prefix.js
- common_static/coffee/src/logger.js - common_static/coffee/src/logger.js
- common_static/js/vendor/jasmine-jquery.js - common_static/js/vendor/jasmine-jquery.js
- common_static/js/vendor/jasmine-imagediff.js
- common_static/js/vendor/require.js - common_static/js/vendor/require.js
- RequireJS-namespace-undefine.js - RequireJS-namespace-undefine.js
- common_static/js/vendor/jquery.min.js - common_static/js/vendor/jquery.min.js
......
...@@ -303,6 +303,102 @@ describe 'Problem', -> ...@@ -303,6 +303,102 @@ describe 'Problem', ->
expect($('input#1_2_1_choiceinput_2bc').attr('disabled')).not.toEqual('disabled') expect($('input#1_2_1_choiceinput_2bc').attr('disabled')).not.toEqual('disabled')
expect($('input#1_2_1').attr('disabled')).not.toEqual('disabled') expect($('input#1_2_1').attr('disabled')).not.toEqual('disabled')
describe 'imageinput', ->
imageinput_html = readFixtures('imageinput.html')
states = [
{
desc: 'rectangle is drawn correctly',
data: {'rectangle': '(10,10)-(30,30)'}
},
{
desc: 'region is drawn correctly',
data: {'regions': '[[10,10],[30,30],[70,30],[20,30]]'}
},
{
desc: 'mixed shapes are drawn correctly',
data: {
'rectangle': '(10,10)-(30,30);(5,5)-(20,20)',
'regions': '''[
[[50,50],[40,40],[70,30],[50,70]],
[[90,95],[95,95],[90,70],[70,70]]
]'''
}
},
]
beforeEach ->
@problem = new Problem($('.xblock-student_view'))
@problem.el.prepend imageinput_html
stubRequest = (data) =>
spyOn($, 'postWithPrefix').andCallFake (url, callback) ->
callback answers: "12345": data
getImage = (coords, c_width, c_height) =>
types =
rectangle: (coords) =>
reg = /^\(([0-9]+),([0-9]+)\)-\(([0-9]+),([0-9]+)\)$/
rects = coords.replace(/\s*/g, '').split(/;/)
$.each rects, (index, rect) =>
abs = Math.abs
points = reg.exec(rect)
if points
width = abs(points[3] - points[1])
height = abs(points[4] - points[2])
ctx.rect(points[1], points[2], width, height)
ctx.stroke()
ctx.fill()
regions: (coords) =>
parseCoords = (coords) =>
reg = JSON.parse(coords)
if typeof reg[0][0][0] == "undefined"
reg = [reg]
return reg
$.each parseCoords(coords), (index, region) =>
ctx.beginPath()
$.each region, (index, point) =>
if index is 0
ctx.moveTo(point[0], point[1])
else
ctx.lineTo(point[0], point[1]);
ctx.closePath()
ctx.stroke()
ctx.fill()
canvas = document.createElement('canvas')
canvas.width = c_width or 100
canvas.height = c_height or 100
if canvas.getContext
ctx = canvas.getContext('2d')
else
return console.log 'Canvas is not supported.'
ctx.fillStyle = 'rgba(255,255,255,.3)';
ctx.strokeStyle = "#FF0000";
ctx.lineWidth = "2";
$.each coords, (key, value) =>
types[key](value) if types[key]?
return canvas
$.each states, (index, state) =>
it state.desc, ->
stubRequest(state.data)
@problem.show()
img = getImage(state.data)
expect(img).toImageDiffEqual($('canvas')[0])
describe 'when the answers are already shown', -> describe 'when the answers are already shown', ->
beforeEach -> beforeEach ->
@problem.el.addClass 'showed' @problem.el.addClass 'showed'
...@@ -409,4 +505,3 @@ describe 'Problem', -> ...@@ -409,4 +505,3 @@ describe 'Problem', ->
expect(@problem.answers).toEqual "input_1_1=one&input_1_2=two" expect(@problem.answers).toEqual "input_1_1=one&input_1_2=two"
...@@ -162,6 +162,8 @@ beforeEach -> ...@@ -162,6 +162,8 @@ beforeEach ->
toBeInArray: (array) -> toBeInArray: (array) ->
return $.inArray(@.actual, array) > -1 return $.inArray(@.actual, array) > -1
@addMatchers imagediff.jasmine
# Stub jQuery.cookie # Stub jQuery.cookie
$.cookie = jasmine.createSpy('jQuery.cookie').andReturn '1.0' $.cookie = jasmine.createSpy('jQuery.cookie').andReturn '1.0'
......
...@@ -482,6 +482,82 @@ class @Problem ...@@ -482,6 +482,82 @@ class @Problem
for choice in answer for choice in answer
element.find("section#forinput#{choice}").addClass 'choicetextgroup_show_correct' element.find("section#forinput#{choice}").addClass 'choicetextgroup_show_correct'
imageinput: (element, display, answers) =>
# answers is a dict of (answer_id, answer_text) for each answer for this
# question.
# @Examples:
# {'anwser_id': {
# 'rectangle': '(10,10)-(20,30);(12,12)-(40,60)',
# 'regions': '[[10,10], [30,30], [10, 30], [30, 10]]'
# } }
types =
rectangle: (coords) =>
reg = /^\(([0-9]+),([0-9]+)\)-\(([0-9]+),([0-9]+)\)$/
rects = coords.replace(/\s*/g, '').split(/;/)
$.each rects, (index, rect) =>
abs = Math.abs
points = reg.exec(rect)
if points
width = abs(points[3] - points[1])
height = abs(points[4] - points[2])
ctx.rect(points[1], points[2], width, height)
ctx.stroke()
ctx.fill()
regions: (coords) =>
parseCoords = (coords) =>
reg = JSON.parse(coords)
# Regions is list of lists [region1, region2, region3, ...] where regionN
# is disordered list of points: [[1,1], [100,100], [50,50], [20, 70]].
# If there is only one region in the list, simpler notation can be used:
# regions="[[10,10], [30,30], [10, 30], [30, 10]]" (without explicitly
# setting outer list)
if typeof reg[0][0][0] == "undefined"
# we have [[1,2],[3,4],[5,6]] - single region
# instead of [[[1,2],[3,4],[5,6], [[1,2],[3,4],[5,6]]]
# or [[[1,2],[3,4],[5,6]]] - multiple regions syntax
reg = [reg]
return reg
$.each parseCoords(coords), (index, region) =>
ctx.beginPath()
$.each region, (index, point) =>
if index is 0
ctx.moveTo(point[0], point[1])
else
ctx.lineTo(point[0], point[1]);
ctx.closePath()
ctx.stroke()
ctx.fill()
element = $(element)
id = element.attr('id').replace(/inputtype_/,'')
container = element.find("#answer_#{id}")
canvas = document.createElement('canvas')
canvas.width = container.data('width')
canvas.height = container.data('height')
if canvas.getContext
ctx = canvas.getContext('2d')
else
return console.log 'Canvas is not supported.'
ctx.fillStyle = 'rgba(255,255,255,.3)';
ctx.strokeStyle = "#FF0000";
ctx.lineWidth = "2";
$.each answers, (key, answer) =>
$.each answer, (key, value) =>
types[key](value) if types[key]?
container.html(canvas)
inputtypeHideAnswerMethods: inputtypeHideAnswerMethods:
choicegroup: (element, display) => choicegroup: (element, display) =>
element = $(element) element = $(element)
......
// js-imagediff 1.0.3
// (c) 2011-2012 Carl Sutherland, Humble Software
// Distributed under the MIT License
// For original source and documentation visit:
// http://www.github.com/HumbleSoftware/js-imagediff
(function (name, definition) {
var root = this;
if (typeof module !== 'undefined') {
var Canvas = require('canvas');
module.exports = definition(root, name, Canvas);
} else if (typeof define === 'function' && typeof define.amd === 'object') {
define(definition);
} else {
root[name] = definition(root, name);
}
})('imagediff', function (root, name, Canvas) {
var
TYPE_ARRAY = /\[object Array\]/i,
TYPE_CANVAS = /\[object (Canvas|HTMLCanvasElement)\]/i,
TYPE_CONTEXT = /\[object CanvasRenderingContext2D\]/i,
TYPE_IMAGE = /\[object (Image|HTMLImageElement)\]/i,
TYPE_IMAGE_DATA = /\[object ImageData\]/i,
UNDEFINED = 'undefined',
canvas = getCanvas(),
context = canvas.getContext('2d'),
previous = root[name],
imagediff, jasmine;
// Creation
function getCanvas (width, height) {
var
canvas = Canvas ?
new Canvas() :
document.createElement('canvas');
if (width) canvas.width = width;
if (height) canvas.height = height;
return canvas;
}
function getImageData (width, height) {
canvas.width = width;
canvas.height = height;
context.clearRect(0, 0, width, height);
return context.createImageData(width, height);
}
// Type Checking
function isImage (object) {
return isType(object, TYPE_IMAGE);
}
function isCanvas (object) {
return isType(object, TYPE_CANVAS);
}
function isContext (object) {
return isType(object, TYPE_CONTEXT);
}
function isImageData (object) {
return !!(object &&
isType(object, TYPE_IMAGE_DATA) &&
typeof(object.width) !== UNDEFINED &&
typeof(object.height) !== UNDEFINED &&
typeof(object.data) !== UNDEFINED);
}
function isImageType (object) {
return (
isImage(object) ||
isCanvas(object) ||
isContext(object) ||
isImageData(object)
);
}
function isType (object, type) {
return typeof (object) === 'object' && !!Object.prototype.toString.apply(object).match(type);
}
// Type Conversion
function copyImageData (imageData) {
var
height = imageData.height,
width = imageData.width,
data = imageData.data,
newImageData, newData, i;
canvas.width = width;
canvas.height = height;
newImageData = context.getImageData(0, 0, width, height);
newData = newImageData.data;
for (i = imageData.data.length; i--;) {
newData[i] = data[i];
}
return newImageData;
}
function toImageData (object) {
if (isImage(object)) { return toImageDataFromImage(object); }
if (isCanvas(object)) { return toImageDataFromCanvas(object); }
if (isContext(object)) { return toImageDataFromContext(object); }
if (isImageData(object)) { return object; }
}
function toImageDataFromImage (image) {
var
height = image.height,
width = image.width;
canvas.width = width;
canvas.height = height;
context.clearRect(0, 0, width, height);
context.drawImage(image, 0, 0);
return context.getImageData(0, 0, width, height);
}
function toImageDataFromCanvas (canvas) {
var
height = canvas.height,
width = canvas.width,
context = canvas.getContext('2d');
return context.getImageData(0, 0, width, height);
}
function toImageDataFromContext (context) {
var
canvas = context.canvas,
height = canvas.height,
width = canvas.width;
return context.getImageData(0, 0, width, height);
}
function toCanvas (object) {
var
data = toImageData(object),
canvas = getCanvas(data.width, data.height),
context = canvas.getContext('2d');
context.putImageData(data, 0, 0);
return canvas;
}
// ImageData Equality Operators
function equalWidth (a, b) {
return a.width === b.width;
}
function equalHeight (a, b) {
return a.height === b.height;
}
function equalDimensions (a, b) {
return equalHeight(a, b) && equalWidth(a, b);
}
function equal (a, b, tolerance) {
var
aData = a.data,
bData = b.data,
length = aData.length,
i;
tolerance = tolerance || 0;
if (!equalDimensions(a, b)) return false;
for (i = length; i--;) if (aData[i] !== bData[i] && Math.abs(aData[i] - bData[i]) > tolerance) return false;
return true;
}
// Diff
function diff (a, b) {
return (equalDimensions(a, b) ? diffEqual : diffUnequal)(a, b);
}
function diffEqual (a, b) {
var
height = a.height,
width = a.width,
c = getImageData(width, height), // c = a - b
aData = a.data,
bData = b.data,
cData = c.data,
length = cData.length,
row, column,
i, j, k, v;
for (i = 0; i < length; i += 4) {
cData[i] = Math.abs(aData[i] - bData[i]);
cData[i+1] = Math.abs(aData[i+1] - bData[i+1]);
cData[i+2] = Math.abs(aData[i+2] - bData[i+2]);
cData[i+3] = Math.abs(255 - Math.abs(aData[i+3] - bData[i+3]));
}
return c;
}
function diffUnequal (a, b) {
var
height = Math.max(a.height, b.height),
width = Math.max(a.width, b.width),
c = getImageData(width, height), // c = a - b
aData = a.data,
bData = b.data,
cData = c.data,
rowOffset,
columnOffset,
row, column,
i, j, k, v;
for (i = cData.length - 1; i > 0; i = i - 4) {
cData[i] = 255;
}
// Add First Image
offsets(a);
for (row = a.height; row--;){
for (column = a.width; column--;) {
i = 4 * ((row + rowOffset) * width + (column + columnOffset));
j = 4 * (row * a.width + column);
cData[i+0] = aData[j+0]; // r
cData[i+1] = aData[j+1]; // g
cData[i+2] = aData[j+2]; // b
// cData[i+3] = aData[j+3]; // a
}
}
// Subtract Second Image
offsets(b);
for (row = b.height; row--;){
for (column = b.width; column--;) {
i = 4 * ((row + rowOffset) * width + (column + columnOffset));
j = 4 * (row * b.width + column);
cData[i+0] = Math.abs(cData[i+0] - bData[j+0]); // r
cData[i+1] = Math.abs(cData[i+1] - bData[j+1]); // g
cData[i+2] = Math.abs(cData[i+2] - bData[j+2]); // b
}
}
// Helpers
function offsets (imageData) {
rowOffset = Math.floor((height - imageData.height) / 2);
columnOffset = Math.floor((width - imageData.width) / 2);
}
return c;
}
// Validation
function checkType () {
var i;
for (i = 0; i < arguments.length; i++) {
if (!isImageType(arguments[i])) {
throw {
name : 'ImageTypeError',
message : 'Submitted object was not an image.'
};
}
}
}
// Jasmine Matchers
function get (element, content) {
element = document.createElement(element);
if (element && content) {
element.innerHTML = content;
}
return element;
}
jasmine = {
toBeImageData : function () {
return imagediff.isImageData(this.actual);
},
toImageDiffEqual : function (expected, tolerance) {
if (typeof (document) !== UNDEFINED) {
this.message = function () {
var
div = get('div'),
a = get('div', '<div>Actual:</div>'),
b = get('div', '<div>Expected:</div>'),
c = get('div', '<div>Diff:</div>'),
diff = imagediff.diff(this.actual, expected),
canvas = getCanvas(),
context;
canvas.height = diff.height;
canvas.width = diff.width;
div.style.overflow = 'hidden';
a.style.float = 'left';
b.style.float = 'left';
c.style.float = 'left';
context = canvas.getContext('2d');
context.putImageData(diff, 0, 0);
a.appendChild(toCanvas(this.actual));
b.appendChild(toCanvas(expected));
c.appendChild(canvas);
div.appendChild(a);
div.appendChild(b);
div.appendChild(c);
return [
div,
"Expected not to be equal."
];
};
}
return imagediff.equal(this.actual, expected, tolerance);
}
};
// Image Output
function imageDataToPNG (imageData, outputFile, callback) {
var
canvas = toCanvas(imageData),
base64Data,
decodedImage;
callback = callback || Function;
base64Data = canvas.toDataURL().replace(/^data:image\/\w+;base64,/,"");
decodedImage = new Buffer(base64Data, 'base64');
require('fs').writeFile(outputFile, decodedImage, callback);
}
// Definition
imagediff = {
createCanvas : getCanvas,
createImageData : getImageData,
isImage : isImage,
isCanvas : isCanvas,
isContext : isContext,
isImageData : isImageData,
isImageType : isImageType,
toImageData : function (object) {
checkType(object);
if (isImageData(object)) { return copyImageData(object); }
return toImageData(object);
},
equal : function (a, b, tolerance) {
checkType(a, b);
a = toImageData(a);
b = toImageData(b);
return equal(a, b, tolerance);
},
diff : function (a, b) {
checkType(a, b);
a = toImageData(a);
b = toImageData(b);
return diff(a, b);
},
jasmine : jasmine,
// Compatibility
noConflict : function () {
root[name] = previous;
return imagediff;
}
};
if (typeof module !== 'undefined') {
imagediff.imageDataToPNG = imageDataToPNG;
}
return imagediff;
});
...@@ -30,6 +30,7 @@ prepend_path: common/static ...@@ -30,6 +30,7 @@ prepend_path: common/static
lib_paths: lib_paths:
- js/vendor/jquery.min.js - js/vendor/jquery.min.js
- js/vendor/jasmine-jquery.js - js/vendor/jasmine-jquery.js
- js/vendor/jasmine-imagediff.js
- js/vendor/underscore-min.js - js/vendor/underscore-min.js
- js/vendor/backbone-min.js - js/vendor/backbone-min.js
- js/vendor/jquery.timeago.js - js/vendor/jquery.timeago.js
......
...@@ -31,6 +31,7 @@ lib_paths: ...@@ -31,6 +31,7 @@ lib_paths:
- xmodule_js/common_static/coffee/src/ajax_prefix.js - xmodule_js/common_static/coffee/src/ajax_prefix.js
- xmodule_js/common_static/coffee/src/logger.js - xmodule_js/common_static/coffee/src/logger.js
- xmodule_js/common_static/js/vendor/jasmine-jquery.js - xmodule_js/common_static/js/vendor/jasmine-jquery.js
- xmodule_js/common_static/js/vendor/jasmine-imagediff.js
- xmodule_js/common_static/js/vendor/require.js - xmodule_js/common_static/js/vendor/require.js
- js/RequireJS-namespace-undefine.js - js/RequireJS-namespace-undefine.js
- xmodule_js/common_static/js/vendor/jquery.min.js - xmodule_js/common_static/js/vendor/jquery.min.js
......
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