diff --git a/common/lib/xmodule/xmodule/gst_module.py b/common/lib/xmodule/xmodule/gst_module.py index f89cb0f990..3d7b8a9f02 100644 --- a/common/lib/xmodule/xmodule/gst_module.py +++ b/common/lib/xmodule/xmodule/gst_module.py @@ -26,10 +26,10 @@ class GraphicalSliderToolModule(XModule): js = { 'js': [ resource_string(__name__, 'js/src/graphical_slider_tool/gst_main.js'), - resource_string(__name__, 'js/src/graphical_slider_tool/mod1.js'), - resource_string(__name__, 'js/src/graphical_slider_tool/mod2.js'), - resource_string(__name__, 'js/src/graphical_slider_tool/mod3.js'), - resource_string(__name__, 'js/src/graphical_slider_tool/mod4.js'), + resource_string(__name__, 'js/src/graphical_slider_tool/state.js'), + resource_string(__name__, 'js/src/graphical_slider_tool/logme.js'), + resource_string(__name__, 'js/src/graphical_slider_tool/general_methods.js'), + resource_string(__name__, 'js/src/graphical_slider_tool/sliders.js'), resource_string(__name__, 'js/src/graphical_slider_tool/mod5.js'), resource_string(__name__, 'js/src/graphical_slider_tool/gst.js') @@ -128,7 +128,7 @@ class GraphicalSliderToolModule(XModule): Simple variant: slider and plot controls are not inside any tag. """ #substitute plot - plot_div = '
\ This is plot
' html_string = html_string.replace('$plot$', plot_div) @@ -139,7 +139,7 @@ class GraphicalSliderToolModule(XModule): sliders = [sliders] vars = [x['@var'] for x in sliders] - slider_div = '
This is input
' for var in vars: diff --git a/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/mod3.js b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/general_methods.js similarity index 65% rename from common/lib/xmodule/xmodule/js/src/graphical_slider_tool/mod3.js rename to common/lib/xmodule/xmodule/js/src/graphical_slider_tool/general_methods.js index 21961f3611..9cdd4fff0f 100644 --- a/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/mod3.js +++ b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/general_methods.js @@ -2,12 +2,16 @@ // define() functions from Require JS available inside the anonymous function. (function (requirejs, require, define) { -define('mod3', ['mod5'], function (mod5) { - console.log('we are in the mod3 callback'); - - console.log('mod5 status: [' + mod5.module_status + '].'); +define('GeneralMethods', [], function () { + if (!String.prototype.trim) { + // http://blog.stevenlevithan.com/archives/faster-trim-javascript + String.prototype.trim = function trim(str) { + return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); + }; + } return { + 'module_name': 'GeneralMethods', 'module_status': 'OK' }; }); 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 66f98eddf7..9f2c4c356d 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 @@ -2,11 +2,19 @@ // define() functions from Require JS available inside the anonymous function. (function (requirejs, require, define) { -define('GstMain', ['mod1', 'mod2', 'mod3', 'mod4'], function (mod1, mod2, mod3, mod4) { +define('GstMain', ['State', 'logme', 'GeneralMethods', 'Sliders'], function (State, logme, GeneralMethods, Sliders) { + logme(GeneralMethods); + return GstMain; function GstMain(gstId) { - console.log('The DOM ID of the current GST element is ' + gstId); + var config, state; + + config = JSON.parse($('#' + gstId + '_json').html()).root; + + state = State(gstId, config); + + Sliders(gstId, config, state); } }); diff --git a/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/logme.js b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/logme.js new file mode 100644 index 0000000000..c045757044 --- /dev/null +++ b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/logme.js @@ -0,0 +1,54 @@ +// 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('logme', [], function () { + var debugMode; + + // debugMode can be one of the following: + // + // true - All messages passed to logme will be written to the internal + // browser console. + // false - Suppress all output to the internal browser console. + // + // Obviously, if anywhere there is a direct console.log() call, we can't do + // anything about it. That's why use logme() - it will allow to turn off + // the output of debug information with a single change to a variable. + debugMode = true; + + return logme; + + /* + * function: logme + * + * A helper function that provides logging facilities. We don't want + * to call console.log() directly, because sometimes it is not supported + * by the browser. Also when everything is routed through this function. + * the logging output can be easily turned off. + * + * logme() supports multiple parameters. Each parameter will be passed to + * console.log() function separately. + * + */ + function logme() { + var i; + + if ( + (typeof debugMode === 'undefined') || + (debugMode !== true) || + (typeof window.console === 'undefined') + ) { + return; + } + + for (i = 0; i < arguments.length; i++) { + window.console.log(arguments[i]); + } + } // End-of: function logme +}); + +// 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/mod1.js b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/mod1.js deleted file mode 100644 index 44674b96d3..0000000000 --- a/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/mod1.js +++ /dev/null @@ -1,16 +0,0 @@ -// 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('mod1', [], function () { - console.log('we are in the mod1 callback'); - return { - 'module_status': 'OK' - }; -}); - -// 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/mod2.js b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/mod2.js deleted file mode 100644 index 9c26bb1dfe..0000000000 --- a/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/mod2.js +++ /dev/null @@ -1,16 +0,0 @@ -// 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('mod2', [], function () { - console.log('we are in the mod2 callback'); - return { - 'module_status': 'OK' - }; -}); - -// 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/mod4.js b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/mod4.js deleted file mode 100644 index 0edf809155..0000000000 --- a/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/mod4.js +++ /dev/null @@ -1,16 +0,0 @@ -// 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('mod4', [], function () { - console.log('we are in the mod4 callback'); - return { - 'module_status': 'OK' - }; -}); - -// 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/sliders.js b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/sliders.js new file mode 100644 index 0000000000..6ef53bdbeb --- /dev/null +++ b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/sliders.js @@ -0,0 +1,142 @@ +// 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('Sliders', ['logme'], function (logme) { + return Sliders; + + function Sliders(gstId, config, state) { + logme('We are inside Sliders function.'); + + logme('gstId: ' + gstId); + logme(config); + logme(state); + + // We will go through all of the sliders. For each one, we will make a + // jQuery UI slider for it, attach "on change" events, and set it's + // state - initial value, max, and min parameters. + if ((typeof config.sliders !== 'undefined') && + (typeof config.sliders.slider !== 'undefined')) { + if ($.isArray(config.sliders.slider)) { + // config.sliders.slider is an array + + for (c1 = 0; c1 < config.sliders.slider.length; c1++) { + createSlider(config.sliders.slider[c1]); + } + } else if ($.isPlainObject(config.sliders.slider)) { + // config.sliders.slider is an object + createSlider(config.sliders.slider); + } + } + + function createSlider(obj) { + var constName, constValue, rangeBlobs, valueMin, valueMax, + sliderDiv, sliderWidth; + + // The name of the constant is obj['@var']. Multiple sliders and/or + // inputs can represent the same constant - therefore we will get + // the most recent const value from the state object. The range is + // a string composed of 3 blobs, separated by commas. The first + // blob is the min value for the slider, the third blob is the max + // value for the slider. + + if (typeof obj['@var'] === 'undefined') { + return; + } + + constName = obj['@var']; + + constValue = state.getConstValue(constName); + if (constValue === undefined) { + constValue = 0; + } + + if (typeof obj['@range'] !== 'string') { + valueMin = constValue - 10; + valueMax = constValue + 10; + } else { + rangeBlobs = obj['@range'].split(','); + + // We must have gotten exactly 3 blobs (pieces) from the split. + if (rangeBlobs.length !== 3) { + valueMin = constValue - 10; + valueMax = constValue + 10; + } else { + // Get the first blob from the split string. + valueMin = parseFloat(rangeBlobs[0]); + + if (isNaN(valueMin) === true) { + valueMin = constValue - 10; + } + + // Get the third blob from the split string. + valueMax = parseFloat(rangeBlobs[2]); + + if (isNaN(valueMax) === true) { + valueMax = constValue + 10; + } + + // Logically, the min, value, and max should make sense. + // I.e. we will make sure that: + // + // min <= value <= max + // + // If this is not the case, we will set some defaults. + if ((valueMin > valueMax) || + (valueMin > constValue) || + (valueMax < constValue)) { + valueMin = constValue - 10; + valueMax = constValue + 10; + } + } + } + + sliderDiv = $('#' + gstId + '_slider_' + constName); + + // If a corresponding slider DIV for this constant does not exist, + // do not do anything. + if (sliderDiv.length === 0) { + return; + } + + // The default slider width. + sliderWidth = 400; + + logme('width: 0'); + logme(obj['@width']); + if (typeof obj['@width'] === 'string') { + logme('width: 1'); + if (isNaN(parseInt(obj['@width'], 10)) === false) { + logme('width: 2'); + sliderWidth = parseInt(obj['@width'], 10); + } + } + + // Set the new width to the slider. + sliderDiv.width(sliderWidth); + + // Create a jQuery UI slider from the current DIV. We will set + // starting parameters, and will also attach a handler to update + // the state on the change event. + sliderDiv.slider({ + 'min': valueMin, + 'max': valueMax, + 'value': constValue, + + 'change': sliderOnChange + }); + + return; + + function sliderOnChange(event, ui) { + state.setConstValue(constName, ui.value); + } + } + } +}); + +// 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/state.js b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/state.js new file mode 100644 index 0000000000..17c8721a73 --- /dev/null +++ b/common/lib/xmodule/xmodule/js/src/graphical_slider_tool/state.js @@ -0,0 +1,165 @@ +// 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('State', ['logme'], function (logme) { + // Since there will be (can be) multiple GST on a page, and each will have + // a separate state, we will create a factory constructor function. The + // constructor will expect the ID of the DIV with the GST contents, and the + // configuration object (parsed from a JSON string). It will return and + // object containing methods to set and get the private state properties. + + // This module defines and returns a factory constructor. + return State; + + /* + * function: State + * + * + */ + function State(gstId, config) { + var constants, c1; + + constants = {}; + + // We must go through all of the input, and slider elements and + // retrieve all of the available constants. These will be added to an + // object as it's properties. + // + // First we will go through all of the inputs. + if ((typeof config.inputs !== 'undefined') && + (typeof config.inputs.input !== 'undefined')) { + if ($.isArray(config.inputs.input)) { + // config.inputs.input is an array + + for (c1 = 0; c1 < config.inputs.input.length; c1++) { + addConstFromInput(config.inputs.input[c1]); + } + } else if ($.isPlainObject(config.inputs.input)) { + // config.inputs.input is an object + addConstFromInput(config.inputs.input); + } + } + + // Now we will go through all of the sliders. + if ((typeof config.sliders !== 'undefined') && + (typeof config.sliders.slider !== 'undefined')) { + if ($.isArray(config.sliders.slider)) { + // config.sliders.slider is an array + + for (c1 = 0; c1 < config.sliders.slider.length; c1++) { + addConstFromSlider(config.sliders.slider[c1]); + } + } else if ($.isPlainObject(config.sliders.slider)) { + // config.sliders.slider is an object + addConstFromSlider(config.sliders.slider); + } + } + + logme(constants); + + // The constructor will return an object with methods to operate on + // it's private properties. + return { + 'getConstValue': getConstValue, + 'setConstValue': setConstValue + }; + + function getConstValue(constName) { + if (constants.hasOwnProperty(constName) === false) { + // If the name of the constant is not tracked by state, return an + // 'undefined' value. + return; + } + + return constants[constName]; + } + + function setConstValue(constName, constValue) { + if (constants.hasOwnProperty(constName) === false) { + // If the name of the constant is not tracked by state, return an + // 'undefined' value. + return; + } + + if (isNaN(parseFloat(constValue)) === true) { + // We are interested only in valid float values. + return; + } + + constants[constName] = parseFloat(constValue); + + logme('From setConstValue: new value for "' + constName + '" is ' + constValue); + } + + function addConstFromInput(obj) { + var constName, constValue; + + // The name of the constant is obj['@var']. The value (initial) of + // the constant is obj['@initial']. I have taken the word 'initial' + // into brackets, because multiple inputs and/or sliders can + // represent the state of a single constant. + + if (typeof obj['@var'] === 'undefined') { + return; + } + + constName = obj['@var']; + + if (typeof obj['@initial'] === 'undefined') { + constValue = 0; + } else { + constValue = parseFloat(obj['@initial']); + + if (isNaN(constValue) === true) { + constValue = 0; + } + } + + constants[constName] = constValue; + } + + function addConstFromSlider(obj) { + var constName, constValue, rangeBlobs; + + // The name of the constant is obj['@var']. The value (initial) of + // the constant is the second blob of the 'range' parameter of the + // slider which is obj['@range']. Multiple sliders and/or inputs + // can represent the same constant - therefore 'initial' is in + // brackets. The range is a string composed of 3 blobs, separated + // by commas. + + if (typeof obj['@var'] === 'undefined') { + return; + } + + constName = obj['@var']; + + if (typeof obj['@range'] !== 'string') { + constValue = 0; + } else { + rangeBlobs = obj['@range'].split(','); + + // We must have gotten exactly 3 blobs (pieces) from the split. + if (rangeBlobs.length !== 3) { + constValue = 0; + } else { + // Get the second blob from the split string. + constValue = parseFloat(rangeBlobs[1]); + + if (isNaN(constValue) === true) { + constValue = 0; + } + } + } + + constants[constName] = constValue; + } + } // End-of: function State +}); + +// 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/lms/templates/graphical_slider_tool.html b/lms/templates/graphical_slider_tool.html index d6cffc67e2..17d2bae5e9 100644 --- a/lms/templates/graphical_slider_tool.html +++ b/lms/templates/graphical_slider_tool.html @@ -1,12 +1,14 @@
- -
+ + - -
+ + - + ${gst_html}