diff --git a/common/lib/xmodule/xmodule/gst_module.py b/common/lib/xmodule/xmodule/gst_module.py
index a6b0fb516b..c29e4e6a04 100644
--- a/common/lib/xmodule/xmodule/gst_module.py
+++ b/common/lib/xmodule/xmodule/gst_module.py
@@ -37,6 +37,7 @@ class GraphicalSliderToolModule(XModule):
resource_string(__name__, 'js/src/graphical_slider_tool/inputs.js'),
resource_string(__name__, 'js/src/graphical_slider_tool/graph.js'),
resource_string(__name__, 'js/src/graphical_slider_tool/el_output.js'),
+ resource_string(__name__, 'js/src/graphical_slider_tool/g_label_el_output.js'),
resource_string(__name__, 'js/src/graphical_slider_tool/gst.js')
]
diff --git a/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/g_label_el_output.js b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/g_label_el_output.js
new file mode 100644
index 0000000000..feac031aee
--- /dev/null
+++ b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/g_label_el_output.js
@@ -0,0 +1,91 @@
+// Wrapper for RequireJS. It will make the standard requirejs(), require(), and
+// define() functions from Require JS available inside the anonymous function.
+(function (requirejs, require, define) {
+
+define('GLabelElOutput', ['logme'], function (logme) {
+ return GLabelElOutput;
+
+ function GLabelElOutput(config, state) {
+ if ($.isPlainObject(config.functions.function)) {
+ processFuncObj(config.functions.function);
+ } else if ($.isArray(config.functions.function)) {
+ (function (c1) {
+ while (c1 < config.functions.function.length) {
+ if ($.isPlainObject(config.functions.function[c1])) {
+ processFuncObj(config.functions.function[c1]);
+ }
+
+ c1 += 1;
+ }
+ }(0));
+ }
+
+ return;
+
+ function processFuncObj(obj) {
+ var paramNames, funcString, func;
+
+ // We are only interested in functions that are meant for output to an
+ // element.
+ if (
+ (typeof obj['@output'] !== 'string') ||
+ (obj['@output'].toLowerCase() !== 'plot_label')
+ ) {
+ return;
+ }
+
+ if (typeof obj['@el_id'] !== 'string') {
+ logme('ERROR: You specified "output" as "plot_label", but did not spify "el_id".');
+
+ return;
+ }
+
+ if (typeof obj['#text'] !== 'string') {
+ logme('ERROR: Function body is not defined.');
+
+ return;
+ }
+
+ funcString = obj['#text'];
+
+ // Make sure that all HTML entities are converted to their proper
+ // ASCII text equivalents.
+ funcString = $('
').html(funcString).text();
+
+ paramNames = state.getAllParameterNames();
+ paramNames.push(funcString);
+
+ try {
+ func = Function.apply(null, paramNames);
+ } catch (err) {
+ logme(
+ 'ERROR: The function body "' +
+ funcString +
+ '" was not converted by the Function constructor.'
+ );
+ logme('Error message: "' + err.message + '".');
+
+ $('#' + gstId).html('
' + 'ERROR IN XML: Could not create a function from string "' + funcString + '".' + '
');
+ $('#' + gstId).append('
' + 'Error message: "' + err.message + '".' + '
');
+
+ paramNames.pop();
+
+ return;
+ }
+
+ paramNames.pop();
+
+ state.plde.push({
+ 'elId': obj['@el_id'],
+ 'func': func
+ });
+ }
+
+ }
+});
+
+// End of wrapper for RequireJS. As you can see, we are passing
+// namespaced Require JS variables to an anonymous function. Within
+// it, you can use the standard requirejs(), require(), and define()
+// functions as if they were in the global namespace.
+}(RequireJS.requirejs, RequireJS.require, RequireJS.define)); // End-of: (function (requirejs, require, define)
diff --git a/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/graph.js b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/graph.js
index f35f92aa98..50d8d7bcd4 100644
--- a/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/graph.js
+++ b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/graph.js
@@ -155,7 +155,10 @@ define('Graph', ['logme'], function (logme) {
}
newAsyObj.label = false;
- if (typeof asyObj['@label'] === 'string') {
+ if (
+ (asyObj.hasOwnProperty('@label') === true) &&
+ (typeof asyObj['@label'] === 'string')
+ ) {
newAsyObj.label = asyObj['@label'];
}
@@ -492,7 +495,10 @@ define('Graph', ['logme'], function (logme) {
// properties as parameters. Rather than writing them out every
// time, we will have a single place where it is done.
function callAddFunction(obj) {
- if (typeof obj['@output'] === 'string') {
+ if (
+ (obj.hasOwnProperty('@output')) &&
+ (typeof obj['@output'] === 'string')
+ ) {
// If this function is meant to be calculated for an
// element then skip it.
@@ -500,13 +506,24 @@ define('Graph', ['logme'], function (logme) {
return;
}
- // It is an error if "output" is not "element" or "graph".
- // Though you can ommit the "output" attribute.
+ // If this function is meant to be calculated for a
+ // dynamic element in a label then skip it.
+ else if (obj['@output'].toLowerCase() === 'plot_label') {
+ return;
+ }
+
+ // It is an error if '@output' is not 'element',
+ // 'plot_label', or 'graph'. However, if the '@output'
+ // attribute is omitted, we will not have reached this.
else if (obj['@output'].toLowerCase() !== 'graph') {
- logme('ERROR: Function "output" attribute can be either "div" or "graph".');
+ logme(
+ 'ERROR: Function "output" attribute can be ' +
+ 'either "element", "plot_label", or "graph".'
+ );
return;
}
+
}
// The user did not specify an "output" attribute, or it is
@@ -525,8 +542,9 @@ define('Graph', ['logme'], function (logme) {
}
function addFunction(funcString, color, line, dot, label,
- pointSize, fillArea, bar, disableAutoReturn) {
- var newFunctionObject, func, paramNames, matches;
+ pointSize, fillArea, bar, disableAutoReturn) {
+
+ var newFunctionObject, func, paramNames, c1, rgxp;
// The main requirement is function string. Without it we can't
// create a function, and the series cannot be calculated.
@@ -678,18 +696,27 @@ define('Graph', ['logme'], function (logme) {
}
if (typeof label === 'string') {
- matches = label.match(/%%_[^%]*%%/g);
- if (
- ($.isArray(matches) === true) &&
- (matches.length > 0)
- ) {
- newFunctionObject['specialLabel'] = true;
- } else {
- newFunctionObject['specialLabel'] = false;
+ newFunctionObject.specialLabel = false;
+ newFunctionObject.pldeHash = [];
+
+ // Let's check the label against all of the plde objects.
+ // plde is an abbreviation for Plot Label Dynamic Elements.
+ for (c1 = 0; c1 < state.plde.length; c1 += 1) {
+ rgxp = new RegExp(state.plde[c1].elId, 'g');
+
+ // If we find a dynamic element in the label, we will
+ // hash the current plde object, and indicate that this
+ // is a special label.
+ if (rgxp.test(label) === true) {
+ newFunctionObject.specialLabel = true;
+ newFunctionObject.pldeHash.push(state.plde[c1]);
+ }
}
- newFunctionObject['label'] = label;
+ newFunctionObject.label = label;
+ } else {
+ newFunctionObject.label = false;
}
functions.push(newFunctionObject);
@@ -706,7 +733,7 @@ define('Graph', ['logme'], function (logme) {
function generateData() {
var c0, c1, functionObj, seriesObj, dataPoints, paramValues, x, y,
- start, end, step, tempX, matches;
+ start, end, step, tempX;
paramValues = state.getAllParameterValues();
@@ -818,33 +845,27 @@ define('Graph', ['logme'], function (logme) {
}
// See if a user defined a label for this function.
- if (functionObj.hasOwnProperty('label') === true) {
+ if (functionObj.label !== false) {
if (functionObj.specialLabel === true) {
- matches = functionObj.label.match(/%%_[^%]*%%/g);
+ (function (c1) {
+ var tempLabel;
- if ($.isArray(matches) === true) {
- (function (c1) {
- var el_id, func, tempLabel;
+ tempLabel = functionObj.label;
- tempLabel = functionObj.label;
+ while (c1 < functionObj.pldeHash.length) {
+ tempLabel = tempLabel.replace(
+ functionObj.pldeHash[c1].elId,
+ functionObj.pldeHash[c1].func.apply(
+ window,
+ state.getAllParameterValues()
+ )
+ );
- while (c1 < matches.length) {
- el_id = matches[c1].replace(/%/g, '');
- func = state.getFuncForSpecialLabel(el_id);
+ c1 += 1;
+ }
- if (func !== null) {
- tempLabel = tempLabel.replace(
- matches[c1],
- func.apply(window, state.getAllParameterValues())
- );
- }
-
- c1 += 1;
- }
-
- seriesObj.label = tempLabel;
- }(0));
- }
+ seriesObj.label = tempLabel;
+ }(0));
} else {
seriesObj.label = functionObj.label;
}
@@ -880,13 +901,22 @@ define('Graph', ['logme'], function (logme) {
}
for (c0 = 0; c0 < asymptotes.length; c0 += 1) {
- if (typeof asymptotes[c0].label === 'string') {
+
+ // If the user defined a label for this asympote, then the
+ // property 'label' will be a string (in the other case it is
+ // a boolean value 'false'). We will create an empty data set,
+ // and add to it a label. This solution is a bit _wrong_ , but
+ // it will have to do for now. Flot JS does not provide a way
+ // to add labels to markings, and we use markings to generate
+ // asymptotes.
+ if (asymptotes[c0].label !== false) {
dataSeries.push({
'data': [],
'label': asymptotes[c0].label,
'color': asymptotes[c0].color
});
}
+
}
return true;
diff --git a/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/gst_main.js b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/gst_main.js
index 71f946e312..34b54b4216 100644
--- a/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/gst_main.js
+++ b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/gst_main.js
@@ -8,8 +8,8 @@ define(
// Even though it is not explicitly in this module, we have to specify
// 'GeneralMethods' as a dependency. It expands some of the core JS objects
// with additional useful methods that are used in other modules.
- ['State', 'GeneralMethods', 'Sliders', 'Inputs', 'Graph', 'ElOutput', 'logme'],
- function (State, GeneralMethods, Sliders, Inputs, Graph, ElOutput, logme) {
+ ['State', 'GeneralMethods', 'Sliders', 'Inputs', 'Graph', 'ElOutput', 'GLabelElOutput', 'logme'],
+ function (State, GeneralMethods, Sliders, Inputs, Graph, ElOutput, GLabelElOutput, logme) {
return GstMain;
@@ -67,6 +67,10 @@ define(
// Configure functions that output to an element instead of the graph.
ElOutput(config, state);
+ // Configure functions that output to an element instead of the graph
+ // label.
+ GLabelElOutput(config, state);
+
// Configure and display the graph. Attach event for the graph to be
// updated on any change of a slider or a text input.
Graph(gstId, config, state);
diff --git a/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/state.js b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/state.js
index 3aea34ceb5..c1cfcd1b04 100644
--- a/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/state.js
+++ b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/state.js
@@ -98,7 +98,10 @@ define('State', ['logme'], function (logme) {
'bindUpdatePlotEvent': bindUpdatePlotEvent,
'addDynamicEl': addDynamicEl,
- 'getFuncForSpecialLabel': getFuncForSpecialLabel
+ 'getFuncForSpecialLabel': getFuncForSpecialLabel,
+
+ // plde is an abbreviation for Plot Label Dynamic Elements.
+ plde: []
};
function getAllParameterNames() {