Merge pull request #10850 from edx/kill-graphical-slider-tool
Remove the graphical slider tool
This commit is contained in:
@@ -1374,15 +1374,6 @@ class TestComponentTemplates(CourseTestCase):
|
||||
self.assertNotEqual(only_template.get('category'), 'video')
|
||||
self.assertNotEqual(only_template.get('category'), 'openassessment')
|
||||
|
||||
def test_advanced_components_without_display_name(self):
|
||||
"""
|
||||
Test that advanced components without display names display their category instead.
|
||||
"""
|
||||
self.course.advanced_modules.append('graphical_slider_tool')
|
||||
self.templates = get_component_templates(self.course)
|
||||
template = self.get_templates_of_type('advanced')[0]
|
||||
self.assertEqual(template.get('display_name'), 'graphical_slider_tool')
|
||||
|
||||
def test_advanced_problems(self):
|
||||
"""
|
||||
Test the handling of advanced problem templates.
|
||||
|
||||
@@ -997,7 +997,6 @@ ADVANCED_COMPONENT_TYPES = [
|
||||
'videoannotation', # module for annotating video (with annotation table)
|
||||
'imageannotation', # module for annotating image (with annotation table)
|
||||
'word_cloud',
|
||||
'graphical_slider_tool',
|
||||
'lti',
|
||||
'lti_consumer',
|
||||
'library_content',
|
||||
@@ -1115,7 +1114,11 @@ CREDIT_PROVIDER_TIMESTAMP_EXPIRATION = 15 * 60
|
||||
|
||||
################################ Deprecated Blocks Info ################################
|
||||
|
||||
DEPRECATED_BLOCK_TYPES = ['peergrading', 'combinedopenended']
|
||||
DEPRECATED_BLOCK_TYPES = [
|
||||
'peergrading',
|
||||
'combinedopenended',
|
||||
'graphical_slider_tool',
|
||||
]
|
||||
|
||||
#### PROCTORING CONFIGURATION DEFAULTS
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ XMODULES = [
|
||||
"static_tab = xmodule.html_module:StaticTabDescriptor",
|
||||
"custom_tag_template = xmodule.raw_module:RawDescriptor",
|
||||
"about = xmodule.html_module:AboutDescriptor",
|
||||
"graphical_slider_tool = xmodule.gst_module:GraphicalSliderToolDescriptor",
|
||||
"annotatable = xmodule.annotatable_module:AnnotatableDescriptor",
|
||||
"textannotation = xmodule.textannotation_module:TextAnnotationDescriptor",
|
||||
"videoannotation = xmodule.videoannotation_module:VideoAnnotationDescriptor",
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
// In the LMS sliders use built-in styles from jquery-ui-1.8.22.custom.css.
|
||||
// CMS uses its own sliders styles.
|
||||
// These styles we use only to sure, that slider in GST module
|
||||
// will be render correctly (just like a duplication some from jquery-ui-1.8.22.custom.css).
|
||||
// Cause, for example, CMS overwrites many jquery-ui-1.8.22.custom.css styles,
|
||||
// and we must overwrite them again.
|
||||
|
||||
.ui-widget-content {
|
||||
border: 1px solid #dddddd;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.ui-widget {
|
||||
font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl {
|
||||
-moz-border-radius-topleft: 4px;
|
||||
-webkit-border-top-left-radius: 4px;
|
||||
-khtml-border-top-left-radius: 4px;
|
||||
border-top-left-radius: 4px;
|
||||
}
|
||||
|
||||
.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr {
|
||||
-moz-border-radius-topright: 4px;
|
||||
-webkit-border-top-right-radius: 4px;
|
||||
-khtml-border-top-right-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
|
||||
.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl {
|
||||
-moz-border-radius-bottomleft: 4px;
|
||||
-webkit-border-bottom-left-radius: 4px;
|
||||
-khtml-border-bottom-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br {
|
||||
-moz-border-radius-bottomright: 4px;
|
||||
-webkit-border-bottom-right-radius: 4px;
|
||||
-khtml-border-bottom-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
"""
|
||||
Graphical slider tool module is ungraded xmodule used by students to
|
||||
understand functional dependencies.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from lxml import etree
|
||||
from lxml import html
|
||||
import xmltodict
|
||||
|
||||
from xmodule.editing_module import XMLEditingDescriptor
|
||||
from xmodule.xml_module import XmlDescriptor
|
||||
from xmodule.x_module import XModule
|
||||
from xmodule.stringify import stringify_children
|
||||
from pkg_resources import resource_string
|
||||
from xblock.fields import String, Scope
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_RENDER = """
|
||||
<h2>Graphic slider tool: Dynamic range and implicit functions.</h2>
|
||||
|
||||
<p>You can make the range of the x axis (but not ticks of x axis) of
|
||||
functions depend on a parameter value. This can be useful when the
|
||||
function domain needs to be variable.</p>
|
||||
<p>Implicit functions like a circle can be plotted as 2 separate
|
||||
functions of the same color.</p>
|
||||
<div style="height:50px;">
|
||||
<slider var='r' style="width:400px;float:left;"/>
|
||||
<textbox var='r' style="float:left;width:60px;margin-left:15px;"/>
|
||||
</div>
|
||||
<plot style="margin-top:15px;margin-bottom:15px;"/>
|
||||
"""
|
||||
|
||||
DEFAULT_CONFIGURATION = """
|
||||
<parameters>
|
||||
<param var="r" min="5" max="25" step="0.5" initial="12.5" />
|
||||
</parameters>
|
||||
<functions>
|
||||
<function color="red">Math.sqrt(r * r - x * x)</function>
|
||||
<function color="red">-Math.sqrt(r * r - x * x)</function>
|
||||
<function color="red">Math.sqrt(r * r / 20 - Math.pow(x-r/2.5, 2)) + r/8</function>
|
||||
<function color="red">-Math.sqrt(r * r / 20 - Math.pow(x-r/2.5, 2)) + r/5.5</function>
|
||||
<function color="red">Math.sqrt(r * r / 20 - Math.pow(x+r/2.5, 2)) + r/8</function>
|
||||
<function color="red">-Math.sqrt(r * r / 20 - Math.pow(x+r/2.5, 2)) + r/5.5</function>
|
||||
<function color="red">-Math.sqrt(r * r / 5 - x * x) - r/5.5</function>
|
||||
</functions>
|
||||
<plot>
|
||||
<xrange>
|
||||
<!-- dynamic range -->
|
||||
<min>-r</min>
|
||||
<max>r</max>
|
||||
</xrange>
|
||||
<num_points>1000</num_points>
|
||||
<xticks>-30, 6, 30</xticks>
|
||||
<yticks>-30, 6, 30</yticks>
|
||||
</plot>
|
||||
"""
|
||||
|
||||
|
||||
class GraphicalSliderToolFields(object):
|
||||
data = String(
|
||||
help="Html contents to display for this module",
|
||||
default='<render>{}</render><configuration>{}</configuration>'.format(
|
||||
DEFAULT_RENDER, DEFAULT_CONFIGURATION),
|
||||
scope=Scope.content
|
||||
)
|
||||
|
||||
|
||||
class GraphicalSliderToolModule(GraphicalSliderToolFields, XModule):
|
||||
''' Graphical-Slider-Tool Module
|
||||
'''
|
||||
|
||||
js = {
|
||||
'coffee': [resource_string(__name__, 'js/src/javascript_loader.coffee')],
|
||||
'js': [
|
||||
# 3rd party libraries used by graphic slider tool.
|
||||
# TODO - where to store them - outside xmodule?
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/gst_main.js'),
|
||||
resource_string(__name__, 'js/src/graphical_slider_tool/state.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/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')
|
||||
]
|
||||
}
|
||||
css = {'scss': [resource_string(__name__, 'css/gst/display.scss')]}
|
||||
js_module_name = "GraphicalSliderTool"
|
||||
|
||||
@property
|
||||
def configuration(self):
|
||||
return stringify_children(
|
||||
html.fromstring(self.data).xpath('configuration')[0]
|
||||
)
|
||||
|
||||
@property
|
||||
def render(self):
|
||||
return stringify_children(
|
||||
html.fromstring(self.data).xpath('render')[0]
|
||||
)
|
||||
|
||||
def get_html(self):
|
||||
""" Renders parameters to template. """
|
||||
|
||||
# these 3 will be used in class methods
|
||||
self.html_id = self.location.html_id()
|
||||
self.html_class = self.location.category
|
||||
|
||||
self.configuration_json = self.build_configuration_json()
|
||||
params = {
|
||||
'gst_html': self.substitute_controls(self.render),
|
||||
'element_id': self.html_id,
|
||||
'element_class': self.html_class,
|
||||
'configuration_json': self.configuration_json
|
||||
}
|
||||
content = self.system.render_template(
|
||||
'graphical_slider_tool.html', params
|
||||
)
|
||||
return content
|
||||
|
||||
def substitute_controls(self, html_string):
|
||||
""" Substitutes control elements (slider, textbox and plot) in
|
||||
html_string with their divs. Html_string is content of <render> tag
|
||||
inside <graphical_slider_tool> tag. Documentation on how information in
|
||||
<render> tag is organized and processed is located in:
|
||||
edx-platform/docs/build/html/graphical_slider_tool.html.
|
||||
|
||||
Args:
|
||||
html_string: content of <render> tag, with controls as xml tags,
|
||||
e.g. <slider var="a"/>.
|
||||
|
||||
Returns:
|
||||
html_string with control tags replaced by proper divs
|
||||
(<slider var="a"/> -> <div class="....slider" > </div>)
|
||||
"""
|
||||
|
||||
xml = html.fromstring(html_string)
|
||||
|
||||
# substitute plot, if presented
|
||||
plot_div = '<div class="{element_class}_plot" id="{element_id}_plot" \
|
||||
style="{style}"></div>'
|
||||
plot_el = xml.xpath('//plot')
|
||||
if plot_el:
|
||||
plot_el = plot_el[0]
|
||||
plot_el.getparent().replace(plot_el, html.fromstring(
|
||||
plot_div.format(
|
||||
element_class=self.html_class,
|
||||
element_id=self.html_id,
|
||||
style=plot_el.get('style', ""))))
|
||||
|
||||
# substitute sliders
|
||||
slider_div = '<div class="{element_class}_slider" \
|
||||
id="{element_id}_slider_{var}" \
|
||||
data-var="{var}" \
|
||||
style="{style}">\
|
||||
</div>'
|
||||
slider_els = xml.xpath('//slider')
|
||||
for slider_el in slider_els:
|
||||
slider_el.getparent().replace(slider_el, html.fromstring(
|
||||
slider_div.format(
|
||||
element_class=self.html_class,
|
||||
element_id=self.html_id,
|
||||
var=slider_el.get('var', ""),
|
||||
style=slider_el.get('style', ""))))
|
||||
|
||||
# substitute inputs aka textboxes
|
||||
input_div = '<input class="{element_class}_input" \
|
||||
id="{element_id}_input_{var}_{input_index}" \
|
||||
data-var="{var}" style="{style}"/>'
|
||||
input_els = xml.xpath('//textbox')
|
||||
for input_index, input_el in enumerate(input_els):
|
||||
input_el.getparent().replace(input_el, html.fromstring(
|
||||
input_div.format(
|
||||
element_class=self.html_class,
|
||||
element_id=self.html_id,
|
||||
var=input_el.get('var', ""),
|
||||
style=input_el.get('style', ""),
|
||||
input_index=input_index)))
|
||||
|
||||
return html.tostring(xml)
|
||||
|
||||
def build_configuration_json(self):
|
||||
"""Creates json element from xml element (with aim to transfer later
|
||||
directly to javascript via hidden field in template). Steps:
|
||||
|
||||
1. Convert xml tree to python dict.
|
||||
|
||||
2. Dump dict to json.
|
||||
|
||||
"""
|
||||
# <root> added for interface compatibility with xmltodict.parse
|
||||
# class added for javascript's part purposes
|
||||
root = '<root class="{}">{}</root>'.format(
|
||||
self.html_class,
|
||||
self.configuration)
|
||||
return json.dumps(xmltodict.parse(root))
|
||||
|
||||
|
||||
class GraphicalSliderToolDescriptor(GraphicalSliderToolFields, XMLEditingDescriptor, XmlDescriptor):
|
||||
module_class = GraphicalSliderToolModule
|
||||
|
||||
@classmethod
|
||||
def definition_from_xml(cls, xml_object, system):
|
||||
"""
|
||||
Pull out the data into dictionary.
|
||||
|
||||
Args:
|
||||
xml_object: xml from file.
|
||||
|
||||
Returns:
|
||||
dict
|
||||
"""
|
||||
# check for presense of required tags in xml
|
||||
expected_children_level_0 = ['render', 'configuration']
|
||||
for child in expected_children_level_0:
|
||||
if len(xml_object.xpath(child)) != 1:
|
||||
raise ValueError(u"Graphical Slider Tool definition must include \
|
||||
exactly one '{0}' tag".format(child))
|
||||
|
||||
expected_children_level_1 = ['functions']
|
||||
for child in expected_children_level_1:
|
||||
if len(xml_object.xpath('configuration')[0].xpath(child)) != 1:
|
||||
raise ValueError(u"Graphical Slider Tool definition must include \
|
||||
exactly one '{0}' tag".format(child))
|
||||
# finished
|
||||
|
||||
return {
|
||||
'data': stringify_children(xml_object)
|
||||
}, []
|
||||
|
||||
def definition_to_xml(self, resource_fs):
|
||||
'''Return an xml element representing this definition.'''
|
||||
data = u'<{tag}>{body}</{tag}>'.format(
|
||||
tag='graphical_slider_tool',
|
||||
body=self.data)
|
||||
xml_object = etree.fromstring(data)
|
||||
return xml_object
|
||||
@@ -1 +0,0 @@
|
||||
!*.js
|
||||
@@ -1,141 +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('ElOutput', [], function () {
|
||||
|
||||
return ElOutput;
|
||||
|
||||
function ElOutput(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, el, disableAutoReturn, updateOnEvent;
|
||||
|
||||
// We are only interested in functions that are meant for output to an
|
||||
// element.
|
||||
if (
|
||||
(typeof obj['@output'] !== 'string') ||
|
||||
((obj['@output'].toLowerCase() !== 'element') && (obj['@output'].toLowerCase() !== 'none'))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof obj['@el_id'] !== 'string') {
|
||||
console.log('ERROR: You specified "output" as "element", but did not spify "el_id".');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof obj['#text'] !== 'string') {
|
||||
console.log('ERROR: Function body is not defined.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
updateOnEvent = 'slide';
|
||||
if (
|
||||
(obj.hasOwnProperty('@update_on') === true) &&
|
||||
(typeof obj['@update_on'] === 'string') &&
|
||||
((obj['@update_on'].toLowerCase() === 'slide') || (obj['@update_on'].toLowerCase() === 'change'))
|
||||
) {
|
||||
updateOnEvent = obj['@update_on'].toLowerCase();
|
||||
}
|
||||
|
||||
disableAutoReturn = obj['@disable_auto_return'];
|
||||
|
||||
funcString = obj['#text'];
|
||||
|
||||
if (
|
||||
(disableAutoReturn === undefined) ||
|
||||
(
|
||||
(typeof disableAutoReturn === 'string') &&
|
||||
(disableAutoReturn.toLowerCase() !== 'true')
|
||||
)
|
||||
) {
|
||||
if (funcString.search(/return/i) === -1) {
|
||||
funcString = 'return ' + funcString;
|
||||
}
|
||||
} else {
|
||||
if (funcString.search(/return/i) === -1) {
|
||||
console.log(
|
||||
'ERROR: You have specified a JavaScript ' +
|
||||
'function without a "return" statemnt. Your ' +
|
||||
'function will return "undefined" by default.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that all HTML entities are converted to their proper
|
||||
// ASCII text equivalents.
|
||||
funcString = $('<div>').html(funcString).text();
|
||||
|
||||
paramNames = state.getAllParameterNames();
|
||||
paramNames.push(funcString);
|
||||
|
||||
try {
|
||||
func = Function.apply(null, paramNames);
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'ERROR: The function body "' +
|
||||
funcString +
|
||||
'" was not converted by the Function constructor.'
|
||||
);
|
||||
console.log('Error message: "' + err.message + '".');
|
||||
|
||||
if (state.showDebugInfo) {
|
||||
$('#' + gstId).html('<div style="color: red;">' + 'ERROR IN XML: Could not create a function from string "' + funcString + '".' + '</div>');
|
||||
$('#' + gstId).append('<div style="color: red;">' + 'Error message: "' + err.message + '".' + '</div>');
|
||||
}
|
||||
|
||||
paramNames.pop();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
paramNames.pop();
|
||||
|
||||
if (obj['@output'].toLowerCase() !== 'none') {
|
||||
el = $('#' + obj['@el_id']);
|
||||
|
||||
if (el.length !== 1) {
|
||||
console.log(
|
||||
'ERROR: DOM element with ID "' + obj['@el_id'] + '" ' +
|
||||
'not found. Dynamic element not created.'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
el.html(func.apply(window, state.getAllParameterValues()));
|
||||
} else {
|
||||
el = null;
|
||||
func.apply(window, state.getAllParameterValues());
|
||||
}
|
||||
|
||||
state.addDynamicEl(el, func, obj['@el_id'], updateOnEvent);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// 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)
|
||||
@@ -1,115 +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('GLabelElOutput', [], function () {
|
||||
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, disableAutoReturn;
|
||||
|
||||
// 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') {
|
||||
console.log('ERROR: You specified "output" as "plot_label", but did not spify "el_id".');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof obj['#text'] !== 'string') {
|
||||
console.log('ERROR: Function body is not defined.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
disableAutoReturn = obj['@disable_auto_return'];
|
||||
|
||||
funcString = obj['#text'];
|
||||
|
||||
if (
|
||||
(disableAutoReturn === undefined) ||
|
||||
(
|
||||
(typeof disableAutoReturn === 'string') &&
|
||||
(disableAutoReturn.toLowerCase() !== 'true')
|
||||
)
|
||||
) {
|
||||
if (funcString.search(/return/i) === -1) {
|
||||
funcString = 'return ' + funcString;
|
||||
}
|
||||
} else {
|
||||
if (funcString.search(/return/i) === -1) {
|
||||
console.log(
|
||||
'ERROR: You have specified a JavaScript ' +
|
||||
'function without a "return" statemnt. Your ' +
|
||||
'function will return "undefined" by default.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that all HTML entities are converted to their proper
|
||||
// ASCII text equivalents.
|
||||
funcString = $('<div>').html(funcString).text();
|
||||
|
||||
paramNames = state.getAllParameterNames();
|
||||
paramNames.push(funcString);
|
||||
|
||||
try {
|
||||
func = Function.apply(null, paramNames);
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'ERROR: The function body "' +
|
||||
funcString +
|
||||
'" was not converted by the Function constructor.'
|
||||
);
|
||||
console.log('Error message: "' + err.message + '".');
|
||||
|
||||
if (state.showDebugInfo) {
|
||||
$('#' + gstId).html('<div style="color: red;">' + 'ERROR IN XML: Could not create a function from string "' + funcString + '".' + '</div>');
|
||||
$('#' + gstId).append('<div style="color: red;">' + 'Error message: "' + err.message + '".' + '</div>');
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -1,23 +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('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'
|
||||
};
|
||||
});
|
||||
|
||||
// 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)
|
||||
@@ -1,1512 +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('Graph', [], function () {
|
||||
|
||||
return Graph;
|
||||
|
||||
function Graph(gstId, config, state) {
|
||||
var plotDiv, dataSeries, functions, xaxis, yaxis, numPoints, xrange,
|
||||
asymptotes, movingLabels, xTicksNames, yTicksNames, graphBarWidth, graphBarAlign;
|
||||
|
||||
// We need plot configuration settings. Without them we can't continue.
|
||||
if ($.isPlainObject(config.plot) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We must have a graph container DIV element available in order to
|
||||
// proceed.
|
||||
plotDiv = $('#' + gstId + '_plot');
|
||||
if (plotDiv.length === 0) {
|
||||
console.log('ERROR: Could not find the plot DIV with ID "' + gstId + '_plot".');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (plotDiv.width() === 0) {
|
||||
plotDiv.width(300);
|
||||
}
|
||||
|
||||
// Sometimes, when height is not explicitly set via CSS (or by some
|
||||
// other means), it is 0 pixels by default. When Flot will try to plot
|
||||
// a graph in this DIV with 0 height, then it will raise an error. To
|
||||
// prevent this, we will set it to be equal to the width.
|
||||
if (plotDiv.height() === 0) {
|
||||
plotDiv.height(plotDiv.width());
|
||||
}
|
||||
|
||||
plotDiv.css('position', 'relative');
|
||||
|
||||
// Configure some settings for the graph.
|
||||
if (setGraphXRange() === false) {
|
||||
console.log('ERROR: Could not configure the xrange. Will not continue.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (setGraphAxes() === false) {
|
||||
console.log('ERROR: Could not process configuration for the axes.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
graphBarWidth = 1;
|
||||
graphBarAlign = null;
|
||||
|
||||
getBarWidth();
|
||||
getBarAlign();
|
||||
|
||||
// Get the user defined functions. If there aren't any, don't do
|
||||
// anything else.
|
||||
createFunctions();
|
||||
|
||||
if (functions.length === 0) {
|
||||
console.log('ERROR: No functions were specified, or something went wrong.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (createMarkingsFunctions() === false) {
|
||||
return;
|
||||
}
|
||||
if (createMovingLabelFunctions() === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the initial graph and plot it for the user to see.
|
||||
if (generateData() === true) {
|
||||
updatePlot();
|
||||
}
|
||||
|
||||
// Bind an event. Whenever some constant changes, the graph will be
|
||||
// redrawn
|
||||
state.bindUpdatePlotEvent(plotDiv, onUpdatePlot);
|
||||
|
||||
return;
|
||||
|
||||
function getBarWidth() {
|
||||
if (config.plot.hasOwnProperty('bar_width') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof config.plot.bar_width !== 'string') {
|
||||
console.log('ERROR: The parameter config.plot.bar_width must be a string.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isFinite(graphBarWidth = parseFloat(config.plot.bar_width)) === false) {
|
||||
console.log('ERROR: The parameter config.plot.bar_width is not a valid floating number.');
|
||||
graphBarWidth = 1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function getBarAlign() {
|
||||
if (config.plot.hasOwnProperty('bar_align') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof config.plot.bar_align !== 'string') {
|
||||
console.log('ERROR: The parameter config.plot.bar_align must be a string.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
(config.plot.bar_align.toLowerCase() !== 'left') &&
|
||||
(config.plot.bar_align.toLowerCase() !== 'center')
|
||||
) {
|
||||
console.log('ERROR: Property config.plot.bar_align can be one of "left", or "center".');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
graphBarAlign = config.plot.bar_align.toLowerCase();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function createMovingLabelFunctions() {
|
||||
var c1, returnStatus;
|
||||
|
||||
returnStatus = true;
|
||||
movingLabels = [];
|
||||
|
||||
if (config.plot.hasOwnProperty('moving_label') !== true) {
|
||||
returnStatus = true;
|
||||
} else if ($.isPlainObject(config.plot.moving_label) === true) {
|
||||
if (processMovingLabel(config.plot.moving_label) === false) {
|
||||
returnStatus = false;
|
||||
}
|
||||
} else if ($.isArray(config.plot.moving_label) === true) {
|
||||
for (c1 = 0; c1 < config.plot.moving_label.length; c1++) {
|
||||
if (processMovingLabel(config.plot.moving_label[c1]) === false) {
|
||||
returnStatus = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return returnStatus;
|
||||
}
|
||||
|
||||
function processMovingLabel(obj) {
|
||||
var labelText, funcString, disableAutoReturn, paramNames, func,
|
||||
fontWeight, fontColor;
|
||||
|
||||
if (obj.hasOwnProperty('@text') === false) {
|
||||
console.log('ERROR: You did not define a "text" attribute for the moving_label.');
|
||||
|
||||
return false;
|
||||
}
|
||||
if (typeof obj['@text'] !== 'string') {
|
||||
console.log('ERROR: "text" attribute is not a string.');
|
||||
|
||||
return false;
|
||||
}
|
||||
labelText = obj['@text'];
|
||||
|
||||
if (obj.hasOwnProperty('#text') === false) {
|
||||
console.log('ERROR: moving_label is missing function declaration.');
|
||||
|
||||
return false;
|
||||
}
|
||||
if (typeof obj['#text'] !== 'string') {
|
||||
console.log('ERROR: Function declaration is not a string.');
|
||||
|
||||
return false;
|
||||
}
|
||||
funcString = obj['#text'];
|
||||
|
||||
fontColor = 'black';
|
||||
if (
|
||||
(obj.hasOwnProperty('@color') === true) &&
|
||||
(typeof obj['@color'] === 'string')
|
||||
) {
|
||||
fontColor = obj['@color'];
|
||||
}
|
||||
|
||||
fontWeight = 'normal';
|
||||
if (
|
||||
(obj.hasOwnProperty('@weight') === true) &&
|
||||
(typeof obj['@weight'] === 'string')
|
||||
) {
|
||||
if (
|
||||
(obj['@weight'].toLowerCase() === 'normal') ||
|
||||
(obj['@weight'].toLowerCase() === 'bold')
|
||||
) {
|
||||
fontWeight = obj['@weight'];
|
||||
} else {
|
||||
console.log('ERROR: Moving label can have a weight property of "normal" or "bold".');
|
||||
}
|
||||
}
|
||||
|
||||
disableAutoReturn = obj['@disable_auto_return'];
|
||||
|
||||
funcString = $('<div>').html(funcString).text();
|
||||
|
||||
if (
|
||||
(disableAutoReturn === undefined) ||
|
||||
(
|
||||
(typeof disableAutoReturn === 'string') &&
|
||||
(disableAutoReturn.toLowerCase() !== 'true')
|
||||
)
|
||||
) {
|
||||
if (funcString.search(/return/i) === -1) {
|
||||
funcString = 'return ' + funcString;
|
||||
}
|
||||
} else {
|
||||
if (funcString.search(/return/i) === -1) {
|
||||
console.log(
|
||||
'ERROR: You have specified a JavaScript ' +
|
||||
'function without a "return" statemnt. Your ' +
|
||||
'function will return "undefined" by default.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
paramNames = state.getAllParameterNames();
|
||||
paramNames.push(funcString);
|
||||
|
||||
try {
|
||||
func = Function.apply(null, paramNames);
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'ERROR: The function body "' +
|
||||
funcString +
|
||||
'" was not converted by the Function constructor.'
|
||||
);
|
||||
console.log('Error message: "' + err.message + '"');
|
||||
|
||||
if (state.showDebugInfo) {
|
||||
$('#' + gstId).html('<div style="color: red;">' + 'ERROR IN XML: Could not create a function from the string "' + funcString + '".' + '</div>');
|
||||
$('#' + gstId).append('<div style="color: red;">' + 'Error message: "' + err.message + '".' + '</div>');
|
||||
}
|
||||
|
||||
paramNames.pop();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
paramNames.pop();
|
||||
|
||||
movingLabels.push({
|
||||
'labelText': labelText,
|
||||
'func': func,
|
||||
'el': null,
|
||||
'fontColor': fontColor,
|
||||
'fontWeight': fontWeight
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function createMarkingsFunctions() {
|
||||
var c1, paramNames, returnStatus;
|
||||
|
||||
returnStatus = true;
|
||||
|
||||
asymptotes = [];
|
||||
paramNames = state.getAllParameterNames();
|
||||
|
||||
if ($.isPlainObject(config.plot.asymptote)) {
|
||||
if (processAsymptote(config.plot.asymptote) === false) {
|
||||
returnStatus = false;
|
||||
}
|
||||
} else if ($.isArray(config.plot.asymptote)) {
|
||||
for (c1 = 0; c1 < config.plot.asymptote.length; c1 += 1) {
|
||||
if (processAsymptote(config.plot.asymptote[c1]) === false) {
|
||||
returnStatus = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return returnStatus;
|
||||
|
||||
// Read configuration options for asymptotes, and store them as
|
||||
// an array of objects. Each object will have 3 properties:
|
||||
//
|
||||
// - color: the color of the asymptote line
|
||||
// - type: 'x' (vertical), or 'y' (horizontal)
|
||||
// - func: the function that will generate the value at which
|
||||
// the asymptote will be plotted; i.e. x = func(), or
|
||||
// y = func(); for now only horizontal and vertical
|
||||
// asymptotes are supported
|
||||
//
|
||||
// Since each asymptote can have a variable function - function
|
||||
// that relies on some parameter specified in the config - we will
|
||||
// generate each asymptote just before we draw the graph. See:
|
||||
//
|
||||
// function updatePlot()
|
||||
// function generateMarkings()
|
||||
//
|
||||
// Asymptotes are really thin rectangles implemented via the Flot's
|
||||
// markings option.
|
||||
function processAsymptote(asyObj) {
|
||||
var newAsyObj, funcString, func;
|
||||
|
||||
newAsyObj = {};
|
||||
|
||||
if (typeof asyObj['@type'] === 'string') {
|
||||
if (asyObj['@type'].toLowerCase() === 'x') {
|
||||
newAsyObj.type = 'x';
|
||||
} else if (asyObj['@type'].toLowerCase() === 'y') {
|
||||
newAsyObj.type = 'y';
|
||||
} else {
|
||||
console.log('ERROR: Attribute "type" for asymptote can be "x" or "y".');
|
||||
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
console.log('ERROR: Attribute "type" for asymptote is not specified.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof asyObj['#text'] === 'string') {
|
||||
funcString = asyObj['#text'];
|
||||
} else {
|
||||
console.log('ERROR: Function body for asymptote is not specified.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
newAsyObj.color = '#000';
|
||||
if (typeof asyObj['@color'] === 'string') {
|
||||
newAsyObj.color = asyObj['@color'];
|
||||
}
|
||||
|
||||
newAsyObj.label = false;
|
||||
if (
|
||||
(asyObj.hasOwnProperty('@label') === true) &&
|
||||
(typeof asyObj['@label'] === 'string')
|
||||
) {
|
||||
newAsyObj.label = asyObj['@label'];
|
||||
}
|
||||
|
||||
funcString = $('<div>').html(funcString).text();
|
||||
|
||||
disableAutoReturn = asyObj['@disable_auto_return'];
|
||||
if (
|
||||
(disableAutoReturn === undefined) ||
|
||||
(
|
||||
(typeof disableAutoReturn === 'string') &&
|
||||
(disableAutoReturn.toLowerCase() !== 'true')
|
||||
)
|
||||
) {
|
||||
if (funcString.search(/return/i) === -1) {
|
||||
funcString = 'return ' + funcString;
|
||||
}
|
||||
} else {
|
||||
if (funcString.search(/return/i) === -1) {
|
||||
console.log(
|
||||
'ERROR: You have specified a JavaScript ' +
|
||||
'function without a "return" statemnt. Your ' +
|
||||
'function will return "undefined" by default.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
paramNames.push(funcString);
|
||||
|
||||
try {
|
||||
func = Function.apply(null, paramNames);
|
||||
} catch (err) {
|
||||
console.log('ERROR: Asymptote function body could not be converted to function object.');
|
||||
console.log('Error message: "".' + err.message);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
paramNames.pop();
|
||||
|
||||
newAsyObj.func = func;
|
||||
asymptotes.push(newAsyObj);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function setGraphAxes() {
|
||||
xaxis = {
|
||||
'tickFormatter': null
|
||||
};
|
||||
|
||||
if (typeof config.plot['xticks'] === 'string') {
|
||||
if (processTicks(config.plot['xticks'], xaxis, 'xunits') === false) {
|
||||
console.log('ERROR: Could not process the ticks for x-axis.');
|
||||
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// console.log('MESSAGE: "xticks" were not specified. Using defaults.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
yaxis = {
|
||||
'tickFormatter': null
|
||||
};
|
||||
if (typeof config.plot['yticks'] === 'string') {
|
||||
if (processTicks(config.plot['yticks'], yaxis, 'yunits') === false) {
|
||||
console.log('ERROR: Could not process the ticks for y-axis.');
|
||||
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// console.log('MESSAGE: "yticks" were not specified. Using defaults.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
xTicksNames = null;
|
||||
yTicksNames = null;
|
||||
|
||||
if (checkForTicksNames('x') === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (checkForTicksNames('y') === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
//
|
||||
// function checkForTicksNames(axisName)
|
||||
//
|
||||
// The parameter "axisName" can be either "x" or "y" (string). Depending on it, the function
|
||||
// will set "xTicksNames" or "yTicksNames" private variable.
|
||||
//
|
||||
// This function does not return anything. It sets the private variable "xTicksNames" ("yTicksNames")
|
||||
// to the object converted by JSON.parse from the XML parameter "plot.xticks_names" ("plot.yticks_names").
|
||||
// If the "plot.xticks_names" ("plot.yticks_names") is missing or it is not a valid JSON string, then
|
||||
// "xTicksNames" ("yTicksNames") will be set to "null".
|
||||
//
|
||||
// Depending on the "xTicksNames" ("yTicksNames") being "null" or an object, the plot will either draw
|
||||
// number ticks, or use the names specified by the opbject.
|
||||
//
|
||||
function checkForTicksNames(axisName) {
|
||||
var tmpObj;
|
||||
|
||||
if ((axisName !== 'x') && (axisName !== 'y')) {
|
||||
// This is not an error. This funcion should simply stop executing.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
(config.plot.hasOwnProperty(axisName + 'ticks_names') === true) ||
|
||||
(typeof config.plot[axisName + 'ticks_names'] === 'string')
|
||||
) {
|
||||
try {
|
||||
tmpObj = JSON.parse(config.plot[axisName + 'ticks_names']);
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'ERROR: plot.' + axisName + 'ticks_names is not a valid JSON string.',
|
||||
'Error message: "' + err.message + '".'
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (axisName === 'x') {
|
||||
xTicksNames = tmpObj;
|
||||
xaxis.tickFormatter = xAxisTickFormatter;
|
||||
}
|
||||
// At this point, we are certain that axisName = 'y'.
|
||||
else {
|
||||
yTicksNames = tmpObj;
|
||||
yaxis.tickFormatter = yAxisTickFormatter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processTicks(ticksStr, ticksObj, unitsType) {
|
||||
var ticksBlobs, tempFloat, tempTicks, c1, c2;
|
||||
|
||||
// The 'ticks' setting is a string containing 3 floating-point
|
||||
// numbers.
|
||||
ticksBlobs = ticksStr.split(',');
|
||||
|
||||
if (ticksBlobs.length !== 3) {
|
||||
console.log('ERROR: Did not get 3 blobs from ticksStr = "' + ticksStr + '".');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
tempFloat = parseFloat(ticksBlobs[0]);
|
||||
if (isNaN(tempFloat) === false) {
|
||||
ticksObj.min = tempFloat;
|
||||
} else {
|
||||
console.log('ERROR: Invalid "min". ticksBlobs[0] = ', ticksBlobs[0]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
tempFloat = parseFloat(ticksBlobs[1]);
|
||||
if (isNaN(tempFloat) === false) {
|
||||
ticksObj.tickSize = tempFloat;
|
||||
} else {
|
||||
console.log('ERROR: Invalid "tickSize". ticksBlobs[1] = ', ticksBlobs[1]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
tempFloat = parseFloat(ticksBlobs[2]);
|
||||
if (isNaN(tempFloat) === false) {
|
||||
ticksObj.max = tempFloat;
|
||||
} else {
|
||||
console.log('ERROR: Invalid "max". ticksBlobs[2] = ', ticksBlobs[2]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is the starting tick to the left of the ending tick (on the
|
||||
// x-axis)? If not, set default starting and ending tick.
|
||||
if (ticksObj.min >= ticksObj.max) {
|
||||
console.log('ERROR: Ticks min >= max.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the range makes sense - i.e. that there are at
|
||||
// least 3 ticks. If not, set a tickSize which will produce
|
||||
// 11 ticks. tickSize is the spacing between the ticks.
|
||||
if (ticksObj.tickSize > ticksObj.max - ticksObj.min) {
|
||||
console.log('ERROR: tickSize > max - min.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// units: change last tick to units
|
||||
if (typeof config.plot[unitsType] === 'string') {
|
||||
tempTicks = [];
|
||||
|
||||
for (c1 = ticksObj.min; c1 <= ticksObj.max; c1 += ticksObj.tickSize) {
|
||||
c2 = roundToPrec(c1, ticksObj.tickSize);
|
||||
tempTicks.push([c2, c2]);
|
||||
}
|
||||
|
||||
tempTicks.pop();
|
||||
tempTicks.push([
|
||||
roundToPrec(ticksObj.max, ticksObj.tickSize),
|
||||
config.plot[unitsType]
|
||||
]);
|
||||
|
||||
ticksObj.tickSize = null;
|
||||
ticksObj.ticks = tempTicks;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
function roundToPrec(num, prec) {
|
||||
var c1, tn1, tn2, digitsBefore, digitsAfter;
|
||||
|
||||
tn1 = Math.abs(num);
|
||||
tn2 = Math.abs(prec);
|
||||
|
||||
// Find out number of digits BEFORE the decimal point.
|
||||
c1 = 0;
|
||||
tn1 = Math.abs(num);
|
||||
while (tn1 >= 1) {
|
||||
c1 += 1;
|
||||
|
||||
tn1 /= 10;
|
||||
}
|
||||
digitsBefore = c1;
|
||||
|
||||
// Find out number of digits AFTER the decimal point.
|
||||
c1 = 0;
|
||||
tn1 = Math.abs(num);
|
||||
while (Math.round(tn1) !== tn1) {
|
||||
c1 += 1;
|
||||
|
||||
tn1 *= 10;
|
||||
}
|
||||
digitsAfter = c1;
|
||||
|
||||
// For precision, find out number of digits AFTER the
|
||||
// decimal point.
|
||||
c1 = 0;
|
||||
while (Math.round(tn2) !== tn2) {
|
||||
c1 += 1;
|
||||
|
||||
tn2 *= 10;
|
||||
}
|
||||
|
||||
// If precision is more than 1 (no digits after decimal
|
||||
// points).
|
||||
if (c1 === 0) {
|
||||
return num;
|
||||
}
|
||||
|
||||
// If the precision contains digits after the decimal
|
||||
// point, we apply special rules.
|
||||
else {
|
||||
tn1 = Math.abs(num);
|
||||
|
||||
// if (digitsAfter > c1) {
|
||||
tn1 = tn1.toFixed(c1);
|
||||
// } else {
|
||||
// tn1 = tn1.toPrecision(digitsBefore + digitsAfter);
|
||||
// }
|
||||
}
|
||||
|
||||
if (num < 0) {
|
||||
return -tn1;
|
||||
}
|
||||
|
||||
return tn1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setGraphXRange() {
|
||||
var xRangeStr, xRangeBlobs, tempNum, allParamNames, funcString,
|
||||
disableAutoReturn;
|
||||
|
||||
xrange = {};
|
||||
|
||||
if ($.isPlainObject(config.plot.xrange) === false) {
|
||||
console.log(
|
||||
'ERROR: Expected config.plot.xrange to be an object. ' +
|
||||
'It is not.'
|
||||
);
|
||||
console.log('config.plot.xrange = ', config.plot.xrange);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.plot.xrange.hasOwnProperty('min') === false) {
|
||||
console.log(
|
||||
'ERROR: Expected config.plot.xrange.min to be ' +
|
||||
'present. It is not.'
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
disableAutoReturn = false;
|
||||
if (typeof config.plot.xrange.min === 'string') {
|
||||
funcString = config.plot.xrange.min;
|
||||
} else if (
|
||||
($.isPlainObject(config.plot.xrange.min) === true) &&
|
||||
(config.plot.xrange.min.hasOwnProperty('#text') === true) &&
|
||||
(typeof config.plot.xrange.min['#text'] === 'string')
|
||||
) {
|
||||
funcString = config.plot.xrange.min['#text'];
|
||||
|
||||
disableAutoReturn =
|
||||
config.plot.xrange.min['@disable_auto_return'];
|
||||
if (
|
||||
(disableAutoReturn === undefined) ||
|
||||
(
|
||||
(typeof disableAutoReturn === 'string') &&
|
||||
(disableAutoReturn.toLowerCase() !== 'true')
|
||||
)
|
||||
) {
|
||||
disableAutoReturn = false;
|
||||
} else {
|
||||
disableAutoReturn = true;
|
||||
}
|
||||
} else {
|
||||
console.log(
|
||||
'ERROR: Could not get a function definition for ' +
|
||||
'xrange.min property.'
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
funcString = $('<div>').html(funcString).text();
|
||||
|
||||
if (disableAutoReturn === false) {
|
||||
if (funcString.search(/return/i) === -1) {
|
||||
funcString = 'return ' + funcString;
|
||||
}
|
||||
} else {
|
||||
if (funcString.search(/return/i) === -1) {
|
||||
console.log(
|
||||
'ERROR: You have specified a JavaScript ' +
|
||||
'function without a "return" statemnt. Your ' +
|
||||
'function will return "undefined" by default.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
allParamNames = state.getAllParameterNames();
|
||||
|
||||
allParamNames.push(funcString);
|
||||
try {
|
||||
xrange.min = Function.apply(null, allParamNames);
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'ERROR: could not create a function from the string "' +
|
||||
funcString + '" for xrange.min.'
|
||||
);
|
||||
console.log('Error message: "' + err.message + '"');
|
||||
|
||||
if (state.showDebugInfo) {
|
||||
$('#' + gstId).html(
|
||||
'<div style="color: red;">' + 'ERROR IN ' +
|
||||
'XML: Could not create a function from the string "' +
|
||||
funcString + '" for xrange.min.' + '</div>'
|
||||
);
|
||||
$('#' + gstId).append(
|
||||
'<div style="color: red;">' + 'Error ' +
|
||||
'message: "' + err.message + '".' + '</div>'
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
allParamNames.pop();
|
||||
|
||||
if (config.plot.xrange.hasOwnProperty('max') === false) {
|
||||
console.log(
|
||||
'ERROR: Expected config.plot.xrange.max to be ' +
|
||||
'present. It is not.'
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
disableAutoReturn = false;
|
||||
if (typeof config.plot.xrange.max === 'string') {
|
||||
funcString = config.plot.xrange.max;
|
||||
} else if (
|
||||
($.isPlainObject(config.plot.xrange.max) === true) &&
|
||||
(config.plot.xrange.max.hasOwnProperty('#text') === true) &&
|
||||
(typeof config.plot.xrange.max['#text'] === 'string')
|
||||
) {
|
||||
funcString = config.plot.xrange.max['#text'];
|
||||
|
||||
disableAutoReturn =
|
||||
config.plot.xrange.max['@disable_auto_return'];
|
||||
if (
|
||||
(disableAutoReturn === undefined) ||
|
||||
(
|
||||
(typeof disableAutoReturn === 'string') &&
|
||||
(disableAutoReturn.toLowerCase() !== 'true')
|
||||
)
|
||||
) {
|
||||
disableAutoReturn = false;
|
||||
} else {
|
||||
disableAutoReturn = true;
|
||||
}
|
||||
} else {
|
||||
console.log(
|
||||
'ERROR: Could not get a function definition for ' +
|
||||
'xrange.max property.'
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
funcString = $('<div>').html(funcString).text();
|
||||
|
||||
if (disableAutoReturn === false) {
|
||||
if (funcString.search(/return/i) === -1) {
|
||||
funcString = 'return ' + funcString;
|
||||
}
|
||||
} else {
|
||||
if (funcString.search(/return/i) === -1) {
|
||||
console.log(
|
||||
'ERROR: You have specified a JavaScript ' +
|
||||
'function without a "return" statemnt. Your ' +
|
||||
'function will return "undefined" by default.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
allParamNames.push(funcString);
|
||||
try {
|
||||
xrange.max = Function.apply(null, allParamNames);
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'ERROR: could not create a function from the string "' +
|
||||
funcString + '" for xrange.max.'
|
||||
);
|
||||
console.log('Error message: "' + err.message + '"');
|
||||
|
||||
if (state.showDebugInfo) {
|
||||
$('#' + gstId).html(
|
||||
'<div style="color: red;">' + 'ERROR IN ' +
|
||||
'XML: Could not create a function from the string "' +
|
||||
funcString + '" for xrange.max.' + '</div>'
|
||||
);
|
||||
$('#' + gstId).append(
|
||||
'<div style="color: red;">' + 'Error message: "' +
|
||||
err.message + '".' + '</div>'
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
allParamNames.pop();
|
||||
|
||||
tempNum = parseInt(config.plot.num_points, 10);
|
||||
if (isFinite(tempNum) === false) {
|
||||
tempNum = plotDiv.width() / 5.0;
|
||||
}
|
||||
|
||||
if (
|
||||
(tempNum < 2) &&
|
||||
(tempNum > 1000)
|
||||
) {
|
||||
console.log(
|
||||
'ERROR: Number of points is outside the allowed range ' +
|
||||
'[2, 1000]'
|
||||
);
|
||||
console.log('config.plot.num_points = ' + tempNum);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
numPoints = tempNum;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function createFunctions() {
|
||||
var c1;
|
||||
|
||||
functions = [];
|
||||
|
||||
if (typeof config.functions === 'undefined') {
|
||||
console.log('ERROR: config.functions is undefined.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof config.functions["function"] === 'string') {
|
||||
|
||||
// If just one function string is present.
|
||||
addFunction(config.functions["function"]);
|
||||
|
||||
} else if ($.isPlainObject(config.functions["function"]) === true) {
|
||||
|
||||
// If a function is present, but it also has properties
|
||||
// defined.
|
||||
callAddFunction(config.functions["function"]);
|
||||
|
||||
} else if ($.isArray(config.functions["function"])) {
|
||||
|
||||
// If more than one function is defined.
|
||||
for (c1 = 0; c1 < config.functions["function"].length; c1 += 1) {
|
||||
|
||||
// For each definition, we must check if it is a simple
|
||||
// string definition, or a complex one with properties.
|
||||
if (typeof config.functions["function"][c1] === 'string') {
|
||||
|
||||
// Simple string.
|
||||
addFunction(config.functions["function"][c1]);
|
||||
|
||||
} else if ($.isPlainObject(config.functions["function"][c1])) {
|
||||
|
||||
// Properties are present.
|
||||
callAddFunction(config.functions["function"][c1]);
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log('ERROR: config.functions.function is of an unsupported type.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
// This function will reduce code duplication. We have to call
|
||||
// the function addFunction() several times passing object
|
||||
// properties as parameters. Rather than writing them out every
|
||||
// time, we will have a single place where it is done.
|
||||
function callAddFunction(obj) {
|
||||
if (
|
||||
(obj.hasOwnProperty('@output')) &&
|
||||
(typeof obj['@output'] === 'string')
|
||||
) {
|
||||
|
||||
// If this function is meant to be calculated for an
|
||||
// element then skip it.
|
||||
if ((obj['@output'].toLowerCase() === 'element') ||
|
||||
(obj['@output'].toLowerCase() === 'none')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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') {
|
||||
console.log(
|
||||
'ERROR: Function "output" attribute can be ' +
|
||||
'either "element", "plot_label", "none" or "graph".'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// The user did not specify an "output" attribute, or it is
|
||||
// "graph".
|
||||
addFunction(
|
||||
obj['#text'],
|
||||
obj['@color'],
|
||||
obj['@line'],
|
||||
obj['@dot'],
|
||||
obj['@label'],
|
||||
obj['@point_size'],
|
||||
obj['@fill_area'],
|
||||
obj['@bar'],
|
||||
obj['@disable_auto_return']
|
||||
);
|
||||
}
|
||||
|
||||
function addFunction(funcString, color, line, dot, label,
|
||||
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.
|
||||
if (typeof funcString !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure that any HTML entities that were escaped will be
|
||||
// unescaped. This is done because if a string with escaped
|
||||
// HTML entities is passed to the Function() constructor, it
|
||||
// will break.
|
||||
funcString = $('<div>').html(funcString).text();
|
||||
|
||||
// If the user did not specifically turn off this feature,
|
||||
// check if the function string contains a 'return', and
|
||||
// prepend a 'return ' to the string if one, or more, is not
|
||||
// found.
|
||||
if (
|
||||
(disableAutoReturn === undefined) ||
|
||||
(
|
||||
(typeof disableAutoReturn === 'string') &&
|
||||
(disableAutoReturn.toLowerCase() !== 'true')
|
||||
)
|
||||
) {
|
||||
if (funcString.search(/return/i) === -1) {
|
||||
funcString = 'return ' + funcString;
|
||||
}
|
||||
} else {
|
||||
if (funcString.search(/return/i) === -1) {
|
||||
console.log(
|
||||
'ERROR: You have specified a JavaScript ' +
|
||||
'function without a "return" statemnt. Your ' +
|
||||
'function will return "undefined" by default.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Some defaults. If no options are set for the graph, we will
|
||||
// make sure that at least a line is drawn for a function.
|
||||
newFunctionObject = {
|
||||
'line': true,
|
||||
'dot': false,
|
||||
'bars': false
|
||||
};
|
||||
|
||||
// Get all of the parameter names defined by the user in the
|
||||
// XML.
|
||||
paramNames = state.getAllParameterNames();
|
||||
|
||||
// The 'x' is always one of the function parameters.
|
||||
paramNames.push('x');
|
||||
|
||||
// Must make sure that the function body also gets passed to
|
||||
// the Function constructor.
|
||||
paramNames.push(funcString);
|
||||
|
||||
// Create the function from the function string, and all of the
|
||||
// available parameters AND the 'x' variable as it's parameters.
|
||||
// For this we will use the built-in Function object
|
||||
// constructor.
|
||||
//
|
||||
// If something goes wrong during this step, most
|
||||
// likely the user supplied an invalid JavaScript function body
|
||||
// string. In this case we will not proceed.
|
||||
try {
|
||||
func = Function.apply(null, paramNames);
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'ERROR: The function body "' +
|
||||
funcString +
|
||||
'" was not converted by the Function constructor.'
|
||||
);
|
||||
console.log('Error message: "' + err.message + '"');
|
||||
|
||||
if (state.showDebugInfo) {
|
||||
$('#' + gstId).html('<div style="color: red;">' + 'ERROR IN XML: Could not create a function from the string "' + funcString + '".' + '</div>');
|
||||
$('#' + gstId).append('<div style="color: red;">' + 'Error message: "' + err.message + '".' + '</div>');
|
||||
}
|
||||
|
||||
paramNames.pop();
|
||||
paramNames.pop();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Return the array back to original state. Remember that it is
|
||||
// a pointer to original array which is stored in state object.
|
||||
paramNames.pop();
|
||||
paramNames.pop();
|
||||
|
||||
newFunctionObject['func'] = func;
|
||||
|
||||
if (typeof color === 'string') {
|
||||
newFunctionObject['color'] = color;
|
||||
}
|
||||
|
||||
if (typeof line === 'string') {
|
||||
if (line.toLowerCase() === 'true') {
|
||||
newFunctionObject['line'] = true;
|
||||
} else if (line.toLowerCase() === 'false') {
|
||||
newFunctionObject['line'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof dot === 'string') {
|
||||
if (dot.toLowerCase() === 'true') {
|
||||
newFunctionObject['dot'] = true;
|
||||
} else if (dot.toLowerCase() === 'false') {
|
||||
newFunctionObject['dot'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof pointSize === 'string') {
|
||||
newFunctionObject['pointSize'] = pointSize;
|
||||
}
|
||||
|
||||
if (typeof bar === 'string') {
|
||||
if (bar.toLowerCase() === 'true') {
|
||||
newFunctionObject['bars'] = true;
|
||||
} else if (bar.toLowerCase() === 'false') {
|
||||
newFunctionObject['bars'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (newFunctionObject['bars'] === true) {
|
||||
newFunctionObject['line'] = false;
|
||||
newFunctionObject['dot'] = false;
|
||||
// To do: See if need to do anything here.
|
||||
} else if (
|
||||
(newFunctionObject['dot'] === false) &&
|
||||
(newFunctionObject['line'] === false)
|
||||
) {
|
||||
newFunctionObject['line'] = true;
|
||||
}
|
||||
|
||||
if (newFunctionObject['line'] === true) {
|
||||
if (typeof fillArea === 'string') {
|
||||
if (fillArea.toLowerCase() === 'true') {
|
||||
newFunctionObject['fillArea'] = true;
|
||||
} else if (fillArea.toLowerCase() === 'false') {
|
||||
newFunctionObject['fillArea'] = false;
|
||||
} else {
|
||||
console.log('ERROR: The attribute fill_area should be either "true" or "false".');
|
||||
console.log('fill_area = "' + fillArea + '".');
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof label === 'string') {
|
||||
|
||||
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;
|
||||
} else {
|
||||
newFunctionObject.label = false;
|
||||
}
|
||||
|
||||
functions.push(newFunctionObject);
|
||||
}
|
||||
}
|
||||
|
||||
// The callback that will be called whenever a constant changes (gets
|
||||
// updated via a slider or a text input).
|
||||
function onUpdatePlot(event) {
|
||||
if (generateData() === true) {
|
||||
updatePlot();
|
||||
}
|
||||
}
|
||||
|
||||
function generateData() {
|
||||
var c0, c1, c3, functionObj, seriesObj, dataPoints, paramValues, x, y,
|
||||
start, end, step, numNotUndefined;
|
||||
|
||||
paramValues = state.getAllParameterValues();
|
||||
|
||||
dataSeries = [];
|
||||
|
||||
for (c0 = 0; c0 < functions.length; c0 += 1) {
|
||||
functionObj = functions[c0];
|
||||
|
||||
try {
|
||||
start = xrange.min.apply(window, paramValues);
|
||||
} catch (err) {
|
||||
console.log('ERROR: Could not determine xrange start.');
|
||||
console.log('Error message: "' + err.message + '".');
|
||||
|
||||
if (state.showDebugInfo) {
|
||||
$('#' + gstId).html('<div style="color: red;">' + 'ERROR IN XML: Could not determine xrange start from defined function.' + '</div>');
|
||||
$('#' + gstId).append('<div style="color: red;">' + 'Error message: "' + err.message + '".' + '</div>');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
end = xrange.max.apply(window, paramValues);
|
||||
} catch (err) {
|
||||
console.log('ERROR: Could not determine xrange end.');
|
||||
console.log('Error message: "' + err.message + '".');
|
||||
|
||||
if (state.showDebugInfo) {
|
||||
$('#' + gstId).html('<div style="color: red;">' + 'ERROR IN XML: Could not determine xrange end from defined function.' + '</div>');
|
||||
$('#' + gstId).append('<div style="color: red;">' + 'Error message: "' + err.message + '".' + '</div>');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
seriesObj = {};
|
||||
dataPoints = [];
|
||||
|
||||
// For counting number of points added. In the end we will
|
||||
// compare this number to 'numPoints' specified in the config
|
||||
// JSON.
|
||||
c1 = 0;
|
||||
|
||||
step = (end - start) / (numPoints - 1);
|
||||
|
||||
// Generate the data points.
|
||||
for (x = start; x <= end; x += step) {
|
||||
|
||||
// Push the 'x' variable to the end of the parameter array.
|
||||
paramValues.push(x);
|
||||
|
||||
// We call the user defined function, passing all of the
|
||||
// available parameter values. Inside this function they
|
||||
// will be accessible by their names.
|
||||
try {
|
||||
y = functionObj.func.apply(window, paramValues);
|
||||
} catch (err) {
|
||||
console.log('ERROR: Could not generate data.');
|
||||
console.log('Error message: "' + err.message + '".');
|
||||
|
||||
if (state.showDebugInfo) {
|
||||
$('#' + gstId).html('<div style="color: red;">' + 'ERROR IN XML: Could not generate data from defined function.' + '</div>');
|
||||
$('#' + gstId).append('<div style="color: red;">' + 'Error message: "' + err.message + '".' + '</div>');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return the paramValues array to how it was before we
|
||||
// added 'x' variable to the end of it.
|
||||
paramValues.pop();
|
||||
|
||||
// Add the generated point to the data points set.
|
||||
dataPoints.push([x, y]);
|
||||
|
||||
c1 += 1;
|
||||
|
||||
}
|
||||
|
||||
// If the last point did not get included because of rounding
|
||||
// of floating-point number addition, then we will include it
|
||||
// manually.
|
||||
if (c1 != numPoints) {
|
||||
x = end;
|
||||
paramValues.push(x);
|
||||
try {
|
||||
y = functionObj.func.apply(window, paramValues);
|
||||
} catch (err) {
|
||||
console.log('ERROR: Could not generate data.');
|
||||
console.log('Error message: "' + err.message + '".');
|
||||
|
||||
if (state.showDebugInfo) {
|
||||
$('#' + gstId).html('<div style="color: red;">' + 'ERROR IN XML: Could not generate data from function.' + '</div>');
|
||||
$('#' + gstId).append('<div style="color: red;">' + 'Error message: "' + err.message + '".' + '</div>');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
paramValues.pop();
|
||||
dataPoints.push([x, y]);
|
||||
}
|
||||
|
||||
// Put the entire data points set into the series object.
|
||||
seriesObj.data = dataPoints;
|
||||
|
||||
// See if user defined a specific color for this function.
|
||||
if (functionObj.hasOwnProperty('color') === true) {
|
||||
seriesObj.color = functionObj.color;
|
||||
}
|
||||
|
||||
// See if a user defined a label for this function.
|
||||
if (functionObj.label !== false) {
|
||||
if (functionObj.specialLabel === true) {
|
||||
(function (c1) {
|
||||
var tempLabel;
|
||||
|
||||
tempLabel = functionObj.label;
|
||||
|
||||
while (c1 < functionObj.pldeHash.length) {
|
||||
tempLabel = tempLabel.replace(
|
||||
functionObj.pldeHash[c1].elId,
|
||||
functionObj.pldeHash[c1].func.apply(
|
||||
window,
|
||||
state.getAllParameterValues()
|
||||
)
|
||||
);
|
||||
|
||||
c1 += 1;
|
||||
}
|
||||
|
||||
seriesObj.label = tempLabel;
|
||||
}(0));
|
||||
} else {
|
||||
seriesObj.label = functionObj.label;
|
||||
}
|
||||
}
|
||||
|
||||
// Should the data points be connected by a line?
|
||||
seriesObj.lines = {
|
||||
'show': functionObj.line
|
||||
};
|
||||
|
||||
if (functionObj.hasOwnProperty('fillArea') === true) {
|
||||
seriesObj.lines.fill = functionObj.fillArea;
|
||||
}
|
||||
|
||||
// Should each data point be represented by a point on the
|
||||
// graph?
|
||||
seriesObj.points = {
|
||||
'show': functionObj.dot
|
||||
};
|
||||
|
||||
seriesObj.bars = {
|
||||
'show': functionObj.bars,
|
||||
'barWidth': graphBarWidth
|
||||
};
|
||||
|
||||
if (graphBarAlign !== null) {
|
||||
seriesObj.bars.align = graphBarAlign;
|
||||
}
|
||||
|
||||
if (functionObj.hasOwnProperty('pointSize')) {
|
||||
seriesObj.points.radius = functionObj.pointSize;
|
||||
}
|
||||
|
||||
// Add the newly created series object to the series set which
|
||||
// will be plotted by Flot.
|
||||
dataSeries.push(seriesObj);
|
||||
}
|
||||
|
||||
if (graphBarAlign === null) {
|
||||
for (c0 = 0; c0 < numPoints; c0 += 1) {
|
||||
// Number of points that have a value other than 'undefined' (undefined).
|
||||
numNotUndefined = 0;
|
||||
|
||||
for (c1 = 0; c1 < dataSeries.length; c1 += 1) {
|
||||
if (dataSeries[c1].bars.show === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isFinite(parseInt(dataSeries[c1].data[c0][1])) === true) {
|
||||
numNotUndefined += 1;
|
||||
}
|
||||
}
|
||||
|
||||
c3 = 0;
|
||||
for (c1 = 0; c1 < dataSeries.length; c1 += 1) {
|
||||
if (dataSeries[c1].bars.show === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dataSeries[c1].data[c0][0] -= graphBarWidth * (0.5 * numNotUndefined - c3);
|
||||
|
||||
if (isFinite(parseInt(dataSeries[c1].data[c0][1])) === true) {
|
||||
c3 += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (c0 = 0; c0 < asymptotes.length; c0 += 1) {
|
||||
|
||||
// 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;
|
||||
} // End-of: function generateData
|
||||
|
||||
function updatePlot() {
|
||||
var paramValues, plotObj;
|
||||
|
||||
paramValues = state.getAllParameterValues();
|
||||
|
||||
if (xaxis.tickFormatter !== null) {
|
||||
xaxis.ticks = null;
|
||||
}
|
||||
|
||||
if (yaxis.tickFormatter !== null) {
|
||||
yaxis.ticks = null;
|
||||
}
|
||||
|
||||
// Tell Flot to draw the graph to our specification.
|
||||
plotObj = $.plot(
|
||||
plotDiv,
|
||||
dataSeries,
|
||||
{
|
||||
'xaxis': xaxis,
|
||||
'yaxis': yaxis,
|
||||
'legend': {
|
||||
|
||||
// To show the legend or not. Note, even if 'show' is
|
||||
// 'true', the legend will only show if labels are
|
||||
// provided for at least one of the series that are
|
||||
// going to be plotted.
|
||||
'show': true,
|
||||
|
||||
// A floating point number in the range [0, 1]. The
|
||||
// smaller the number, the more transparent will the
|
||||
// legend background become.
|
||||
'backgroundOpacity': 0
|
||||
|
||||
},
|
||||
'grid': {
|
||||
'markings': generateMarkings()
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
updateMovingLabels();
|
||||
|
||||
// The first time that the graph gets added to the page, the legend
|
||||
// is created from scratch. When it appears, MathJax works some
|
||||
// magic, and all of the specially marked TeX gets rendered nicely.
|
||||
// The next time when we update the graph, no such thing happens.
|
||||
// We must ask MathJax to typeset the legend again (well, we will
|
||||
// ask it to look at our entire graph DIV), the next time it's
|
||||
// worker queue is available.
|
||||
MathJax.Hub.Queue([
|
||||
'Typeset',
|
||||
MathJax.Hub,
|
||||
plotDiv.attr('id')
|
||||
]);
|
||||
|
||||
return;
|
||||
|
||||
function updateMovingLabels() {
|
||||
var c1, labelCoord, pointOffset;
|
||||
|
||||
for (c1 = 0; c1 < movingLabels.length; c1 += 1) {
|
||||
if (movingLabels[c1].el === null) {
|
||||
movingLabels[c1].el = $(
|
||||
'<div>' +
|
||||
movingLabels[c1].labelText +
|
||||
'</div>'
|
||||
);
|
||||
movingLabels[c1].el.css('position', 'absolute');
|
||||
movingLabels[c1].el.css('color', movingLabels[c1].fontColor);
|
||||
movingLabels[c1].el.css('font-weight', movingLabels[c1].fontWeight);
|
||||
movingLabels[c1].el.appendTo(plotDiv);
|
||||
|
||||
movingLabels[c1].elWidth = movingLabels[c1].el.width();
|
||||
movingLabels[c1].elHeight = movingLabels[c1].el.height();
|
||||
} else {
|
||||
movingLabels[c1].el.detach();
|
||||
movingLabels[c1].el.appendTo(plotDiv);
|
||||
}
|
||||
|
||||
labelCoord = movingLabels[c1].func.apply(window, paramValues);
|
||||
|
||||
pointOffset = plotObj.pointOffset({'x': labelCoord.x, 'y': labelCoord.y});
|
||||
|
||||
movingLabels[c1].el.css('left', pointOffset.left - 0.5 * movingLabels[c1].elWidth);
|
||||
movingLabels[c1].el.css('top', pointOffset.top - 0.5 * movingLabels[c1].elHeight);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate markings to represent asymptotes defined by the user.
|
||||
// See the following function for more details:
|
||||
//
|
||||
// function processAsymptote()
|
||||
//
|
||||
function generateMarkings() {
|
||||
var c1, asymptote, markings, val;
|
||||
|
||||
markings = [];
|
||||
|
||||
for (c1 = 0; c1 < asymptotes.length; c1 += 1) {
|
||||
asymptote = asymptotes[c1];
|
||||
|
||||
try {
|
||||
val = asymptote.func.apply(window, paramValues);
|
||||
} catch (err) {
|
||||
console.log('ERROR: Could not generate value from asymptote function.');
|
||||
console.log('Error message: ', err.message);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (asymptote.type === 'x') {
|
||||
markings.push({
|
||||
'color': asymptote.color,
|
||||
'lineWidth': 2,
|
||||
'xaxis': {
|
||||
'from': val,
|
||||
'to': val
|
||||
}
|
||||
});
|
||||
} else {
|
||||
markings.push({
|
||||
'color': asymptote.color,
|
||||
'lineWidth': 2,
|
||||
'yaxis': {
|
||||
'from': val,
|
||||
'to': val
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return markings;
|
||||
}
|
||||
}
|
||||
|
||||
function xAxisTickFormatter(val, axis) {
|
||||
if (xTicksNames.hasOwnProperty(val.toFixed(axis.tickDecimals)) === true) {
|
||||
return xTicksNames[val.toFixed(axis.tickDecimals)];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function yAxisTickFormatter(val, axis) {
|
||||
if (yTicksNames.hasOwnProperty(val.toFixed(axis.tickDecimals)) === true) {
|
||||
return yTicksNames[val.toFixed(axis.tickDecimals)];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
// 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)
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* We will add a function that will be called for all GraphicalSliderTool
|
||||
* xmodule module instances. It must be available globally by design of
|
||||
* xmodule.
|
||||
*/
|
||||
window.GraphicalSliderTool = function (el) {
|
||||
// All the work will be performed by the GstMain module. We will get access
|
||||
// to it, and all it's dependencies, via Require JS. Currently Require JS
|
||||
// is namespaced and is available via a global object RequireJS.
|
||||
RequireJS.require(['GstMain'], function (GstMain) {
|
||||
// The GstMain module expects the DOM ID of a Graphical Slider Tool
|
||||
// element. Since we are given a <section> element which might in
|
||||
// theory contain multiple graphical_slider_tool <div> elements (each
|
||||
// with a unique DOM ID), we will iterate over all children, and for
|
||||
// each match, we will call GstMain module.
|
||||
$(el).children('.graphical_slider_tool').each(function (index, value) {
|
||||
JavascriptLoader.executeModuleScripts($(value), function(){
|
||||
GstMain($(value).attr('id'));
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,86 +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(
|
||||
'GstMain',
|
||||
|
||||
// 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', 'GLabelElOutput'],
|
||||
function (State, GeneralMethods, Sliders, Inputs, Graph, ElOutput, GLabelElOutput) {
|
||||
|
||||
return GstMain;
|
||||
|
||||
function GstMain(gstId) {
|
||||
var config, gstClass, state;
|
||||
|
||||
if ($('#' + gstId).attr('data-processed') !== 'processed') {
|
||||
$('#' + gstId).attr('data-processed', 'processed');
|
||||
} else {
|
||||
// console.log('MESSAGE: Already processed GST with ID ' + gstId + '. Skipping.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the JSON configuration, parse it, and store as an object.
|
||||
try {
|
||||
config = JSON.parse($('#' + gstId + '_json').html()).root;
|
||||
} catch (err) {
|
||||
console.log('ERROR: could not parse config JSON.');
|
||||
console.log('$("#" + gstId + "_json").html() = ', $('#' + gstId + '_json').html());
|
||||
console.log('JSON.parse(...) = ', JSON.parse($('#' + gstId + '_json').html()));
|
||||
console.log('config = ', config);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the class name of the GST. All elements are assigned a class
|
||||
// name that is based on the class name of the GST. For example, inputs
|
||||
// are assigned a class name '{GST class name}_input'.
|
||||
if (typeof config['@class'] !== 'string') {
|
||||
console.log('ERROR: Could not get the class name of GST.');
|
||||
console.log('config["@class"] = ', config['@class']);
|
||||
|
||||
return;
|
||||
}
|
||||
gstClass = config['@class'];
|
||||
|
||||
// Parse the configuration settings for parameters, and store them in a
|
||||
// state object.
|
||||
state = State(gstId, config);
|
||||
|
||||
state.showDebugInfo = false;
|
||||
|
||||
// It is possible that something goes wrong while extracting parameters
|
||||
// from the JSON config object. In this case, we will not continue.
|
||||
if (state === undefined) {
|
||||
console.log('ERROR: The state object was not initialized properly.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the sliders and the text inputs, attaching them to
|
||||
// appropriate parameters.
|
||||
Sliders(gstId, state);
|
||||
Inputs(gstId, gstClass, state);
|
||||
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
|
||||
// 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)
|
||||
@@ -1,88 +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('Inputs', [], function () {
|
||||
return Inputs;
|
||||
|
||||
function Inputs(gstId, gstClass, state) {
|
||||
var c1, paramName, allParamNames;
|
||||
|
||||
allParamNames = state.getAllParameterNames();
|
||||
|
||||
for (c1 = 0; c1 < allParamNames.length; c1 += 1) {
|
||||
$('#' + gstId).find('.' + gstClass + '_input').each(function (index, value) {
|
||||
var inputDiv, paramName;
|
||||
|
||||
paramName = allParamNames[c1];
|
||||
inputDiv = $(value);
|
||||
|
||||
if (paramName === inputDiv.data('var')) {
|
||||
createInput(inputDiv, paramName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
function createInput(inputDiv, paramName) {
|
||||
var paramObj;
|
||||
|
||||
paramObj = state.getParamObj(paramName);
|
||||
|
||||
// Check that the retrieval went OK.
|
||||
if (paramObj === undefined) {
|
||||
console.log('ERROR: Could not get a paramObj for parameter "' + paramName + '".');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Bind a function to the 'change' event. Whenever the user changes
|
||||
// the value of this text input, and presses 'enter' (or clicks
|
||||
// somewhere else on the page), this event will be triggered, and
|
||||
// our callback will be called.
|
||||
inputDiv.bind('change', inputOnChange);
|
||||
|
||||
inputDiv.val(paramObj.value);
|
||||
|
||||
// Lets style the input element nicely. We will use the button()
|
||||
// widget for this since there is no native widget for the text
|
||||
// input.
|
||||
inputDiv.button().css({
|
||||
'font': 'inherit',
|
||||
'color': 'inherit',
|
||||
'text-align': 'left',
|
||||
'outline': 'none',
|
||||
'cursor': 'text',
|
||||
'height': '15px'
|
||||
});
|
||||
|
||||
// Tell the parameter object from state that we are attaching a
|
||||
// text input to it. Next time the parameter will be updated with
|
||||
// a new value, tis input will also be updated.
|
||||
paramObj.inputDivs.push(inputDiv);
|
||||
|
||||
return;
|
||||
|
||||
// Update the 'state' - i.e. set the value of the parameter this
|
||||
// input is attached to to a new value.
|
||||
//
|
||||
// This will cause the plot to be redrawn each time after the user
|
||||
// changes the value in the input. Note that he has to either press
|
||||
// 'Enter', or click somewhere else on the page in order for the
|
||||
// 'change' event to be tiggered.
|
||||
function inputOnChange(event) {
|
||||
var inputDiv;
|
||||
|
||||
inputDiv = $(this);
|
||||
state.setParameterValue(paramName, inputDiv.val(), inputDiv);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 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)
|
||||
@@ -1,236 +0,0 @@
|
||||
function jstat(){}
|
||||
j=jstat;(function(){var initializing=false,fnTest=/xyz/.test(function(){xyz;})?/\b_super\b/:/.*/;this.Class=function(){};Class.extend=function(prop){var _super=this.prototype;initializing=true;var prototype=new this();initializing=false;for(var name in prop){prototype[name]=typeof prop[name]=="function"&&typeof _super[name]=="function"&&fnTest.test(prop[name])?(function(name,fn){return function(){var tmp=this._super;this._super=_super[name];var ret=fn.apply(this,arguments);this._super=tmp;return ret;};})(name,prop[name]):prop[name];}
|
||||
function Class(){if(!initializing&&this.init)
|
||||
this.init.apply(this,arguments);}
|
||||
Class.prototype=prototype;Class.constructor=Class;Class.extend=arguments.callee;return Class;};})();jstat.ONE_SQRT_2PI=0.3989422804014327;jstat.LN_SQRT_2PI=0.9189385332046727417803297;jstat.LN_SQRT_PId2=0.225791352644727432363097614947;jstat.DBL_MIN=2.22507e-308;jstat.DBL_EPSILON=2.220446049250313e-16;jstat.SQRT_32=5.656854249492380195206754896838;jstat.TWO_PI=6.283185307179586;jstat.DBL_MIN_EXP=-999;jstat.SQRT_2dPI=0.79788456080287;jstat.LN_SQRT_PI=0.5723649429247;jstat.seq=function(min,max,length){var r=new Range(min,max,length);return r.getPoints();}
|
||||
jstat.dnorm=function(x,mean,sd,log){if(mean==null)mean=0;if(sd==null)sd=1;if(log==null)log=false;var n=new NormalDistribution(mean,sd);if(!isNaN(x)){return n._pdf(x,log);}else if(x.length){var res=[];for(var i=0;i<x.length;i++){res.push(n._pdf(x[i],log));}
|
||||
return res;}else{throw"Illegal argument: x";}}
|
||||
jstat.pnorm=function(q,mean,sd,lower_tail,log){if(mean==null)mean=0;if(sd==null)sd=1;if(lower_tail==null)lower_tail=true;if(log==null)log=false;var n=new NormalDistribution(mean,sd);if(!isNaN(q)){return n._cdf(q,lower_tail,log);}else if(q.length){var res=[];for(var i=0;i<q.length;i++){res.push(n._cdf(q[i],lower_tail,log));}
|
||||
return res;}else{throw"Illegal argument: x";}}
|
||||
jstat.dlnorm=function(x,meanlog,sdlog,log){if(meanlog==null)meanlog=0;if(sdlog==null)sdlog=1;if(log==null)log=false;var n=new LogNormalDistribution(meanlog,sdlog);if(!isNaN(x)){return n._pdf(x,log);}else if(x.length){var res=[];for(var i=0;i<x.length;i++){res.push(n._pdf(x[i],log));}
|
||||
return res;}else{throw"Illegal argument: x";}}
|
||||
jstat.plnorm=function(q,meanlog,sdlog,lower_tail,log){if(meanlog==null)meanlog=0;if(sdlog==null)sdlog=1;if(lower_tail==null)lower_tail=true;if(log==null)log=false;var n=new LogNormalDistribution(meanlog,sdlog);if(!isNaN(q)){return n._cdf(q,lower_tail,log);}
|
||||
else if(q.length){var res=[];for(var i=0;i<q.length;i++){res.push(n._cdf(q[i],lower_tail,log));}
|
||||
return res;}else{throw"Illegal argument: x";}}
|
||||
jstat.dbeta=function(x,alpha,beta,ncp,log){if(ncp==null)ncp=0;if(log==null)log=false;var b=new BetaDistribution(alpha,beta);if(!isNaN(x)){return b._pdf(x,log);}
|
||||
else if(x.length){var res=[];for(var i=0;i<x.length;i++){res.push(b._pdf(x[i],log));}
|
||||
return res;}else{throw"Illegal argument: x";}}
|
||||
jstat.pbeta=function(q,alpha,beta,ncp,lower_tail,log){if(ncp==null)ncp=0;if(log==null)log=false;if(lower_tail==null)lower_tail=true;var b=new BetaDistribution(alpha,beta);if(!isNaN(q)){return b._cdf(q,lower_tail,log);}else if(q.length){var res=[];for(var i=0;i<q.length;i++){res.push(b._cdf(q[i],lower_tail,log));}
|
||||
return res;}
|
||||
else{throw"Illegal argument: x";}}
|
||||
jstat.dgamma=function(x,shape,rate,scale,log){if(rate==null)rate=1;if(scale==null)scale=1/rate;if(log==null)log=false;var g=new GammaDistribution(shape,scale);if(!isNaN(x)){return g._pdf(x,log);}else if(x.length){var res=[];for(var i=0;i<x.length;i++){res.push(g._pdf(x[i],log));}
|
||||
return res;}else{throw"Illegal argument: x";}}
|
||||
jstat.pgamma=function(q,shape,rate,scale,lower_tail,log){if(rate==null)rate=1;if(scale==null)scale=1/rate;if(lower_tail==null)lower_tail=true;if(log==null)log=false;var g=new GammaDistribution(shape,scale);if(!isNaN(q)){return g._cdf(q,lower_tail,log);}else if(q.length){var res=[];for(var i=0;i<q.length;i++){res.push(g._cdf(q[i],lower_tail,log));}
|
||||
return res;}else{throw"Illegal argument: x";}}
|
||||
jstat.dt=function(x,df,ncp,log){if(log==null)log=false;var t=new StudentTDistribution(df,ncp);if(!isNaN(x)){return t._pdf(x,log);}else if(x.length){var res=[];for(var i=0;i<x.length;i++){res.push(t._pdf(x[i],log));}
|
||||
return res;}else{throw"Illegal argument: x";}}
|
||||
jstat.pt=function(q,df,ncp,lower_tail,log){if(lower_tail==null)lower_tail=true;if(log==null)log=false;var t=new StudentTDistribution(df,ncp);if(!isNaN(q)){return t._cdf(q,lower_tail,log);}else if(q.length){var res=[];for(var i=0;i<q.length;i++){res.push(t._cdf(q[i],lower_tail,log));}
|
||||
return res;}else{throw"Illegal argument: x";}}
|
||||
jstat.plot=function(x,y,options){if(x==null){throw"x is undefined in jstat.plot";}
|
||||
if(y==null){throw"y is undefined in jstat.plot";}
|
||||
if(x.length!=y.length){throw"x and y lengths differ in jstat.plot";}
|
||||
var flotOpt={series:{lines:{},points:{}}};var series=[];if(x.length==undefined){series.push([x,y]);flotOpt.series.points.show=true;}else{for(var i=0;i<x.length;i++){series.push([x[i],y[i]]);}}
|
||||
var title='jstat graph';if(options!=null){if(options.type!=null){if(options.type=='l'){flotOpt.series.lines.show=true;}else if(options.type=='p'){flotOpt.series.lines.show=false;flotOpt.series.points.show=true;}}
|
||||
if(options.hover!=null){flotOpt.grid={hoverable:options.hover}}
|
||||
if(options.main!=null){title=options.main;}}
|
||||
var now=new Date();var hash=now.getMilliseconds()*now.getMinutes()+now.getSeconds();$('body').append('<div title="'+title+'" style="display: none;" id="'+hash+'"><div id="graph-'+hash+'" style="width:95%; height: 95%"></div></div>');$('#'+hash).dialog({modal:false,width:475,height:475,resizable:true,resize:function(){$.plot($('#graph-'+hash),[series],flotOpt);},open:function(event,ui){var id='#graph-'+hash;$.plot($('#graph-'+hash),[series],flotOpt);}})}
|
||||
jstat.log10=function(arg){return Math.log(arg)/Math.LN10;}
|
||||
jstat.toSigFig=function(num,n){if(num==0){return 0;}
|
||||
var d=Math.ceil(jstat.log10(num<0?-num:num));var power=n-parseInt(d);var magnitude=Math.pow(10,power);var shifted=Math.round(num*magnitude);return shifted/magnitude;}
|
||||
jstat.trunc=function(x){return(x>0)?Math.floor(x):Math.ceil(x);}
|
||||
jstat.isFinite=function(x){return(!isNaN(x)&&(x!=Number.POSITIVE_INFINITY)&&(x!=Number.NEGATIVE_INFINITY));}
|
||||
jstat.dopois_raw=function(x,lambda,give_log){if(lambda==0){if(x==0){return(give_log)?0.0:1.0;}
|
||||
return(give_log)?Number.NEGATIVE_INFINITY:0.0;}
|
||||
if(!jstat.isFinite(lambda))return(give_log)?Number.NEGATIVE_INFINITY:0.0;if(x<0)return(give_log)?Number.NEGATIVE_INFINITY:0.0;if(x<=lambda*jstat.DBL_MIN){return(give_log)?-lambda:Math.exp(-lambda);}
|
||||
if(lambda<x*jstat.DBL_MIN){var param=-lambda+x*Math.log(lambda)-jstat.lgamma(x+1);return(give_log)?param:Math.exp(param);}
|
||||
var param1=jstat.TWO_PI*x;var param2=-jstat.stirlerr(x)-jstat.bd0(x,lambda);return(give_log)?-0.5*Math.log(param1)+param2:Math.exp(param2)/Math.sqrt(param1);}
|
||||
jstat.bd0=function(x,np){var ej,s,s1,v,j;if(!jstat.isFinite(x)||!jstat.isFinite(np)||np==0.0)throw"illegal parameter in jstat.bd0";if(Math.abs(x-np)>0.1*(x+np)){v=(x-np)/(x+np);s=(x-np)*v;ej=2*x*v;v=v*v;for(j=1;;j++){ej*=v;s1=s+ej/((j<<1)+1);if(s1==s)
|
||||
return(s1);s=s1;}}
|
||||
return(x*Math.log(x/np)+np-x);}
|
||||
jstat.stirlerr=function(n){var S0=0.083333333333333333333;var S1=0.00277777777777777777778;var S2=0.00079365079365079365079365;var S3=0.000595238095238095238095238;var S4=0.0008417508417508417508417508;var sferr_halves=[0.0,0.1534264097200273452913848,0.0810614667953272582196702,0.0548141210519176538961390,0.0413406959554092940938221,0.03316287351993628748511048,0.02767792568499833914878929,0.02374616365629749597132920,0.02079067210376509311152277,0.01848845053267318523077934,0.01664469118982119216319487,0.01513497322191737887351255,0.01387612882307074799874573,0.01281046524292022692424986,0.01189670994589177009505572,0.01110455975820691732662991,0.010411265261972096497478567,0.009799416126158803298389475,0.009255462182712732917728637,0.008768700134139385462952823,0.008330563433362871256469318,0.007934114564314020547248100,0.007573675487951840794972024,0.007244554301320383179543912,0.006942840107209529865664152,0.006665247032707682442354394,0.006408994188004207068439631,0.006171712263039457647532867,0.005951370112758847735624416,0.005746216513010115682023589,0.005554733551962801371038690];var nn;if(n<=15.0){nn=n+n;if(nn==parseInt(nn))return(sferr_halves[parseInt(nn)]);return(jstat.lgamma(n+1.0)-(n+0.5)*Math.log(n)+n-jstat.LN_SQRT_2PI);}
|
||||
nn=n*n;if(n>500)return((S0-S1/nn)/n);if(n>80)return((S0-(S1-S2/nn)/nn)/n);if(n>35)return((S0-(S1-(S2-S3/nn)/nn)/nn)/n);return((S0-(S1-(S2-(S3-S4/nn)/nn)/nn)/nn)/n);}
|
||||
jstat.lgamma=function(x){function lgammafn_sign(x,sgn){var ans,y,sinpiy;var xmax=2.5327372760800758e+305;var dxrel=1.490116119384765696e-8;if(sgn!=null)sgn=1;if(isNaN(x))return x;if(x<0&&(Math.floor(-x)%2.0)==0)
|
||||
if(sgn!=null)sgn=-1;if(x<=0&&x==jstat.trunc(x)){console.warn("Negative integer argument in lgammafn_sign");return Number.POSITIVE_INFINITY;}
|
||||
y=Math.abs(x);if(y<=10)return Math.log(Math.abs(jstat.gamma(x)));if(y>xmax){console.warn("Illegal arguement passed to lgammafn_sign");return Number.POSITIVE_INFINITY;}
|
||||
if(x>0){if(x>1e17){return(x*(Math.log(x)-1.0));}else if(x>4934720.0){return(jstat.LN_SQRT_2PI+(x-0.5)*Math.log(x)-x);}else{return jstat.LN_SQRT_2PI+(x-0.5)*Math.log(x)-x+jstat.lgammacor(x);}}
|
||||
sinpiy=Math.abs(Math.sin(Math.PI*y));if(sinpiy==0){throw"Should never happen!!";}
|
||||
ans=jstat.LN_SQRT_PId2+(x-0.5)*Math.log(y)-x-Math.log(sinpiy)-jstat.lgammacor(y);if(Math.abs((x-jstat.trunc(x-0.5))*ans/x)<dxrel){throw"The answer is less than half the precision argument too close to a negative integer";}
|
||||
return ans;}
|
||||
return lgammafn_sign(x,null);}
|
||||
jstat.gamma=function(x){var xbig=171.624;var p=[-1.71618513886549492533811,24.7656508055759199108314,-379.804256470945635097577,629.331155312818442661052,866.966202790413211295064,-31451.2729688483675254357,-36144.4134186911729807069,66456.1438202405440627855];var q=[-30.8402300119738975254353,315.350626979604161529144,-1015.15636749021914166146,-3107.77167157231109440444,22538.1184209801510330112,4755.84627752788110767815,-134659.959864969306392456,-115132.259675553483497211];var c=[-.001910444077728,8.4171387781295e-4,-5.952379913043012e-4,7.93650793500350248e-4,-.002777777777777681622553,.08333333333333333331554247,.0057083835261];var i,n,parity,fact,xden,xnum,y,z,yi,res,sum,ysq;parity=(0);fact=1.0;n=0;y=x;if(y<=0.0){y=-x;yi=jstat.trunc(y);res=y-yi;if(res!=0.0){if(yi!=jstat.trunc(yi*0.5)*2.0)
|
||||
parity=(1);fact=-Math.PI/Math.sin(Math.PI*res);y+=1.0;}else{return(Number.POSITIVE_INFINITY);}}
|
||||
if(y<jstat.DBL_EPSILON){if(y>=jstat.DBL_MIN){res=1.0/y;}else{return(Number.POSITIVE_INFINITY);}}else if(y<12.0){yi=y;if(y<1.0){z=y;y+=1.0;}else{n=parseInt(y)-1;y-=parseFloat(n);z=y-1.0;}
|
||||
xnum=0.0;xden=1.0;for(i=0;i<8;++i){xnum=(xnum+p[i])*z;xden=xden*z+q[i];}
|
||||
res=xnum/xden+1.0;if(yi<y){res/=yi;}else if(yi>y){for(i=0;i<n;++i){res*=y;y+=1.0;}}}else{if(y<=xbig){ysq=y*y;sum=c[6];for(i=0;i<6;++i){sum=sum/ysq+c[i];}
|
||||
sum=sum/y-y+jstat.LN_SQRT_2PI;sum+=(y-0.5)*Math.log(y);res=Math.exp(sum);}else{return(Number.POSITIVE_INFINITY);}}
|
||||
if(parity)
|
||||
res=-res;if(fact!=1.0)
|
||||
res=fact/res;return res;}
|
||||
jstat.lgammacor=function(x){var algmcs=[+.1666389480451863247205729650822e+0,-.1384948176067563840732986059135e-4,+.9810825646924729426157171547487e-8,-.1809129475572494194263306266719e-10,+.6221098041892605227126015543416e-13,-.3399615005417721944303330599666e-15,+.2683181998482698748957538846666e-17,-.2868042435334643284144622399999e-19,+.3962837061046434803679306666666e-21,-.6831888753985766870111999999999e-23,+.1429227355942498147573333333333e-24,-.3547598158101070547199999999999e-26,+.1025680058010470912000000000000e-27,-.3401102254316748799999999999999e-29,+.1276642195630062933333333333333e-30];var tmp;var nalgm=5;var xbig=94906265.62425156;var xmax=3.745194030963158e306;if(x<10){return Number.NaN;}else if(x>=xmax){throw"Underflow error in lgammacor";}else if(x<xbig){tmp=10/x;return jstat.chebyshev(tmp*tmp*2-1,algmcs,nalgm)/x;}
|
||||
return 1/(x*12);}
|
||||
jstat.incompleteBeta=function(a,b,x){function betacf(a,b,x){var MAXIT=100;var EPS=3.0e-12;var FPMIN=1.0e-30;var m,m2,aa,c,d,del,h,qab,qam,qap;qab=a+b;qap=a+1.0;qam=a-1.0;c=1.0;d=1.0-qab*x/qap;if(Math.abs(d)<FPMIN){d=FPMIN;}
|
||||
d=1.0/d;h=d;for(m=1;m<=MAXIT;m++){m2=2*m;aa=m*(b-m)*x/((qam+m2)*(a+m2));d=1.0+aa*d;if(Math.abs(d)<FPMIN){d=FPMIN;}
|
||||
c=1.0+aa/c;if(Math.abs(c)<FPMIN){c=FPMIN;}
|
||||
d=1.0/d;h*=d*c;aa=-(a+m)*(qab+m)*x/((a+m2)*(qap+m2));d=1.0+aa*d;if(Math.abs(d)<FPMIN){d=FPMIN;}
|
||||
c=1.0+aa/c;if(Math.abs(c)<FPMIN){c=FPMIN;}
|
||||
d=1.0/d;del=d*c;h*=del;if(Math.abs(del-1.0)<EPS){break;}}
|
||||
if(m>MAXIT){console.warn("a or b too big, or MAXIT too small in betacf: "+a+", "+b+", "+x+", "+h);return h;}
|
||||
if(isNaN(h)){console.warn(a+", "+b+", "+x);}
|
||||
return h;}
|
||||
var bt;if(x<0.0||x>1.0){throw"bad x in routine incompleteBeta";}
|
||||
if(x==0.0||x==1.0){bt=0.0;}else{bt=Math.exp(jstat.lgamma(a+b)-jstat.lgamma(a)-jstat.lgamma(b)+a*Math.log(x)+b*Math.log(1.0-x));}
|
||||
if(x<(a+1.0)/(a+b+2.0)){return bt*betacf(a,b,x)/a;}else{return 1.0-bt*betacf(b,a,1.0-x)/b;}}
|
||||
jstat.chebyshev=function(x,a,n){var b0,b1,b2,twox;var i;if(n<1||n>1000)return Number.NaN;if(x<-1.1||x>1.1)return Number.NaN;twox=x*2;b2=b1=0;b0=0;for(i=1;i<=n;i++){b2=b1;b1=b0;b0=twox*b1-b2+a[n-i];}
|
||||
return(b0-b2)*0.5;}
|
||||
jstat.fmin2=function(x,y){return(x<y)?x:y;}
|
||||
jstat.log1p=function(x){var ret=0,n=50;if(x<=-1){return Number.NEGATIVE_INFINITY;}
|
||||
if(x<0||x>1){return Math.log(1+x);}
|
||||
for(var i=1;i<n;i++){if((i%2)===0){ret-=Math.pow(x,i)/i;}else{ret+=Math.pow(x,i)/i;}}
|
||||
return ret;}
|
||||
jstat.expm1=function(x){var y,a=Math.abs(x);if(a<jstat.DBL_EPSILON)return x;if(a>0.697)return Math.exp(x)-1;if(a>1e-8){y=Math.exp(x)-1;}else{y=(x/2+1)*x;}
|
||||
y-=(1+y)*(jstat.log1p(y)-x);return y;}
|
||||
jstat.logBeta=function(a,b){var corr,p,q;p=q=a;if(b<p)p=b;if(b>q)q=b;if(p<0){console.warn('Both arguements must be >= 0');return Number.NaN;}
|
||||
else if(p==0){return Number.POSITIVE_INFINITY;}
|
||||
else if(!jstat.isFinite(q)){return Number.NEGATIVE_INFINITY;}
|
||||
if(p>=10){corr=jstat.lgammacor(p)+jstat.lgammacor(q)-jstat.lgammacor(p+q);return Math.log(q)*-0.5+jstat.LN_SQRT_2PI+corr
|
||||
+(p-0.5)*Math.log(p/(p+q))+q*jstat.log1p(-p/(p+q));}
|
||||
else if(q>=10){corr=jstat.lgammacor(q)-jstat.lgammacor(p+q);return jstat.lgamma(p)+corr+p-p*Math.log(p+q)
|
||||
+(q-0.5)*jstat.log1p(-p/(p+q));}
|
||||
else
|
||||
return Math.log(jstat.gamma(p)*(jstat.gamma(q)/jstat.gamma(p+q)));}
|
||||
jstat.dbinom_raw=function(x,n,p,q,give_log){if(give_log==null)give_log=false;var lf,lc;if(p==0){if(x==0){return(give_log)?0.0:1.0;}else{return(give_log)?Number.NEGATIVE_INFINITY:0.0;}}
|
||||
if(q==0){if(x==n){return(give_log)?0.0:1.0;}else{return(give_log)?Number.NEGATIVE_INFINITY:0.0;}}
|
||||
if(x==0){if(n==0)return(give_log)?0.0:1.0;lc=(p<0.1)?-jstat.bd0(n,n*q)-n*p:n*Math.log(q);return(give_log)?lc:Math.exp(lc);}
|
||||
if(x==n){lc=(q<0.1)?-jstat.bd0(n,n*p)-n*q:n*Math.log(p);return(give_log)?lc:Math.exp(lc);}
|
||||
if(x<0||x>n)return(give_log)?Number.NEGATIVE_INFINITY:0.0;lc=jstat.stirlerr(n)-jstat.stirlerr(x)-jstat.stirlerr(n-x)-jstat.bd0(x,n*p)-jstat.bd0(n-x,n*q);lf=Math.log(jstat.TWO_PI)+Math.log(x)+jstat.log1p(-x/n);return(give_log)?lc-0.5*lf:Math.exp(lc-0.5*lf);}
|
||||
jstat.max=function(values){var max=Number.NEGATIVE_INFINITY;for(var i=0;i<values.length;i++){if(values[i]>max){max=values[i];}}
|
||||
return max;}
|
||||
var Range=Class.extend({init:function(min,max,numPoints){this._minimum=parseFloat(min);this._maximum=parseFloat(max);this._numPoints=parseFloat(numPoints);},getMinimum:function(){return this._minimum;},getMaximum:function(){return this._maximum;},getNumPoints:function(){return this._numPoints;},getPoints:function(){var results=[];var x=this._minimum;var step=(this._maximum-this._minimum)/(this._numPoints-1);for(var i=0;i<this._numPoints;i++){results[i]=parseFloat(x.toFixed(6));x+=step;}
|
||||
return results;}});Range.validate=function(range){if(!range instanceof Range){return false;}
|
||||
if(isNaN(range.getMinimum())||isNaN(range.getMaximum())||isNaN(range.getNumPoints())||range.getMaximum()<range.getMinimum()||range.getNumPoints()<=0){return false;}
|
||||
return true;}
|
||||
var ContinuousDistribution=Class.extend({init:function(name){this._name=name;},toString:function(){return this._string;},getName:function(){return this._name;},getClassName:function(){return this._name+'Distribution';},density:function(valueOrRange){if(!isNaN(valueOrRange)){return parseFloat(this._pdf(valueOrRange).toFixed(15));}else if(Range.validate(valueOrRange)){var points=valueOrRange.getPoints();var result=[];for(var i=0;i<points.length;i++){result[i]=parseFloat(this._pdf(points[i]));}
|
||||
return result;}else{throw"Invalid parameter supplied to "+this.getClassName()+".density()";}},cumulativeDensity:function(valueOrRange){if(!isNaN(valueOrRange)){return parseFloat(this._cdf(valueOrRange).toFixed(15));}else if(Range.validate(valueOrRange)){var points=valueOrRange.getPoints();var result=[];for(var i=0;i<points.length;i++){result[i]=parseFloat(this._cdf(points[i]));}
|
||||
return result;}else{throw"Invalid parameter supplied to "+this.getClassName()+".cumulativeDensity()";}},getRange:function(standardDeviations,numPoints){if(standardDeviations==null){standardDeviations=5;}
|
||||
if(numPoints==null){numPoints=100;}
|
||||
var min=this.getMean()-standardDeviations*Math.sqrt(this.getVariance());var max=this.getMean()+standardDeviations*Math.sqrt(this.getVariance());if(this.getClassName()=='GammaDistribution'||this.getClassName()=='LogNormalDistribution'){min=0.0;max=this.getMean()+standardDeviations*Math.sqrt(this.getVariance());}else if(this.getClassName()=='BetaDistribution'){min=0.0;max=1.0;}
|
||||
var range=new Range(min,max,numPoints);return range;},getVariance:function(){},getMean:function(){},getQuantile:function(p){var self=this;function findClosestMatch(range,p){var ERR=1.0e-5;var xs=range.getPoints();var closestIndex=0;var closestDistance=999;for(var i=0;i<xs.length;i++){var pp=self.cumulativeDensity(xs[i]);var distance=Math.abs(pp-p);if(distance<closestDistance){closestIndex=i;closestDistance=distance;}}
|
||||
if(closestDistance<=ERR){return xs[closestIndex];}else{var newRange=new Range(xs[closestIndex-1],xs[closestIndex+1],20);return findClosestMatch(newRange,p);}}
|
||||
var range=this.getRange(5,20);return findClosestMatch(range,p);}});var NormalDistribution=ContinuousDistribution.extend({init:function(mean,sigma){this._super('Normal');this._mean=parseFloat(mean);this._sigma=parseFloat(sigma);this._string="Normal ("+this._mean.toFixed(2)+", "+this._sigma.toFixed(2)+")";},_pdf:function(x,give_log){if(give_log==null){give_log=false;}
|
||||
var sigma=this._sigma;var mu=this._mean;if(!jstat.isFinite(sigma)){return(give_log)?Number.NEGATIVE_INFINITY:0.0}
|
||||
if(!jstat.isFinite(x)&&mu==x){return Number.NaN;}
|
||||
if(sigma<=0){if(sigma<0){throw"invalid sigma in _pdf";}
|
||||
return(x==mu)?Number.POSITIVE_INFINITY:(give_log)?Number.NEGATIVE_INFINITY:0.0;}
|
||||
x=(x-mu)/sigma;if(!jstat.isFinite(x)){return(give_log)?Number.NEGATIVE_INFINITY:0.0;}
|
||||
return(give_log?-(jstat.LN_SQRT_2PI+0.5*x*x+Math.log(sigma)):jstat.ONE_SQRT_2PI*Math.exp(-0.5*x*x)/sigma);},_cdf:function(x,lower_tail,log_p){if(lower_tail==null)lower_tail=true;if(log_p==null)log_p=false;function pnorm_both(x,cum,ccum,i_tail,log_p){var a=[2.2352520354606839287,161.02823106855587881,1067.6894854603709582,18154.981253343561249,0.065682337918207449113];var b=[47.20258190468824187,976.09855173777669322,10260.932208618978205,45507.789335026729956];var c=[0.39894151208813466764,8.8831497943883759412,93.506656132177855979,597.27027639480026226,2494.5375852903726711,6848.1904505362823326,11602.651437647350124,9842.7148383839780218,1.0765576773720192317e-8];var d=[22.266688044328115691,235.38790178262499861,1519.377599407554805,6485.558298266760755,18615.571640885098091,34900.952721145977266,38912.003286093271411,19685.429676859990727];var p=[0.21589853405795699,0.1274011611602473639,0.022235277870649807,0.001421619193227893466,2.9112874951168792e-5,0.02307344176494017303];var q=[1.28426009614491121,0.468238212480865118,0.0659881378689285515,0.00378239633202758244,7.29751555083966205e-5];var xden,xnum,temp,del,eps,xsq,y,i,lower,upper;eps=jstat.DBL_EPSILON*0.5;lower=i_tail!=1;upper=i_tail!=0;y=Math.abs(x);if(y<=0.67448975){if(y>eps){xsq=x*x;xnum=a[4]*xsq;xden=xsq;for(i=0;i<3;++i){xnum=(xnum+a[i])*xsq;xden=(xden+b[i])*xsq;}}else{xnum=xden=0.0;}
|
||||
temp=x*(xnum+a[3])/(xden+b[3]);if(lower)cum=0.5+temp;if(upper)ccum=0.5-temp;if(log_p){if(lower)cum=Math.log(cum);if(upper)ccum=Math.log(ccum);}}else if(y<=jstat.SQRT_32){xnum=c[8]*y;xden=y;for(i=0;i<7;++i){xnum=(xnum+c[i])*y;xden=(xden+d[i])*y;}
|
||||
temp=(xnum+c[7])/(xden+d[7]);xsq=jstat.trunc(x*16)/16;del=(x-xsq)*(x+xsq);if(log_p){cum=(-xsq*xsq*0.5)+(-del*0.5)+Math.log(temp);if((lower&&x>0.)||(upper&&x<=0.))
|
||||
ccum=jstat.log1p(-Math.exp(-xsq*xsq*0.5)*Math.exp(-del*0.5)*temp);}
|
||||
else{cum=Math.exp(-xsq*xsq*0.5)*Math.exp(-del*0.5)*temp;ccum=1.0-cum;}
|
||||
if(x>0.0){temp=cum;if(lower){cum=ccum;}
|
||||
ccum=temp;}}
|
||||
else if((log_p&&y<1e170)||(lower&&-37.5193<x&&x<8.2924)||(upper&&-8.2924<x&&x<37.5193)){xsq=1.0/(x*x);xnum=p[5]*xsq;xden=xsq;for(i=0;i<4;++i){xnum=(xnum+p[i])*xsq;xden=(xden+q[i])*xsq;}
|
||||
temp=xsq*(xnum+p[4])/(xden+q[4]);temp=(jstat.ONE_SQRT_2PI-temp)/y;xsq=jstat.trunc(x*16)/16;del=(x-xsq)*(x+xsq);if(log_p){cum=(-xsq*xsq*0.5)+(-del*0.5)+Math.log(temp);if((lower&&x>0.)||(upper&&x<=0.))
|
||||
ccum=jstat.log1p(-Math.exp(-xsq*xsq*0.5)*Math.exp(-del*0.5)*temp);}
|
||||
else{cum=Math.exp(-xsq*xsq*0.5)*Math.exp(-del*0.5)*temp;ccum=1.0-cum;}
|
||||
if(x>0.0){temp=cum;if(lower){cum=ccum;}
|
||||
ccum=temp;}}else{if(x>0){cum=(log_p)?0.0:1.0;ccum=(log_p)?Number.NEGATIVE_INFINITY:0.0;}else{cum=(log_p)?Number.NEGATIVE_INFINITY:0.0;ccum=(log_p)?0.0:1.0;}}
|
||||
return[cum,ccum];}
|
||||
var p,cp;var mu=this._mean;var sigma=this._sigma;var R_DT_0,R_DT_1;if(lower_tail){if(log_p){R_DT_0=Number.NEGATIVE_INFINITY;R_DT_1=0.0;}else{R_DT_0=0.0;R_DT_1=1.0;}}else{if(log_p){R_DT_0=0.0;R_DT_1=Number.NEGATIVE_INFINITY;}else{R_DT_0=1.0;R_DT_1=0.0;}}
|
||||
if(!jstat.isFinite(x)&&mu==x)return Number.NaN;if(sigma<=0){if(sigma<0){console.warn("Sigma is less than 0");return Number.NaN;}
|
||||
return(x<mu)?R_DT_0:R_DT_1;}
|
||||
p=(x-mu)/sigma;if(!jstat.isFinite(p)){return(x<mu)?R_DT_0:R_DT_1;}
|
||||
x=p;var result=pnorm_both(x,p,cp,(lower_tail?false:true),log_p);return(lower_tail?result[0]:result[1]);},getMean:function(){return this._mean;},getSigma:function(){return this._sigma;},getVariance:function(){return this._sigma*this._sigma;}});var LogNormalDistribution=ContinuousDistribution.extend({init:function(location,scale){this._super('LogNormal')
|
||||
this._location=parseFloat(location);this._scale=parseFloat(scale);this._string="LogNormal ("+this._location.toFixed(2)+", "+this._scale.toFixed(2)+")";},_pdf:function(x,give_log){var y;var sdlog=this._scale;var meanlog=this._location;if(give_log==null){give_log=false;}
|
||||
if(sdlog<=0)throw"Illegal parameter in _pdf";if(x<=0){return(give_log)?Number.NEGATIVE_INFINITY:0.0;}
|
||||
y=(Math.log(x)-meanlog)/sdlog;return(give_log?-(jstat.LN_SQRT_2PI+0.5*y*y+Math.log(x*sdlog)):jstat.ONE_SQRT_2PI*Math.exp(-0.5*y*y)/(x*sdlog));},_cdf:function(x,lower_tail,log_p){var sdlog=this._scale;var meanlog=this._location;if(lower_tail==null){lower_tail=true;}
|
||||
if(log_p==null){log_p=false;}
|
||||
if(sdlog<=0){throw"illegal std in _cdf";}
|
||||
if(x>0){var nd=new NormalDistribution(meanlog,sdlog);return nd._cdf(Math.log(x),lower_tail,log_p);}
|
||||
if(lower_tail){return(log_p)?Number.NEGATIVE_INFINITY:0.0;}else{return(log_p)?0.0:1.0;}},getLocation:function(){return this._location;},getScale:function(){return this._scale;},getMean:function(){return Math.exp((this._location+this._scale)/2);},getVariance:function(){var ans=(Math.exp(this._scale)-1)*Math.exp(2*this._location+this._scale);return ans;}});var GammaDistribution=ContinuousDistribution.extend({init:function(shape,scale){this._super('Gamma');this._shape=parseFloat(shape);this._scale=parseFloat(scale);this._string="Gamma ("+this._shape.toFixed(2)+", "+this._scale.toFixed(2)+")";},_pdf:function(x,give_log){var pr;var shape=this._shape;var scale=this._scale;if(give_log==null){give_log=false;}
|
||||
if(shape<0||scale<=0){throw"Illegal argument in _pdf";}
|
||||
if(x<0){return(give_log)?Number.NEGATIVE_INFINITY:0.0;}
|
||||
if(shape==0){return(x==0)?Number.POSITIVE_INFINITY:(give_log)?Number.NEGATIVE_INFINITY:0.0;}
|
||||
if(x==0){if(shape<1)return Number.POSITIVE_INFINITY;if(shape>1)return(give_log)?Number.NEGATIVE_INFINITY:0.0;return(give_log)?-Math.log(scale):1/scale;}
|
||||
if(shape<1){pr=jstat.dopois_raw(shape,x/scale,give_log);return give_log?pr+Math.log(shape/x):pr*shape/x;}
|
||||
pr=jstat.dopois_raw(shape-1,x/scale,give_log);return give_log?pr-Math.log(scale):pr/scale;},_cdf:function(x,lower_tail,log_p){function USE_PNORM(){pn1=Math.sqrt(alph)*3.0*(Math.pow(x/alph,1.0/3.0)+1.0/(9.0*alph)-1.0);var norm_dist=new NormalDistribution(0.0,1.0);return norm_dist._cdf(pn1,lower_tail,log_p);}
|
||||
if(lower_tail==null)lower_tail=true;if(log_p==null)log_p=false;var alph=this._shape;var scale=this._scale;var xbig=1.0e+8;var xlarge=1.0e+37;var alphlimit=1e5;var pn1,pn2,pn3,pn4,pn5,pn6,arg,a,b,c,an,osum,sum,n,pearson;if(alph<=0.||scale<=0.){console.warn('Invalid gamma params in _cdf');return Number.NaN;}
|
||||
x/=scale;if(isNaN(x))return x;if(x<=0.0){if(lower_tail){return(log_p)?Number.NEGATIVE_INFINITY:0.0;}else{return(log_p)?0.0:1.0;}}
|
||||
if(alph>alphlimit){return USE_PNORM();}
|
||||
if(x>xbig*alph){if(x>jstat.DBL_MAX*alph){if(lower_tail){return(log_p)?0.0:1.0;}else{return(log_p)?Number.NEGATIVE_INFINITY:0.0;}}else{return USE_PNORM();}}
|
||||
if(x<=1.0||x<alph){pearson=1;arg=alph*Math.log(x)-x-jstat.lgamma(alph+1.0);c=1.0;sum=1.0;a=alph;do{a+=1.0;c*=x/a;sum+=c;}while(c>jstat.DBL_EPSILON*sum);}else{pearson=0;arg=alph*Math.log(x)-x-jstat.lgamma(alph);a=1.-alph;b=a+x+1.;pn1=1.;pn2=x;pn3=x+1.;pn4=x*b;sum=pn3/pn4;for(n=1;;n++){a+=1.;b+=2.;an=a*n;pn5=b*pn3-an*pn1;pn6=b*pn4-an*pn2;if(Math.abs(pn6)>0.){osum=sum;sum=pn5/pn6;if(Math.abs(osum-sum)<=jstat.DBL_EPSILON*jstat.fmin2(1.0,sum))
|
||||
break;}
|
||||
pn1=pn3;pn2=pn4;pn3=pn5;pn4=pn6;if(Math.abs(pn5)>=xlarge){pn1/=xlarge;pn2/=xlarge;pn3/=xlarge;pn4/=xlarge;}}}
|
||||
arg+=Math.log(sum);lower_tail=(lower_tail==pearson);if(log_p&&lower_tail)
|
||||
return(arg);if(lower_tail){return Math.exp(arg);}else{if(log_p){return(arg>-Math.LN2)?Math.log(-jstat.expm1(arg)):jstat.log1p(-Math.exp(arg));}else{return-jstat.expm1(arg);}}},getShape:function(){return this._shape;},getScale:function(){return this._scale;},getMean:function(){return this._shape*this._scale;},getVariance:function(){return this._shape*Math.pow(this._scale,2);}});var BetaDistribution=ContinuousDistribution.extend({init:function(alpha,beta){this._super('Beta');this._alpha=parseFloat(alpha);this._beta=parseFloat(beta);this._string="Beta ("+this._alpha.toFixed(2)+", "+this._beta.toFixed(2)+")";},_pdf:function(x,give_log){if(give_log==null)give_log=false;var a=this._alpha;var b=this._beta;var lval;if(a<=0||b<=0){console.warn('Illegal arguments in _pdf');return Number.NaN;}
|
||||
if(x<0||x>1){return(give_log)?Number.NEGATIVE_INFINITY:0.0;}
|
||||
if(x==0){if(a>1){return(give_log)?Number.NEGATIVE_INFINITY:0.0;}
|
||||
if(a<1){return Number.POSITIVE_INFINITY;}
|
||||
return(give_log)?Math.log(b):b;}
|
||||
if(x==1){if(b>1){return(give_log)?Number.NEGATIVE_INFINITY:0.0;}
|
||||
if(b<1){return Number.POSITIVE_INFINITY;}
|
||||
return(give_log)?Math.log(a):a;}
|
||||
if(a<=2||b<=2){lval=(a-1)*Math.log(x)+(b-1)*jstat.log1p(-x)-jstat.logBeta(a,b);}else{lval=Math.log(a+b-1)+jstat.dbinom_raw(a-1,a+b-2,x,1-x,true);}
|
||||
return(give_log)?lval:Math.exp(lval);},_cdf:function(x,lower_tail,log_p){if(lower_tail==null)lower_tail=true;if(log_p==null)log_p=false;var pin=this._alpha;var qin=this._beta;if(pin<=0||qin<=0){console.warn('Invalid argument in _cdf');return Number.NaN;}
|
||||
if(x<=0){if(lower_tail){return(log_p)?Number.NEGATIVE_INFINITY:0.0;}else{return(log_p)?0.1:1.0;}}
|
||||
if(x>=1){if(lower_tail){return(log_p)?0.1:1.0;}else{return(log_p)?Number.NEGATIVE_INFINITY:0.0;}}
|
||||
return jstat.incompleteBeta(pin,qin,x);},getAlpha:function(){return this._alpha;},getBeta:function(){return this._beta;},getMean:function(){return this._alpha/(this._alpha+this._beta);},getVariance:function(){var ans=(this._alpha*this._beta)/(Math.pow(this._alpha+this._beta,2)*(this._alpha+this._beta+1));return ans;}});var StudentTDistribution=ContinuousDistribution.extend({init:function(degreesOfFreedom,mu){this._super('StudentT');this._dof=parseFloat(degreesOfFreedom);if(mu!=null){this._mu=parseFloat(mu);this._string="StudentT ("+this._dof.toFixed(2)+", "+this._mu.toFixed(2)+")";}else{this._mu=0.0;this._string="StudentT ("+this._dof.toFixed(2)+")";}},_pdf:function(x,give_log){if(give_log==null)give_log=false;if(this._mu==null){return this._dt(x,give_log);}else{var y=this._dnt(x,give_log);if(y>1){console.warn('x:'+x+', y: '+y);}
|
||||
return y;}},_cdf:function(x,lower_tail,give_log){if(lower_tail==null)lower_tail=true;if(give_log==null)give_log=false;if(this._mu==null){return this._pt(x,lower_tail,give_log);}else{return this._pnt(x,lower_tail,give_log);}},_dt:function(x,give_log){var t,u;var n=this._dof;if(n<=0){console.warn('Invalid parameters in _dt');return Number.NaN;}
|
||||
if(!jstat.isFinite(x)){return(give_log)?Number.NEGATIVE_INFINITY:0.0;}
|
||||
if(!jstat.isFinite(n)){var norm=new NormalDistribution(0.0,1.0);return norm.density(x,give_log);}
|
||||
t=-jstat.bd0(n/2.0,(n+1)/2.0)+jstat.stirlerr((n+1)/2.0)-jstat.stirlerr(n/2.0);if(x*x>0.2*n)
|
||||
u=Math.log(1+x*x/n)*n/2;else
|
||||
u=-jstat.bd0(n/2.0,(n+x*x)/2.0)+x*x/2.0;var p1=jstat.TWO_PI*(1+x*x/n);var p2=t-u;return(give_log)?-0.5*Math.log(p1)+p2:Math.exp(p2)/Math.sqrt(p1);},_dnt:function(x,give_log){if(give_log==null)give_log=false;var df=this._dof;var ncp=this._mu;var u;if(df<=0.0){console.warn("Illegal arguments _dnf");return Number.NaN;}
|
||||
if(ncp==0.0){return this._dt(x,give_log);}
|
||||
if(!jstat.isFinite(x)){if(give_log){return Number.NEGATIVE_INFINITY;}else{return 0.0;}}
|
||||
if(!isFinite(df)||df>1e8){var dist=new NormalDistribution(ncp,1.);return dist.density(x,give_log);}
|
||||
if(Math.abs(x)>Math.sqrt(df*jstat.DBL_EPSILON)){var newT=new StudentTDistribution(df+2,ncp);u=Math.log(df)-Math.log(Math.abs(x))+
|
||||
Math.log(Math.abs(newT._pnt(x*Math.sqrt((df+2)/df),true,false)-
|
||||
this._pnt(x,true,false)));}
|
||||
else{u=jstat.lgamma((df+1)/2)-jstat.lgamma(df/2)
|
||||
-.5*(Math.log(Math.PI)+Math.log(df)+ncp*ncp);}
|
||||
return(give_log?u:Math.exp(u));},_pt:function(x,lower_tail,log_p){if(lower_tail==null)lower_tail=true;if(log_p==null)log_p=false;var val,nx;var n=this._dof;var DT_0,DT_1;if(lower_tail){if(log_p){DT_0=Number.NEGATIVE_INFINITY;DT_1=1.;}else{DT_0=0.;DT_1=1.;}}else{if(log_p){DT_0=0.;DT_1=Number.NEGATIVE_INFINITY;}else{DT_0=1.;DT_1=0.;}}
|
||||
if(n<=0.0){console.warn("Invalid T distribution _pt");return Number.NaN;}
|
||||
var norm=new NormalDistribution(0,1);if(!jstat.isFinite(x)){return(x<0)?DT_0:DT_1;}
|
||||
if(!jstat.isFinite(n)){return norm._cdf(x,lower_tail,log_p);}
|
||||
if(n>4e5){val=1./(4.*n);return norm._cdf(x*(1.-val)/sqrt(1.+x*x*2.*val),lower_tail,log_p);}
|
||||
nx=1+(x/n)*x;if(nx>1e100){var lval;lval=-0.5*n*(2*Math.log(Math.abs(x))-Math.log(n))
|
||||
-jstat.logBeta(0.5*n,0.5)-Math.log(0.5*n);val=log_p?lval:Math.exp(lval);}else{if(n>x*x){var beta=new BetaDistribution(0.5,n/2.);return beta._cdf(x*x/(n+x*x),false,log_p);}else{beta=new BetaDistribution(n/2.,0.5);return beta._cdf(1./nx,true,log_p);}}
|
||||
if(x<=0.)
|
||||
lower_tail=!lower_tail;if(log_p){if(lower_tail)return jstat.log1p(-0.5*Math.exp(val));else return val-M_LN2;}
|
||||
else{val/=2.;if(lower_tail){return(0.5-val+0.5);}else{return val;}}},_pnt:function(t,lower_tail,log_p){var dof=this._dof;var ncp=this._mu;var DT_0,DT_1;if(lower_tail){if(log_p){DT_0=Number.NEGATIVE_INFINITY;DT_1=1.;}else{DT_0=0.;DT_1=1.;}}else{if(log_p){DT_0=0.;DT_1=Number.NEGATIVE_INFINITY;}else{DT_0=1.;DT_1=0.;}}
|
||||
var albeta,a,b,del,errbd,lambda,rxb,tt,x;var geven,godd,p,q,s,tnc,xeven,xodd;var it,negdel;var ITRMAX=1000;var ERRMAX=1.e-7;if(dof<=0.0){return Number.NaN;}else if(dof==0.0){return this._pt(t);}
|
||||
if(!jstat.isFinite(t)){return(t<0)?DT_0:DT_1;}
|
||||
if(t>=0.){negdel=false;tt=t;del=ncp;}else{if(ncp>=40&&(!log_p||!lower_tail)){return DT_0;}
|
||||
negdel=true;tt=-t;del=-ncp;}
|
||||
if(dof>4e5||del*del>2*Math.LN2*(-(jstat.DBL_MIN_EXP))){s=1./(4.*dof);var norm=new NormalDistribution(del,Math.sqrt(1.+tt*tt*2.*s));var result=norm._cdf(tt*(1.-s),lower_tail!=negdel,log_p);return result;}
|
||||
x=t*t;rxb=dof/(x+dof);x=x/(x+dof);if(x>0.){lambda=del*del;p=.5*Math.exp(-.5*lambda);if(p==0.){console.warn("underflow in _pnt");return DT_0;}
|
||||
q=jstat.SQRT_2dPI*p*del;s=.5-p;if(s<1e-7){s=-0.5*jstat.expm1(-0.5*lambda);}
|
||||
a=.5;b=.5*dof;rxb=Math.pow(rxb,b);albeta=jstat.LN_SQRT_PI+jstat.lgamma(b)-jstat.lgamma(.5+b);xodd=jstat.incompleteBeta(a,b,x);godd=2.*rxb*Math.exp(a*Math.log(x)-albeta);tnc=b*x;xeven=(tnc<jstat.DBL_EPSILON)?tnc:1.-rxb;geven=tnc*rxb;tnc=p*xodd+q*xeven;for(it=1;it<=ITRMAX;it++){a+=1.;xodd-=godd;xeven-=geven;godd*=x*(a+b-1.)/a;geven*=x*(a+b-.5)/(a+.5);p*=lambda/(2*it);q*=lambda/(2*it+1);tnc+=p*xodd+q*xeven;s-=p;if(s<-1.e-10){console.write("precision error _pnt");break;}
|
||||
if(s<=0&&it>1)break;errbd=2.*s*(xodd-godd);if(Math.abs(errbd)<ERRMAX)break;}
|
||||
if(it==ITRMAX){throw"Non-convergence _pnt";}}else{tnc=0.;}
|
||||
norm=new NormalDistribution(0,1);tnc+=norm._cdf(-del,true,false);lower_tail=lower_tail!=negdel;if(tnc>1-1e-10&&lower_tail){console.warn("precision error _pnt");}
|
||||
var res=jstat.fmin2(tnc,1.);if(lower_tail){if(log_p){return Math.log(res);}else{return res;}}else{if(log_p){return jstat.log1p(-(res));}else{return(0.5-(res)+0.5);}}},getDegreesOfFreedom:function(){return this._dof;},getNonCentralityParameter:function(){return this._mu;},getMean:function(){if(this._dof>1){var ans=(1/2)*Math.log(this._dof/2)+jstat.lgamma((this._dof-1)/2)-jstat.lgamma(this._dof/2)
|
||||
return Math.exp(ans)*this._mu;}else{return Number.NaN;}},getVariance:function(){if(this._dof>2){var ans=this._dof*(1+this._mu*this._mu)/(this._dof-2)-(((this._mu*this._mu*this._dof)/2)*Math.pow(Math.exp(jstat.lgamma((this._dof-1)/2)-jstat.lgamma(this._dof/2)),2));return ans;}else{return Number.NaN;}}});var Plot=Class.extend({init:function(id,options){this._container='#'+String(id);this._plots=[];this._flotObj=null;this._locked=false;if(options!=null){this._options=options;}else{this._options={};}},getContainer:function(){return this._container;},getGraph:function(){return this._flotObj;},setData:function(data){this._plots=data;},clear:function(){this._plots=[];},showLegend:function(){this._options.legend={show:true}
|
||||
this.render();},hideLegend:function(){this._options.legend={show:false}
|
||||
this.render();},render:function(){this._flotObj=null;this._flotObj=$.plot($(this._container),this._plots,this._options);}});var DistributionPlot=Plot.extend({init:function(id,distribution,range,options){this._super(id,options);this._showPDF=true;this._showCDF=false;this._pdfValues=[];this._cdfValues=[];this._maxY=1;this._plotType='line';this._fill=false;this._distribution=distribution;if(range!=null&&Range.validate(range)){this._range=range;}else{this._range=this._distribution.getRange();}
|
||||
if(this._distribution!=null){this._maxY=this._generateValues();}else{this._options.xaxis={min:range.getMinimum(),max:range.getMaximum()}
|
||||
this._options.yaxis={max:1}}
|
||||
this.render();},setHover:function(bool){if(bool){if(this._options.grid==null){this._options.grid={hoverable:true,mouseActiveRadius:25}}else{this._options.grid.hoverable=true,this._options.grid.mouseActiveRadius=25}
|
||||
function showTooltip(x,y,contents,color){$('<div id="jstat_tooltip">'+contents+'</div>').css({position:'absolute',display:'none',top:y+15,'font-size':'small',left:x+5,border:'1px solid '+color[1],color:color[2],padding:'5px','background-color':color[0],opacity:0.80}).appendTo("body").show();}
|
||||
var previousPoint=null;$(this._container).bind("plothover",function(event,pos,item){$("#x").text(pos.x.toFixed(2));$("#y").text(pos.y.toFixed(2));if(item){if(previousPoint!=item.datapoint){previousPoint=item.datapoint;$("#jstat_tooltip").remove();var x=jstat.toSigFig(item.datapoint[0],2),y=jstat.toSigFig(item.datapoint[1],2);var text=null;var color=item.series.color;if(item.series.label=='PDF'){text="P("+x+") = "+y;color=["#fee","#fdd","#C05F5F"];}else{text="F("+x+") = "+y;color=["#eef","#ddf","#4A4AC0"];}
|
||||
showTooltip(item.pageX,item.pageY,text,color);}}
|
||||
else{$("#jstat_tooltip").remove();previousPoint=null;}});$(this._container).bind("mouseleave",function(){if($('#jstat_tooltip').is(':visible')){$('#jstat_tooltip').remove();previousPoint=null;}});}else{if(this._options.grid==null){this._options.grid={hoverable:false}}else{this._options.grid.hoverable=false}
|
||||
$(this._container).unbind("plothover");}
|
||||
this.render();},setType:function(type){this._plotType=type;var lines={};var points={};if(this._plotType=='line'){lines.show=true;points.show=false;}else if(this._plotType=='points'){lines.show=false;points.show=true;}else if(this._plotType=='both'){lines.show=true;points.show=true;}
|
||||
if(this._options.series==null){this._options.series={lines:lines,points:points}}else{if(this._options.series.lines==null){this._options.series.lines=lines;}else{this._options.series.lines.show=lines.show;}
|
||||
if(this._options.series.points==null){this._options.series.points=points;}else{this._options.series.points.show=points.show;}}
|
||||
this.render();},setFill:function(bool){this._fill=bool;if(this._options.series==null){this._options.series={lines:{fill:bool}}}else{if(this._options.series.lines==null){this._options.series.lines={fill:bool}}else{this._options.series.lines.fill=bool;}}
|
||||
this.render();},clear:function(){this._super();this._distribution=null;this._pdfValues=[];this._cdfValues=[];this.render();},_generateValues:function(){this._cdfValues=[];this._pdfValues=[];var xs=this._range.getPoints();this._options.xaxis={min:xs[0],max:xs[xs.length-1]}
|
||||
var pdfs=this._distribution.density(this._range);var cdfs=this._distribution.cumulativeDensity(this._range);for(var i=0;i<xs.length;i++){if(pdfs[i]==Number.POSITIVE_INFINITY||pdfs[i]==Number.NEGATIVE_INFINITY){pdfs[i]=null;}
|
||||
if(cdfs[i]==Number.POSITIVE_INFINITY||cdfs[i]==Number.NEGATIVE_INFINITY){cdfs[i]=null;}
|
||||
this._pdfValues.push([xs[i],pdfs[i]]);this._cdfValues.push([xs[i],cdfs[i]]);}
|
||||
return jstat.max(pdfs);},showPDF:function(){this._showPDF=true;this.render();},hidePDF:function(){this._showPDF=false;this.render();},showCDF:function(){this._showCDF=true;this.render();},hideCDF:function(){this._showCDF=false;this.render();},setDistribution:function(distribution,range){this._distribution=distribution;if(range!=null){this._range=range;}else{this._range=distribution.getRange();}
|
||||
this._maxY=this._generateValues();this._options.yaxis={max:this._maxY*1.1}
|
||||
this.render();},getDistribution:function(){return this._distribution;},getRange:function(){return this._range;},setRange:function(range){this._range=range;this._generateValues();this.render();},render:function(){if(this._distribution!=null){if(this._showPDF&&this._showCDF){this.setData([{yaxis:1,data:this._pdfValues,color:'rgb(237,194,64)',clickable:false,hoverable:true,label:"PDF"},{yaxis:2,data:this._cdfValues,clickable:false,color:'rgb(175,216,248)',hoverable:true,label:"CDF"}]);this._options.yaxis={max:this._maxY*1.1}}else if(this._showPDF){this.setData([{data:this._pdfValues,hoverable:true,color:'rgb(237,194,64)',clickable:false,label:"PDF"}]);this._options.yaxis={max:this._maxY*1.1}}else if(this._showCDF){this.setData([{data:this._cdfValues,hoverable:true,color:'rgb(175,216,248)',clickable:false,label:"CDF"}]);this._options.yaxis={max:1.1}}}else{this.setData([]);}
|
||||
this._super();}});var DistributionFactory={};DistributionFactory.build=function(json){if(json.NormalDistribution){if(json.NormalDistribution.mean!=null&&json.NormalDistribution.standardDeviation!=null){return new NormalDistribution(json.NormalDistribution.mean[0],json.NormalDistribution.standardDeviation[0]);}else{throw"Malformed JSON provided to DistributionBuilder "+json;}}else if(json.LogNormalDistribution){if(json.LogNormalDistribution.location!=null&&json.LogNormalDistribution.scale!=null){return new LogNormalDistribution(json.LogNormalDistribution.location[0],json.LogNormalDistribution.scale[0]);}else{throw"Malformed JSON provided to DistributionBuilder "+json;}}else if(json.BetaDistribution){if(json.BetaDistribution.alpha!=null&&json.BetaDistribution.beta!=null){return new BetaDistribution(json.BetaDistribution.alpha[0],json.BetaDistribution.beta[0]);}else{throw"Malformed JSON provided to DistributionBuilder "+json;}}else if(json.GammaDistribution){if(json.GammaDistribution.shape!=null&&json.GammaDistribution.scale!=null){return new GammaDistribution(json.GammaDistribution.shape[0],json.GammaDistribution.scale[0]);}else{throw"Malformed JSON provided to DistributionBuilder "+json;}}else if(json.StudentTDistribution){if(json.StudentTDistribution.degreesOfFreedom!=null&&json.StudentTDistribution.nonCentralityParameter!=null){return new StudentTDistribution(json.StudentTDistribution.degreesOfFreedom[0],json.StudentTDistribution.nonCentralityParameter[0]);}else if(json.StudentTDistribution.degreesOfFreedom!=null){return new StudentTDistribution(json.StudentTDistribution.degreesOfFreedom[0]);}else{throw"Malformed JSON provided to DistributionBuilder "+json;}}else{throw"Malformed JSON provided to DistributionBuilder "+json;}}
|
||||
@@ -1,89 +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('Sliders', [], function () {
|
||||
return Sliders;
|
||||
|
||||
function Sliders(gstId, state) {
|
||||
var c1, paramName, allParamNames, sliderDiv;
|
||||
|
||||
allParamNames = state.getAllParameterNames();
|
||||
|
||||
for (c1 = 0; c1 < allParamNames.length; c1 += 1) {
|
||||
paramName = allParamNames[c1];
|
||||
|
||||
sliderDiv = $('#' + gstId + '_slider_' + paramName);
|
||||
|
||||
if (sliderDiv.length === 1) {
|
||||
createSlider(sliderDiv, paramName);
|
||||
} else if (sliderDiv.length > 1) {
|
||||
console.log('ERROR: Found more than one slider for the parameter "' + paramName + '".');
|
||||
console.log('sliderDiv.length = ', sliderDiv.length);
|
||||
} // else {
|
||||
// console.log('MESSAGE: Did not find a slider for the parameter "' + paramName + '".');
|
||||
// }
|
||||
}
|
||||
|
||||
function createSlider(sliderDiv, paramName) {
|
||||
var paramObj;
|
||||
|
||||
paramObj = state.getParamObj(paramName);
|
||||
|
||||
// Check that the retrieval went OK.
|
||||
if (paramObj === undefined) {
|
||||
console.log('ERROR: Could not get a paramObj for parameter "' + paramName + '".');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a jQuery UI slider from the slider DIV. We will set
|
||||
// starting parameters, and will also attach a handler to update
|
||||
// the 'state' on the 'slide' event.
|
||||
sliderDiv.slider({
|
||||
'min': paramObj.min,
|
||||
'max': paramObj.max,
|
||||
'value': paramObj.value,
|
||||
'step': paramObj.step
|
||||
});
|
||||
|
||||
// Tell the parameter object stored in state that we have a slider
|
||||
// that is attached to it. Next time when the parameter changes, it
|
||||
// will also update the value of this slider.
|
||||
paramObj.sliderDiv = sliderDiv;
|
||||
|
||||
// Atach callbacks to update the slider's parameter.
|
||||
paramObj.sliderDiv.on('slide', sliderOnSlide);
|
||||
paramObj.sliderDiv.on('slidechange', sliderOnChange);
|
||||
|
||||
return;
|
||||
|
||||
// Update the 'state' - i.e. set the value of the parameter this
|
||||
// slider is attached to to a new value.
|
||||
//
|
||||
// This will cause the plot to be redrawn each time after the user
|
||||
// drags the slider handle and releases it.
|
||||
function sliderOnSlide(event, ui) {
|
||||
// Last parameter passed to setParameterValue() will be 'true'
|
||||
// so that the function knows we are a slider, and it can
|
||||
// change the our value back in the case when the new value is
|
||||
// invalid for some reason.
|
||||
if (state.setParameterValue(paramName, ui.value, sliderDiv, true, 'slide') === undefined) {
|
||||
console.log('ERROR: Could not update the parameter named "' + paramName + '" with the value "' + ui.value + '".');
|
||||
}
|
||||
}
|
||||
|
||||
function sliderOnChange(event, ui) {
|
||||
if (state.setParameterValue(paramName, ui.value, sliderDiv, true, 'change') === undefined) {
|
||||
console.log('ERROR: Could not update the parameter named "' + paramName + '" with the value "' + 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)
|
||||
@@ -1,395 +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('State', [], function () {
|
||||
var stateInst;
|
||||
|
||||
// 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 an
|
||||
// object containing methods to set and get the private state properties.
|
||||
|
||||
stateInst = 0;
|
||||
|
||||
// This module defines and returns a factory constructor.
|
||||
return State;
|
||||
|
||||
function State(gstId, config) {
|
||||
var parameters, allParameterNames, allParameterValues,
|
||||
plotDiv, dynamicEl, dynamicElByElId;
|
||||
|
||||
dynamicEl = [];
|
||||
dynamicElByElId = {};
|
||||
|
||||
stateInst += 1;
|
||||
// console.log('MESSAGE: Creating state instance # ' + stateInst + '.');
|
||||
|
||||
// Initially, there are no parameters to track. So, we will instantiate
|
||||
// an empty object.
|
||||
//
|
||||
// As we parse the JSON config object, we will add parameters as
|
||||
// named properties. For example
|
||||
//
|
||||
// parameters.a = {...};
|
||||
//
|
||||
// will be created for the parameter 'a'.
|
||||
parameters = {};
|
||||
|
||||
// Check that the required parameters config object is available.
|
||||
if ($.isPlainObject(config.parameters) === false) {
|
||||
console.log('ERROR: Expected config.parameters to be an object. It is not.');
|
||||
console.log('config.parameters = ', config.parameters);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If config.parameters.param is an array, pass it to the processor
|
||||
// element by element.
|
||||
if ($.isArray(config.parameters.param) === true) {
|
||||
(function (c1) {
|
||||
while (c1 < config.parameters.param.length) {
|
||||
processParameter(config.parameters.param[c1]);
|
||||
c1 += 1;
|
||||
}
|
||||
}(0));
|
||||
}
|
||||
|
||||
// If config.parameters.param is an object, pass this object to the
|
||||
// processor directly.
|
||||
else if ($.isPlainObject(config.parameters.param) === true) {
|
||||
processParameter(config.parameters.param);
|
||||
}
|
||||
|
||||
// If config.parameters.param is some other type, report an error and
|
||||
// do not continue.
|
||||
else {
|
||||
console.log('ERROR: config.parameters.param is of an unsupported type.');
|
||||
console.log('config.parameters.param = ', config.parameters.param);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Instead of building these arrays every time when some component
|
||||
// requests them, we will create them in the beginning, and then update
|
||||
// each element individually when some parameter's value changes.
|
||||
//
|
||||
// Then we can just return the required array, instead of iterating
|
||||
// over all of the properties of the 'parameters' object, and
|
||||
// extracting their names/values one by one.
|
||||
allParameterNames = [];
|
||||
allParameterValues = [];
|
||||
|
||||
// Populate 'allParameterNames', and 'allParameterValues' with data.
|
||||
generateHelperArrays();
|
||||
|
||||
// The constructor will return an object with methods to operate on
|
||||
// it's private properties.
|
||||
return {
|
||||
'getParameterValue': getParameterValue,
|
||||
'setParameterValue': setParameterValue,
|
||||
|
||||
'getParamObj': getParamObj,
|
||||
|
||||
'getAllParameterNames': getAllParameterNames,
|
||||
'getAllParameterValues': getAllParameterValues,
|
||||
|
||||
'bindUpdatePlotEvent': bindUpdatePlotEvent,
|
||||
'addDynamicEl': addDynamicEl,
|
||||
|
||||
// plde is an abbreviation for Plot Label Dynamic Elements.
|
||||
plde: []
|
||||
};
|
||||
|
||||
function getAllParameterNames() {
|
||||
return allParameterNames;
|
||||
}
|
||||
|
||||
function getAllParameterValues() {
|
||||
return allParameterValues;
|
||||
}
|
||||
|
||||
function getParamObj(paramName) {
|
||||
if (parameters.hasOwnProperty(paramName) === false) {
|
||||
console.log('ERROR: Object parameters does not have a property named "' + paramName + '".');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return parameters[paramName];
|
||||
}
|
||||
|
||||
function bindUpdatePlotEvent(newPlotDiv, callback) {
|
||||
plotDiv = newPlotDiv;
|
||||
|
||||
plotDiv.bind('update_plot', callback);
|
||||
}
|
||||
|
||||
function addDynamicEl(el, func, elId, updateOnEvent) {
|
||||
var newLength;
|
||||
|
||||
newLength = dynamicEl.push({
|
||||
'el': el,
|
||||
'func': func,
|
||||
'elId': elId,
|
||||
'updateOnEvent': updateOnEvent
|
||||
});
|
||||
|
||||
if (typeof dynamicElByElId[elId] !== 'undefined') {
|
||||
console.log(
|
||||
'ERROR: Duplicate dynamic element ID "' + elId + '" found.'
|
||||
);
|
||||
} else {
|
||||
dynamicElByElId[elId] = dynamicEl[newLength - 1];
|
||||
}
|
||||
}
|
||||
|
||||
function getParameterValue(paramName) {
|
||||
|
||||
// If the name of the constant is not tracked by state, return an
|
||||
// 'undefined' value.
|
||||
if (parameters.hasOwnProperty(paramName) === false) {
|
||||
console.log('ERROR: Object parameters does not have a property named "' + paramName + '".');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return parameters[paramname].value;
|
||||
}
|
||||
|
||||
// ####################################################################
|
||||
//
|
||||
// Function: setParameterValue(paramName, paramValue, element)
|
||||
// --------------------------------------------------
|
||||
//
|
||||
//
|
||||
// This function can be called from a callback, registered by a slider
|
||||
// or a text input, when specific events ('slide' or 'change') are
|
||||
// triggered.
|
||||
//
|
||||
// The 'paramName' is the name of the parameter in 'parameters' object
|
||||
// whose value must be updated to the new value of 'paramValue'.
|
||||
//
|
||||
// Before we update the value, we must check that:
|
||||
//
|
||||
// 1.) the parameter named as 'paramName' actually exists in the
|
||||
// 'parameters' object;
|
||||
// 2.) the value 'paramValue' is a valid floating-point number, and
|
||||
// it lies within the range specified by the 'min' and 'max'
|
||||
// properties of the stored parameter object.
|
||||
//
|
||||
// If 'paramName' and 'paramValue' turn out to be valid, we will update
|
||||
// the stored value in the parameter with the new value, and also
|
||||
// update all of the text inputs and the slider that correspond to this
|
||||
// parameter (if any), so that they reflect the new parameter's value.
|
||||
// Finally, the helper array 'allParameterValues' will also be updated
|
||||
// to reflect the change.
|
||||
//
|
||||
// If something went wrong (for example the new value is outside the
|
||||
// allowed range), then we will reset the 'element' to display the
|
||||
// original value.
|
||||
//
|
||||
// ####################################################################
|
||||
function setParameterValue(paramName, paramValue, element, slider, updateOnEvent) {
|
||||
var paramValueNum, c1;
|
||||
|
||||
// If a parameter with the name specified by the 'paramName'
|
||||
// parameter is not tracked by state, do not do anything.
|
||||
if (parameters.hasOwnProperty(paramName) === false) {
|
||||
console.log('ERROR: Object parameters does not have a property named "' + paramName + '".');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to convert the passed value to a valid floating-point
|
||||
// number.
|
||||
paramValueNum = parseFloat(paramValue);
|
||||
|
||||
// We are interested only in valid float values. NaN, -INF,
|
||||
// +INF we will disregard.
|
||||
if (isFinite(paramValueNum) === false) {
|
||||
console.log('ERROR: New parameter value is not a floating-point number.');
|
||||
console.log('paramValue = ', paramValue);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (paramValueNum < parameters[paramName].min) {
|
||||
paramValueNum = parameters[paramName].min;
|
||||
} else if (paramValueNum > parameters[paramName].max) {
|
||||
paramValueNum = parameters[paramName].max;
|
||||
}
|
||||
|
||||
parameters[paramName].value = paramValueNum;
|
||||
|
||||
// Update all text inputs with the new parameter's value.
|
||||
for (c1 = 0; c1 < parameters[paramName].inputDivs.length; c1 += 1) {
|
||||
parameters[paramName].inputDivs[c1].val(paramValueNum);
|
||||
}
|
||||
|
||||
// Update the single slider with the new parameter's value.
|
||||
if ((slider === false) && (parameters[paramName].sliderDiv !== null)) {
|
||||
parameters[paramName].sliderDiv.slider('value', paramValueNum);
|
||||
}
|
||||
|
||||
// Update the helper array with the new parameter's value.
|
||||
allParameterValues[parameters[paramName].helperArrayIndex] = paramValueNum;
|
||||
|
||||
for (c1 = 0; c1 < dynamicEl.length; c1++) {
|
||||
if (
|
||||
((updateOnEvent !== undefined) && (dynamicEl[c1].updateOnEvent === updateOnEvent)) ||
|
||||
(updateOnEvent === undefined)
|
||||
) {
|
||||
// If we have a DOM element, call the function "paste" the answer into the DIV.
|
||||
if (dynamicEl[c1].el !== null) {
|
||||
dynamicEl[c1].el.html(dynamicEl[c1].func.apply(window, allParameterValues));
|
||||
}
|
||||
// If we DO NOT have an element, simply call the function. The function can then
|
||||
// manipulate all the DOM elements it wants, without the fear of them being overwritten
|
||||
// by us afterwards.
|
||||
else {
|
||||
dynamicEl[c1].func.apply(window, allParameterValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a plot DIV to work with, tell to update.
|
||||
if (plotDiv !== undefined) {
|
||||
plotDiv.trigger('update_plot');
|
||||
}
|
||||
|
||||
return true;
|
||||
} // End-of: function setParameterValue
|
||||
|
||||
// ####################################################################
|
||||
//
|
||||
// Function: processParameter(obj)
|
||||
// -------------------------------
|
||||
//
|
||||
//
|
||||
// This function will be run once for each instance of a GST when
|
||||
// parsing the JSON config object.
|
||||
//
|
||||
// 'newParamObj' must be empty from the start for each invocation of
|
||||
// this function, that's why we will declare it locally.
|
||||
//
|
||||
// We will parse the passed object 'obj' and populate the 'newParamObj'
|
||||
// object with required properties.
|
||||
//
|
||||
// Since there will be many properties that are of type floating-point
|
||||
// number, we will have a separate function for parsing them.
|
||||
//
|
||||
// processParameter() will fail right away if 'obj' does not have a
|
||||
// '@var' property which represents the name of the parameter we want
|
||||
// to process.
|
||||
//
|
||||
// If, after all of the properties have been processed, we reached the
|
||||
// end of the function successfully, the 'newParamObj' will be added to
|
||||
// the 'parameters' object (that is defined in the scope of State()
|
||||
// function) as a property named as the name of the parameter.
|
||||
//
|
||||
// If at least one of the properties from 'obj' does not get correctly
|
||||
// parsed, then the parameter represented by 'obj' will be disregarded.
|
||||
// It will not be available to user-defined plotting functions, and
|
||||
// things will most likely break. We will notify the user about this.
|
||||
//
|
||||
// ####################################################################
|
||||
function processParameter(obj) {
|
||||
var paramName, newParamObj;
|
||||
|
||||
if (typeof obj['@var'] !== 'string') {
|
||||
console.log('ERROR: Expected obj["@var"] to be a string. It is not.');
|
||||
console.log('obj["@var"] = ', obj['@var']);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
paramName = obj['@var'];
|
||||
newParamObj = {};
|
||||
|
||||
if (
|
||||
(processFloat('@min', 'min') === false) ||
|
||||
(processFloat('@max', 'max') === false) ||
|
||||
(processFloat('@step', 'step') === false) ||
|
||||
(processFloat('@initial', 'value') === false)
|
||||
) {
|
||||
console.log('ERROR: A required property is missing. Not creating parameter "' + paramName + '"');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Pointers to text input and slider DIV elements that this
|
||||
// parameter will be attached to. Initially there are none. When we
|
||||
// will create text inputs and sliders, we will update these
|
||||
// properties.
|
||||
newParamObj.inputDivs = [];
|
||||
newParamObj.sliderDiv = null;
|
||||
|
||||
// Everything went well, so save the new parameter object.
|
||||
parameters[paramName] = newParamObj;
|
||||
|
||||
return;
|
||||
|
||||
function processFloat(attrName, newAttrName) {
|
||||
var attrValue;
|
||||
|
||||
if (typeof obj[attrName] !== 'string') {
|
||||
console.log('ERROR: Expected obj["' + attrName + '"] to be a string. It is not.');
|
||||
console.log('obj["' + attrName + '"] = ', obj[attrName]);
|
||||
|
||||
return false;
|
||||
} else {
|
||||
attrValue = parseFloat(obj[attrName]);
|
||||
|
||||
if (isFinite(attrValue) === false) {
|
||||
console.log('ERROR: Expected obj["' + attrName + '"] to be a valid floating-point number. It is not.');
|
||||
console.log('obj["' + attrName + '"] = ', obj[attrName]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
newParamObj[newAttrName] = attrValue;
|
||||
|
||||
return true;
|
||||
} // End-of: function processFloat
|
||||
} // End-of: function processParameter
|
||||
|
||||
// ####################################################################
|
||||
//
|
||||
// Function: generateHelperArrays()
|
||||
// -------------------------------
|
||||
//
|
||||
//
|
||||
// Populate 'allParameterNames' and 'allParameterValues' with data.
|
||||
// Link each parameter object with the corresponding helper array via
|
||||
// an index 'helperArrayIndex'. It will be the same for both of the
|
||||
// arrays.
|
||||
//
|
||||
// NOTE: It is important to remember to update these helper arrays
|
||||
// whenever a new parameter is added (or one is removed), or when a
|
||||
// parameter's value changes.
|
||||
//
|
||||
// ####################################################################
|
||||
function generateHelperArrays() {
|
||||
var paramName, c1;
|
||||
|
||||
c1 = 0;
|
||||
for (paramName in parameters) {
|
||||
allParameterNames.push(paramName);
|
||||
allParameterValues.push(parameters[paramName].value);
|
||||
|
||||
parameters[paramName].helperArrayIndex = c1;
|
||||
|
||||
c1 += 1;
|
||||
}
|
||||
}
|
||||
} // 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)
|
||||
@@ -82,7 +82,6 @@ class RoundTripTestCase(unittest.TestCase):
|
||||
"conditional_and_poll",
|
||||
"conditional",
|
||||
"self_assessment",
|
||||
"graphic_slider_tool",
|
||||
"test_exam_registration",
|
||||
"word_cloud",
|
||||
"pure_xblock",
|
||||
|
||||
@@ -614,22 +614,6 @@ class ImportTestCase(BaseCourseTestCase):
|
||||
|
||||
self.assertRaises(etree.XMLSyntaxError, system.process_xml, bad_xml)
|
||||
|
||||
def test_graphicslidertool_import(self):
|
||||
'''
|
||||
Check to see if definition_from_xml in gst_module.py
|
||||
works properly. Pulls data from the graphic_slider_tool directory
|
||||
in the test data directory.
|
||||
'''
|
||||
modulestore = XMLModuleStore(DATA_DIR, source_dirs=['graphic_slider_tool'])
|
||||
|
||||
sa_id = SlashSeparatedCourseKey("edX", "gst_test", "2012_Fall")
|
||||
location = sa_id.make_usage_key("graphical_slider_tool", "sample_gst")
|
||||
gst_sample = modulestore.get_item(location)
|
||||
render_string_from_sample_gst_xml = """
|
||||
<slider var="a" style="width:400px;float:left;"/>\
|
||||
<plot style="margin-top:15px;margin-bottom:15px;"/>""".strip()
|
||||
self.assertIn(render_string_from_sample_gst_xml, gst_sample.data)
|
||||
|
||||
def test_word_cloud_import(self):
|
||||
modulestore = XMLModuleStore(DATA_DIR, source_dirs=['word_cloud'])
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ from xmodule.annotatable_module import AnnotatableDescriptor
|
||||
from xmodule.capa_module import CapaDescriptor
|
||||
from xmodule.course_module import CourseDescriptor
|
||||
from xmodule.discussion_module import DiscussionDescriptor
|
||||
from xmodule.gst_module import GraphicalSliderToolDescriptor
|
||||
from xmodule.html_module import HtmlDescriptor
|
||||
from xmodule.poll_module import PollDescriptor
|
||||
from xmodule.word_cloud_module import WordCloudDescriptor
|
||||
@@ -53,7 +52,6 @@ LEAF_XMODULES = {
|
||||
AnnotatableDescriptor: [{}],
|
||||
CapaDescriptor: [{}],
|
||||
DiscussionDescriptor: [{}],
|
||||
GraphicalSliderToolDescriptor: [{}],
|
||||
HtmlDescriptor: [{}],
|
||||
PollDescriptor: [{'display_name': 'Poll Display Name'}],
|
||||
WordCloudDescriptor: [{}],
|
||||
@@ -75,10 +73,9 @@ CONTAINER_XMODULES = {
|
||||
WrapperBlock: [{}],
|
||||
}
|
||||
|
||||
# These modules are editable in studio yet
|
||||
# These modules are not editable in studio yet
|
||||
NOT_STUDIO_EDITABLE = (
|
||||
CrowdsourceHinterDescriptor,
|
||||
GraphicalSliderToolDescriptor,
|
||||
PollDescriptor
|
||||
)
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
This is a very very simple course, useful for debugging graphical slider tool
|
||||
code.
|
||||
@@ -1 +0,0 @@
|
||||
roots/2012_Fall.xml
|
||||
@@ -1,5 +0,0 @@
|
||||
<course>
|
||||
<chapter url_name="Overview">
|
||||
<graphical_slider_tool url_name="sample_gst"/>
|
||||
</chapter>
|
||||
</course>
|
||||
@@ -1,30 +0,0 @@
|
||||
|
||||
<graphical_slider_tool>
|
||||
<render>
|
||||
<slider var='a' style="width:400px;float:left;"/><plot style="margin-top:15px;margin-bottom:15px;"/>
|
||||
</render>
|
||||
<configuration>
|
||||
<parameters>
|
||||
<param var="a" min="5" max="25" step="0.5" initial="12.5" />
|
||||
</parameters>
|
||||
<functions>
|
||||
<function color="red">return Math.sqrt(a * a - x * x);</function>
|
||||
<function color="red">return -Math.sqrt(a * a - x * x);</function>
|
||||
</functions>
|
||||
<plot>
|
||||
<xrange>
|
||||
<!-- dynamic range -->
|
||||
<min>
|
||||
return -a;
|
||||
</min>
|
||||
<max>
|
||||
return a;
|
||||
</max>
|
||||
</xrange>
|
||||
<num_points>1000</num_points>
|
||||
<xticks>-30, 6, 30</xticks>
|
||||
<yticks>-30, 6, 30</yticks>
|
||||
</plot>
|
||||
</configuration>
|
||||
</graphical_slider_tool>
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"course/2012_Fall": {
|
||||
"graceperiod": "2 days 5 hours 59 minutes 59 seconds",
|
||||
"start": "2015-07-17T12:00",
|
||||
"display_name": "GST Test",
|
||||
"graded": "false"
|
||||
},
|
||||
"chapter/Overview": {
|
||||
"display_name": "Overview"
|
||||
},
|
||||
"graphical_slider_tool/sample_gst": {
|
||||
"display_name": "Sample GST"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<course org="edX" course="gst_test" url_name="2012_Fall"/>
|
||||
@@ -1,10 +0,0 @@
|
||||
@shard_2
|
||||
Feature: LMS.Graphical Slider Tool Module
|
||||
As a student, I want to view a Graphical Slider Tool Component
|
||||
|
||||
Scenario: The slider changes values on the page
|
||||
Given that I have a course with a Graphical Slider Tool
|
||||
When I view the Graphical Slider Tool
|
||||
Then the displayed value should be 0
|
||||
And I move the slider to the right
|
||||
Then the displayed value should be 10
|
||||
@@ -1,76 +0,0 @@
|
||||
|
||||
from lettuce import world, steps
|
||||
from nose.tools import assert_equals
|
||||
|
||||
from common import i_am_registered_for_the_course, visit_scenario_item
|
||||
|
||||
|
||||
DEFAULT_DATA = """\
|
||||
<render>
|
||||
<p>Test of the graphical slider tool</p>
|
||||
<div class='gst-value'>
|
||||
<span id="value-display" style="width:50px; float:left; margin-left:10px;"/>
|
||||
</div>
|
||||
<div class='gst-input'>
|
||||
<slider var="a" style="width:400px;float:left;margin-left:10px;"/>
|
||||
</div>
|
||||
</render>
|
||||
<configuration>
|
||||
<parameters>
|
||||
<param var="a" min="0" max="10" step="1" initial="0"/>
|
||||
</parameters>
|
||||
<functions>
|
||||
<function output="element" el_id="value-display">a</function>
|
||||
</functions>
|
||||
</configuration>
|
||||
"""
|
||||
|
||||
|
||||
@steps
|
||||
class GraphicalSliderToolSteps(object):
|
||||
COURSE_NUM = 'test_course'
|
||||
|
||||
def setup_gst(self, step):
|
||||
r'that I have a course with a Graphical Slider Tool$'
|
||||
|
||||
i_am_registered_for_the_course(step, self.COURSE_NUM)
|
||||
|
||||
world.scenario_dict['GST'] = world.ItemFactory(
|
||||
parent_location=world.scenario_dict['SECTION'].location,
|
||||
category='graphical_slider_tool',
|
||||
display_name="Test GST",
|
||||
data=DEFAULT_DATA
|
||||
)
|
||||
|
||||
def view_gst(self, step):
|
||||
r'I view the Graphical Slider Tool$'
|
||||
visit_scenario_item('GST')
|
||||
world.wait_for_js_variable_truthy('$(".xblock-student_view[data-type=GraphicalSliderTool]").data("initialized")')
|
||||
world.wait_for_ajax_complete()
|
||||
|
||||
def check_value(self, step, value):
|
||||
r'the displayed value should be (?P<value>\d+)$'
|
||||
|
||||
assert_equals(world.css_text('.gst-value'), value)
|
||||
|
||||
def move_slider(self, step):
|
||||
r'I move the slider to the right$'
|
||||
|
||||
handle_selector = '.gst-input .ui-slider-handle'
|
||||
world.wait_for_visible(handle_selector)
|
||||
world.wait_for_visible('.gst-value #value-display')
|
||||
|
||||
def try_move():
|
||||
handle = world.css_find(handle_selector).first
|
||||
slider = world.css_find('.gst-input .ui-slider').first
|
||||
(handle.action_chains
|
||||
.click_and_hold(handle._element)
|
||||
.move_by_offset(
|
||||
int(handle._element.location['x'] + 400),
|
||||
0
|
||||
).release().perform())
|
||||
|
||||
world.retry_on_exception(try_move)
|
||||
|
||||
|
||||
GraphicalSliderToolSteps()
|
||||
@@ -1,9 +0,0 @@
|
||||
<div align="center" id="${element_id}" class="${element_class}">
|
||||
<!-- hidden field to read configuration json from -->
|
||||
<div class="${element_class}" id="${element_id}_json" style="display: none;">
|
||||
${configuration_json}
|
||||
</div>
|
||||
|
||||
<!-- main xml with marked places for sliders, numbers, and plot -->
|
||||
${gst_html}
|
||||
</div>
|
||||
@@ -6,7 +6,7 @@ from django.template.defaultfilters import escapejs
|
||||
|
||||
## The JS for this is defined in xqa_interface.html
|
||||
${block_content}
|
||||
%if location.category in ['problem','video','html','combinedopenended','graphical_slider_tool', 'library_content']:
|
||||
%if location.category in ['problem','video','html','combinedopenended','library_content']:
|
||||
% if edit_link:
|
||||
<div>
|
||||
<a href="${edit_link}">Edit</a>
|
||||
|
||||
Reference in New Issue
Block a user