diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py
index 490f49a41c..d701db33a3 100644
--- a/cms/djangoapps/contentstore/views.py
+++ b/cms/djangoapps/contentstore/views.py
@@ -53,7 +53,7 @@ def index(request):
"""
courses = modulestore().get_items(['i4x', None, None, 'course', None])
return render_to_response('index.html', {
- 'courses': [(course.metadata['display_name'],
+ 'courses': [(course.metadata.get('display_name'),
reverse('course_index', args=[
course.location.org,
course.location.course,
diff --git a/cms/static/sass/_base.scss b/cms/static/sass/_base.scss
index 410f74ee07..90a9629351 100644
--- a/cms/static/sass/_base.scss
+++ b/cms/static/sass/_base.scss
@@ -14,9 +14,11 @@ $yellow: #fff8af;
$cream: #F6EFD4;
$border-color: #ddd;
+
// edX colors
$blue: rgb(29,157,217);
$pink: rgb(182,37,104);
+$error-red: rgb(253, 87, 87);
@mixin hide-text {
background-color: transparent;
diff --git a/cms/static/sass/_calendar.scss b/cms/static/sass/_calendar.scss
index 35609b2d56..4c007bb561 100644
--- a/cms/static/sass/_calendar.scss
+++ b/cms/static/sass/_calendar.scss
@@ -330,11 +330,6 @@ section.cal {
&:hover {
opacity: 1;
- width: flex-grid(5) + flex-gutter();
-
- + section.main-content {
- width: flex-grid(7);
- }
}
> header {
diff --git a/common/djangoapps/external_auth/admin.py b/common/djangoapps/external_auth/admin.py
index 343492bca7..e93325bcb2 100644
--- a/common/djangoapps/external_auth/admin.py
+++ b/common/djangoapps/external_auth/admin.py
@@ -5,4 +5,8 @@ django admin pages for courseware model
from external_auth.models import *
from django.contrib import admin
-admin.site.register(ExternalAuthMap)
+class ExternalAuthMapAdmin(admin.ModelAdmin):
+ search_fields = ['external_id','user__username']
+ date_hierarchy = 'dtcreated'
+
+admin.site.register(ExternalAuthMap, ExternalAuthMapAdmin)
diff --git a/common/djangoapps/track/admin.py b/common/djangoapps/track/admin.py
new file mode 100644
index 0000000000..1f19c59a93
--- /dev/null
+++ b/common/djangoapps/track/admin.py
@@ -0,0 +1,8 @@
+'''
+django admin pages for courseware model
+'''
+
+from track.models import *
+from django.contrib import admin
+
+admin.site.register(TrackingLog)
diff --git a/common/lib/capa/capa/javascript_problem_generator.js b/common/lib/capa/capa/javascript_problem_generator.js
index 8c8d39b19f..1cd4616c5a 100644
--- a/common/lib/capa/capa/javascript_problem_generator.js
+++ b/common/lib/capa/capa/javascript_problem_generator.js
@@ -11,13 +11,11 @@ importAll("xproblem");
generatorModulePath = process.argv[2];
dependencies = JSON.parse(process.argv[3]);
-seed = process.argv[4];
+seed = JSON.parse(process.argv[4]);
params = JSON.parse(process.argv[5]);
if(seed==null){
seed = 4;
-}else{
- seed = parseInt(seed);
}
for(var i = 0; i < dependencies.length; i++){
diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py
index b803452b8c..7f1ff32f67 100644
--- a/common/lib/capa/capa/responsetypes.py
+++ b/common/lib/capa/capa/responsetypes.py
@@ -408,7 +408,7 @@ class JavascriptResponse(LoncapaResponse):
output = self.call_node([generator_file,
self.generator,
json.dumps(self.generator_dependencies),
- json.dumps(str(self.system.seed)),
+ json.dumps(str(self.context['the_lcp'].seed)),
json.dumps(self.params)]).strip()
return json.loads(output)
diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py
index d186bcc39c..8bf1a56404 100644
--- a/common/lib/xmodule/xmodule/capa_module.py
+++ b/common/lib/xmodule/xmodule/capa_module.py
@@ -507,8 +507,12 @@ class CapaModule(XModule):
# 'success' will always be incorrect
event_info['correct_map'] = correct_map.get_dict()
event_info['success'] = success
+ event_info['attempts'] = self.attempts
self.system.track_function('save_problem_check', event_info)
+ if hasattr(self.system,'psychometrics_handler'): # update PsychometricsData using callback
+ self.system.psychometrics_handler(self.get_instance_state())
+
# render problem into HTML
html = self.get_problem_html(encapsulate=False)
diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py
index f00a22782c..7aa904205d 100644
--- a/common/lib/xmodule/xmodule/course_module.py
+++ b/common/lib/xmodule/xmodule/course_module.py
@@ -99,7 +99,14 @@ class CourseDescriptor(SequenceDescriptor):
def definition_from_xml(cls, xml_object, system):
textbooks = []
for textbook in xml_object.findall("textbook"):
- textbooks.append(cls.Textbook.from_xml_object(textbook))
+ try:
+ txt = cls.Textbook.from_xml_object(textbook)
+ except:
+ # If we can't get to S3 (e.g. on a train with no internet), don't break
+ # the rest of the courseware.
+ log.exception("Couldn't load textbook")
+ continue
+ textbooks.append(txt)
xml_object.remove(textbook)
#Load the wiki tag if it exists
diff --git a/common/lib/xmodule/xmodule/css/sequence/display.scss b/common/lib/xmodule/xmodule/css/sequence/display.scss
index 25d2c26dda..0533465298 100644
--- a/common/lib/xmodule/xmodule/css/sequence/display.scss
+++ b/common/lib/xmodule/xmodule/css/sequence/display.scss
@@ -32,7 +32,7 @@ nav.sequence-nav {
.sequence-list-wrapper {
position: relative;
- z-index: 9999;
+ z-index: 99;
border: 1px solid #ccc;
height: 44px;
margin: 0 30px;
diff --git a/common/lib/xmodule/xmodule/js/src/capa/schematic.js b/common/lib/xmodule/xmodule/js/src/capa/schematic.js
index e07b98d63c..b01f6e12e8 100644
--- a/common/lib/xmodule/xmodule/js/src/capa/schematic.js
+++ b/common/lib/xmodule/xmodule/js/src/capa/schematic.js
@@ -2023,7 +2023,16 @@ function add_schematic_handler(other_onload) {
update_schematics();
}
}
-window.onload = add_schematic_handler(window.onload);
+/*
+ * THK: Attaching update_schematic to window.onload is rather presumptuous...
+ * The function is called for EVERY page load, whether in courseware or in
+ * course info, in 6.002x or the public health course. It is also redundant
+ * because courseware includes an explicit call to update_schematic after
+ * each ajax exchange. In this case, calling update_schematic twice appears
+ * to contribute to a bug in Firefox that does not render the schematic
+ * properly depending on timing.
+ */
+//window.onload = add_schematic_handler(window.onload);
// ask each schematic input widget to update its value field for submission
function prepare_schematics() {
diff --git a/common/lib/xmodule/xmodule/seq_module.py b/common/lib/xmodule/xmodule/seq_module.py
index 65f692957c..b05ea36e50 100644
--- a/common/lib/xmodule/xmodule/seq_module.py
+++ b/common/lib/xmodule/xmodule/seq_module.py
@@ -75,7 +75,7 @@ class SequenceModule(XModule):
contents = []
for child in self.get_display_items():
progress = child.get_progress()
- contents.append({
+ childinfo = {
'content': child.get_html(),
'title': "\n".join(
grand_child.display_name.strip()
@@ -85,7 +85,10 @@ class SequenceModule(XModule):
'progress_status': Progress.to_js_status_str(progress),
'progress_detail': Progress.to_js_detail_str(progress),
'type': child.get_icon_class(),
- })
+ }
+ if childinfo['title']=='':
+ childinfo['title'] = child.metadata.get('display_name','')
+ contents.append(childinfo)
params = {'items': contents,
'element_id': self.location.html_id(),
diff --git a/common/lib/xmodule/xmodule/tests/__init__.py b/common/lib/xmodule/xmodule/tests/__init__.py
index 4103a7373e..654b6beb15 100644
--- a/common/lib/xmodule/xmodule/tests/__init__.py
+++ b/common/lib/xmodule/xmodule/tests/__init__.py
@@ -29,7 +29,7 @@ from nose.plugins.skip import SkipTest
from mock import Mock
i4xs = ModuleSystem(
- ajax_url='/',
+ ajax_url='courses/course_id/modx/a_location',
track_function=Mock(),
get_module=Mock(),
render_template=Mock(),
diff --git a/common/static/js/vendor/flot/jquery.flot.axislabels.js b/common/static/js/vendor/flot/jquery.flot.axislabels.js
new file mode 100644
index 0000000000..797f82ec9f
--- /dev/null
+++ b/common/static/js/vendor/flot/jquery.flot.axislabels.js
@@ -0,0 +1,412 @@
+/*
+Axis Labels Plugin for flot.
+http://github.com/markrcote/flot-axislabels
+
+Original code is Copyright (c) 2010 Xuan Luo.
+Original code was released under the GPLv3 license by Xuan Luo, September 2010.
+Original code was rereleased under the MIT license by Xuan Luo, April 2012.
+
+Improvements by Mark Cote.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ */
+(function ($) {
+ var options = { };
+
+ function canvasSupported() {
+ return !!document.createElement('canvas').getContext;
+ }
+
+ function canvasTextSupported() {
+ if (!canvasSupported()) {
+ return false;
+ }
+ var dummy_canvas = document.createElement('canvas');
+ var context = dummy_canvas.getContext('2d');
+ return typeof context.fillText == 'function';
+ }
+
+ function css3TransitionSupported() {
+ var div = document.createElement('div');
+ return typeof div.style.MozTransition != 'undefined' // Gecko
+ || typeof div.style.OTransition != 'undefined' // Opera
+ || typeof div.style.webkitTransition != 'undefined' // WebKit
+ || typeof div.style.transition != 'undefined';
+ }
+
+
+ function AxisLabel(axisName, position, padding, plot, opts) {
+ this.axisName = axisName;
+ this.position = position;
+ this.padding = padding;
+ this.plot = plot;
+ this.opts = opts;
+ this.width = 0;
+ this.height = 0;
+ }
+
+
+ CanvasAxisLabel.prototype = new AxisLabel();
+ CanvasAxisLabel.prototype.constructor = CanvasAxisLabel;
+ function CanvasAxisLabel(axisName, position, padding, plot, opts) {
+ AxisLabel.prototype.constructor.call(this, axisName, position, padding,
+ plot, opts);
+ }
+
+ CanvasAxisLabel.prototype.calculateSize = function() {
+ if (!this.opts.axisLabelFontSizePixels)
+ this.opts.axisLabelFontSizePixels = 14;
+ if (!this.opts.axisLabelFontFamily)
+ this.opts.axisLabelFontFamily = 'sans-serif';
+
+ var textWidth = this.opts.axisLabelFontSizePixels + this.padding;
+ var textHeight = this.opts.axisLabelFontSizePixels + this.padding;
+ if (this.position == 'left' || this.position == 'right') {
+ this.width = this.opts.axisLabelFontSizePixels + this.padding;
+ this.height = 0;
+ } else {
+ this.width = 0;
+ this.height = this.opts.axisLabelFontSizePixels + this.padding;
+ }
+ };
+
+ CanvasAxisLabel.prototype.draw = function(box) {
+ var ctx = this.plot.getCanvas().getContext('2d');
+ ctx.save();
+ ctx.font = this.opts.axisLabelFontSizePixels + 'px ' +
+ this.opts.axisLabelFontFamily;
+ var width = ctx.measureText(this.opts.axisLabel).width;
+ var height = this.opts.axisLabelFontSizePixels;
+ var x, y, angle = 0;
+ if (this.position == 'top') {
+ x = box.left + box.width/2 - width/2;
+ y = box.top + height*0.72;
+ } else if (this.position == 'bottom') {
+ x = box.left + box.width/2 - width/2;
+ y = box.top + box.height - height*0.72;
+ } else if (this.position == 'left') {
+ x = box.left + height*0.72;
+ y = box.height/2 + box.top + width/2;
+ angle = -Math.PI/2;
+ } else if (this.position == 'right') {
+ x = box.left + box.width - height*0.72;
+ y = box.height/2 + box.top - width/2;
+ angle = Math.PI/2;
+ }
+ ctx.translate(x, y);
+ ctx.rotate(angle);
+ ctx.fillText(this.opts.axisLabel, 0, 0);
+ ctx.restore();
+ };
+
+
+ HtmlAxisLabel.prototype = new AxisLabel();
+ HtmlAxisLabel.prototype.constructor = HtmlAxisLabel;
+ function HtmlAxisLabel(axisName, position, padding, plot, opts) {
+ AxisLabel.prototype.constructor.call(this, axisName, position,
+ padding, plot, opts);
+ }
+
+ HtmlAxisLabel.prototype.calculateSize = function() {
+ var elem = $('
' +
+ this.opts.axisLabel + '
');
+ this.plot.getPlaceholder().append(elem);
+ // store height and width of label itself, for use in draw()
+ this.labelWidth = elem.outerWidth(true);
+ this.labelHeight = elem.outerHeight(true);
+ elem.remove();
+
+ this.width = this.height = 0;
+ if (this.position == 'left' || this.position == 'right') {
+ this.width = this.labelWidth + this.padding;
+ } else {
+ this.height = this.labelHeight + this.padding;
+ }
+ };
+
+ HtmlAxisLabel.prototype.draw = function(box) {
+ this.plot.getPlaceholder().find('#' + this.axisName + 'Label').remove();
+ var elem = $(''
+ + this.opts.axisLabel + '
');
+ this.plot.getPlaceholder().append(elem);
+ if (this.position == 'top') {
+ elem.css('left', box.left + box.width/2 - this.labelWidth/2 + 'px');
+ elem.css('top', box.top + 'px');
+ } else if (this.position == 'bottom') {
+ elem.css('left', box.left + box.width/2 - this.labelWidth/2 + 'px');
+ elem.css('top', box.top + box.height - this.labelHeight + 'px');
+ } else if (this.position == 'left') {
+ elem.css('top', box.top + box.height/2 - this.labelHeight/2 + 'px');
+ elem.css('left', box.left + 'px');
+ } else if (this.position == 'right') {
+ elem.css('top', box.top + box.height/2 - this.labelHeight/2 + 'px');
+ elem.css('left', box.left + box.width - this.labelWidth + 'px');
+ }
+ };
+
+
+ CssTransformAxisLabel.prototype = new HtmlAxisLabel();
+ CssTransformAxisLabel.prototype.constructor = CssTransformAxisLabel;
+ function CssTransformAxisLabel(axisName, position, padding, plot, opts) {
+ HtmlAxisLabel.prototype.constructor.call(this, axisName, position,
+ padding, plot, opts);
+ }
+
+ CssTransformAxisLabel.prototype.calculateSize = function() {
+ HtmlAxisLabel.prototype.calculateSize.call(this);
+ this.width = this.height = 0;
+ if (this.position == 'left' || this.position == 'right') {
+ this.width = this.labelHeight + this.padding;
+ } else {
+ this.height = this.labelHeight + this.padding;
+ }
+ };
+
+ CssTransformAxisLabel.prototype.transforms = function(degrees, x, y) {
+ var stransforms = {
+ '-moz-transform': '',
+ '-webkit-transform': '',
+ '-o-transform': '',
+ '-ms-transform': ''
+ };
+ if (x != 0 || y != 0) {
+ var stdTranslate = ' translate(' + x + 'px, ' + y + 'px)';
+ stransforms['-moz-transform'] += stdTranslate;
+ stransforms['-webkit-transform'] += stdTranslate;
+ stransforms['-o-transform'] += stdTranslate;
+ stransforms['-ms-transform'] += stdTranslate;
+ }
+ if (degrees != 0) {
+ var rotation = degrees / 90;
+ var stdRotate = ' rotate(' + degrees + 'deg)';
+ stransforms['-moz-transform'] += stdRotate;
+ stransforms['-webkit-transform'] += stdRotate;
+ stransforms['-o-transform'] += stdRotate;
+ stransforms['-ms-transform'] += stdRotate;
+ }
+ var s = 'top: 0; left: 0; ';
+ for (var prop in stransforms) {
+ if (stransforms[prop]) {
+ s += prop + ':' + stransforms[prop] + ';';
+ }
+ }
+ s += ';';
+ return s;
+ };
+
+ CssTransformAxisLabel.prototype.calculateOffsets = function(box) {
+ var offsets = { x: 0, y: 0, degrees: 0 };
+ if (this.position == 'bottom') {
+ offsets.x = box.left + box.width/2 - this.labelWidth/2;
+ offsets.y = box.top + box.height - this.labelHeight;
+ } else if (this.position == 'top') {
+ offsets.x = box.left + box.width/2 - this.labelWidth/2;
+ offsets.y = box.top;
+ } else if (this.position == 'left') {
+ offsets.degrees = -90;
+ offsets.x = box.left - this.labelWidth/2 + this.labelHeight/2;
+ offsets.y = box.height/2 + box.top;
+ } else if (this.position == 'right') {
+ offsets.degrees = 90;
+ offsets.x = box.left + box.width - this.labelWidth/2
+ - this.labelHeight/2;
+ offsets.y = box.height/2 + box.top;
+ }
+ return offsets;
+ };
+
+ CssTransformAxisLabel.prototype.draw = function(box) {
+ this.plot.getPlaceholder().find("." + this.axisName + "Label").remove();
+ var offsets = this.calculateOffsets(box);
+ var elem = $('' + this.opts.axisLabel + '
');
+ this.plot.getPlaceholder().append(elem);
+ };
+
+
+ IeTransformAxisLabel.prototype = new CssTransformAxisLabel();
+ IeTransformAxisLabel.prototype.constructor = IeTransformAxisLabel;
+ function IeTransformAxisLabel(axisName, position, padding, plot, opts) {
+ CssTransformAxisLabel.prototype.constructor.call(this, axisName,
+ position, padding,
+ plot, opts);
+ this.requiresResize = false;
+ }
+
+ IeTransformAxisLabel.prototype.transforms = function(degrees, x, y) {
+ // I didn't feel like learning the crazy Matrix stuff, so this uses
+ // a combination of the rotation transform and CSS positioning.
+ var s = '';
+ if (degrees != 0) {
+ var rotation = degrees/90;
+ while (rotation < 0) {
+ rotation += 4;
+ }
+ s += ' filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=' + rotation + '); ';
+ // see below
+ this.requiresResize = (this.position == 'right');
+ }
+ if (x != 0) {
+ s += 'left: ' + x + 'px; ';
+ }
+ if (y != 0) {
+ s += 'top: ' + y + 'px; ';
+ }
+ return s;
+ };
+
+ IeTransformAxisLabel.prototype.calculateOffsets = function(box) {
+ var offsets = CssTransformAxisLabel.prototype.calculateOffsets.call(
+ this, box);
+ // adjust some values to take into account differences between
+ // CSS and IE rotations.
+ if (this.position == 'top') {
+ // FIXME: not sure why, but placing this exactly at the top causes
+ // the top axis label to flip to the bottom...
+ offsets.y = box.top + 1;
+ } else if (this.position == 'left') {
+ offsets.x = box.left;
+ offsets.y = box.height/2 + box.top - this.labelWidth/2;
+ } else if (this.position == 'right') {
+ offsets.x = box.left + box.width - this.labelHeight;
+ offsets.y = box.height/2 + box.top - this.labelWidth/2;
+ }
+ return offsets;
+ };
+
+ IeTransformAxisLabel.prototype.draw = function(box) {
+ CssTransformAxisLabel.prototype.draw.call(this, box);
+ if (this.requiresResize) {
+ var elem = this.plot.getPlaceholder().find("." + this.axisName + "Label");
+ // Since we used CSS positioning instead of transforms for
+ // translating the element, and since the positioning is done
+ // before any rotations, we have to reset the width and height
+ // in case the browser wrapped the text (specifically for the
+ // y2axis).
+ elem.css('width', this.labelWidth);
+ elem.css('height', this.labelHeight);
+ }
+ };
+
+
+ function init(plot) {
+ // This is kind of a hack. There are no hooks in Flot between
+ // the creation and measuring of the ticks (setTicks, measureTickLabels
+ // in setupGrid() ) and the drawing of the ticks and plot box
+ // (insertAxisLabels in setupGrid() ).
+ //
+ // Therefore, we use a trick where we run the draw routine twice:
+ // the first time to get the tick measurements, so that we can change
+ // them, and then have it draw it again.
+ var secondPass = false;
+
+ var axisLabels = {};
+ var axisOffsetCounts = { left: 0, right: 0, top: 0, bottom: 0 };
+
+ var defaultPadding = 2; // padding between axis and tick labels
+ plot.hooks.draw.push(function (plot, ctx) {
+ if (!secondPass) {
+ // MEASURE AND SET OPTIONS
+ $.each(plot.getAxes(), function(axisName, axis) {
+ var opts = axis.options // Flot 0.7
+ || plot.getOptions()[axisName]; // Flot 0.6
+ if (!opts || !opts.axisLabel || !axis.show)
+ return;
+
+ var renderer = null;
+
+ if (!opts.axisLabelUseHtml &&
+ navigator.appName == 'Microsoft Internet Explorer') {
+ var ua = navigator.userAgent;
+ var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
+ if (re.exec(ua) != null) {
+ rv = parseFloat(RegExp.$1);
+ }
+ if (rv >= 9 && !opts.axisLabelUseCanvas && !opts.axisLabelUseHtml) {
+ renderer = CssTransformAxisLabel;
+ } else if (!opts.axisLabelUseCanvas && !opts.axisLabelUseHtml) {
+ renderer = IeTransformAxisLabel;
+ } else if (opts.axisLabelUseCanvas) {
+ renderer = CanvasAxisLabel;
+ } else {
+ renderer = HtmlAxisLabel;
+ }
+ } else {
+ if (opts.axisLabelUseHtml || (!css3TransitionSupported() && !canvasTextSupported()) && !opts.axisLabelUseCanvas) {
+ renderer = HtmlAxisLabel;
+ } else if (opts.axisLabelUseCanvas || !css3TransitionSupported()) {
+ renderer = CanvasAxisLabel;
+ } else {
+ renderer = CssTransformAxisLabel;
+ }
+ }
+
+ var padding = opts.axisLabelPadding === undefined ?
+ defaultPadding : opts.axisLabelPadding;
+
+ axisLabels[axisName] = new renderer(axisName,
+ axis.position, padding,
+ plot, opts);
+
+ // flot interprets axis.labelHeight and .labelWidth as
+ // the height and width of the tick labels. We increase
+ // these values to make room for the axis label and
+ // padding.
+
+ axisLabels[axisName].calculateSize();
+
+ // AxisLabel.height and .width are the size of the
+ // axis label and padding.
+ axis.labelHeight += axisLabels[axisName].height;
+ axis.labelWidth += axisLabels[axisName].width;
+ opts.labelHeight = axis.labelHeight;
+ opts.labelWidth = axis.labelWidth;
+ });
+ // re-draw with new label widths and heights
+ secondPass = true;
+ plot.setupGrid();
+ plot.draw();
+ } else {
+ // DRAW
+ $.each(plot.getAxes(), function(axisName, axis) {
+ var opts = axis.options // Flot 0.7
+ || plot.getOptions()[axisName]; // Flot 0.6
+ if (!opts || !opts.axisLabel || !axis.show)
+ return;
+
+ axisLabels[axisName].draw(axis.box);
+ });
+ }
+ });
+ }
+
+
+ $.plot.plugins.push({
+ init: init,
+ options: options,
+ name: 'axisLabels',
+ version: '2.0b0'
+ });
+})(jQuery);
diff --git a/common/test/data/full/html/sound_labs/mosfet_amplifier.html b/common/test/data/full/html/sound_labs/mosfet_amplifier.html
deleted file mode 100644
index c7f2f1ad81..0000000000
--- a/common/test/data/full/html/sound_labs/mosfet_amplifier.html
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
-
-
-
-LAB 5B: MOSFET AMPLIFIER EXPERIMENT
-
-
-Note: This part of the lab is just to develop your intuition about
-amplifiers and biasing, and to have fun with music! There are no responses
-that need to be checked.
-The graph plots the selected voltages from the amplifier circuit below. You
-can also listen to various signals by selecting from the radio buttons to
-the right of the graph. This way you can both see and hear various signals.
-You can use the sliders to the right of the amplifier circuit to control
-various parameters of the MOSFET and the amplifier. The parameter \(V_{MAX}\)
-sets the maximum range on the plots. You can also select an input voltage
-type (e.g., sine wave, square wave, various types of music) using the drop
-down menu to the right of the graph. When describing AC signals, the
-voltages on the sliders refer to peak-to-peak values.
-1. To begin your first experiment, go ahead and use the pull down menu to
-select a sine wave input. Then, adjust the sliders to an approximate
-baseline setting shown below.
-Baseline setting of sliders:
-
-\(V_{S}=1.6V\), \(v_{IN}=3V\), \(Frequency=1000Hz\), \(V_{BIAS}=2.5V\), \(R=10K\Omega\), \(k=1mA/V^{2}\), \(V_{T}=1V\), \(V_{MAX}=2V\).
-You will observe in the plot that the baseline setting of the sliders for
-the various amplifiers parameters produces a distorted sine wave signal for
-\(v_{OUT}\). Next, go ahead and select one of the music signals as the input and
-listen to each of \(v_{IN}\) and \(v_{OUT}\), and confirm for yourself that the
-output sounds distorted for the chosen slider settings. You will notice
-that the graph now plots the music signal waveforms. Think about all the
-reasons why the amplifier is producing a distorted output.
-2. For the second experiment, we will study the amplifier's small signal
-behavior. Select a sine wave as the input signal. To study the small
-signal behavior, reduce the value of \(v_{IN}\) to 0.1V (peak-to-peak) by
-using the \(v_{IN}\) slider. Keeping the rest of the parameters at their
-baseline settings, derive an appropriate value of \(V_{BIAS}\) that will ensure
-saturation region operation for the MOSFET for the 0.1V peak-to-peak swing
-for \(v_{IN}\). Make sure to think about both positive and negative excursions
-of the signals.
-Next, use the \(V_{BIAS}\) slider to choose your computed value for \(V_{BIAS}\) and
-see if the observed plot of \(v_{OUT}\) is more or less distortion free. If
-your calculation was right, then the output will indeed be distortion free.
-Next, select one of the music signals as the input and listen to each of
-\(v_{IN}\) and \(v_{OUT}\), and confirm for yourself that the output sounds much
-better than in Step 1. Also, based on sound volume, confirm that \(v_{OUT}\) is
-an amplified version of \(v_{IN}\).
-3. Now go ahead and experiment with various other settings while listening
-to the music signal at \(v_{OUT}\). Observe the plots and listen to \(v_{OUT}\) as
-you change, for example, the bias voltage \(V_{BIAS}\). You will notice that
-the amplifier distorts the input signal when \(V_{BIAS}\) becomes too small, or
-when it becomes too large. You can also experiment with various values of
-\(v_{IN}\), \(R_{L}\), etc., and see how they affect the amplification and distortion.
-
-
-
-
diff --git a/common/test/data/full/html/sound_labs/rc_filters.html b/common/test/data/full/html/sound_labs/rc_filters.html
deleted file mode 100644
index c52f8022a4..0000000000
--- a/common/test/data/full/html/sound_labs/rc_filters.html
+++ /dev/null
@@ -1,111 +0,0 @@
-
-
-
-
-LAB 10B: RC FILTERS WITH FREQUENCY RESPONSE EXPERIMENT
-
-
-Note: Use this part of the lab to build your intuition about filters and frequency response, and to have fun with music! There are no responses that need to be checked.
-Recall from the audio lab in Week 5 that the graph plots the selected voltages from the circuit shown below. This week the circuit is an RC filter. You can also listen to various signals by selecting from the radio buttons to the right of the graph. This way you can both see and hear various signals. You can use the sliders to the right of the circuit to control various circuit and input signal parameters. (Note that you can get finer control of some of the slider values by clicking on the slider and using the arrow keys). Recall that the parameter \(V_{MAX}\) sets the maximum range on the graph. You can also select an input voltage type (e.g., sine wave, square wave, various types of music) using the drop down menu to the right of the graph. When describing AC signals, the voltages on the sliders refer to peak-to-peak values.
-1. To begin your first experiment, use the pull down menu to select a sine wave input. Then, adjust the sliders to these approximate baseline settings:
-
-\(v_{IN} = 3V\), \(Frequency = 1000 Hz\), \(V_{BIAS} = 0V\), \(R = 1K\Omega\), \(v_C(0) = 0V\), \(C = 110nF\), \(V_{MAX} = 2V\).
-
-Observe the waveforms for \(v_{IN}\) and \(v_C\) in the graph. You can also listen to \(v_{IN}\) and \(v_C\). You will observe that the amplitude of \(v_C\) is slightly smaller than the amplitude of \(v_{IN}\).
-
-Compute the break frequency of the filter circuit for the given circuit parameters. (Note that the break frequency is also called the cutoff frequency or the corner frequency).
-
-Change the frequency of the sinusoid so that it is approximately 3 times the break frequency.
-
-Observe the waveforms for \(v_{IN}\) and \(v_C\) in the graph. Also listen to \(v_{IN}\) and \(v_C\). Think about why the sinusoid at \(v_C\) is significantly more attenuated than the original 1KHz sinusoid.
-
-Keeping the input signal unchanged, observe the waveforms for \(v_{IN}\) and \(v_R\) in the graph. Also listen to \(v_{IN}\) and \(v_R\). Think about why the sinusoid at \(v_R\) is significantly louder than the sinusoid at \(v_C\).
-2. Next, use the pull down menu to select a music signal of your choice. Adjust the sliders to the approximate baseline settings:
-
-\(v_{IN} = 3V\), \(V_{BIAS} = 0V\), \(R = 1K\Omega\), \(v_C(0) = 0V\), \(C = 110nF\), \(V_{MAX} = 2V\).
-
-Listen to the signals at \(v_{IN}\) and \(v_C\). Notice any difference between the signals?
-
-Next, increase the capacitance value and observe the difference in the sound of \(v_{IN}\) and \(v_C\) as the capacitance increases. You should notice that the higher frequency components of \(v_C\) are attenuated as the capacitance is increased.
-Convince yourself that when the signal is taken at \(v_C\), the circuit behaves like a low-pass filter.
-3. Re-adjust the sliders to the approximate baseline settings:
-
-\(v_{IN} = 3V\), \(V_{BIAS} = 0V\), \(R = 1K\Omega\), \(v_C(0) = 0V\), \(C = 110nF\), \(V_{MAX} = 2V\).
-
-Try to create a high-pass filter from the same circuit by taking the signal output across a different element and possibly changing some of the element values.
-
-
-
-
-
diff --git a/common/test/data/full/html/sound_labs/series_rlc.html b/common/test/data/full/html/sound_labs/series_rlc.html
deleted file mode 100644
index 4856d5c5ed..0000000000
--- a/common/test/data/full/html/sound_labs/series_rlc.html
+++ /dev/null
@@ -1,111 +0,0 @@
-
-
-
-
-SERIES RLC CIRCUIT WITH FREQUENCY RESPONSE EXPERIMENT
-
-
-\(I(s) = \frac{1}{R + Ls + 1/Cs}V_{in}(s) = \frac{s/L}{s^2 + sR/L + 1/LC}V_{in}(s)\)
-\(I(s) = \frac{s/L}{s^2 + 2\alpha s + \omega_0^2}V_{in}(s)\)
-\(\omega_0 = \frac{1}{\sqrt{LC}} , \alpha = \frac{R}{2L}\)
-Band-Pass Filter:
-\(V_r(s) = RI(s) = \frac{sR/L}{s^2 + 2\alpha s + \omega_0^2}V_{in}(s) = \frac{2\alpha s}{s^2 + 2\alpha s + \omega_0^2}V_{in}(s) = \frac{2\alpha s}{(s-s_1)(s-s_2)}V_{in}(s)\)
-Gain magnitude: \(G_R = \frac{2\alpha w}{|j\omega - s_1||j\omega - s_2|}\)
-Phase: \(\Phi_R = \pi/2-\Phi(j\omega - s_1) -\Phi(j\omega - s_2)\)
-Low-Pass Filter:
-\(V_c(s) = I(s)/sC = \frac{1/LC}{s^2 + 2\alpha s + \omega_0^2}V_{in}(s) = \frac{\omega_0^2}{s^2 + 2\alpha s + \omega_0^2}V_{in}(s) = \frac{\omega_0^2}{(s-s_1)(s-s_2)}V_{in}(s)\)
-Gain magnitude: \(G_C = \frac{\omega_0^2}{|j\omega - s_1||j\omega - s_2|}\)
-Phase: \(\Phi_C = -\Phi(j\omega - s_1) -\Phi(j\omega - s_2)\)
-High-Pass Filter:
-\(V_l(s) = sLI(s) = \frac{s^2}{s^2 + 2\alpha s + \omega_0^2}V_{in}(s) = \frac{s^2}{(s-s_1)(s-s_2)}V_{in}(s)\)
-Gain magnitude: \(G_L = \frac{\omega^2}{|j\omega - s_1||j\omega - s_2|}\)
-Phase: \(\Phi_L = -\Phi(j\omega - s_1) -\Phi(j\omega - s_2)\)
-
-Under-Damped: \(\alpha < \omega_0\)
-Complex roots: \(s_{1,2} = -\alpha \pm j\sqrt{\omega_0^2 - \alpha^2}\)
-Critically-Damped: \(\alpha = \omega_0\)
-Double real root: \(s_{1,2} = -\alpha\)
-Over-Damped: \(\alpha > \omega_0\)
-Real roots: \(s_{1,2} = -\alpha \pm\sqrt{\alpha^2 - \omega_0^2}\)
-
-
-
-
diff --git a/common/test/data/full/circuits/120V60Hz.gif b/common/test/data/full/static/circuits/120V60Hz.gif
similarity index 100%
rename from common/test/data/full/circuits/120V60Hz.gif
rename to common/test/data/full/static/circuits/120V60Hz.gif
diff --git a/common/test/data/full/circuits/Lab1_1.png b/common/test/data/full/static/circuits/Lab1_1.png
similarity index 100%
rename from common/test/data/full/circuits/Lab1_1.png
rename to common/test/data/full/static/circuits/Lab1_1.png
diff --git a/common/test/data/full/circuits/duality.gif b/common/test/data/full/static/circuits/duality.gif
similarity index 100%
rename from common/test/data/full/circuits/duality.gif
rename to common/test/data/full/static/circuits/duality.gif
diff --git a/common/test/data/full/circuits/heaters-bad.gif b/common/test/data/full/static/circuits/heaters-bad.gif
similarity index 100%
rename from common/test/data/full/circuits/heaters-bad.gif
rename to common/test/data/full/static/circuits/heaters-bad.gif
diff --git a/common/test/data/full/circuits/heaters-parallel.gif b/common/test/data/full/static/circuits/heaters-parallel.gif
similarity index 100%
rename from common/test/data/full/circuits/heaters-parallel.gif
rename to common/test/data/full/static/circuits/heaters-parallel.gif
diff --git a/common/test/data/full/handouts/schematic_tutorial.pdf b/common/test/data/full/static/handouts/schematic_tutorial.pdf
similarity index 100%
rename from common/test/data/full/handouts/schematic_tutorial.pdf
rename to common/test/data/full/static/handouts/schematic_tutorial.pdf
diff --git a/common/test/data/full/js/cktsim.js b/common/test/data/full/static/js/cktsim.js
similarity index 100%
rename from common/test/data/full/js/cktsim.js
rename to common/test/data/full/static/js/cktsim.js
diff --git a/common/test/data/full/js/schematic.js b/common/test/data/full/static/js/schematic.js
similarity index 100%
rename from common/test/data/full/js/schematic.js
rename to common/test/data/full/static/js/schematic.js
diff --git a/common/test/data/full/static/js/sound_labs/circuit.js b/common/test/data/full/static/js/sound_labs/circuit.js
deleted file mode 100644
index dfd756a4a2..0000000000
--- a/common/test/data/full/static/js/sound_labs/circuit.js
+++ /dev/null
@@ -1,1247 +0,0 @@
-var Circuit = (function() {
-
- var Color =
- {
- background : "rgb(0, 51, 102)", //0.0, 0.2, 0.4
- black : "rgb(0, 0, 0)", //0.0
- lodarkgray : "rgb(26, 26, 26)", //0.1 = 25.5
- darkgray : "rgb(51, 51, 51)", //0.2
- lomidgray : "rgb(102, 102, 102)", //0.4
- midgray : "rgb(128, 128, 128)", //0.5 = 127.5
- himidgray : "rgb(153, 153, 153)", //0.6
- litegray : "rgb(204, 204, 204)", //0.8
- white : "rgb(255, 255, 255)", //1.0
-
- red : "rgb(255, 0, 0)",
- green : "rgb(0, 255, 0)",
- blue : "rgb(0, 0, 255)",
- yellow : "rgb(255, 255, 0)",
- cyan : "rgb(0, 255, 255)",
- magenta : "rgb(255, 0, 255)"
- };
-
- var Utils =
- {
- TWO_PI: 2.0*Math.PI,
- PI_DIV_2: Math.PI/2.0
- };
-
- function distance(x1, y1, x2, y2)
- {
- var dx = x2 - x1;
- var dy = y2 - y1;
-
- return Math.sqrt(dx * dx + dy * dy);
- }
-
- function transform(x, y, xt, yt, rot)
- {
- //First translate
- x -= xt;
- y -= yt;
- //Then rotate
- return {x: x * Math.cos(rot) - y * Math.sin(rot), y: x * Math.sin(rot) + y * Math.cos(rot)};
- }
-
- function closestGridPoint(gridStep, x)
- {
- return gridStep * Math.round(x / gridStep);
- }
-
- function getMousePosition(diagram, event)
- {
- var mouseX = event.pageX - (parseInt(diagram.element.offset().left) + parseInt(diagram.element.css('paddingLeft')) + parseInt(diagram.element.css('borderLeftWidth')));
- var mouseY = event.pageY - (parseInt(diagram.element.offset().top) + parseInt(diagram.element.css('paddingTop')) + parseInt(diagram.element.css('borderTopWidth')));
- return {x : mouseX, y : mouseY};
- }
-
- function diagramMouseDown(event)
- {
- if (!event) event = window.event;
- else event.preventDefault();
- var canvas = (window.event) ? event.srcElement : event.target;
- var diagram = canvas.diagram;
- var mpos = getMousePosition(diagram, event);
-
- for(var i = 0, len = diagram.components.length; i < len; i++)
- {
- if(diagram.components[i].isInside(mpos.x, mpos.y))
- {
- diagram.components[i].selected = true;
- diagram.startx = closestGridPoint(diagram.gridStep, mpos.x);
- diagram.starty = closestGridPoint(diagram.gridStep, mpos.y);
- }
- }
-
- return false;
- }
-
- function diagramMouseMove(event)
- {
- if (!event) event = window.event;
- else event.preventDefault();
- var canvas = (window.event) ? event.srcElement : event.target;
- var diagram = canvas.diagram;
- var mpos = getMousePosition(diagram, event);
- var componentSelected = false;
-
- //First check if any component if selected
- for(var i = 0, len = diagram.components.length; i < len; i++)
- {
- if(diagram.components[i].selected)
- {
- diagram.endx = closestGridPoint(diagram.gridStep, mpos.x);
- diagram.components[i].x += (diagram.endx - diagram.startx);
- diagram.startx = diagram.endx;
- diagram.endy = closestGridPoint(diagram.gridStep, mpos.y);
- diagram.components[i].y += (diagram.endy - diagram.starty);
- diagram.starty = diagram.endy;
- diagram.paint();
- componentSelected = true;
- }
- }
-
- if(!componentSelected)
- {
- for(var i = 0, len = diagram.components.length; i < len; i++)
- {
- if(diagram.components[i].isInside(mpos.x, mpos.y))
- diagram.components[i].selectable = true;
- else
- diagram.components[i].selectable = false;
- //Repaint only once, on a mouse enter or mouse leave
- if(diagram.components[i].previousSelectable != diagram.components[i].selectable)
- {
- diagram.components[i].previousSelectable = diagram.components[i].selectable;
- diagram.paint();
- }
- }
- }
-
- return false;
- }
-
- function diagramMouseUp(event)
- {
- if (!event) event = window.event;
- else event.preventDefault();
- var canvas = (window.event) ? event.srcElement : event.target;
- var diagram = canvas.diagram;
- var mpos = getMousePosition(diagram, event);
-
- for(var i = 0, len = diagram.components.length; i < len; i++)
- {
- //Unselect all
- diagram.components[i].selected = false;
- }
- diagram.startx = 0;
- diagram.endx = diagram.startx;
- diagram.starty = 0;
- diagram.endx = diagram.starty;
-
- return false;
- }
-
- function diagramDoubleClick(event)
- {
- if (!event) event = window.event;
- else event.preventDefault();
- var canvas = (window.event) ? event.srcElement : event.target;
- var diagram = canvas.diagram;
-
- alert(diagram.toString());
-
- return false;
- }
-
- function copyPrototype(descendant, parent)
- {
- var sConstructor = parent.toString();
- var aMatch = sConstructor.match(/\s*function (.*)\(/);
- if(aMatch != null)
- {
- descendant.prototype[aMatch[1]] = parent;
- }
- for(var m in parent.prototype)
- {
- descendant.prototype[m] = parent.prototype[m];
- }
- }
-
- function Diagram(element, frozen)
- {
- this.element = element;
- this.frozen = frozen;
- this.canvas = element[0];
- this.canvas.diagram = this;
- this.width = this.canvas.width;
- this.height = this.canvas.height;
- this.ctx = this.canvas.getContext("2d");
- this.background = Color.black;
- if (!this.frozen)
- {
- this.canvas.addEventListener('mousedown', diagramMouseDown, false);
- this.canvas.addEventListener('mousemove', diagramMouseMove, false);
- this.canvas.addEventListener('mouseup', diagramMouseUp, false);
- this.canvas.addEventListener('dblclick', diagramDoubleClick, false);
- }
- //To disable text selection outside the canvas
- this.canvas.onselectstart = function(){return false;};
- this.components = [];
- this.gridStep = 5;
- this.startx = 0;
- this.endx = 0;
- this.starty = 0;
- this.endy = 0;
- this.showGrid = false;
- this.xGridMin = 10;
- this.xGridMax = 500;
- this.yGridMin = 10;
- this.yGridMax = 500;
- this.xOrigin = 0;
- this.yOrigin = 0;
- this.scale = 2; //Scaling is the same in x and y directions
- this.fontSize = 6;
- this.fontType = 'sans-serif';
- }
-
- Diagram.prototype.toString = function()
- {
- var result = "";
- for(var i = 0, len = this.components.length; i < len; i++)
- {
- result += this.components[i].toString();
- }
-
- return result;
- }
-
- Diagram.prototype.addNode = function(x, y)
- {
- var n = new Node(x, y);
- n.ctx = this.ctx;
- n.diagram = this;
- n.updateBoundingBox();
- this.components.push(n);
- return n;
- }
-
- Diagram.prototype.addWire = function(x1, y1, x2, y2)
- {
- var w = new Wire(x1, y1, x2, y2)
- w.ctx = this.ctx;
- w.diagram = this;
- w.updateBoundingBox();
- this.components.push(w);
- return w;
- }
-
- Diagram.prototype.addLabel = function(x, y, value, textAlign)
- {
- var l = new Label(x, y, value, textAlign)
- l.ctx = this.ctx;
- l.diagram = this;
- l.updateBoundingBox();
- this.components.push(l);
- return l;
- }
-
- Diagram.prototype.addResistor = function(x, y, value)
- {
- var r = new Resistor(x, y, value)
- r.ctx = this.ctx;
- r.diagram = this;
- r.updateBoundingBox();
- this.components.push(r);
- return r;
- }
-
- Diagram.prototype.addInductor = function(x, y, value)
- {
- var l = new Inductor(x, y, value)
- l.ctx = this.ctx;
- l.diagram = this;
- l.updateBoundingBox();
- this.components.push(l);
- return l;
- }
-
- Diagram.prototype.addCapacitor = function(x, y, value)
- {
- var c = new Capacitor(x, y, value)
- c.ctx = this.ctx;
- c.diagram = this;
- c.updateBoundingBox();
- this.components.push(c);
- return c;
- }
-
- Diagram.prototype.addMosfet = function(x, y, value, type)
- {
- var m = new Mosfet(x, y, value, type)
- m.ctx = this.ctx;
- m.diagram = this;
- m.updateBoundingBox();
- this.components.push(m);
- return m;
- }
-
- Diagram.prototype.addGround = function(x, y)
- {
- var g = new Ground(x, y)
- g.ctx = this.ctx;
- g.diagram = this;
- g.updateBoundingBox();
- this.components.push(g);
- return g;
- }
-
- Diagram.prototype.addDiode = function(x, y, value)
- {
- var d = new Diode(x, y, value)
- d.ctx = this.ctx;
- d.diagram = this;
- d.updateBoundingBox();
- this.components.push(d);
- return d;
- }
-
- Diagram.prototype.addSource = function(x, y, value, type)
- {
- var v = new Source(x, y, value, type)
- v.ctx = this.ctx;
- v.diagram = this;
- v.updateBoundingBox();
- this.components.push(v);
- return v;
- }
-
- Diagram.prototype.paint = function()
- {
- this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
- if (this.showGrid)
- this.drawGrid();
-
- for(var i = 0, len = this.components.length; i < len; i++)
- {
- this.components[i].paint();
- }
- }
-
- Diagram.prototype.drawGrid = function()
- {
- this.ctx.fillStyle = Color.black;
- for(x = this.xGridMin; x <= this.xGridMax; x += this.gridStep)
- {
- for( y = this.yGridMin; y <= this.yGridMax; y += this.gridStep)
- {
- this.drawPixel(this.ctx, x, y);
- }
- }
- }
- //Drawing routines from schematic
- Diagram.prototype.drawLine = function(c, x1, y1, x2, y2)
- {
- c.beginPath();
- c.moveTo((x1 - this.xOrigin) * this.scale, (y1 - this.yOrigin) * this.scale);
- c.lineTo((x2 - this.xOrigin) * this.scale, (y2 - this.yOrigin) * this.scale);
- c.stroke();
- }
-
- Diagram.prototype.drawArc = function(c, x, y, radius,startRadians, endRadians, anticlockwise, width, filled)
- {
- c.lineWidth = width;
- c.beginPath();
- c.arc((x - this.xOrigin)*this.scale, (y - this.yOrigin)*this.scale, radius*this.scale, startRadians, endRadians, anticlockwise);
- if (filled) c.fill();
- else c.stroke();
- }
-
- Diagram.prototype.drawCircle = function(c, x, y, radius, filled)
- {
- this.drawArc(c, x, y, radius, 0, 2*Math.PI, false, 1, filled);
- }
-
- Diagram.prototype.drawText = function(c, str, x, y)
- {
- c.font = this.scale*this.fontSize + "pt " + this.fontType;
- c.fillText(str, (x - this.xOrigin) * this.scale, (y - this.yOrigin) * this.scale);
- }
- //End drawing routines
-
- Diagram.prototype.parseSubSuperScriptText = function(str)
- {
- /*var regExpSub = /_\{(.*?)\}/g;
- var regExpSup = /\^\{(.*?)\}/g;
- var subs = [];
- var sups = [];
- var text = [];
- var finalText = [];
- var isSub = false;
- var isSup = false;
-
- subs = str.match(regExpSub);
- for (var i = 0; i < subs.length; i++)
- {
- subs[i] = subs[i].substring(2, subs[i].length - 1); //Discard _{ and }
- }
-
- sups = str.match(regExpSup);
- for (var i = 0; i < sups.length; i++)
- {
- sups[i] = sups[i].substring(2, sups[i].length - 1); //Discard ^{ and }
- }*/
-
- var len = str.length;
- var i = 0;
- var start;
- var end;
- found = false;
- var text = [];
- var type;
- var ntext = "";
-
- while (i < len)
- {
- if (str[i] == "_") //Encountered a potential subscript _
- type = "sub";
- else if (str[i] == "^") //Encountered a potential superscript ^
- type = "sup";
-
- if (type == "sub" || type == "sup")
- {
- if (str[i+1] == "{")
- {
- i += 2; //Discard _{ or ^{
- start = i;
- found = false;
- while (i < len) //Look for }
- {
- if (str[i] == "}")
- {
- found = true;
- end = i;
- break;
- }
- i++;
- }
- if (found && end > start) //Discard empty subscript ie _{}
- {
- //Store previous normal text if not empty and tag it as so
- if (ntext.length != 0)
- {
- text.push({s: ntext, type: "normal"});
- ntext = "";
- }
- //Store subscript or superscript and tag it as so
- if (type == "sub")
- text.push({s: str.substring(start, end), type: "sub"});
- else if (type == "sup")
- text.push({s: str.substring(start, end), type: "sup"});
- i = end + 1;
- }
- else
- i = start - 2; //Nothing was found, backtrack to _ or ^
- }
- }
- ntext += str[i];
- if (i == len - 1 && ntext.length != 0) //We've reached the end, store normal text if not empty and tag it as so
- text.push({s: ntext, type: "normal"});
- i++;
- }
-
- return text;
- }
-
- Diagram.prototype.subSuperScriptLength = function(c, text)
- {
- var fontNormal = this.scale*this.fontSize + "pt " + this.fontType;
- var fontSubSup = this.scale*(this.fontSize-2) + "pt " + this.fontType;
-
- var xpos = 0;
-
- for (var i = 0; i < text.length; i++)
- {
- if (text[i].type == "normal")
- c.font = fontNormal;
- else if (text[i].type == "sub")
- c.font = fontSubSup;
- else
- c.font = fontSubSup;
- xpos += c.measureText(text[i].s).width;
- }
-
- return xpos;
- }
-
- Diagram.prototype.drawSubSuperScript = function(c, str, x, y, way)
- {
- var fontNormal = this.scale*this.fontSize + "pt " + this.fontType;
- var fontSubSup = this.scale*(this.fontSize-2) + "pt " + this.fontType;
-
- var text = this.parseSubSuperScriptText(str);
- var len = this.subSuperScriptLength(c, text);
- var xposIni = (x - this.xOrigin) * this.scale;
- var yposIni = (y - this.yOrigin) * this.scale;
- var xpos, ypos;
-
- if (way == "left")
- xpos = xposIni;
- else if (way == "right")
- xpos = xposIni - len;
- else if (way == "center")
- xpos = xposIni - len/2;
-
- //Draw the text
- for (var i = 0; i < text.length; i++)
- {
- if (text[i].type == "normal")
- {
- c.font = fontNormal;
- ypos = yposIni;
- }
- else if (text[i].type == "sub")
- {
- c.font = fontSubSup;
- ypos = yposIni + 3;
- }
- else
- {
- c.font = fontSubSup;
- ypos = yposIni - 5;
- }
- c.fillText(text[i].s, xpos, ypos);
- //Advance x position
- xpos += c.measureText(text[i].s).width;
- }
- }
-
- //Draws a rectangle, top left corner x1, y1 and bottom right corner x2, y2
- Diagram.prototype.drawCrispLine = function(c, x1, y1, x2, y2)
- {
- c.beginPath();
- c.moveTo(x1 + 0.5, y1 + 0.5);
- c.lineTo(x2 + 0.5, y2 + 0.5);
- c.stroke();
- }
-
- Diagram.prototype.drawRect = function(c, x1, y1, x2, y2)
- {
- c.strokeRect(x1 + 0.5, y1 + 0.5, x2 - x1 + 1.0, y2 - y1 + 1.0);
- }
-
- Diagram.prototype.fillRect = function(c, x1, y1, x2, y2)
- {
- c.fillRect(x1, y1, x2 - x1 + 1.0, y2 - y1 + 1.0);
- }
-
- Diagram.prototype.clearRect = function(c, x1, y1, x2, y2)
- {
- c.clearRect(x1 + 0.5, y1 + 0.5, x2 - x1 + 1.0, y2 - y1 + 1.0);
- }
-
- Diagram.prototype.drawPixel = function(c, x, y)
- {
- c.fillRect(x, y, 1.0, 1.0);
- }
-
- Diagram.prototype.drawPoint = function(c, x, y, radius)
- {
- c.beginPath();
- c.arc(x + 0.5, y + 0.5, radius, 0, Utils.TWO_PI, true); //Last param is anticlockwise
- c.fill();
- }
-
- Diagram.prototype.drawHollowPoint = function(c, x, y, radius)
- {
- c.beginPath();
- c.arc(x + 0.5, y + 0.5, radius, 0, Utils.TWO_PI, true); //Last param is anticlockwise
- c.stroke();
- }
-
- Diagram.prototype.drawTriangle = function(c, x1, y1, x2, y2, x3, y3)
- {
- c.beginPath();
- c.moveTo(x1 + 0.5, y1 + 0.5);
- c.lineTo(x2 + 0.5, y2 + 0.5);
- c.lineTo(x3 + 0.5, y3 + 0.5);
- c.closePath();
- c.stroke();
- }
-
- Diagram.prototype.fillTriangle = function(c, x1, y1, x2, y2, x3, y3)
- {
- c.beginPath();
- c.moveTo(x1 + 0.5, y1 + 0.5);
- c.lineTo(x2 + 0.5, y2 + 0.5);
- c.lineTo(x3 + 0.5, y3 + 0.5);
- c.closePath();
- c.fill();
- }
-
- Diagram.prototype.drawHalfCircle = function(c, x, y, radius, concaveDown) //For inductance only
- {
- c.beginPath();
- if (concaveDown)
- c.arc(x + 0.5, y + 0.5, radius, 0, Math.PI, true); //Last param is anticlockwise
- else
- c.arc(x + 0.5, y + 0.5, radius, Math.PI, 0, true); //Last param is anticlockwise
- c.stroke();
- }
-
- Diagram.prototype.drawDiamond = function(c, x, y, h)
- {
- var xc = x + 0.5;
- var yc = y + 0.5;
-
- c.beginPath();
- c.moveTo(xc-h, yc);
- c.lineTo(xc, yc-h);
- c.lineTo(xc+h, yc);
- c.lineTo(xc, yc+h);
- c.closePath();
-
- c.fill();
- }
-
- Diagram.prototype.drawX = function(c, x, y, h)
- {
- var xc = x + 0.5;
- var yc = y + 0.5;
-
- c.beginPath();
- c.moveTo(xc+h, yc-h);
- c.lineTo(xc-h, yc+h);
- c.moveTo(xc-h, yc-h);
- c.lineTo(xc+h, yc+h);
- c.stroke();
- }
-
- Diagram.prototype.drawArrow = function(c, x1, y1, x2, y2, base, height)
- {
- var xs1 = x1 + 0.5;
- var ys1 = y1 + 0.5;
- var xs2 = x2 + 0.5;
- var ys2 = y2 + 0.5;
- var xv = x2 - x1;
- var yv = y2 - y1;
- var ang = Math.atan2(-yv, xv);
-
- c.beginPath();
- //Arrow line
- c.moveTo(xs1, ys1);
- c.lineTo(xs2, ys2);
- c.stroke();
- //Arrow head, first draw a triangle with top on origin then translate/rotate to orient and fit on line
- c.save();
- c.beginPath();
- c.translate(xs2, ys2);
- c.rotate(Utils.PI_DIV_2-ang);
-
- c.moveTo(0, 0);
- c.lineTo(-base, height);
- c.lineTo(base, height);
- c.closePath();
- c.fill();
- //c.stroke();
- c.restore();
- }
-
- //***** COMPONENT *****//
- function Component(x, y, width, height)
- {
- this.x = x;
- this.y = y;
-
- this.boundingBox = [0, 0, 0, 0];
- this.transBoundingBox = [0, 0, 0, 0];
- this.xMiddle = 0;
- this.yMiddle = 0;
-
- this.previousSelectable = false;
- this.selectable = false;
- this.selected = false;
- this.ctx;
- this.diagram;
- this.color = Color.white;
- this.selectedColor = Color.red;
- this.eventListeners = {};
- //Label to the left
- this.label = {str: "", x: 0, y: 0, position: "left", show: true, color: Color.white}; //color: Color.lodarkgray
- //String representing value to the right
- this.valueString = {x: 0, y: 0, position: "right", show: true, suffix: "", decimal: -1, color: Color.white}; //color: Color.lodarkgray
-
- this.lineWidth = 1;
- this.rotation = 0;
- this.value = 0;
- }
-
- Component.prototype.addEventListener = function(type, eventListener)
- {
- if(!(type in this.eventListeners))
- this.eventListeners[type] = eventListener;
- }
-
- Component.prototype.removeEventListener = function(type, eventListener)
- {
- for(var i in this.eventListeners)
- {
- if(this.eventListeners[i] === eventListener)
- delete this.eventListeners[i].eventListener;
- }
- }
-
- Component.prototype.fireEvent = function(event)
- {
- if( typeof event == "string")
- (this.eventListeners[event])();
- else
- throw new Error("Event object missing 'type' property.");
- }
-
- Component.prototype.updateBoundingBox = function()
- {
- //Apply global transform
- this.transBoundingBox[0] = (this.boundingBox[0] - this.diagram.xOrigin) * this.diagram.scale;
- this.transBoundingBox[1] = (this.boundingBox[1] - this.diagram.yOrigin) * this.diagram.scale;
- this.transBoundingBox[2] = (this.boundingBox[2] - this.diagram.xOrigin) * this.diagram.scale;
- this.transBoundingBox[3] = (this.boundingBox[3] - this.diagram.yOrigin) * this.diagram.scale;
- //this.getMiddle();
- this.label.x = this.transBoundingBox[0]- 5;
- this.label.y = (this.transBoundingBox[3] - this.transBoundingBox[1]) / 2;
- this.valueString.x = this.transBoundingBox[2] + 5;
- this.valueString.y = (this.transBoundingBox[3] - this.transBoundingBox[1]) / 2;
- }
-
- Component.prototype.initPaint = function()
- {
- if(this.selectable)
- {
- this.ctx.strokeStyle = this.selectedColor;
- this.ctx.fillStyle = this.selectedColor;
- }
- else
- {
- this.ctx.strokeStyle = this.color;
- this.ctx.fillStyle = this.color;
- }
- }
-
- Component.prototype.transform = function()
- {
- this.ctx.translate(this.x, this.y);
- if(this.rotation != 0)
- this.ctx.rotate(-this.rotation);
- }
-
- Component.prototype.getMiddle = function()
- {
- this.xMiddle = (this.boundingBox[2] - this.boundingBox[0]) / 2;
- this.yMiddle = (this.boundingBox[3] - this.boundingBox[1]) / 2;
- }
-
- Component.prototype.drawLabel = function()
- {
- if (this.label.show)
- {
- var textAlign;
- this.ctx.save();
- this.ctx.fillStyle = this.label.color;
- this.ctx.textAlign = "left";
- if (this.rotation == 0) //Component is vertical
- {
- if (this.label.position == "left") //Label is on left
- {
- this.ctx.textBaseline = "middle";
- textAlign = "right";
- }
- else if (this.label.position == "right") //Label is on right
- {
- this.ctx.textBaseline = "middle";
- textAlign = "left";
- }
- }
- else if (this.rotation == Math.PI/2) //Component is horizontal
- {
- if (this.label.position == "left") //Label now on bottom
- {
- this.ctx.textBaseline = "top";
- textAlign = "center";
- }
- else if (this.label.position == "right") //Label on top
- {
- this.ctx.textBaseline = "bottom";
- textAlign = "center";
- }
- }
- else if (this.rotation == Math.PI) //Component is horizontal
- {
- if (this.label.position == "left") //Label now on right
- {
- this.ctx.textBaseline = "middle";
- textAlign = "left";
- }
- else if (this.label.position == "right") //Label now on left
- {
- this.ctx.textBaseline = "middle";
- textAlign = "right";
- }
- }
- else if (this.rotation == 2*Math.PI/3) //Component is vertical
- {
- if (this.label.position == "left") //Label is on right
- {
- this.ctx.textBaseline = "middle";
- textAlign = "left";
- }
- else if (this.label.position == "right") //Label is on right
- {
- this.ctx.textBaseline = "middle";
- textAlign = "right";
- }
- }
- this.ctx.translate(this.label.x, this.label.y);
- this.ctx.rotate(this.rotation);
- this.diagram.drawSubSuperScript(this.ctx, this.label.str, 0, 0, textAlign);
- this.ctx.restore();
- }
- }
-
- Component.prototype.drawValueString = function()
- {
- if (this.valueString.show)
- {
- var textAlign;
- this.ctx.save();
- this.ctx.fillStyle = this.valueString.color;
- this.ctx.textAlign = "left";
- if (this.rotation == 0) //Component is vertical
- {
- if (this.valueString.position == "left") //Label is on left
- {
- this.ctx.textBaseline = "middle";
- textAlign = "right";
- }
- else if (this.valueString.position == "right") //Label is on right
- {
- this.ctx.textBaseline = "middle";
- textAlign = "left";
- }
- }
- else if (this.rotation == Math.PI/2) //Component is horizontal
- {
- if (this.valueString.position == "left") //Label now on bottom
- {
- this.ctx.textBaseline = "top";
- textAlign = "center";
- }
- else if (this.valueString.position == "right") //Label on top
- {
- this.ctx.textBaseline = "bottom";
- textAlign = "center";
- }
- }
- else if (this.rotation == Math.PI) //Component is horizontal
- {
- if (this.valueString.position == "left") //Label now on right
- {
- this.ctx.textBaseline = "middle";
- textAlign = "left";
- }
- else if (this.valueString.position == "right") //Label now on left
- {
- this.ctx.textBaseline = "middle";
- textAlign = "right";
- }
- }
- else if (this.rotation == 2*Math.PI/3) //Component is vertical
- {
- if (this.valueString.position == "left") //Label is on right
- {
- this.ctx.textBaseline = "middle";
- textAlign = "left";
- }
- else if (this.valueString.position == "right") //Label is on right
- {
- this.ctx.textBaseline = "middle";
- textAlign = "right";
- }
- }
- this.ctx.translate(this.valueString.x, this.valueString.y);
- this.ctx.rotate(this.rotation);
- var str;
- if (this.valueString.decimal < 0)
- str = this.value + " " + this.valueString.suffix;
- else //Force a certain number of digits
- str = (this.value).toFixed(this.valueString.decimal) + " " + this.valueString.suffix;
-
- this.diagram.drawSubSuperScript(this.ctx, str, 0, 0, textAlign);
- this.ctx.restore();
- }
- }
-
- Component.prototype.isInside = function(x, y)
- {
- var pt = transform(x, y, this.x, this.y, this.rotation);
- if((this.transBoundingBox[0] <= pt.x) && (pt.x <= this.transBoundingBox[2]) && (this.transBoundingBox[1] <= pt.y) && (pt.y <= this.transBoundingBox[3]))
- return true;
- else
- return false;
- }
-
- //***** NODE COMPONENT *****//
- function Node(x, y)
- {
- //Call super class
- this.Component(x, y);
- this.boundingBox = [-2, -2, 2, 2];
- this.nodeRadius = 2;
- }
-
- copyPrototype(Node, Component);
- Node.prototype.paint = function()
- {
- this.initPaint();
- this.ctx.save();
- this.transform();
- this.ctx.strokeStyle = this.color;
- this.ctx.fillStyle = this.color;
- this.diagram.drawCircle(this.ctx, 0, 0, this.nodeRadius, true);
- this.drawLabel();
- this.ctx.restore();
- }
-
- Node.prototype.toString = function()
- {
- return "";
- }
-
- //***** WIRE COMPONENT *****//
- function Wire(x1, y1, x2, y2)
- {
- //Call super class
- this.Component(x1, y1);
- this.dx = x2 - x1;
- this.dy = y2 - y1;
- this.boundingBox = [-5, -5, this.dx + 5, this.dy + 5];
- }
-
- copyPrototype(Wire, Component);
- Wire.prototype.paint = function()
- {
- this.initPaint();
- this.ctx.save();
- this.transform();
- this.ctx.strokeStyle = this.color;
- this.ctx.fillStyle = this.color;
- this.diagram.drawLine(this.ctx, 0, 0, this.dx, this.dy);
- this.ctx.restore();
- }
-
- Wire.prototype.toString = function()
- {
- return "";
- }
-
- //***** LABEL *****//
- function Label(x, y, value, textAlign)
- {
- //Call super class
- this.Component(x, y);
- this.boundingBox = [-10, -10, 10, 10];
- this.value = value;
- this.textAlign = textAlign;
- }
-
- copyPrototype(Label, Component);
- Label.prototype.paint = function()
- {
- this.ctx.save();
- this.ctx.textAlign = "left";
- this.ctx.translate(this.x, this.y);
- this.ctx.rotate(this.rotation);
- this.ctx.strokeStyle = this.color;
- this.ctx.fillStyle = this.color;
- this.diagram.drawSubSuperScript(this.ctx, this.value, 0, 0, this.textAlign);
- this.ctx.restore();
- }
-
- Label.prototype.toString = function()
- {
- return "";
- }
-
- //***** CAPACITOR COMPONENT *****//
- function Capacitor(x, y, value)
- {
- //Call super class
- this.Component(x, y);
- this.boundingBox = [-8, 0, 8, 48];
- this.value = value;
- }
-
- copyPrototype(Capacitor, Component);
- Capacitor.prototype.paint = function()
- {
- this.initPaint();
- this.ctx.save();
- this.transform();
- this.ctx.strokeStyle = this.color;
- this.ctx.fillStyle = this.color;
- this.diagram.drawLine(this.ctx, 0, 0, 0, 22);
- this.diagram.drawLine(this.ctx, -8, 22, 8, 22);
- this.diagram.drawLine(this.ctx, -8, 26, 8, 26);
- this.diagram.drawLine(this.ctx, 0, 26, 0, 48);
- this.drawLabel();
- this.drawValueString();
- this.ctx.restore();
- }
-
- Capacitor.prototype.toString = function()
- {
- return "";
- }
-
- //***** RESISTOR COMPONENT *****//
- function Resistor(x, y, value)
- {
- //Call super class
- this.Component(x, y);
- this.boundingBox = [-5, 0, 5, 48];
- this.value = value;
- }
-
- copyPrototype(Resistor, Component);
- Resistor.prototype.paint = function()
- {
- this.initPaint();
- this.ctx.save();
- this.transform();
- this.ctx.strokeStyle = this.color;
- this.ctx.fillStyle = this.color;
- this.diagram.drawLine(this.ctx, 0, 0, 0, 12);
- this.diagram.drawLine(this.ctx, 0, 12, 4, 14);
- this.diagram.drawLine(this.ctx, 4, 14, -4, 18);
- this.diagram.drawLine(this.ctx, -4, 18, 4, 22);
- this.diagram.drawLine(this.ctx, 4, 22, -4, 26);
- this.diagram.drawLine(this.ctx, -4, 26, 4, 30);
- this.diagram.drawLine(this.ctx, 4, 30, -4, 34);
- this.diagram.drawLine(this.ctx, -4, 34, 0, 36);
- this.diagram.drawLine(this.ctx, 0, 36, 0, 48);
- this.drawLabel();
- this.drawValueString();
- this.ctx.restore();
- }
-
- Resistor.prototype.toString = function()
- {
- return "";
- }
-
- //***** INDUCTOR COMPONENT *****//
- function Inductor(x, y, value)
- {
- //Call super class
- this.Component(x, y);
- this.boundingBox = [-4, 0, 5, 48];
- this.value = value;
- }
-
- copyPrototype(Inductor, Component);
- Inductor.prototype.paint = function()
- {
- this.initPaint();
- this.ctx.save();
- this.transform();
- this.ctx.strokeStyle = this.color;
- this.ctx.fillStyle = this.color;
- this.diagram.drawLine(this.ctx, 0, 0, 0, 14);
- this.diagram.drawArc(this.ctx, 0, 18, 4, 6*Math.PI/4, 3*Math.PI/4);
- this.diagram.drawArc(this.ctx, 0, 24, 4, 5*Math.PI/4, 3*Math.PI/4);
- this.diagram.drawArc(this.ctx, 0, 30, 4, 5*Math.PI/4, 2*Math.PI/4);
- this.diagram.drawLine(this.ctx, 0, 34, 0, 48);
- this.drawLabel();
- this.drawValueString();
- this.ctx.restore();
- }
-
- Inductor.prototype.toString = function()
- {
- return "";
- }
-
- //***** N-CHANNEL AND P-CHANNEL MOSFET COMPONENT *****//
- function Mosfet(x, y, value, type)
- {
- //Call super class
- this.Component(x, y);
- this.boundingBox = [-24, 0, 8, 48];
- this.value = value;
- this.type = type;
- }
-
- copyPrototype(Mosfet, Component);
- Mosfet.prototype.paint = function()
- {
- this.initPaint();
- this.ctx.save();
- this.transform();
- this.ctx.strokeStyle = this.color;
- this.ctx.fillStyle = this.color;
- this.diagram.drawLine(this.ctx, 0, 0, 0, 16);
- this.diagram.drawLine(this.ctx, -8, 16, 0, 16);
- this.diagram.drawLine(this.ctx, -8, 16, -8, 32);
- this.diagram.drawLine(this.ctx, -8, 32, 0, 32);
- this.diagram.drawLine(this.ctx, 0, 32, 0, 48);
- if (this.type == "n")
- {
- this.diagram.drawLine(this.ctx,-24,24,-12,24);
- this.diagram.drawLine(this.ctx,-12,16,-12,32);
- }
- else if (this.type == "p")
- {
- this.diagram.drawLine(this.ctx, -24, 24, -16, 24);
- this.diagram.drawCircle(this.ctx, -14, 24, 2, false);
- this.diagram.drawLine(this.ctx, -12, 16, -12, 32);
- }
- this.drawLabel();
- this.drawValueString();
- this.ctx.restore();
- }
-
- Mosfet.prototype.toString = function()
- {
- if (this.type = "n")
- return "";
- else if (this.type = "p")
- return "";
- }
-
- //***** VOLTAGE AND CURRENT SOURCE COMPONENT *****//
- function Source(x, y, value, type)
- {
- //Call super class
- this.Component(x, y);
- this.boundingBox = [-12, 0, 12, 48];
- this.value = value;
- this.type = type;
- }
-
- copyPrototype(Source, Component);
- Source.prototype.paint = function()
- {
- this.initPaint();
- this.ctx.save();
- this.transform();
- this.ctx.strokeStyle = this.color;
- this.ctx.fillStyle = this.color;
- this.diagram.drawLine(this.ctx, 0, 0, 0, 12);
- this.diagram.drawCircle(this.ctx, 0, 24, 12, false);
- this.diagram.drawLine(this.ctx, 0, 36, 0, 48);
- if (this.type == "v")
- {
- //Plus sign, vertical bar
- this.ctx.save();
- this.ctx.translate(0, this.diagram.scale*18);
- this.ctx.rotate(this.rotation);
- this.diagram.drawLine(this.ctx, 0, -3, 0, 3); //this.diagram.drawLine(this.ctx, 0, 15, 0, 21);
- this.ctx.restore();
-
- //Plus sign, horizontal bar
- this.ctx.save();
- this.ctx.translate(0, this.diagram.scale*18);
- this.ctx.rotate(this.rotation);
- this.diagram.drawLine(this.ctx, -3, 0, 3, 0); //this.diagram.drawLine(this.ctx, -3, 18, 3, 18);
- this.ctx.restore();
- //Minus sign
- this.ctx.save();
- this.ctx.translate(0, this.diagram.scale*30);
- this.ctx.rotate(this.rotation);
- this.diagram.drawLine(this.ctx, -3, 0, 3, 0); //this.diagram.drawLine(this.ctx, -3, 30, 3, 30);
- this.ctx.restore();
- }
- else if (this.type == "i")
- {
- this.diagram.drawLine(this.ctx, 0, 15, 0, 32);
- this.diagram.drawLine(this.ctx,-3, 26, 0, 32);
- this.diagram.drawLine(this.ctx,3, 26, 0, 32);
- }
- this.drawLabel();
- this.drawValueString();
- this.ctx.restore();
- }
-
- Source.prototype.toString = function()
- {
- if (this.type = "v")
- return "";
- else if (this.type = "i")
- return "";
- }
-
- //***** GROUND COMPONENT *****//
- function Ground(x, y)
- {
- //Call super class
- this.Component(x, y);
- this.boundingBox = [-6, 0, 6, 8];
- }
-
- copyPrototype(Ground, Component);
- Ground.prototype.paint = function()
- {
- this.initPaint();
- this.ctx.save();
- this.transform();
- this.ctx.strokeStyle = this.color;
- this.ctx.fillStyle = this.color;
- this.diagram.drawLine(this.ctx, 0, 0, 0, 8);
- this.diagram.drawLine(this.ctx, -6, 8, 6, 8);
- this.ctx.restore();
- }
-
- Ground.prototype.toString = function()
- {
- return "";
- }
-
- //***** DIODE COMPONENT *****//
- function Diode(x, y, value)
- {
- //Call super class
- this.Component(x, y);
- this.boundingBox = [-8, 0, 8, 48];
- this.value = value;
- }
-
- copyPrototype(Diode, Component);
- Diode.prototype.paint = function()
- {
- this.initPaint();
- this.ctx.save();
- this.transform();
- this.drawLabel();
- this.ctx.strokeStyle = this.color;
- this.ctx.fillStyle = this.color;
- this.diagram.drawLine(this.ctx, 0, 0, 0, 16);
- this.diagram.drawLine(this.ctx, -8, 16, 8, 16);
- this.diagram.drawLine(this.ctx, -8, 16, 0, 32);
- this.diagram.drawLine(this.ctx, 8, 16, 0, 32);
- this.diagram.drawLine(this.ctx, -8, 32, 8, 32);
- this.diagram.drawLine(this.ctx,0 , 32, 0, 48);
- this.ctx.restore();
- }
-
- Diode.prototype.toString = function()
- {
- return "";
- }
-
-//////////PUBLIC FIELDS AND METHODS//////////
- return {
-
- Utils: Utils,
- Color: Color,
- Diagram: Diagram,
- };
-}());
diff --git a/common/test/data/full/static/js/sound_labs/mosfet_amplifier.js b/common/test/data/full/static/js/sound_labs/mosfet_amplifier.js
deleted file mode 100644
index 614388df27..0000000000
--- a/common/test/data/full/static/js/sound_labs/mosfet_amplifier.js
+++ /dev/null
@@ -1,658 +0,0 @@
-$(document).ready(function()
-{
- //The try catch block checks if canvas and audio libraries are present. If not, we exit and alert the user.
- try
- {
- //Add corresponding listener to various UI elements
- $('#musicTypeSelect').change(onSelectChange);
- $('input:checkbox').click(checkboxClicked);
- $('input:radio').click(radioButtonClicked);
- $('#playButton').click(playButtonClicked);
- initSound();
- initDiagram();
- initGraph();
- setGraph();
- generateBuffer();
- calculateSignals();
- draw();
- labEnabled = true;
- }
- catch(err)
- {
- labEnabled = false;
- alert(err + " The tool is disabled.");
- }
-});
-
-function initGraph()
-{
- //Test if canvas is supported. If not, exit.
- var testCanvas = document.createElement("canvas")
- if (!testCanvas.getContext)
- throw "Canvas element is not supported in this browser."
- //Get canvas
- var canvas = $('#graph')[0];
- //To disable text selection outside the canvas
- canvas.onselectstart = function(){return false;};
- //Create an offscreen buffer
- var buffer = document.createElement('canvas');
- buffer.width = canvas.width;
- buffer.height = canvas.height;
- graph = new Plotter.Graph(50, 50, 400, 400, canvas, buffer);
-}
-
-var diagram, VS, VIn, VBias, R;
-
-function initDiagram()
-{
- //Test if canvas is supported. If not, exit.
- var testCanvas = document.createElement("canvas")
- if (!testCanvas.getContext)
- throw "Canvas element is not supported in this browser."
-
- var element = $('#diag1');
- diagram = new Circuit.Diagram(element, true);
-
- //Lines
- var wirev1 = diagram.addWire(100, 289, 100, 361);
- var wirev2 = diagram.addWire(100, 78, 100, 135.5);
- var wirev3 = diagram.addWire(380, 78.5, 380, 89.5);
- var wirev4 = diagram.addWire(380, 290, 380, 361.5);
-
- var wireh1 = diagram.addWire(100, 78, 240, 78);
- var wireh2 = diagram.addWire(240, 243, 286, 243);
- var wireh3 = diagram.addWire(100, 433, 240, 433);
-
- var vOutPlus = diagram.addLabel(396, 219, "\u002B", "left");
- var vOutLabel = diagram.addLabel(396, 244, "v_{OUT}", "left");
- var vOutMinus = diagram.addLabel(396, 274, "\u2212", "left");
- vOutPlus.color = Plotter.Color.lightyellow;
- vOutLabel.color = Plotter.Color.lightyellow;
- vOutMinus.color = Plotter.Color.lightyellow;
-
- var vRPlus = diagram.addLabel(310, 127, "\u002B", "left");
- var vRLabel = diagram.addLabel(310, 152, "v_{R}", "left");
- var vRMinus = diagram.addLabel(310, 182, "\u2212", "left");
- vRPlus.color = Plotter.Color.lightgreen;
- vRLabel.color = Plotter.Color.lightgreen;
- vRMinus.color = Plotter.Color.lightgreen;
-
- //vin
- //Plotter.Color.lightblue);
- //vout
- //Plotter.Color.lightyellow);
- //vr
- //Plotter.Color.lightgreen);
-
- //Ground
- var ground = diagram.addGround(240, 433);
-
- //Resistor
- R = diagram.addResistor(380, 99.5, 10);
- R.label.str = "R";
- R.valueString.suffix = "k\u03A9";
-
- //Voltage sources
- VS = diagram.addSource(100, 193, 1.6, "v");
- VS.label.str = "V_{S}";
- VS.valueString.suffix = "V";
- VIn = diagram.addSource(240, 243, 3, "v");
- VIn.label.str = "v_{IN}";
- VIn.label.color = Plotter.Color.lightblue;
- VIn.valueString.suffix = "V";
- VIn.valueString.color = Plotter.Color.lightblue;
- VBias = diagram.addSource(240, 338, 2.5, "v");
- VBias.label.str = "v_{BIAS}";
- VBias.valueString.suffix = "V";
-
- //Mosfet
- var nMosfet = diagram.addMosfet(380, 195, "", "n");
-
- //diagram.showGrid = true;
- //diagram.gridStep = 1;
- diagram.paint();
-}
-
-function setGraph()
-{
- var lticks = 1;
- var sticks = 0.5;
- //x axis
- graph.xText = xLab;
- graph.yText = "V_{MAX} (Volts)";
- graph.xmin = 0;
- graph.xmax = maxTime;
- graph.xspan = maxTime;
- graph.xShortTickMin = 0;
- graph.xShortTickMax = maxTime;
- graph.xShortTickStep = maxTime/20;
- graph.xLongTickMin = 0;
- graph.xLongTickMax = maxTime;
- graph.xLongTickStep = maxTime/10;
- graph.xLabelMin = 0;
- graph.xLabelMax = maxTime;
- graph.xLabelStep = maxTime/10;
- graph.xGridMin = 0;
- graph.xGridMax = maxTime;
- graph.xGridStep = maxTime/10;
- //y axis
- graph.ymin = -maxVolt;
- graph.ymax = maxVolt;
- graph.yspan = 2*maxVolt;
- graph.yShortTickMin = -maxVolt + (maxVolt % sticks);
- graph.yShortTickMax = maxVolt - (maxVolt % sticks);
- graph.yShortTickStep = sticks;
- graph.yLongTickMin = -maxVolt + (maxVolt % lticks);
- graph.yLongTickMax = maxVolt - (maxVolt % lticks);
- graph.yLongTickStep = lticks;
- graph.yLabelMin = -maxVolt + (maxVolt % lticks);
- graph.yLabelMax = maxVolt - (maxVolt % lticks);
- graph.yLabelStep = lticks;
- graph.yGridMin = -maxVolt + (maxVolt % lticks);
- graph.yGridMax = maxVolt - (maxVolt % lticks);
- graph.yGridStep = lticks;
-}
-
-function generateBuffer()
-{
- //Draw on offscreen image buffer
- graph.paintOn("buffer");
- graph.paint();
-}
-
-function draw()
-{
- //Paint buffer on canvas
- graph.paintBuffer();
-
- //Draw on canvas
- graph.paintOn("canvas"); //Draw on screen image
-
- if (vinChecked)
- graph.drawArray(time, insig, Plotter.Color.lightblue);
- if (voutChecked)
- graph.drawArray(time, outsig, Plotter.Color.lightyellow);
- if (vrChecked)
- graph.drawArray(time, rsig, Plotter.Color.lightgreen);
-}
-
-function initSound()
-{
- sp = new Sound.Player();
- sp.soundStarted = function()
- {
- $('#playButton').prop('value', "Stop");
- }
-
- sp.soundStopped = function()
- {
- $('#playButton').prop('value', "Play");
- }
-}
-
-function communSlide()
-{
- if (labEnabled)
- {
- if (sp.isPlaying)
- sp.stopTone();
- calculateSignals();
- draw();
- diagram.paint();
- }
-}
-
-$(function()
-{
- $("#vsSlider" ).slider({value: vS, min: 0, max: 10, step: 0.01,
- slide: function(event, ui)
- {
- $("#vs").html("VS = " + ui.value + " V");
- vS = ui.value;
- VS.value = vS;
- communSlide();
- }
- });
- $("#vs").html("VS = "+ $("#vsSlider").slider("value") + " V");
-
- $("#vinSlider").slider({value: vIn, min: 0, max: 5, step: 0.01,
- slide: function(event, ui)
- {
- $("#vin").html("vIN = " + ui.value + " V");
- vIn = ui.value;
- VIn.value = vIn;
- communSlide();
- }
- });
- $("#vin").html("vIN = " + $("#vinSlider").slider("value") + " V");
-
- $("#freqSlider").slider({value: freq, min: 0, max: 5000, step: 100,
- slide: function(event, ui)
- {
- $("#freq").html("Frequency = " + ui.value + " Hz");
- freq = ui.value;
- communSlide();
- }
- });
- $("#freq").html("Frequency = " + $("#freqSlider").slider("value") + " Hz");
-
- $("#vbiasSlider").slider({value: vBias, min: 0, max: 10, step: 0.01,
- slide: function(event, ui)
- {
- $("#vbias").html("VBIAS = " + ui.value + " V");
- vBias = ui.value;
- VBias.value = vBias;
- communSlide();
- }
- });
- $("#vbias").html("VBIAS = " + $("#vbiasSlider").slider("value") + " V");
-
- $("#rSlider").slider({value: 1, min: 0.1, max: 10, step: 0.01,
- slide: function(event, ui)
- {
- //Values of slider are in Kilo Ohms
- var val = getResistance(ui.value);
- $(this).slider("value", val);
- if (val >= 1.0) //kOhms
- {
- $("#r").html("R = " + val + " kΩ");
- R.value = val;
- R.valueString.suffix = "k\u03A9";
- }
- else
- {
- $("#r").html("R = " + kiloToUnit(val) + " Ω");
- R.value = kiloToUnit(val);
- R.valueString.suffix = "\u03A9";
- }
-
- r = kiloToUnit(val);
- communSlide();
- //return false; //Blocks keystrokes if enabled
- }
- });
- $("#r").html("R = " + $("#rSlider").slider("value") + " kΩ");
-
- $("#kSlider").slider({value: k*1000, min: 0, max: 10, step: 0.01,
- slide: function(event, ui)
- {
- $("#k").html("k = " + ui.value + " mA/V2 ");
- k = ui.value / 1000; //Values are in mA
- communSlide();
- }
- });
- $("#k").html("k = " + $("#kSlider").slider("value") + " mA/V2 ");
-
- $("#vtSlider").slider({value: vt, min: 0, max: 10, step: 0.01,
- slide: function(event, ui)
- {
- $("#vt").html("VT = " + ui.value + " V");
- vt = ui.value;
- communSlide();
- }
- });
- $("#vt").html("VT = " + $("#vtSlider").slider("value") + " V");
-
- $("#vmaxSlider" ).slider({value: vMax, min: 1, max: 20, step: 0.1,
- slide: function(event, ui)
- {
- $("#vmax").html("VMAX = " + ui.value + " V");
- maxVolt = ui.value;
- if (labEnabled)
- {
- if (sp.isPlaying)
- sp.stopTone();
- setGraph();
- generateBuffer();
- calculateSignals();
- draw();
- }
- }
- });
- $("#vmax").html("VMAX = " + $("#vmaxSlider").slider("value") + " V");
-});
-
-function getCheckboxesState()
-{
- if($('#vinCheckbox').prop('checked'))
- vinChecked = true;
- else
- vinChecked = false;
- if($('#voutCheckbox').prop('checked'))
- voutChecked = true;
- else
- voutChecked = false;
- if($('#vrCheckbox').prop('checked'))
- vrChecked = true;
- else
- vrChecked = false;
-}
-
-function getRadioButtonsState()
-{
- if($('#vinRadioButton').prop('checked'))
- sp.inSignal.listen = true;
- else
- sp.inSignal.listen = false;
- if($('#voutRadioButton').prop('checked'))
- sp.outSignals[0].listen = true;
- else
- sp.outSignals[0].listen = false;
- if($('#vrRadioButton').prop('checked'))
- sp.outSignals[1].listen = true;
- else
- sp.outSignals[1].listen = false;
-}
-
-function onSelectChange()
-{
- if (labEnabled)
- {
- musicType = $("#musicTypeSelect").val();
- sp.stopTone();
- if (musicType == 0) //Zero Input
- {
- $("#vinSlider").slider( "option", "disabled", true);
- $("#freqSlider").slider( "option", "disabled", true);
- maxTime = 10; //ms
- xLab = "t (ms)";
- musicLoaded();
- }
- else if (musicType == 1) //Unit Impulse
- {
- $("#vinSlider").slider( "option", "disabled", true);
- $("#freqSlider").slider( "option", "disabled", true);
- maxTime = 10; //ms
- xLab = "t (ms)";
- musicLoaded();
- }
- else if (musicType == 2) //Unit Step
- {
- $("#vinSlider").slider( "option", "disabled", true);
- $("#freqSlider").slider( "option", "disabled", true);
- maxTime = 10; //ms
- xLab = "t (ms)";
- musicLoaded();
- }
- if (musicType == 3) //Sine Wave
- {
- $("#vinSlider").slider( "option", "disabled", false);
- $("#freqSlider").slider( "option", "disabled", false);
- maxTime = 10; //ms
- xLab = "t (ms)";
- musicLoaded();
- }
- else if (musicType == 4) //Square Wave
- {
- $("#vinSlider").slider( "option", "disabled", false);
- $("#freqSlider").slider( "option", "disabled", false);
- maxTime = 10; //ms
- xLab = "t (ms)";
- musicLoaded();
- }
- else if (musicType == 5 || musicType == 6 || musicType == 7 || musicType == 8) //Music
- {
- $("#vinSlider").slider( "option", "disabled", false);
- $("#freqSlider").slider( "option", "disabled", true);
- maxTime = 20; //s
- xLab = "t (s)";
-
- if (musicType == 5)
- sp.load("classical.wav", musicLoaded);
- else if (musicType == 6)
- sp.load("folk.wav", musicLoaded);
- else if (musicType == 7)
- sp.load("jazz.wav", musicLoaded);
- else
- sp.load("reggae.wav", musicLoaded);
- }
- }
-}
-
-function musicLoaded()
-{
- setGraph();
- generateBuffer();
- calculateSignals();
- draw();
-}
-
-function checkboxClicked()
-{
- if (labEnabled)
- {
- getCheckboxesState();
- draw();
- }
-}
-
-function radioButtonClicked()
-{
- if (labEnabled)
- {
- if (sp.isPlaying)
- sp.stopTone();
- getRadioButtonsState();
- }
-}
-
-function playButtonClicked()
-{
- if (labEnabled)
- {
- if (sp.isPlaying)
- sp.stopTone();
- else
- sp.playTone();
- }
-}
-
-//TO DO: PUT ALL THE FOLLOWING GLOBAL VARIABLES IN A NAMESPACE
-var labEnabled = true;
-//Graph
-var graph;
-var maxTime = 10; //In ms
-var xLab = "t (ms)";
-var maxVolt = 2;
-var time;
-var insig;
-var outsig;
-//Sound Player
-var sp;
-
-//Drop variable down for Type of Input
-var musicType = 3;
-//Checkboxes variables for Graph
-var vinChecked = true;
-var voutChecked = true;
-var vrChecked = false;
-//Slider variables
-var vS = 1.6;
-var vIn = 3.0;
-var vInMax = 5.0;
-var freq = 1000;
-var vBias = 2.5;
-var r = 10000;
-var k = 0.001;
-var vt = 1;
-var vMax = 2;
-
-function calculateSignals()
-{
- if (musicType == 0 || musicType == 1 || musicType == 2 || musicType == 3)
- {
- sp.soundLength = 1;
- sp.sampleRate = 50000;
- }
- else if (musicType == 4)
- {
- sp.soundLength = 1;
- sp.sampleRate = 88200;
- }
- else if (musicType == 5 || musicType == 6 || musicType == 7 || musicType == 8) //Classical, Folk, Jazz, Reggae
- {
- sp.soundLength = 20;
- sp.sampleRate = 22050;
- }
-
- sp.createBuffers(2); //We have two outputs, first one is the voltage across Drain, Source, the second across resistor R
- getRadioButtonsState(); //Set what we are listening to, input, or one of the above
-
- if (musicType == 0) //Zero Input
- sp.generateZero();
- else if (musicType == 1) //Unit Impulse
- sp.generateUnitImpulse();
- else if (musicType == 2) //Unit Step
- sp.generateUnitStep();
- else if (musicType == 3) //Sine Wave
- sp.generateSineWave(vIn, freq, 0);
- else if (musicType == 4) //Square Wave
- sp.generateSquareWave(vIn, freq, 0);
- else if (musicType == 5 || musicType == 6 || musicType == 7 || musicType == 8) //Classical, Folk, Jazz, Reggae
- {
- //TO DO: MOVE OUT
- var max = Number.NEGATIVE_INFINITY;
- var amp = 0.0;
-
- //Find the max and normalize
- for (var i = 0, l = sp.inSignal.data.length; i < l; i++)
- {
- amp = Math.abs(sp.audioData[i]);
- if (amp > max)
- max = amp;
- }
- max /= 0.5;
- if (max != 0.0)
- {
- for (var i = 0, l = sp.inSignal.data.length; i < l; i++)
- {
- sp.inSignal.data[i] = vIn*sp.audioData[i] / max;
- }
- }
- else //Fill in with zeros
- {
- for (var i = 0, l = sp.inSignal.data.length; i < l; i++)
- {
- sp.inSignal.data[i] = 0.0;
- }
- }
- }
-
- getVDS(sp.inSignal.data, sp.outSignals[0].data, vBias, vS, r, k, vt);
- getVr(sp.outSignals[0].data, sp.outSignals[1].data);
-
- time = [];
- insig = [];
- outsig = [];
- rsig = [];
- var i = 0;
- var ii;
- var imult;
- var imax;
- var x = 0;
- var xinc;
-
-
- //Scale of graph is 500 px
- //All generated sound (sine wave etc.) except square wave have sampling rate of 50000 Hz, length 1s. We will plot the first 10 ms. That's 500 samples for 10 ms and 500 px
- if (musicType == 0 || musicType == 1 || musicType == 2 || musicType == 3)
- {
- xinc = 10/500;
- imax = 500;
- imult = 1;
- }
- else if (musicType == 4) //At 50000 Hz, square wave plays very poorly, we use 88200 Hz
- {
- xinc = 10/882;
- imax = 882;
- imult = 1;
- }
- else if (musicType == 5 || musicType == 6 || musicType == 7 || musicType == 8) //All music files have a sampling rate 22050 Hz, length 20s. 20s/500px --> get value every 0.04 s ie every 882 samples.
- {
- xinc = 20/500;
- imax = 500;
- imult = 882;
- }
-
- while (i <= imax)
- {
- ii = imult*i;
- time[i] = x;
- insig[i] = sp.inSignal.data[ii];
- outsig[i] = sp.outSignals[0].data[ii];
- rsig[i] = sp.outSignals[1].data[ii];
- x += xinc;
- i++;
- }
-
- sp.normalizeAllSounds();
-}
-
-var resistance = [0.1, 0.11, 0.12, 0.13, 0.15, 0.16, 0.18, 0.2, 0.22, 0.24, 0.27, 0.3, 0.33, 0.36, 0.39, 0.43, 0.47, 0.51, 0.56, 0.62, 0.68, 0.75, 0.82, 0.91, 1, 1.1, 1.2, 1.3, 1.50, 1.6, 1.8, 2, 2.2, 2.4, 2.7, 3, 3.3, 3.6, 3.9, 4.3, 4.7, 5.1, 5.6, 6.2, 6.8, 7.5, 8.2, 9.1, 10];
-
-function getResistance(value)
-{
- var distance;
- var minDistance = Number.POSITIVE_INFINITY;
- var minIndex;
-
- for (var i = 0, l = resistance.length; i < l; i++)
- {
- distance = Math.abs(value - resistance[i]);
- if (distance < minDistance)
- {
- minDistance = distance;
- minIndex = i;
- }
- }
- return resistance[minIndex];
-}
-
-function kiloToUnit(k)
-{
- return k*1000;
-}
-
-function getVDS(inData, outData, VBIAS, VS, R, K, VT)
-{
- // Given vector of inputs (VGS), compute vector of outputs (VDS)
- // VGS: input source in vector
- // VDS: voltage across MOSFET
- // VS: Supply Voltage
- // R: load resistor
- // VC: gate-to-source below above which MOSFET is in saturation
- // K, VT: mosfet parameters
-
- var b;
- var VC = getVC(VS, R, K, VT);
- var indata;
-
- for (var i = 0, l = inData.length; i < l; i++)
- {
- indata = inData[i] + VBIAS;
-
- if (indata < VT)
- outData[i] = VS;
- else if (indata < VC)
- outData[i] = VS - R*(K/2)*Math.pow(indata - VT, 2);
- else
- {
- b = -R*K*(indata - VT) - 1;
- outData[i] = (-b - Math.sqrt(b*b - 2*R*K*VS))/(R*K);
- }
- }
-};
-
-// Solve for VC, where VC is the VGS below which the MOSFET is in saturation
-function getVC(VS, R, K, VT)
-{
- return VT + (-1 + Math.sqrt(1 + 2*VS*R*K))/(R*K);
-}
-
-function getVr(inData, outData)
-{
- for (var i = 0, l = outData.length; i < l; i++)
- {
- outData[i] = vS - inData[i];
- }
-}
diff --git a/common/test/data/full/static/js/sound_labs/plotter.js b/common/test/data/full/static/js/sound_labs/plotter.js
deleted file mode 100644
index 0ce37927c5..0000000000
--- a/common/test/data/full/static/js/sound_labs/plotter.js
+++ /dev/null
@@ -1,1038 +0,0 @@
-var Plotter = (function() {
-
- //////////PRIVATE FIELDS AND METHODS//////////
- var Utils =
- {
- TWO_PI: 2.0*Math.PI,
- PI_DIV_2: Math.PI/2.0,
-
- getxPix : function(fx, fleft, fwidth, wleft, wwidth)
- {
- return Math.round(wleft + wwidth * (fx - fleft) / fwidth);
- },
-
- getxFromPix : function(wx, wleft, wwidth, fleft, fwidth)
- {
- return fleft + fwidth * (wx - wleft) / wwidth;
- },
-
- getyPix : function(fy, fbottom, fheight, wbottom, wheight)
- {
- return Math.round(wbottom - wheight * (fy - fbottom) / fheight);
- },
-
- getyFromPix : function(wy, wbottom, wheight, fbottom, fheight)
- {
- return fbottom + fheight * (wbottom - wy) / wheight;
- },
-
- log10: function(x)
- {
- return Math.log(x)/Math.LN10;
- }
- };
-
- var Color =
- {
- //Old palette
- /*background : "rgb(0, 51, 102)", //0.0, 0.2, 0.4
- black : "rgb(0, 0, 0)", //0.0
- lodarkgray : "rgb(26, 26, 26)", //0.1 = 25.5
- darkgray : "rgb(51, 51, 51)", //0.2
- lomidgray : "rgb(102, 102, 102)", //0.4
- midgray : "rgb(128, 128, 128)", //0.5 = 127.5
- himidgray : "rgb(153, 153, 153)", //0.6
- litegray : "rgb(204, 204, 204)", //0.8
- white : "rgb(255, 255, 255)", //1.0
-
- red : "rgb(255, 0, 0)",
- green : "rgb(0, 255, 0)",
- blue : "rgb(255, 255, 0)",
- yellow : "rgb(255, 255, 0)",
- cyan : "rgb(0, 255, 255)",
- magenta : "rgb(255, 0, 255)",*/
-
-
- //Solarized palette: http://ethanschoonover.com/solarized
- base03 : "#002b36",
- base02 : "#073642",
- base015: "#30535c",
- base01 : "#586e75",
- base00 : "#657b83",
- base0 : "#839496",
- base1 : "#93a1a1",
- base2 : "#eee8d5",
- base3 : "#fdf6e3",
- yellow : "#b58900",
- orange : "#cb4b16",
- red : "#dc322f",
- magenta: "#d33682",
- violet : "#6c71c4",
- blue : "#268bd2",
- cyan : "#2aa198",
- green : "#859900",
- //lightgreen: "#c3cd82
- //lightblue: "#95c6e9",
- lightblue: "#00bfff",
- lightyellow: "#ffcf48",
- lightgreen: "#1df914",
- lightmagenta: "#ff3656"
- };
-
- ////////// GENERAL DRAWING ROUTINES //////////
-
- function drawLine(c, x1, y1, x2, y2)
- {
- c.beginPath();
- c.moveTo(x1 + 0.5, y1 + 0.5);
- c.lineTo(x2 + 0.5, y2 + 0.5);
- c.stroke();
- }
-
- //Draws a rectangle, top left corner x1, y1 and bottom right corner x2, y2
- function drawRect(c, x1, y1, x2, y2)
- {
- c.strokeRect(x1 + 0.5, y1 + 0.5, x2 - x1 + 1.0, y2 - y1 + 1.0);
- }
-
- function fillRect(c, x1, y1, x2, y2)
- {
- c.fillRect(x1, y1, x2 - x1 + 1.0, y2 - y1 + 1.0);
- }
-
- function clearRect(c, x1, y1, x2, y2)
- {
- c.clearRect(x1 + 0.5, y1 + 0.5, x2 - x1 + 1.0, y2 - y1 + 1.0);
- }
-
- function drawPixel(c, x, y)
- {
- c.fillRect(x, y, 1.0, 1.0);
- }
-
- function drawPoint(c, x, y, radius)
- {
- c.beginPath();
- c.arc(x + 0.5, y + 0.5, radius, 0, Utils.TWO_PI, true); //Last param is anticlockwise
- c.fill();
- }
-
- function drawHollowPoint(c, x, y, radius)
- {
- c.beginPath();
- c.arc(x + 0.5, y + 0.5, radius, 0, Utils.TWO_PI, true); //Last param is anticlockwise
- c.stroke();
- }
-
- function drawTriangle(c, x1, y1, x2, y2, x3, y3)
- {
- c.beginPath();
- c.moveTo(x1 + 0.5, y1 + 0.5);
- c.lineTo(x2 + 0.5, y2 + 0.5);
- c.lineTo(x3 + 0.5, y3 + 0.5);
- c.closePath();
- c.stroke();
- }
-
- function fillTriangle(c, x1, y1, x2, y2, x3, y3)
- {
- c.beginPath();
- c.moveTo(x1 + 0.5, y1 + 0.5);
- c.lineTo(x2 + 0.5, y2 + 0.5);
- c.lineTo(x3 + 0.5, y3 + 0.5);
- c.closePath();
- c.fill();
- }
-
- function drawHalfCircle(c, x, y, radius, concaveDown) //For inductance only
- {
- c.beginPath();
- if (concaveDown)
- c.arc(x + 0.5, y + 0.5, radius, 0, Math.PI, true); //Last param is anticlockwise
- else
- c.arc(x + 0.5, y + 0.5, radius, Math.PI, 0, true); //Last param is anticlockwise
- c.stroke();
- }
-
- function drawDiamond(c, x, y, h)
- {
- var xc = x + 0.5;
- var yc = y + 0.5;
-
- c.beginPath();
- c.moveTo(xc-h, yc);
- c.lineTo(xc, yc-h);
- c.lineTo(xc+h, yc);
- c.lineTo(xc, yc+h);
- c.closePath();
-
- c.fill();
- }
-
- function drawX(c, x, y, h)
- {
- var xc = x + 0.5;
- var yc = y + 0.5;
-
- c.beginPath();
- c.moveTo(xc+h, yc-h);
- c.lineTo(xc-h, yc+h);
- c.moveTo(xc-h, yc-h);
- c.lineTo(xc+h, yc+h);
- c.stroke();
- }
-
- function drawArrow(c, x1, y1, x2, y2, base, height)
- {
- var xs1 = x1 + 0.5;
- var ys1 = y1 + 0.5;
- var xs2 = x2 + 0.5;
- var ys2 = y2 + 0.5;
- var xv = x2 - x1;
- var yv = y2 - y1;
- var ang = Math.atan2(-yv, xv);
-
- c.beginPath();
- //Arrow line
- c.moveTo(xs1, ys1);
- c.lineTo(xs2, ys2);
- c.stroke();
- //Arrow head, first draw a triangle with top on origin then translate/rotate to orient and fit on line
- c.save();
- c.beginPath();
- c.translate(xs2, ys2);
- c.rotate(Utils.PI_DIV_2-ang);
-
- c.moveTo(0, 0);
- c.lineTo(-base, height);
- c.lineTo(base, height);
- c.closePath();
- c.fill();
- //c.stroke();
- c.restore();
- }
-
- function DrawingZone(left, top, width, height)
- {
- this.left = left;
- this.top = top;
- this.width = width;
- this.height = height;
- this.right = left + width - 1;
- this.bottom = top + height - 1;
- }
-
- function Graph(x, y, width, height, canvas, buffer)
- {
- this.canvas = canvas;
- this.buffer = buffer;
- this.canvas_ctx = canvas.getContext("2d");
- this.buffer_ctx = buffer.getContext("2d");
- this.canvasColor = Color.base02; //Color.background : "rgb(0, 51, 102)"
-
- //Use the screen canvas
- this.ctx = this.canvas_ctx;
-
- this.drawingZone = new DrawingZone(x, y, width, height);
- this.drawingZoneColor = Color.base03; //Color.black;
- this.drawingZoneBorderColor = Color.base01; //Color.lomidgray;
-
- this.xGridColor = Color.base015; //Color.darkGray;
- this.xAxisColor = Color.base00; //Color.himidgray;
- this.xLabelColor = Color.base1; //Color.himidgray;
- this.xTextColor = Color.base2; //Color.litegray;
-
- this.yGridColor = Color.base015; //Color.darkGray;
- this.yAxisColor = Color.base00; //Color.himidgray;
- this.yLabelColor = Color.base1; //Color.himidgray;
- this.yTextColor = Color.base2; //Color.litegray;
-
- this.xText = "x";
- this.yText = "y";
-
- this.xmin = -1.0;
- this.xmax = 1.0;
- this.xspan = 2.0;
- this.ymin = -10.0;
- this.ymax = 10.0;
- this.yspan = 20.0;
-
- this.x0 = 0.0;
- this.y0 = 0.0;
- this.wx0 = 0;
- this.wy0 = 0;
- this.xShortTickStep = 0.1;
- this.xShortTickMin = this.xmin;
- this.xShortTickMax = this.xmax;
-
- this.xLongTickStep = 0.2;
- this.xLongTickMin = this.xmin;
- this.xLongTickMax = this.xmax;
-
- this.xLabelStep = 0.2;
- this.xLabelMin = this.xmin;
- this.xLabelMax = this.xmax;
-
- this.xGridStep = 0.2;
- this.xGridMin = this.xmin;
- this.xGridMax = this.xmax;
-
- this.formatxzero = true;
- this.formatyzero = true;
-
- this.yShortTickStep = 1;
- this.yShortTickMin = this.ymin;
- this.yShortTickMax = this.ymax;
-
- this.yLongTickStep = 2;
- this.yLongTickMin = this.ymin;
- this.yLongTickMax = this.ymax;
-
- this.yLabelStep = 2;
- this.yLabelMin = this.ymin;
- this.yLabelMax = this.ymax;
-
- this.yGridStep = 2;
- this.yGridMin = this.ymin;
- this.yGridMax = this.ymax;
-
- this.automaticxLabels = true;
- this.xLabelyOffset = 7;
- this.automaticyLabels = true;
- this.yLabelxOffset = -7;
-
- this.xTextxOffset = 9;
- this.yTextyOffset = -9;
-
- this.hasxLog = false;
- this.hasyLog = false;
- this.xPowerMin = 1;
- this.xPowerMax = 5;
- this.yPowerMin = 1;
- this.yPowerMax = 5;
- this.xLabelDecimalDigits = 1;
- this.yLabelDecimalDigits = 1;
-
- this.showxGrid = true;
- this.showyGrid = true;
- this.showBorder = true;
- this.showxShortTicks = true;
- this.showxLongTicks = true;
- this.showxLabels = true;
- this.showyShortTicks = true;
- this.showyLongTicks = true;
- this.showyLabels = true;
- this.showxAxis = true;
- this.showxText = true;
- this.showyAxis = true;
- this.showyText = true;
-
- this.paintOn = function(where) //On what context the drawing commands will operate
- {
- if (where == "buffer")
- this.ctx = this.buffer_ctx;
- else if (where == "canvas")
- this.ctx = this.canvas_ctx; //Default behavior
- };
-
- this.paintBuffer = function() //Paints buffer on screen canvas
- {
- this.canvas_ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.canvas_ctx.drawImage(buffer, 0, 0);
- };
-
- this.paintCanvas = function() //Paints screen canvas on buffer
- {
- this.buffer_ctx.clearRect(0, 0, this.buffer.width, this.buffer.height);
- this.buffer_ctx.drawImage(canvas, 0, 0);
- };
-
- this.drawBorder = function()
- {
- this.ctx.strokeStyle = this.drawingZoneBorderColor;
- drawRect(this.ctx, this.drawingZone.left, this.drawingZone.top, this.drawingZone.right - 1, this.drawingZone.bottom - 1);
- };
-
- this.drawxAxis = function()
- {
- this.wy0 = this.getyPix(this.y0);
- this.ctx.strokeStyle = this.xAxisColor;
- drawLine(this.ctx, this.drawingZone.left, this.wy0, this.drawingZone.right + 6, this.wy0);
- drawLine(this.ctx, this.drawingZone.right + 3, this.wy0 - 3, this.drawingZone.right + 3, this.wy0 + 3);
- drawLine(this.ctx, this.drawingZone.right + 4, this.wy0 - 2, this.drawingZone.right + 4, this.wy0 + 2);
- drawLine(this.ctx, this.drawingZone.right + 5, this.wy0 - 1, this.drawingZone.right + 5, this.wy0 + 1);
- };
-
- /*
- if (this.hasxLog)
- wx = this.getxPix(Utils.log10(x));
- if (this.hasyLog)
- wy = this.getyPix(Utils.log10(y));
- */
-
- /*
- this.ctx.textAlign = "left";
- this.ctx.textAlign = "center";
- this.ctx.textAlign = "right";
- this.ctx.textBaseline = "top";
- this.ctx.textBaseline = "middle";
- this.ctx.textBaseline = "bottom";
- this.ctx.textBaseline = "alphabetic";
- */
-
- this.drawxLog = function()
- {
- var power;
- var x;
- var wx;
- var wy = this.drawingZone.bottom + 12;
- var str;
-
- //Don't draw grid line when on border of graph
- for(var p = this.xPowerMin; p <= this.xPowerMax; p++)
- {
- wx = this.getxPix(p);
- if(wx > this.drawingZone.right)
- wx = this.drawingZone.right;
- //Labeled grid line
- if (p != this.xPowerMin && p != this.xPowerMax) //Don't draw line on left or right border of graph
- {
- this.ctx.strokeStyle = this.xGridColor;
- drawLine(this.ctx, wx, this.drawingZone.bottom, wx, this.drawingZone.top);
- }
- //Long ticks
- this.ctx.strokeStyle = this.xLabelColor;
- drawLine(this.ctx, wx, this.drawingZone.bottom, wx, this.drawingZone.bottom + 4);
- //Now the labels
- this.ctx.fillStyle = this.xLabelColor;
- this.ctx.strokeStyle = this.xLabelColor;
- str = "10^{" + p.toFixed(0) + "}";
- this.drawSubSuperScript(this.ctx, str, wx, wy, "center", "top");
-
- if (p != this.xPowerMax)
- {
- for(var i = 2; i < 10; i++)
- {
- x = p + Utils.log10(i);
- wx = this.getxPix(x);
- //Grid
- this.ctx.strokeStyle = this.xGridColor;
- drawLine(this.ctx, wx, this.drawingZone.bottom, wx, this.drawingZone.top);
- //Short ticks
- this.ctx.strokeStyle = this.xLabelColor;
- drawLine(this.ctx, wx, this.drawingZone.bottom, wx, this.drawingZone.bottom + 2);
- }
- }
- }
- }
-
- this.drawyLog = function()
- {
- var power;
- var y;
- var wy;
- var wx = this.drawingZone.left - 7;
- var str;
-
- //Don't draw grid line when on border of graph
- for(var p = this.yPowerMin; p <= this.yPowerMax; p++)
- {
- wy = this.getyPix(p);
- if(wy < this.drawingZone.top)
- wy = this.drawingZone.top;
- //Labeled grid line
- if (p != this.yPowerMin && p != this.yPowerMax) //Don't draw line on left or right border of graph
- {
- this.ctx.strokeStyle = this.yGridColor;
- drawLine(this.ctx, this.drawingZone.left, wy, this.drawingZone.right, wy);
- }
- //Long ticks
- this.ctx.strokeStyle = this.yLabelColor;
- drawLine(this.ctx, this.drawingZone.left, wy, this.drawingZone.left - 4, wy);
- //Now the labels
- this.ctx.fillStyle = this.yLabelColor;
- this.ctx.strokeStyle = this.yLabelColor;
- str = "10^{" + p.toFixed(0) + "}";
- this.drawSubSuperScript(this.ctx, str, wx, wy, "right", "middle");
-
- if (p != this.xPowerMax)
- {
- for(var i = 2; i < 10; i++)
- {
- y = p + Utils.log10(i);
- wy = this.getyPix(y);
- //Grid
- this.ctx.strokeStyle = this.yGridColor;
- drawLine(this.ctx, this.drawingZone.left, wy, this.drawingZone.right, wy);
- //Short ticks
- this.ctx.strokeStyle = this.xLabelColor;
- drawLine(this.ctx, this.drawingZone.left, wy, this.drawingZone.left - 2, wy);
- }
- }
- }
- }
-
- this.drawxGrid = function()
- {
- var x;
- var wx;
-
- this.ctx.strokeStyle = this.xGridColor;
-
- if(this.xGridStep > 0)
- {
- for(x = this.xGridMin; x <= this.xGridMax; x += this.xGridStep)
- {
- wx = this.getxPix(x);
- if(wx > this.drawingZone.right)
- wx = this.drawingZone.right;
- drawLine(this.ctx, wx, this.drawingZone.bottom, wx, this.drawingZone.top);
- }
- }
- };
-
- this.drawxLongTicks = function()
- {
- var x;
- var wx;
-
- this.ctx.strokeStyle = this.xLabelColor;
-
- if(this.xLongTickStep > 0)
- {
- for(x = this.xLongTickMin; x <= this.xLongTickMax; x += this.xLongTickStep)
- {
- wx = this.getxPix(x);
- if(wx > this.drawingZone.right)
- wx = this.drawingZone.right;
- drawLine(this.ctx, wx, this.drawingZone.bottom, wx, this.drawingZone.bottom + 4);
- }
- }
- };
-
- this.drawxShortTicks = function()
- {
- var x;
- var wx;
-
- this.ctx.strokeStyle = this.xLabelColor;
-
- if(this.xShortTickStep > 0)
- {
- for(x = this.xShortTickMin; x <= this.xShortTickMax; x += this.xShortTickStep)
- {
- wx = this.getxPix(x);
- if(wx > this.drawingZone.right)
- wx = this.drawingZone.right;
- drawLine(this.ctx, wx, this.drawingZone.bottom, wx, this.drawingZone.bottom + 2);
- }
- }
- };
-
- this.drawyAxis = function()
- {
- this.wx0 = this.getxPix(this.x0);
-
- this.ctx.strokeStyle = this.yAxisColor;
- drawLine(this.ctx, this.wx0, this.drawingZone.bottom, this.wx0, this.drawingZone.top - 6);
- drawLine(this.ctx, this.wx0 - 3, this.drawingZone.top - 3, this.wx0 + 3, this.drawingZone.top - 3);
- drawLine(this.ctx, this.wx0 - 2, this.drawingZone.top - 4, this.wx0 + 2, this.drawingZone.top - 4);
- drawLine(this.ctx, this.wx0 - 1, this.drawingZone.top - 5, this.wx0 + 1, this.drawingZone.top - 5);
- };
-
- this.drawyLongTicks = function()
- {
- var y;
- var wy;
-
- this.ctx.strokeStyle = this.yLabelColor;
-
- if(this.yLongTickStep > 0)
- {
- for(y = this.yLongTickMin; y <= this.yLongTickMax; y += this.yLongTickStep)
- {
- wy = this.getyPix(y);
- if(wy < this.drawingZone.top)
- wy = this.drawingZone.top;
- drawLine(this.ctx, this.drawingZone.left, wy, this.drawingZone.left - 4, wy);
- }
- }
- };
-
- this.drawyShortTicks = function()
- {
- var y;
- var wy;
-
- this.ctx.strokeStyle = this.yLabelColor;
-
- if(this.yShortTickStep > 0)
- {
- for(y = this.yShortTickMin; y <= this.yShortTickMax; y += this.yShortTickStep)
- {
- wy = this.getyPix(y);
- if(wy < this.drawingZone.top)
- wy = this.drawingZone.top;
- drawLine(this.ctx, this.drawingZone.left, wy, this.drawingZone.left - 2, wy);
- }
- }
- };
-
- this.drawyGrid = function()
- {
- var y;
- var wy;
-
- this.ctx.strokeStyle = this.yGridColor;
-
- if(this.yGridStep > 0)
- {
- for(y = this.yGridMin; y <= this.yGridMax; y += this.yGridStep)
- {
- wy = this.getyPix(y);
- if(wy < this.drawingZone.top)
- wy = this.drawingZone.top;
- drawLine(this.ctx, this.drawingZone.left, wy, this.drawingZone.right, wy);
- }
- }
- };
-
- this.drawxLabels = function()
- {
- var x;
- var wx = 0;
- var wy = this.drawingZone.bottom + this.xLabelyOffset;
- //y coordinate of all labels
- var str;
-
- this.ctx.font = "8pt Verdana bold";
- this.ctx.fillStyle = this.xLabelColor;
- this.ctx.strokeStyle = this.xLabelColor;
- this.ctx.textAlign = "center";
- this.ctx.textBaseline = "top";
-
- if(this.automaticxLabels)
- {
- for( x = this.xLabelMin; x <= this.xLabelMax; x += this.xLabelStep)
- {
- wx = this.getxPix(x);
-
- if(Math.abs(x) < 0.00001 && this.formatxzero)
- str = "0";
- else
- str = x.toFixed(this.xLabelDecimalDigits);
-
- //this.ctx.fillText(this.text, xmid, ymid);
- this.ctx.strokeText(str, wx, wy);
- this.ctx.fillText(str, wx, wy);
- }
- }
- }
-
- this.drawxText = function()
- {
- var x;
- var wx = this.drawingZone.right + this.xTextxOffset;
- var wy = this.getyPix(this.y0);
-
- this.ctx.fillStyle = this.xTextColor;
- this.ctx.strokeStyle = this.xTextColor;
- this.drawSubSuperScript(this.ctx, this.xText, wx, wy, "left", "middle", "10pt Verdana bold", "8pt Verdana bold");
- };
-
- this.drawyLabels = function()
- {
- var y;
- var wy = 0;
- var wx = this.drawingZone.left + this.yLabelxOffset;
- var str;
-
- this.ctx.font = "8pt Verdana bold";
- this.ctx.fillStyle = this.yLabelColor;
- this.ctx.strokeStyle = this.yLabelColor;
- this.ctx.textAlign = "right";
- this.ctx.textBaseline = "middle";
-
- if(this.automaticyLabels)
- {
- for( y = this.yLabelMin; y <= this.yLabelMax; y += this.yLabelStep)
- {
- wy = this.getyPix(y);
-
- if(Math.abs(y) < 0.00001 && this.formatyzero)
- str = "0";
- else
- str = y.toFixed(this.yLabelDecimalDigits);
-
- this.ctx.strokeText(str, wx, wy);
- this.ctx.fillText(str, wx, wy);
- }
- }
- };
-
- this.drawyText = function()
- {
- var x;
- var wx = this.getxPix(this.x0);
- var wy = this.drawingZone.top + this.yTextyOffset;
-
- this.ctx.fillStyle = this.yTextColor;
- this.ctx.strokeStyle = this.yTextColor;
- this.drawSubSuperScript(this.ctx, this.yText, wx, wy, "left", "bottom", "10pt Verdana bold", "8pt Verdana bold");
- };
-
- this.parseSubSuperScriptText = function(str)
- {
- /*var regExpSub = /_\{(.*?)\}/g;
- var regExpSup = /\^\{(.*?)\}/g;
- var subs = [];
- var sups = [];
- var text = [];
- var finalText = [];
- var isSub = false;
- var isSup = false;
-
- subs = str.match(regExpSub);
- for (var i = 0; i < subs.length; i++)
- {
- subs[i] = subs[i].substring(2, subs[i].length - 1); //Discard _{ and }
- }
-
- sups = str.match(regExpSup);
- for (var i = 0; i < sups.length; i++)
- {
- sups[i] = sups[i].substring(2, sups[i].length - 1); //Discard ^{ and }
- }*/
-
- var len = str.length;
- var i = 0;
- var start;
- var end;
- found = false;
- var text = [];
- var type;
- var ntext = "";
-
- while (i < len)
- {
- if (str[i] == "_") //Encountered a potential subscript _
- type = "sub";
- else if (str[i] == "^") //Encountered a potential superscript ^
- type = "sup";
-
- if (type == "sub" || type == "sup")
- {
- if (str[i+1] == "{")
- {
- i += 2; //Discard _{ or ^{
- start = i;
- found = false;
- while (i < len) //Look for }
- {
- if (str[i] == "}")
- {
- found = true;
- end = i;
- break;
- }
- i++;
- }
- if (found && end > start) //Discard empty subscript ie _{}
- {
- //Store previous normal text if not empty and tag it as so
- if (ntext.length != 0)
- {
- text.push({s: ntext, type: "normal"});
- ntext = "";
- }
- //Store subscript or superscript and tag it as so
- if (type == "sub")
- text.push({s: str.substring(start, end), type: "sub"});
- else if (type == "sup")
- text.push({s: str.substring(start, end), type: "sup"});
- i = end + 1;
- }
- else
- i = start - 2; //Nothing was found, backtrack to _ or ^
- }
- }
- ntext += str[i];
- if (i == len - 1 && ntext.length != 0) //We've reached the end, store normal text if not empty and tag it as so
- text.push({s: ntext, type: "normal"});
- i++;
- }
-
- return text;
- }
-
- this.subSuperScriptLength = function(c, text, fNormal, fSubSup)
- {
- var fontNormal = fNormal;
- var fontSubSup = fSubSup;
-
- var xpos = 0;
-
- for (var i = 0; i < text.length; i++)
- {
- if (text[i].type == "normal")
- c.font = fontNormal;
- else if (text[i].type == "sub")
- c.font = fontSubSup;
- else
- c.font = fontSubSup;
- xpos += c.measureText(text[i].s).width;
- }
-
- return xpos;
- }
-
- this.drawSubSuperScript = function(c, str, x, y, xway, yway, fNormal, fSubSup)
- {
- var fontNormal = (typeof fNormal == 'undefined') ? "8pt Verdana bold" : fNormal;
- var fontSubSup = (typeof fSubSup == 'undefined') ? "7pt Verdana bold" : fSubSup;
-
- this.ctx.textAlign = "left";
- this.ctx.textBaseline = yway;
-
- var text = this.parseSubSuperScriptText(str);
- var len = this.subSuperScriptLength(c, text, fontNormal, fontSubSup);
- var xposIni = x;
- var yposIni = y;
- var xpos, ypos;
-
- if (xway == "left")
- xpos = xposIni;
- else if (xway == "right")
- xpos = xposIni - len;
- else if (xway == "center")
- xpos = xposIni - len/2;
-
- //Draw the text
- for (var i = 0; i < text.length; i++)
- {
- if (text[i].type == "normal")
- {
- c.font = fontNormal;
- ypos = yposIni;
- }
- else if (text[i].type == "sub")
- {
- c.font = fontSubSup;
- ypos = yposIni + 3;
- }
- else
- {
- c.font = fontSubSup;
- ypos = yposIni - 5;
- }
- c.strokeText(text[i].s, xpos, ypos);
- c.fillText(text[i].s, xpos, ypos);
- //Advance x position
- xpos += c.measureText(text[i].s).width + 2;
- }
- }
-
- this.paint = function()
- {
- //Clears the canvas entirely with background color
- this.ctx.fillStyle = this.canvasColor;
- this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
-
- //Clear drawing zone
- this.ctx.fillStyle = this.drawingZoneColor;
- fillRect(this.ctx, this.drawingZone.left, this.drawingZone.top, this.drawingZone.right, this.drawingZone.bottom);
-
- if (!this.hasxLog)
- {
- if(this.showxGrid)
- this.drawxGrid();
- }
-
- if (!this.hasyLog)
- {
- if(this.showyGrid)
- this.drawyGrid();
- }
-
- if(this.showBorder)
- this.drawBorder();
-
- if (!this.hasxLog)
- {
- if(this.showxShortTicks)
- this.drawxShortTicks();
- if(this.showxLongTicks)
- this.drawxLongTicks();
- if(this.showxLabels)
- this.drawxLabels();
- }
-
- if (!this.hasyLog)
- {
- if(this.showyShortTicks)
- this.drawyShortTicks();
- if(this.showyLongTicks)
- this.drawyLongTicks();
- if(this.showyLabels)
- this.drawyLabels();
- }
-
- if (this.hasxLog)
- this.drawxLog();
-
- if (this.hasyLog)
- this.drawyLog();
-
- if(this.showxAxis)
- this.drawxAxis();
- if(this.showxText)
- this.drawxText();
-
- if(this.showyAxis)
- this.drawyAxis();
- if(this.showyText)
- this.drawyText();
-
-
- };
-
- this.drawCurve = function(f, color)
- {
- var wx, wy;
- var x, y;
-
- this.ctx.strokeStyle = color;
- wx = this.drawingZone.left;
- x = this.getxFromPix(wx);
- y = f(x);
- wy = this.getyPix(y);
-
- this.ctx.beginPath();
- this.ctx.moveTo(wx + 0.5, wy + 0.5);
-
- while(wx < this.drawingZone.right)
- {
- wx++;
- x = this.getxFromPix(wx);
- y = f(x);
- wy = this.getyPix(y);
- this.ctx.lineTo(wx + 0.5, wy + 0.5);
- }
- //this.ctx.closePath();
-
- this.ctx.stroke();
- };
-
- this.drawArray = function(tt, ff, color)
- {
- var wx, wy;
- var x, y;
- var l = tt.length;
- this.ctx.save();
- this.ctx.beginPath();
- this.ctx.rect(this.drawingZone.left, this.drawingZone.top, this.drawingZone.width, this.drawingZone.height);
- this.ctx.clip();
- this.ctx.strokeStyle = color;//"rgb(256, 0, 0)";// Color.orange; //yellow, orange, red, magenta, violet, blue, cyan, green
-
- wx = this.getxPix(tt[0]);
- wy = this.getyPix(ff[0]);
- this.ctx.beginPath();
- this.ctx.moveTo(wx + 0.5, wy + 0.5);
-
- for (var i = 0; i < l; i++)
- {
- wx = this.getxPix(tt[i]);
- wy = this.getyPix(ff[i]);
- //this.ctx.lineTo(wx + 0.5, wy + 0.5);
- this.ctx.lineTo(wx, wy);
- }
-
- //this.ctx.closePath();
-
- this.ctx.stroke();
- this.ctx.restore();
- };
-
- this.drawPoint = function(x, y, color)
- {
- this.ctx.fillStyle = color;
- drawPoint(this.ctx, this.getxPix(x), this.getyPix(y), 4);
- };
-
- this.drawHollowPoint = function(x, y, color)
- {
- this.ctx.strokeStyle = color;
- drawHollowPoint(this.ctx, this.getxPix(x), this.getyPix(y), 4);
- };
-
- this.drawDiamond = function(x, y, color)
- {
- this.ctx.fillStyle = color;
- drawDiamond(this.ctx, this.getxPix(x), this.getyPix(y), 4);
- };
-
- this.drawX = function(x, y, color)
- {
- this.ctx.strokeStyle = color;
- drawX(this.ctx, this.getxPix(x), this.getyPix(y), 4);
- };
-
- this.drawLine = function(x1, y1, x2, y2, color)
- {
- this.ctx.strokeStyle = color;
- drawLine(this.ctx, this.getxPix(x1), this.getyPix(y1), this.getxPix(x2), this.getyPix(y2));
- };
-
- this.drawArrow = function(x1, y1, x2, y2, color)
- {
- this.ctx.strokeStyle = color;
- this.ctx.fillStyle = color;
- drawArrow(this.ctx, this.getxPix(x1), this.getyPix(y1), this.getxPix(x2), this.getyPix(y2), 5, 10);
- };
-
- this.getxPix = function(x)
- {
- return Math.round(this.drawingZone.left + this.drawingZone.width * (x - this.xmin) / this.xspan);
- };
-
- this.getyPix = function(y)
- {
- return Math.round(this.drawingZone.bottom - this.drawingZone.height * (y - this.ymin) / this.yspan);
- };
-
- this.getxFromPix = function(wx)
- {
- return (this.xmin + this.xspan * (wx - this.drawingZone.left) / this.drawingZone.width);
- };
-
- this.getyFromPix = function(wy)
- {
- return (this.ymin + this.yspan * (this.drawingZone.bottom - wy) / this.drawingZone.height);
- };
-
- this.isInside = function(x, y)
- {
- if((this.drawingZone.left <= x) && (x <= this.drawingZone.right) && (this.drawingZone.top <= y) && (y <= this.drawingZone.bottom))
- return true;
- else
- return false;
- };
-
- this.inBounds = function(x, y)
- {
- if((this.xmin <= x) && (x <= this.xmax) && (this.ymin <= y) && (y <= this.ymax))
- return true;
- else
- return false;
- };
- }
-
- //////////PUBLIC FIELDS AND METHODS//////////
- return {
-
- Utils: Utils,
- Color: Color,
- DrawingZone: DrawingZone,
- Graph: Graph,
- };
-}());
diff --git a/common/test/data/full/static/js/sound_labs/rc_filters.js b/common/test/data/full/static/js/sound_labs/rc_filters.js
deleted file mode 100644
index caa3781b4a..0000000000
--- a/common/test/data/full/static/js/sound_labs/rc_filters.js
+++ /dev/null
@@ -1,938 +0,0 @@
-$(document).ready(function()
-{
- //The try catch block checks if canvas and audio libraries are present. If not, we exit and alert the user.
- try
- {
- //Add corresponding listener to various UI elements
- $('#musicTypeSelect').change(onSelectChange);
- $('input:checkbox').click(checkboxClicked);
- $('input:radio').click(radioButtonClicked);
- $('#playButton').click(playButtonClicked);
- initSound();
- initDiagram();
- initGraphs();
- setTimeGraph();
- setMagGraph();
- setPhaseGraph();
- generateBuffer();
- calculateSignals();
- draw();
- labEnabled = true;
- $("#graphTabs").tabs();
- $("#graphTabs").bind("tabsselect", tabSelected);
- }
- catch(err)
- {
- labEnabled = false;
- alert(err + " The tool is disabled.");
- }
-});
-
-function initGraphs()
-{
- //Test if canvas is supported. If not, exit.
- var testCanvas = document.createElement("canvas")
- if (!testCanvas.getContext)
- throw "Canvas element is not supported in this browser."
-
- //Time graph
- //Get canvas
- var timeCanvas = $('#time')[0];
- //To disable text selection outside the canvas
- timeCanvas.onselectstart = function(){return false;};
- //Create an offscreen buffer
- var timeBuffer = document.createElement('canvas');
- timeBuffer.width = timeCanvas.width;
- timeBuffer.height = timeCanvas.height;
- timeGraph = new Plotter.Graph(50, 50, 400, 400, timeCanvas, timeBuffer);
-
- //Magnitude graph
- //Get canvas
- var magCanvas = $('#magnitude')[0];
- //To disable text selection outside the canvas
- magCanvas.onselectstart = function(){return false;};
- //Create an offscreen buffer
- var magBuffer = document.createElement('canvas');
- magBuffer.width = magCanvas.width;
- magBuffer.height = magCanvas.height;
- magGraph = new Plotter.Graph(50, 50, 400, 400, magCanvas, magBuffer);
-
- //Phase graph
- //Get canvas
- var phaseCanvas = $('#phase')[0];
- //To disable text selection outside the canvas
- phaseCanvas.onselectstart = function(){return false;};
- //Create an offscreen buffer
- var phaseBuffer = document.createElement('canvas');
- phaseBuffer.width = phaseCanvas.width;
- phaseBuffer.height = phaseCanvas.height;
- phaseGraph = new Plotter.Graph(50, 50, 400, 400, phaseCanvas, phaseBuffer);
-}
-
-var diagram, VIn, R, C;
-
-function initDiagram()
-{
- //Test if canvas is supported. If not, exit.
- var testCanvas = document.createElement("canvas")
- if (!testCanvas.getContext)
- throw "Canvas element is not supported in this browser."
-
- var element = $('#diag2');
- diagram = new Circuit.Diagram(element, true);
-
- //Lines
- var wirev1 = diagram.addWire(100, 295, 100, 325);
- var wirev2 = diagram.addWire(100, 140, 100, 170);
- var wirev3 = diagram.addWire(380, 295, 380, 325);
- var wirev4 = diagram.addWire(380, 140, 380, 170);
- var wireh1 = diagram.addWire(100, 140, 145, 140);
- var wireh2 = diagram.addWire(285, 140, 333, 140);
- var wireh3 = diagram.addWire(100, 355, 240, 355);
-
- var rLabel = diagram.addLabel(205, 75, "\u002B v_{R} \u2212", "left");
- var cLabelPlus = diagram.addLabel(305, 225, "\u002B", "left");
- var cLabel = diagram.addLabel(305, 250, "v_{C}", "left");
- var cLabelMinus = diagram.addLabel(305, 270, "\u2212", "left");
- rLabel.color = Plotter.Color.lightgreen;
- cLabelPlus.color = Plotter.Color.lightyellow;
- cLabel.color = Plotter.Color.lightyellow;
- cLabelMinus.color = Plotter.Color.lightyellow;
-
- //Ground
- var ground = diagram.addGround(240, 355);
-
- //Resistor
- R = diagram.addResistor(190, 140, 1);
- R.rotation = Math.PI/2;
- R.label.str = "R";
- R.valueString.suffix = "k\u03A9";
-
- //Capacitor
- C = diagram.addCapacitor(380, 200, 110);
- C.label.str = "C";
- C.valueString.suffix = "nF";
-
- //Voltage source
- VIn = diagram.addSource(100, 200, 3, "v");
- VIn.label.str = "v_{IN}";
- VIn.valueString.suffix = "V";
- VIn.label.color = Plotter.Color.lightblue;
- VIn.valueString.color = Plotter.Color.lightblue;
-
- //diagram.showGrid = true;
- diagram.paint();
-}
-
-function setTimeGraph()
-{
- var lticks = 1;
- var sticks = 0.5;
- //x axis
- timeGraph.xText = xLab;
- timeGraph.yText = "V_{MAX} (Volts)";
- timeGraph.xmin = 0;
- timeGraph.xmax = maxTime;
- timeGraph.xspan = maxTime;
- timeGraph.xShortTickMin = 0;
- timeGraph.xShortTickMax = maxTime;
- timeGraph.xShortTickStep = maxTime/20;
- timeGraph.xLongTickMin = 0;
- timeGraph.xLongTickMax = maxTime;
- timeGraph.xLongTickStep = maxTime/10;
- timeGraph.xLabelMin = 0;
- timeGraph.xLabelMax = maxTime;
- timeGraph.xLabelStep = maxTime/10;
- timeGraph.xGridMin = 0;
- timeGraph.xGridMax = maxTime;
- timeGraph.xGridStep = maxTime/10;
- //y axis
- timeGraph.ymin = -maxVolt;
- timeGraph.ymax = maxVolt;
- timeGraph.yspan = 2*maxVolt;
- timeGraph.yShortTickMin = -maxVolt + (maxVolt % sticks);
- timeGraph.yShortTickMax = maxVolt - (maxVolt % sticks);
- timeGraph.yShortTickStep = sticks;
- timeGraph.yLongTickMin = -maxVolt + (maxVolt % lticks);
- timeGraph.yLongTickMax = maxVolt - (maxVolt % lticks);
- timeGraph.yLongTickStep = lticks;
- timeGraph.yLabelMin = -maxVolt + (maxVolt % lticks);
- timeGraph.yLabelMax = maxVolt - (maxVolt % lticks);
- timeGraph.yLabelStep = lticks;
- timeGraph.yGridMin = -maxVolt + (maxVolt % lticks);
- timeGraph.yGridMax = maxVolt - (maxVolt % lticks);
- timeGraph.yGridStep = lticks;
-}
-
-function setMagGraph()
-{
- var lticks = 1;
- var sticks = 0.5;
- //x axis
- magGraph.xText = "f (Hz)";
- magGraph.yText = "Magnitude (dB)";
- magGraph.xmin = -1;
- magGraph.xmax = 5;
- magGraph.xspan = 6;
- magGraph.xPowerMin = -1;
- magGraph.xPowerMax = 5;
-
- //y axis
- magGraph.ymin = -100;
- magGraph.ymax = 10;
- magGraph.yspan = 110;
- magGraph.yShortTickMin = -100;
- magGraph.yShortTickMax = 10;
- magGraph.yShortTickStep = 5;
- magGraph.yLongTickMin = -100;
- magGraph.yLongTickMax = 10;
- magGraph.yLongTickStep = 10;
- magGraph.yLabelMin = -100;
- magGraph.yLabelMax = 10;
- magGraph.yLabelStep = 10;
- magGraph.yGridMin = -100;
- magGraph.yGridMax = 10;
- magGraph.yGridStep = 10;
- magGraph.x0 = magGraph.xPowerMin;
- magGraph.y0 = magGraph.ymin;
- magGraph.hasxLog = true;
- magGraph.hasxPowers = true;
- magGraph.hasyLog = false;
- magGraph.hasyPowers = false;
-}
-
-function setPhaseGraph()
-{
- var lticks = 1;
- var sticks = 0.5;
- //x axis
- phaseGraph.xText = "f (Hz)";
- phaseGraph.yText = "Phase (degrees)";
- phaseGraph.xmin = -1;
- phaseGraph.xmax = 5;
- phaseGraph.xspan = 6;
- phaseGraph.xPowerMin = -1;
- phaseGraph.xPowerMax = 5;
-
- //y axis
- phaseGraph.ymin = -100;
- phaseGraph.ymax = 100;
- phaseGraph.yspan = 200;
- phaseGraph.yShortTickMin = -100;
- phaseGraph.yShortTickMax = 100;
- phaseGraph.yShortTickStep = 5;
- phaseGraph.yLongTickMin = -100;
- phaseGraph.yLongTickMax = 100;
- phaseGraph.yLongTickStep = 10;
- phaseGraph.yLabelMin = -100;
- phaseGraph.yLabelMax = 100;
- phaseGraph.yLabelStep = 10;
- phaseGraph.yGridMin = -100;
- phaseGraph.yGridMax = 100;
- phaseGraph.yGridStep = 10;
- phaseGraph.x0 = phaseGraph.xPowerMin;
- phaseGraph.y0 = phaseGraph.ymin;
- phaseGraph.hasxLog = true;
- phaseGraph.hasxPowers = true;
- phaseGraph.hasyLog = false;
- phaseGraph.hasyPowers = false;
-}
-
-function generateBuffer()
-{
- timeGraph.paintOn("buffer");
- timeGraph.paint();
- magGraph.paintOn("buffer");
- magGraph.paint();
- phaseGraph.paintOn("buffer");
- phaseGraph.paint();
-}
-
-function draw()
-{
- //Paint buffer on canvas
- timeGraph.paintBuffer();
-
- //Draw on canvas
- timeGraph.paintOn("canvas"); //Draw on screen image
-
- if (vinChecked)
- timeGraph.drawArray(time, insig, Plotter.Color.lightblue);
- if (vcChecked)
- timeGraph.drawArray(time, csig, Plotter.Color.lightyellow);
- if (vrChecked)
- timeGraph.drawArray(time, rsig, Plotter.Color.lightgreen);
-
- magGraph.paintBuffer();
- magGraph.paintOn("canvas");
- if (vcChecked)
- magGraph.drawArray(frequencies, cmag, Plotter.Color.lightyellow);
- if (vrChecked)
- magGraph.drawArray(frequencies, rmag, Plotter.Color.lightgreen);
-
- phaseGraph.paintBuffer();
- phaseGraph.paintOn("canvas");
- if (vcChecked)
- phaseGraph.drawArray(frequencies, cphase, Plotter.Color.lightyellow);
- if (vrChecked)
- phaseGraph.drawArray(frequencies, rphase, Plotter.Color.lightgreen);
-}
-
-function initSound()
-{
- sp = new Sound.Player();
- sp.soundStarted = function()
- {
- $('#playButton').prop('value', "Stop");
- }
-
- sp.soundStopped = function()
- {
- $('#playButton').prop('value', "Play");
- }
-}
-
-function communSlide()
-{
- if (labEnabled)
- {
- if (sp.isPlaying)
- sp.stopTone();
- calculateSignals();
- draw();
- diagram.paint();
- fc = getfCutoff(r, c);
- $("#fc").html("fC = " + fc.toFixed(0) + " Hz");
- }
-}
-
-$(function()
-{
- fc = getfCutoff(r, c);
- $("#fc").html("fC = " + fc.toFixed(0) + " Hz");
- $("#vinSlider").slider({value: vIn, min: 0, max: 5, step: 0.01,
- slide: function(event, ui)
- {
- $("#vin").html("vIN = " + ui.value + " V");
- vIn = ui.value;
- VIn.value = vIn;
- communSlide();
- }
- });
- $("#vin").html("vIN = " + $("#vinSlider").slider("value") + " V");
-
- $("#freqSlider").slider({value: freq, min: 100, max: 5000, step: 100,
- slide: function(event, ui)
- {
- $("#freq").html("Frequency = " + ui.value + " Hz");
- freq = ui.value;
- communSlide();
- }
- });
- $("#freq").html("Frequency = " + $("#freqSlider").slider("value") + " Hz");
-
- $("#vbiasSlider").slider({value: vBias, min: -5, max: 5, step: 0.01,
- slide: function(event, ui)
- {
- $("#vbias").html("VBIAS = " + ui.value + " V");
- vBias = ui.value;
- communSlide();
- }
- });
- $("#vbias").html("VBIAS = " + $("#vbiasSlider").slider("value") + " V");
-
- $("#rSlider").slider({value: 1, min: 0.1, max: 10, step: 0.01,
- slide: function(event, ui)
- {
- //Values of slider are in Kilo Ohms
- var val = getResistance(ui.value);
- $(this).slider("value", val);
- if (val >= 1.0) //kOhms
- {
- $("#r").html("R = " + val + " kΩ");
- R.value = val;
- R.valueString.suffix = "k\u03A9";
- }
- else
- {
- $("#r").html("R = " + kiloToUnit(val) + " Ω");
- R.value = kiloToUnit(val);
- R.valueString.suffix = "\u03A9";
- }
-
- r = kiloToUnit(val);
- communSlide();
- //return false; //Blocks keystrokes if enabled
- }
- });
- $("#r").html("R = " + $("#rSlider").slider("value") + " kΩ");
-
- $("#vc0Slider").slider({value: vC0, min: 0, max: 5, step: 0.01,
- slide: function(event, ui)
- {
- $("#vc0").html("vC (0) = " + ui.value + " V");
- vC0 = ui.value;
- communSlide();
- }
- });
- $("#vc0").html("vC (0) = " + $("#vc0Slider").slider("value") + " V");
-
- $("#cSlider").slider({value: 110, min: 0, max: 1000, step: 1,
- slide: function(event, ui)
- {
- //Values of slider are in nano Farad
- var val = getCapacitance(ui.value);
- $(this).slider("value", val);
- if (val >= 1000)
- {
- $("#c").html("C = " + nanoToMicro(val) + " μF");
- C.value = nanoToMicro(val);
- C.valueString.suffix = "\u03BCF";
- }
- else
- {
- $("#c").html("C = " + val + " nF");
- C.value = val;
- C.valueString.suffix = "nF";
- }
-
- c = nanoToUnit(val);
- communSlide();
- //return false; //Blocks keystrokes if enabled
- }
- });
- $("#c").html("C = " + $("#cSlider").slider("value") + " nF");
- $("#vmaxSlider" ).slider({value: vMax, min: 1, max: 20, step: 0.1,
- slide: function(event, ui)
- {
- $("#vmax").html("VMAX = " + ui.value + " V");
- maxVolt = ui.value;
- if (labEnabled)
- {
- if (sp.isPlaying)
- sp.stopTone();
- setTimeGraph();
- generateBuffer();
- calculateSignals();
- draw();
- }
- }
- });
- $("#vmax").html("VMAX = " + $("#vmaxSlider").slider("value") + " V");
-});
-
-function getCheckboxesState()
-{
- if($('#vinCheckbox').prop('checked'))
- vinChecked = true;
- else
- vinChecked = false;
- if($('#vcCheckbox').prop('checked'))
- vcChecked = true;
- else
- vcChecked = false;
- if($('#vrCheckbox').prop('checked'))
- vrChecked = true;
- else
- vrChecked = false;
-}
-
-function getRadioButtonsState()
-{
- if($('#vinRadioButton').prop('checked'))
- sp.inSignal.listen = true;
- else
- sp.inSignal.listen = false;
- if($('#vcRadioButton').prop('checked'))
- sp.outSignals[0].listen = true;
- else
- sp.outSignals[0].listen = false;
- if($('#vrRadioButton').prop('checked'))
- sp.outSignals[1].listen = true;
- else
- sp.outSignals[1].listen = false;
-}
-
-function onSelectChange()
-{
- if (labEnabled)
- {
- musicType = $("#musicTypeSelect").val();
- sp.stopTone();
- if (musicType == 0) //Zero Input
- {
- $("#vinSlider").slider( "option", "disabled", true);
- $("#freqSlider").slider( "option", "disabled", true);
- maxTime = 10; //ms
- xLab = "t (ms)";
- musicLoaded();
- }
- else if (musicType == 1) //Unit Impulse
- {
- $("#vinSlider").slider( "option", "disabled", true);
- $("#freqSlider").slider( "option", "disabled", true);
- maxTime = 10; //ms
- xLab = "t (ms)";
- musicLoaded();
- }
- else if (musicType == 2) //Unit Step
- {
- $("#vinSlider").slider( "option", "disabled", true);
- $("#freqSlider").slider( "option", "disabled", true);
- maxTime = 10; //ms
- xLab = "t (ms)";
- musicLoaded();
- }
- if (musicType == 3) //Sine Wave
- {
- $("#vinSlider").slider( "option", "disabled", false);
- $("#freqSlider").slider( "option", "disabled", false);
- maxTime = 10; //ms
- xLab = "t (ms)";
- musicLoaded();
- }
- else if (musicType == 4) //Square Wave
- {
- $("#vinSlider").slider( "option", "disabled", false);
- $("#freqSlider").slider( "option", "disabled", false);
- maxTime = 10; //ms
- xLab = "t (ms)";
- musicLoaded();
- }
- else if (musicType == 5 || musicType == 6 || musicType == 7 || musicType == 8) //Music
- {
- $("#vinSlider").slider( "option", "disabled", false);
- $("#freqSlider").slider( "option", "disabled", true);
- maxTime = 20; //s
- xLab = "t (s)";
-
- if (musicType == 5)
- sp.load("classical.wav", musicLoaded);
- else if (musicType == 6)
- sp.load("folk.wav", musicLoaded);
- else if (musicType == 7)
- sp.load("jazz.wav", musicLoaded);
- else
- sp.load("reggae.wav", musicLoaded);
- }
- }
-}
-
-function tabSelected(event, ui)
-{
- if (ui.index == 0)
- {
- //Time, renable all sliders
- $("#vinSlider").slider("option", "disabled", false);
- $("#freqSlider").slider("option", "disabled", false);
- $("#vbiasSlider").slider("option", "disabled", false);
- $("#vc0Slider").slider("option", "disabled", false);
- $("#vmaxSlider" ).slider("option", "disabled", false);
- //And vinCheckbox
- $('#vinCheckbox').attr("disabled", false);
-
- }
- else if (ui.index == 1 || ui.index == 2)
- {
- //Magnitude or phase, disable elements that have no effect on graphs
- $("#vinSlider").slider("option", "disabled", true);
- $("#freqSlider").slider("option", "disabled", true);
- $("#vbiasSlider").slider("option", "disabled", true);
- $("#vc0Slider").slider("option", "disabled", true);
- $("#vmaxSlider" ).slider("option", "disabled", true);
- $('#vinCheckbox').attr("disabled", true);
- }
-
-}
-
-function musicLoaded()
-{
- setTimeGraph();
- generateBuffer();
- calculateSignals();
- draw();
-}
-
-function checkboxClicked()
-{
- if (labEnabled)
- {
- getCheckboxesState();
- draw();
- }
-}
-
-function radioButtonClicked()
-{
- if (labEnabled)
- {
- if (sp.isPlaying)
- sp.stopTone();
- getRadioButtonsState();
- }
-}
-
-function playButtonClicked()
-{
- if (labEnabled)
- {
- if (sp.isPlaying)
- sp.stopTone();
- else
- sp.playTone();
- }
-}
-
-//TO DO: PUT ALL THE FOLLOWING GLOBAL VARIABLES IN A NAMESPACE
-var labEnabled = true;
-//Graph
-var timeGraph, magGraph, phaseGraph;
-var maxTime = 10; //In ms
-var xLab = "t (ms)";
-var maxVolt = 2;
-var time;
-var insig, csig, rsig, frequencies, cmag, rmag, cphase, rphase;
-//Sound Player
-var sp;
-
-//Drop variable down for Type of Input
-var musicType = 3;
-//Checkboxes variables for Graph
-var vinChecked = true;
-var vcChecked = true;
-var vrChecked = false;
-//Slider variables
-var vIn = 3.0;
-var vInMax = 5.0;
-var freq = 1000;
-var vBias = 0.0;
-var r = kiloToUnit(1);
-var vC0 = 0.0;
-var c = nanoToUnit(110);
-var vMax = 2;
-var fc;
-
-function calculateSignals()
-{
- if (musicType == 0 || musicType == 1 || musicType == 2 || musicType == 3)
- {
- sp.soundLength = 1;
- sp.sampleRate = 50000;
- }
- else if (musicType == 4)
- {
- sp.soundLength = 1;
- sp.sampleRate = 88200;
- }
- else if (musicType == 5 || musicType == 6 || musicType == 7 || musicType == 8) //Classical, Folk, Jazz, Reggae
- {
- sp.soundLength = 20;
- sp.sampleRate = 22050;
- }
-
- sp.createBuffers(2); //We have two outputs, first one is the voltage across capacitor C, the second across resistor R
- getRadioButtonsState(); //Set what we are listening to, input, or one of the above
-
- if (musicType == 0) //Zero Input
- sp.generateZero();
- else if (musicType == 1) //Unit Impulse
- sp.generateUnitImpulse();
- else if (musicType == 2) //Unit Step
- sp.generateUnitStep();
- else if (musicType == 3) //Sine Wave
- sp.generateSineWave(vIn, freq, vBias);
- else if (musicType == 4) //Square Wave
- sp.generateSquareWave(vIn, freq, vBias);
- else if (musicType == 5 || musicType == 6 || musicType == 7 || musicType == 8) //Classical, Folk, Jazz, Reggae
- {
- //TO DO: MOVE OUT
- var max = Number.NEGATIVE_INFINITY;
- var amp = 0.0;
-
- //Find the max and normalize
- for (var i = 0, l = sp.inSignal.data.length; i < l; i++)
- {
- amp = Math.abs(sp.audioData[i]);
- if (amp > max)
- max = amp;
- }
- max /= 0.5;
- if (vBias != 0.0)
- {
- if (max != 0.0)
- {
- for (var i = 0, l = sp.inSignal.data.length; i < l; i++)
- {
- sp.inSignal.data[i] = vBias + vIn*sp.audioData[i] / max;
- }
- }
- else //Fill in with vBias
- {
- for (var i = 0, l = sp.inSignal.data.length; i < l; i++)
- {
- sp.inSignal.data[i] = vBias;
- }
- }
- }
- else
- {
- if (max != 0.0)
- {
- for (var i = 0, l = sp.inSignal.data.length; i < l; i++)
- {
- sp.inSignal.data[i] = vIn*sp.audioData[i] / max;
- }
- }
- else //Fill in with zeros
- {
- for (var i = 0, l = sp.inSignal.data.length; i < l; i++)
- {
- sp.inSignal.data[i] = 0.0;
- }
- }
- }
- }
- getVRVC(sp.inSignal.data, sp.outSignals[0].data, sp.outSignals[1].data, r, c, vC0, sp.sampleRate);
-
- time = [];
- insig = [];
- csig = [];
- rsig = [];
- frequencies = [];
- cmag = [];
- rmag = [];
- cphase = [];
- rphase = [];
- var i = 0;
- var ii;
- var imult;
- var imax;
- var x = 0;
- var xinc;
-
- //Scale of graph is 500 px
- //All generated sound (sine wave etc.) except square wave have sampling rate of 50000 Hz, length 1s. We will plot the first 10 ms. That's 500 samples for 10 ms and 500 px
- if (musicType == 0 || musicType == 1 || musicType == 2 || musicType == 3)
- {
- xinc = 10/500;
- imax = 500;
- imult = 1;
- }
- else if (musicType == 4) //At 50000 Hz, square wave plays very poorly, we use 88200 Hz
- {
- xinc = 10/882;
- imax = 882;
- imult = 1;
- }
- else if (musicType == 5 || musicType == 6 || musicType == 7 || musicType == 8) //All music files have a sampling rate 22050 Hz, length 20s. 20s/500px --> get value every 0.04 s ie every 882 samples.
- {
- xinc = 20/500;
- imax = 500;
- imult = 882;
- }
-
- while (i <= imax)
- {
- ii = imult*i;
- time[i] = x;
- insig[i] = sp.inSignal.data[ii];
- csig[i] = sp.outSignals[0].data[ii];
- rsig[i] = sp.outSignals[1].data[ii];
- x += xinc;
- i++;
- }
-
- sp.normalizeAllSounds();
-
- //Bode plots
- fc = getfCutoff(r, c);
- var df = magGraph.xspan / 500; //magGraph is 500 pix large
- var fp = magGraph.xmin;
- var f;
-
- //Scale of magGraph is 500 px
- for (var i = 0; i <= 500; i++)
- {
- frequencies[i] = fp;
- f = Math.pow(10, fp);
- cmag[i] = getGainC_DB(f, fc);
- rmag[i] = getGainR_DB(f, fc);
- cphase[i] = getPhaseC(f, fc);
- rphase[i] = getPhaseR(f, fc);
- fp += df;
- }
-}
-
-//Constants
-var TWO_PI = 2.0*Math.PI;
-var PI_DIV_2 = Math.PI/2.0;
-//var tau = R*C; //tau: Time constant
-
-var resistance = [0.1, 0.11, 0.12, 0.13, 0.15, 0.16, 0.18, 0.2, 0.22, 0.24, 0.27, 0.3, 0.33, 0.36, 0.39, 0.43, 0.47, 0.51, 0.56, 0.62, 0.68, 0.75, 0.82, 0.91, 1, 1.1, 1.2, 1.3, 1.50, 1.6, 1.8, 2, 2.2, 2.4, 2.7, 3, 3.3, 3.6, 3.9, 4.3, 4.7, 5.1, 5.6, 6.2, 6.8, 7.5, 8.2, 9.1, 10];
-
-var capacitance = [10, 11, 12, 13, 15, 16, 18, 20, 22, 24, 27, 30, 33, 36, 39, 43, 47, 51, 56, 62, 68, 75, 82, 91, 100, 110, 120, 130, 150, 160, 180, 200, 220, 240, 270, 300, 330, 360, 390, 430, 470, 510, 560, 620, 680, 750, 820, 910, 1000];
-
-function getResistance(value)
-{
- var distance;
- var minDistance = Number.POSITIVE_INFINITY;
- var minIndex;
-
- for (var i = 0, l = resistance.length; i < l; i++)
- {
- distance = Math.abs(value - resistance[i]);
- if (distance < minDistance)
- {
- minDistance = distance;
- minIndex = i;
- }
- }
- return resistance[minIndex];
-}
-
-function getCapacitance(value)
-{
- var distance;
- var minDistance = Number.POSITIVE_INFINITY;
- var minIndex;
-
- for (var i = 0, l = capacitance.length; i < l; i++)
- {
- distance = Math.abs(value - capacitance[i]);
- if (distance < minDistance)
- {
- minDistance = distance;
- minIndex = i;
- }
- }
- return capacitance[minIndex];
-}
-
-function unitToKilo(u)
-{
- return u/1000;
-}
-
-function unitToMicro(u)
-{
- return u*1000000;
-}
-
-function unitToNano(u)
-{
- return u*1000000000;
-}
-
-function unitToPico(u)
-{
- return u*1000000000000;
-}
-
-function kiloToUnit(k)
-{
- return k*1000;
-}
-
-function microToUnit(m)
-{
- return m/1000000;
-}
-
-function nanoToUnit(n)
-{
- return n/1000000000;
-}
-
-function picoToUnit(p)
-{
- return p/1000000000000;
-}
-
-function nanoToMicro(p)
-{
- return p/1000;
-}
-
-//vIN - vOut = RC dvOUT/dt
-//xi = vIN
-//yi = vOUT
-
-//yi = alpha*x[i] + (1 - alpha)y[i-1] with alpha = dt/(RC + dt). dt is the sampling period. 0 <= alpha <= 1 is the smoothing factor. Exponentially-weighted moving average
-
-function getVRVC(inData, outData, rData, R, C, VC0, sampleRate)
-{
- var dt = 1.0 / sampleRate;
- var alpha = dt/(R*C + dt);
-
- if (musicType != 1)
- {
- outData[0] = VC0;
- rData[0] = inData[0] - outData[0];
- }
- else //Unit Impulse
- {
- outData[0] = inData[0];
- rData[0] = -inData[0];
- }
-
- for (var i = 1, l = outData.length; i < l; i++)
- {
- outData[i] = outData[i-1] + alpha * (inData[i] - outData[i-1]);
- rData[i] = inData[i] - outData[i];
- }
-}
-
-function getfCutoff(R, C)
-{
- return 1.0/(TWO_PI*R*C);
-}
-
-function radToDeg(angle)
-{
- return angle*180.0/Math.PI;
-}
-
-function degToRad(angle)
-{
- return angle*Math.PI/180.0;
-}
-
-//db for voltages: 20*log(|gain|)
-
-//LOW PASS FILTER: vC
-//Complex Gain is 1/(1+j(f/fc))
-function getGainC(f, fc)
-{
- var frac = f/fc;
- return 1.0/Math.sqrt(1.0 + frac*frac);
-}
-
-function getGainC_DB(f, fc)
-{
- var frac = f/fc;
- return -20.0*Plotter.Utils.log10(Math.sqrt(1.0 + frac*frac));
-}
-
-function getPhaseC(f, fc)
-{
- return radToDeg(-Math.atan2(f/fc, 1.0));
-}
-
-//HIGH PASS FILTER: vR
-//Complex Gain is j(f/fc)/(1+j(f/fc))
-function getGainR(f, fc)
-{
- var frac = f/fc;
- return frac/Math.sqrt(1.0 + frac*frac);
-}
-
-function getGainR_DB(f, fc)
-{
- var frac = f/fc;
- return 20.0*(Plotter.Utils.log10(frac) - Plotter.Utils.log10(Math.sqrt(1.0 + frac*frac)));
-}
-
-function getPhaseR(f, fc)
-{
- return radToDeg(PI_DIV_2 - Math.atan2(f/fc, 1.0));
-}
diff --git a/common/test/data/full/static/js/sound_labs/series_rlc.js b/common/test/data/full/static/js/sound_labs/series_rlc.js
deleted file mode 100644
index a5e1b02e38..0000000000
--- a/common/test/data/full/static/js/sound_labs/series_rlc.js
+++ /dev/null
@@ -1,1150 +0,0 @@
-$(document).ready(function()
-{
- //The try catch block checks if canvas and audio libraries are present. If not, we exit and alert the user.
- try
- {
- //Add corresponding listener to various UI elements
- $('#musicTypeSelect').change(onSelectChange);
- $('input:checkbox').click(checkboxClicked);
- $('input:radio').click(radioButtonClicked);
- $('#playButton').click(playButtonClicked);
- initSound();
- initDiagram();
- initGraphs();
- setTimeGraph();
- setMagGraph();
- setPhaseGraph();
- generateBuffer();
- calculateSignals();
- draw();
- labEnabled = true;
- $("#graphTabs").tabs();
- $("#graphTabs").bind("tabsselect", tabSelected);
- }
- catch(err)
- {
- labEnabled = false;
- alert(err + " The tool is disabled.");
- }
-});
-
-function initGraphs()
-{
- //Test if canvas is supported. If not, exit.
- var testCanvas = document.createElement("canvas")
- if (!testCanvas.getContext)
- throw "Canvas element is not supported in this browser."
-
- //Time graph
- //Get canvas
- var timeCanvas = $('#time')[0];
- //To disable text selection outside the canvas
- timeCanvas.onselectstart = function(){return false;};
- //Create an offscreen buffer
- var timeBuffer = document.createElement('canvas');
- timeBuffer.width = timeCanvas.width;
- timeBuffer.height = timeCanvas.height;
- timeGraph = new Plotter.Graph(50, 50, 400, 400, timeCanvas, timeBuffer);
-
- //Magnitude graph
- //Get canvas
- var magCanvas = $('#magnitude')[0];
- //To disable text selection outside the canvas
- magCanvas.onselectstart = function(){return false;};
- //Create an offscreen buffer
- var magBuffer = document.createElement('canvas');
- magBuffer.width = magCanvas.width;
- magBuffer.height = magCanvas.height;
- magGraph = new Plotter.Graph(50, 50, 400, 400, magCanvas, magBuffer);
-
- //Phase graph
- //Get canvas
- var phaseCanvas = $('#phase')[0];
- //To disable text selection outside the canvas
- phaseCanvas.onselectstart = function(){return false;};
- //Create an offscreen buffer
- var phaseBuffer = document.createElement('canvas');
- phaseBuffer.width = phaseCanvas.width;
- phaseBuffer.height = phaseCanvas.height;
- phaseGraph = new Plotter.Graph(50, 50, 400, 400, phaseCanvas, phaseBuffer);
-}
-
-var diagram, VIn, R, L, C;
-
-function initDiagram()
-{
- //Test if canvas is supported. If not, exit.
- var testCanvas = document.createElement("canvas")
- if (!testCanvas.getContext)
- throw "Canvas element is not supported in this browser."
-
- var element = $('#diag3');
- diagram = new Circuit.Diagram(element, true);
-
- //Lines
- var wirev1 = diagram.addWire(100, 295, 100, 325);
- var wirev2 = diagram.addWire(100, 140, 100, 170);
- var wirev3 = diagram.addWire(380, 295, 380, 325);
- var wirev4 = diagram.addWire(380, 140, 380, 170);
- var wireh1 = diagram.addWire(100, 140, 115, 140);
- var wireh2 = diagram.addWire(225, 140, 240, 140);
- var wireh3 = diagram.addWire(350, 140, 365, 140);
- var wireh4 = diagram.addWire(100, 355, 240, 355);
-
- var rLabel = diagram.addLabel(145, 75, "\u002B v_{R} \u2212", "left");
- var lLabel = diagram.addLabel(275, 75, "\u002B v_{L} \u2212", "left");
- var cLabelPlus = diagram.addLabel(305, 225, "\u002B", "left");
- var cLabel = diagram.addLabel(305, 250, "v_{C}", "left");
- var cLabelMinus = diagram.addLabel(305, 270, "\u2212", "left");
- rLabel.color = Plotter.Color.lightgreen;
- lLabel.color = Plotter.Color.lightmagenta;
- cLabelPlus.color = Plotter.Color.lightyellow;
- cLabel.color = Plotter.Color.lightyellow;
- cLabelMinus.color = Plotter.Color.lightyellow;
-
- //Ground
- var ground = diagram.addGround(240, 355);
-
- //Resistor
- R = diagram.addResistor(130, 140, 1);
- R.rotation = Math.PI/2;
- R.label.str = "R";
- R.valueString.suffix = "k\u03A9";
-
- //Inductor
- L = diagram.addInductor(255, 140, 10);
- L.rotation = Math.PI/2;
- L.label.str = "L";
- L.valueString.suffix = "mH"
-
- //Capacitor
- C = diagram.addCapacitor(380, 200, 110);
- C.label.str = "C";
- C.valueString.suffix = "nF";
-
- //Voltage source
- VIn = diagram.addSource(100, 200, 3.0, "v");
- VIn.label.str = "v_{IN}";
- VIn.valueString.decimal = 2;
- VIn.valueString.suffix = "V";
- VIn.label.color = Plotter.Color.lightblue;
- VIn.valueString.color = Plotter.Color.lightblue;
-
- //diagram.showGrid = true;
- diagram.paint();
-}
-
-function setTimeGraph()
-{
- var lticks = 1;
- var sticks = 0.5;
- //x axis
- timeGraph.xText = xLab;
- timeGraph.yText = "V_{MAX} (Volts)";
- timeGraph.xmin = 0;
- timeGraph.xmax = maxTime;
- timeGraph.xspan = maxTime;
- timeGraph.xShortTickMin = 0;
- timeGraph.xShortTickMax = maxTime;
- timeGraph.xShortTickStep = maxTime/20;
- timeGraph.xLongTickMin = 0;
- timeGraph.xLongTickMax = maxTime;
- timeGraph.xLongTickStep = maxTime/10;
- timeGraph.xLabelMin = 0;
- timeGraph.xLabelMax = maxTime;
- timeGraph.xLabelStep = maxTime/10;
- timeGraph.xGridMin = 0;
- timeGraph.xGridMax = maxTime;
- timeGraph.xGridStep = maxTime/10;
- //y axis
- timeGraph.ymin = -maxVolt;
- timeGraph.ymax = maxVolt;
- timeGraph.yspan = 2*maxVolt;
- timeGraph.yShortTickMin = -maxVolt + (maxVolt % sticks);
- timeGraph.yShortTickMax = maxVolt - (maxVolt % sticks);
- timeGraph.yShortTickStep = sticks;
- timeGraph.yLongTickMin = -maxVolt + (maxVolt % lticks);
- timeGraph.yLongTickMax = maxVolt - (maxVolt % lticks);
- timeGraph.yLongTickStep = lticks;
- timeGraph.yLabelMin = -maxVolt + (maxVolt % lticks);
- timeGraph.yLabelMax = maxVolt - (maxVolt % lticks);
- timeGraph.yLabelStep = lticks;
- timeGraph.yGridMin = -maxVolt + (maxVolt % lticks);
- timeGraph.yGridMax = maxVolt - (maxVolt % lticks);
- timeGraph.yGridStep = lticks;
-}
-
-function setMagGraph()
-{
- var lticks = 1;
- var sticks = 0.5;
- //x axis
- magGraph.xText = "f (Hz)";
- magGraph.xPowerMin = -1;
- magGraph.xPowerMax = 9;
- magGraph.xspan = 10;
-
- //y axis
- magGraph.yText = "Magnitude (dB)";
- magGraph.ymin = -300;
- magGraph.ymax = 40;
- magGraph.yspan = 340;
- magGraph.yShortTickMin = -300;
- magGraph.yShortTickMax = 40;
- magGraph.yShortTickStep = 10;
- magGraph.yLongTickMin = -300;
- magGraph.yLongTickMax = 40;
- magGraph.yLongTickStep = 20;
- magGraph.yLabelMin = -300;
- magGraph.yLabelMax = 40;
- magGraph.yLabelStep = 20;
- magGraph.yGridMin = -300;
- magGraph.yGridMax = 40;
- magGraph.yGridStep = 20;
- magGraph.x0 = magGraph.xPowerMin;
- magGraph.y0 = magGraph.ymin;
- magGraph.hasxLog = true;
- magGraph.hasxPowers = true;
- magGraph.hasyLog = false;
- magGraph.hasyPowers = false;
- magGraph.yLabelDecimalDigits = 0;
-}
-
-function setPhaseGraph()
-{
- var lticks = 1;
- var sticks = 0.5;
- //x axis
- phaseGraph.xText = "f (Hz)";
- phaseGraph.yText = "Phase (degrees)";
- phaseGraph.xmin = -1;
- phaseGraph.xmax = 5;
- phaseGraph.xspan = 6;
- phaseGraph.xPowerMin = -1;
- phaseGraph.xPowerMax = 5;
-
- //y axis
- phaseGraph.ymin = -200;
- phaseGraph.ymax = 200;
- phaseGraph.yspan = 400;
- phaseGraph.yShortTickMin = -180;
- phaseGraph.yShortTickMax = 180;
- phaseGraph.yShortTickStep = 10;
- phaseGraph.yLongTickMin = -180;
- phaseGraph.yLongTickMax = 180;
- phaseGraph.yLongTickStep = 45;
- phaseGraph.yLabelMin = -180;
- phaseGraph.yLabelMax = 180;
- phaseGraph.yLabelStep = 45;
- phaseGraph.yGridMin = -180;
- phaseGraph.yGridMax = 180;
- phaseGraph.yGridStep = 10;
- phaseGraph.x0 = phaseGraph.xPowerMin;
- phaseGraph.y0 = phaseGraph.ymin;
- phaseGraph.hasxLog = true;
- phaseGraph.hasxPowers = true;
- phaseGraph.hasyLog = false;
- phaseGraph.hasyPowers = false;
- phaseGraph.yLabelDecimalDigits = 0;
-}
-
-function generateBuffer()
-{
- timeGraph.paintOn("buffer");
- timeGraph.paint();
- magGraph.paintOn("buffer");
- magGraph.paint();
- phaseGraph.paintOn("buffer");
- phaseGraph.paint();
-}
-
-function draw()
-{
- //Paint buffer on canvas
- timeGraph.paintBuffer();
-
- //Draw on canvas
- timeGraph.paintOn("canvas"); //Draw on screen image
-
- if (vinChecked)
- timeGraph.drawArray(time, insig, Plotter.Color.lightblue);
- if (vrChecked)
- timeGraph.drawArray(time, rsig, Plotter.Color.lightgreen);
- if (vlChecked)
- timeGraph.drawArray(time, lsig, Plotter.Color.lightmagenta);
- if (vcChecked)
- timeGraph.drawArray(time, csig, Plotter.Color.lightyellow);
-
-
- magGraph.paintBuffer();
- magGraph.paintOn("canvas");
- if (vrChecked)
- magGraph.drawArray(frequencies, rmag, Plotter.Color.lightgreen);
- if (vlChecked)
- magGraph.drawArray(frequencies, lmag, Plotter.Color.lightmagenta);
- if (vcChecked)
- magGraph.drawArray(frequencies, cmag, Plotter.Color.lightyellow);
-
- phaseGraph.paintBuffer();
- phaseGraph.paintOn("canvas");
- if (vrChecked)
- phaseGraph.drawArray(frequencies, rphase, Plotter.Color.lightgreen);
- if (vlChecked)
- phaseGraph.drawArray(frequencies, lphase, Plotter.Color.lightmagenta);
- if (vcChecked)
- phaseGraph.drawArray(frequencies, cphase, Plotter.Color.lightyellow);
-}
-
-function initSound()
-{
- sp = new Sound.Player();
- sp.soundStarted = function()
- {
- $('#playButton').prop('value', "Stop");
- }
-
- sp.soundStopped = function()
- {
- $('#playButton').prop('value', "Play");
- }
-}
-
-function communSlide()
-{
- if (labEnabled)
- {
- if (sp.isPlaying)
- sp.stopTone();
- calculateSignals();
- draw();
- diagram.paint();
- //fc = getfCutoff(r, c);
- //$("#fc").html("fC = " + fc.toFixed(0) + " Hz");
- }
-}
-
-$(function()
-{
- //fc = getfCutoff(r, c);
- //$("#fc").html("fC = " + fc.toFixed(0) + " Hz");
- $("#vinSlider").slider({value: vIn, min: 0, max: 5, step: 0.01,
- slide: function(event, ui)
- {
- $("#vin").html("vIN = " + ui.value + " V");
- vIn = ui.value;
- VIn.value = vIn;
- VIn.valueString.decimal = -1; //Bug?????
- communSlide();
- }
- });
- $("#vin").html("vIN = " + $("#vinSlider").slider("value") + " V");
-
- $("#freqSlider").slider({value: freq, min: 100, max: 5000, step: 100,
- slide: function(event, ui)
- {
- $("#freq").html("Frequency = " + ui.value + " Hz");
- freq = ui.value;
- communSlide();
- }
- });
- $("#freq").html("Frequency = " + $("#freqSlider").slider("value") + " Hz");
-
- $("#vbiasSlider").slider({value: vBias, min: -5, max: 5, step: 0.01,
- slide: function(event, ui)
- {
- $("#vbias").html("VBIAS = " + ui.value + " V");
- vBias = ui.value;
- communSlide();
- }
- });
- $("#vbias").html("VBIAS = " + $("#vbiasSlider").slider("value") + " V");
-
- $("#rSlider").slider({value: 10, min: 10, max: 1000, step: 1,
- slide: function(event, ui)
- {
- //Values of slider are in Ohms
- var val = getResistance(ui.value);
- $(this).slider("value", val);
- if (val >= 1000.0) //kOhms
- {
- $("#r").html("R = " + unitToKilo(val) + " kΩ");
- R.value = unitToKilo(val);
- R.valueString.suffix = "k\u03A9";
- }
- else
- {
- $("#r").html("R = " + val + " Ω");
- R.value = val;
- R.valueString.suffix = "\u03A9";
- }
-
- r = val;
- communSlide();
- //return false; //Blocks keystrokes
- }
- });
- $("#r").html("R = " + $("#rSlider").slider("value") + " Ω");
-
- $("#lSlider").slider({value: 10, min: 0, max: 1000, step: 1,
- slide: function(event, ui)
- {
- //Values of slider are in milli Henry
- var val = getInductance(ui.value);
- $(this).slider("value", val);
- if (val >= 1000.0) //H
- {
- $("#l").html("L = " + milliToUnit(val) + " H");
- L.value = milliToUnit(val);
- L.valueString.suffix = "H";
- }
- else
- {
- $("#l").html("L = " + val + " mH");
- L.value = val;
- L.valueString.suffix = "mH";
- }
-
- l = milliToUnit(val);
- communSlide();
- }
- });
- $("#l").html("L = " + $("#lSlider").slider("value") + " mH");
-
- $("#cSlider").slider({value: 10, min: 10, max: 1000, step: 1,
- slide: function(event, ui)
- {
- //Values of slider are in micro Farad
- var val = getCapacitance(ui.value);
- $(this).slider("value", val);
- if (val >= 1000)
- {
- $("#c").html("C = " + val + " F");
- C.value = microToUnit(val);
- C.valueString.suffix = "F";
- }
- else
- {
- $("#c").html("C = " + val + " μF");
- C.value = val;
- C.valueString.suffix = "\u03BCF";
- }
-
- c = microToUnit(val);
- communSlide();
- }
- });
- $("#c").html("C = " + $("#cSlider").slider("value") + " μF");
- $("#vc0Slider").slider({value: vC0, min: 0, max: 5, step: 0.01,
- slide: function(event, ui)
- {
- $("#vc0").html("vC (0) = " + ui.value + " V");
- vC0 = ui.value;
- communSlide();
- }
- });
- $("#vc0").html("vC (0) = " + $("#vc0Slider").slider("value") + " V");
- $("#i0Slider").slider({value: i0, min: 0, max: 1, step: 0.01,
- slide: function(event, ui)
- {
- $("#i0").html("i(0) = " + ui.value + " A");
- i0 = ui.value;
- communSlide();
- }
- });
- $("#i0").html("i(0) = " + $("#i0Slider").slider("value") + " A");
- $("#vmaxSlider" ).slider({value: vMax, min: 1, max: 20, step: 0.1,
- slide: function(event, ui)
- {
- $("#vmax").html("VMAX = " + ui.value + " V");
- maxVolt = ui.value;
- if (labEnabled)
- {
- if (sp.isPlaying)
- sp.stopTone();
- setTimeGraph();
- generateBuffer();
- calculateSignals();
- draw();
- }
- }
- });
- $("#vmax").html("VMAX = " + $("#vmaxSlider").slider("value") + " V");
-});
-
-function getCheckboxesState()
-{
- if($('#vinCheckbox').prop('checked'))
- vinChecked = true;
- else
- vinChecked = false;
- if($('#vrCheckbox').prop('checked'))
- vrChecked = true;
- else
- vrChecked = false;
- if($('#vlCheckbox').prop('checked'))
- vlChecked = true;
- else
- vlChecked = false;
- if($('#vcCheckbox').prop('checked'))
- vcChecked = true;
- else
- vcChecked = false;
-}
-
-function getRadioButtonsState()
-{
- if($('#vinRadioButton').prop('checked'))
- sp.inSignal.listen = true;
- else
- sp.inSignal.listen = false;
- if($('#vrRadioButton').prop('checked'))
- sp.outSignals[1].listen = true;
- else
- sp.outSignals[1].listen = false;
- if($('#vlRadioButton').prop('checked'))
- sp.outSignals[2].listen = true;
- else
- sp.outSignals[2].listen = false;
- if($('#vcRadioButton').prop('checked'))
- sp.outSignals[3].listen = true;
- else
- sp.outSignals[3].listen = false;
-}
-
-function onSelectChange()
-{
- if (labEnabled)
- {
- musicType = $("#musicTypeSelect").val();
- sp.stopTone();
- if (musicType == 0) //Zero Input
- {
- $("#vinSlider").slider( "option", "disabled", true);
- $("#freqSlider").slider( "option", "disabled", true);
- maxTime = 10; //ms
- xLab = "t (ms)";
- musicLoaded();
- }
- else if (musicType == 1) //Unit Impulse
- {
- $("#vinSlider").slider( "option", "disabled", true);
- $("#freqSlider").slider( "option", "disabled", true);
- maxTime = 10; //ms
- xLab = "t (ms)";
- musicLoaded();
- }
- else if (musicType == 2) //Unit Step
- {
- $("#vinSlider").slider( "option", "disabled", true);
- $("#freqSlider").slider( "option", "disabled", true);
- maxTime = 10; //ms
- xLab = "t (ms)";
- musicLoaded();
- }
- if (musicType == 3) //Sine Wave
- {
- $("#vinSlider").slider( "option", "disabled", false);
- $("#freqSlider").slider( "option", "disabled", false);
- maxTime = 10; //ms
- xLab = "t (ms)";
- musicLoaded();
- }
- else if (musicType == 4) //Square Wave
- {
- $("#vinSlider").slider( "option", "disabled", false);
- $("#freqSlider").slider( "option", "disabled", false);
- maxTime = 10; //ms
- xLab = "t (ms)";
- musicLoaded();
- }
- else if (musicType == 5 || musicType == 6 || musicType == 7 || musicType == 8) //Music
- {
- $("#vinSlider").slider( "option", "disabled", false);
- $("#freqSlider").slider( "option", "disabled", true);
- maxTime = 20; //s
- xLab = "t (s)";
-
- if (musicType == 5)
- sp.load("classical.wav", musicLoaded);
- else if (musicType == 6)
- sp.load("folk.wav", musicLoaded);
- else if (musicType == 7)
- sp.load("jazz.wav", musicLoaded);
- else
- sp.load("reggae.wav", musicLoaded);
- }
- }
-}
-
-function tabSelected(event, ui)
-{
- if (ui.index == 0)
- {
- //Time, renable all sliders
- $("#vinSlider").slider("option", "disabled", false);
- $("#freqSlider").slider("option", "disabled", false);
- $("#vbiasSlider").slider("option", "disabled", false);
- $("#vc0Slider").slider("option", "disabled", false);
- $("#i0Slider").slider("option", "disabled", false);
- $("#vmaxSlider" ).slider("option", "disabled", false);
- //And vinCheckbox
- $('#vinCheckbox').attr("disabled", false);
-
- }
- else if (ui.index == 1 || ui.index == 2)
- {
- //Magnitude or phase, disable elements that have no effect on graphs
- $("#vinSlider").slider("option", "disabled", true);
- $("#freqSlider").slider("option", "disabled", true);
- $("#vbiasSlider").slider("option", "disabled", true);
- $("#vc0Slider").slider("option", "disabled", true);
- $("#i0Slider").slider("option", "disabled", true);
- $("#vmaxSlider" ).slider("option", "disabled", true);
- $('#vinCheckbox').attr("disabled", true);
- }
-}
-
-function musicLoaded()
-{
- setTimeGraph();
- generateBuffer();
- calculateSignals();
- draw();
-}
-
-function checkboxClicked()
-{
- if (labEnabled)
- {
- getCheckboxesState();
- draw();
- }
-}
-
-function radioButtonClicked()
-{
- if (labEnabled)
- {
- if (sp.isPlaying)
- sp.stopTone();
- getRadioButtonsState();
- }
-}
-
-function playButtonClicked()
-{
- if (labEnabled)
- {
- if (sp.isPlaying)
- sp.stopTone();
- else
- sp.playTone();
- }
-}
-
-//TO DO: PUT ALL THE FOLLOWING GLOBAL VARIABLES IN A NAMESPACE
-var labEnabled = true;
-//Graph
-var graph;
-var maxTime = 10; //In ms
-var xLab = "t (ms)";
-var maxVolt = 2;
-var time;
-var insig, rsig, lsig, csig, frequencies, rmag, lmag, cmag, rphase, lphase, cphase;
-
-//Sound Player
-var sp;
-
-//Drop variable down for Type of Input
-var musicType = 3;
-//Checkboxes variables for Graph
-var vinChecked = true, vrChecked = false, vlChecked = false, vcChecked = true;
-//Slider variables
-var vIn = 3.0;
-var vInMax = 5.0;
-var freq = 1000;
-var vBias = 0.0;
-var r = 10;
-var l = milliToUnit(10);
-var c = microToUnit(10);
-var vC0 = 0.0;
-var i0 = 0.0;
-var vMax = 2;
-var fc;
-
-function calculateSignals()
-{
- if (musicType == 0 || musicType == 1 || musicType == 2 || musicType == 3)
- {
- sp.soundLength = 1;
- sp.sampleRate = 50000;
- }
- else if (musicType == 4)
- {
- sp.soundLength = 1;
- sp.sampleRate = 88200;
- }
- else if (musicType == 5 || musicType == 6 || musicType == 7 || musicType == 8) //Classical, Folk, Jazz, Reggae
- {
- sp.soundLength = 20;
- sp.sampleRate = 22050;
- }
-
- //We have 4 outputs, 1: current, 2: vR, 3: vL, 4: vC
- sp.createBuffers(4);
-
- if (musicType == 0) //Zero Input
- sp.generateZero();
- else if (musicType == 1) //Unit Impulse
- sp.generateUnitImpulse();
- else if (musicType == 2) //Unit Step
- sp.generateUnitStep();
- else if (musicType == 3) //Sine Wave
- sp.generateSineWave(vIn, freq, vBias);
- else if (musicType == 4) //Square Wave
- sp.generateSquareWave(vIn, freq, vBias);
- else if (musicType == 5 || musicType == 6 || musicType == 7 || musicType == 8) //Classical, Folk, Jazz, Reggae
- {
- //TO DO: MOVE OUT
- var max = Number.NEGATIVE_INFINITY;
- var amp = 0.0;
-
- //Find the max and normalize
- for (var i = 0, len = sp.inSignal.data.length; i < len; i++)
- {
- amp = Math.abs(sp.audioData[i]);
- if (amp > max)
- max = amp;
- }
- max /= 0.5;
- if (vBias != 0.0)
- {
- if (max != 0.0)
- {
- for (var i = 0, len = sp.inSignal.data.length; i < len; i++)
- {
- sp.inSignal.data[i] = vBias + vIn*sp.audioData[i] / max;
- }
- }
- else //Fill in with vBias
- {
- for (var i = 0, len = sp.inSignal.data.length; i < len; i++)
- {
- sp.inSignal.data[i] = vBias;
- }
- }
- }
- else
- {
- if (max != 0.0)
- {
- for (var i = 0, len = sp.inSignal.data.length; i < len; i++)
- {
- sp.inSignal.data[i] = vIn*sp.audioData[i] / max;
- }
- }
- else //Fill in with zeros
- {
- for (var i = 0, len = sp.inSignal.data.length; i < len; i++)
- {
- sp.inSignal.data[i] = 0.0;
- }
- }
- }
- }
-
- getSeriesRLC(sp.inSignal.data, sp.outSignals[0].data, sp.outSignals[1].data, sp.outSignals[2].data, sp.outSignals[3].data, r, l, c, vC0, i0, sp.sampleRate);
-
- time = [];
- insig = [];
- rsig = [];
- lsig = [];
- csig = [];
- frequencies = [];
- rmag = [];
- lmag = [];
- cmag = [];
- rphase = [];
- lphase = [];
- cphase = [];
-
- var i = 0;
- var ii;
- var imult;
- var imax;
- var x = 0;
- var xinc;
-
-
- //Scale of graph is 500 px
- //All generated sound (sine wave etc.) except square wave have sampling rate of 50000 Hz, length 1s. We will plot the first 10 ms. That's 500 samples for 10 ms and 500 px
- if (musicType == 0 || musicType == 1 || musicType == 2 || musicType == 3)
- {
- xinc = 10/500;
- imax = 500;
- imult = 1;
- }
- else if (musicType == 4) //At 50000 Hz, square wave plays very poorly, we use 88200 Hz
- {
- xinc = 10/882;
- imax = 882;
- imult = 1;
- }
- else if (musicType == 5 || musicType == 6 || musicType == 7 || musicType == 8) //All music files have a sampling rate 22050 Hz, length 20s. 20s/500px --> get value every 0.04 s ie every 882 samples.
- {
- xinc = 20/500;
- imax = 500;
- imult = 882;
- }
-
- while (i <= imax)
- {
- ii = imult*i;
- time[i] = x;
- insig[i] = sp.inSignal.data[ii];
- //MISSING I PLOT
- rsig[i] = sp.outSignals[1].data[ii];
- lsig[i] = sp.outSignals[2].data[ii];
- csig[i] = sp.outSignals[3].data[ii];
-
- x += xinc;
- i++;
- }
-
- sp.normalizeAllSounds();
-
- //Bode plots
- var df = magGraph.xspan / 500; //magGraph is 500 pix large
- var fp = magGraph.xmin;
- var f;
- var w;
-
- //Scale of magGraph is 500 px
- for (var i = 0; i <= 500; i++)
- {
- frequencies[i] = fp;
- f = Math.pow(10, fp);
- w = Plotter.Utils.TWO_PI*f;
- rmag[i] = getGainR(w, r, l, c);
- lmag[i] = getGainL(w, r, l, c);
- cmag[i] = getGainC(w, r, l, c);
- rphase[i] = getPhaseR(w, r, l, c);
- lphase[i] = getPhaseL(w, r, l, c);
- cphase[i] = getPhaseC(w, r, l, c);
- fp += df;
- }
-}
-
-var resistance = [10, 11, 12, 13, 15, 16, 18, 20, 22, 24, 27, 30, 33, 36, 39, 43, 47, 51, 56, 62, 68, 75, 82, 91, 100, 110, 120, 130, 150, 160, 180, 200, 220, 240, 270, 300, 330, 360, 390, 430, 470, 510, 560, 620, 680, 750, 820, 910, 1000];
-
-var inductance = [10, 11, 12, 13, 15, 16, 18, 20, 22, 24, 27, 30, 33, 36, 39, 43, 47, 51, 56, 62, 68, 75, 82, 87, 91, 100, 110, 120, 130, 150, 160, 180, 200, 220, 240, 270, 300, 330, 360, 390, 430, 470, 510, 560, 620, 680, 750, 820, 870, 910, 1000]; //Note: 87 and 870?
-
-var capacitance = [10, 11, 12, 13, 15, 16, 18, 20, 22, 24, 27, 30, 33, 36, 39, 43, 47, 51, 56, 62, 68, 75, 82, 91, 100, 110, 120, 130, 150, 160, 180, 200, 220, 240, 270, 300, 330, 360, 390, 430, 470, 510, 560, 620, 680, 750, 820, 910, 1000];
-
-function getResistance(value)
-{
- var distance;
- var minDistance = Number.POSITIVE_INFINITY;
- var minIndex;
-
- for (var i = 0, l = resistance.length; i < l; i++)
- {
- distance = Math.abs(value - resistance[i]);
- if (distance < minDistance)
- {
- minDistance = distance;
- minIndex = i;
- }
- }
- return resistance[minIndex];
-}
-
-function getInductance(value)
-{
- var distance;
- var minDistance = Number.POSITIVE_INFINITY;
- var minIndex;
-
- for (var i = 0, l = inductance.length; i < l; i++)
- {
- distance = Math.abs(value - inductance[i]);
- if (distance < minDistance)
- {
- minDistance = distance;
- minIndex = i;
- }
- }
- return inductance[minIndex];
-}
-
-function getCapacitance(value)
-{
- var distance;
- var minDistance = Number.POSITIVE_INFINITY;
- var minIndex;
-
- for (var i = 0, l = capacitance.length; i < l; i++)
- {
- distance = Math.abs(value - capacitance[i]);
- if (distance < minDistance)
- {
- minDistance = distance;
- minIndex = i;
- }
- }
- return capacitance[minIndex];
-}
-
-function radToDeg(angle)
-{
- return angle*180.0/Math.PI;
-}
-
-function degToRad(angle)
-{
- return angle*Math.PI/180.0;
-}
-
-function unitToKilo(u)
-{
- return u/1000;
-}
-
-function unitToMilli(u)
-{
- return u*1000;
-}
-
-function unitToMicro(u)
-{
- return u*1000000;
-}
-
-function unitToNano(u)
-{
- return u*1000000000;
-}
-
-function unitToPico(u)
-{
- return u*1000000000000;
-}
-
-function kiloToUnit(k)
-{
- return k*1000;
-}
-
-function milliToUnit(m)
-{
- return m/1000;
-}
-
-function microToUnit(m)
-{
- return m/1000000;
-}
-
-function nanoToUnit(n)
-{
- return n/1000000000;
-}
-
-function picoToUnit(p)
-{
- return p/1000000000000;
-}
-
-function nanoToMicro(p)
-{
- return p/1000;
-}
-
-/*
- Vin = RI + LdI/dt + Vout
- I = CdVout/dt
-
- LCd^2Vout/dt^2 + RCdVout/dt + Vout = Vin
-
- leads to, for x[i] array of input, y[i] array out outputs:
-
- (dy/dt)[i] = (y[i+1] - y[i])/deltaT
- (d^2y/dt^2)[i] = (y[i+2]-2y[i+1]+y[i])/(deltaT^2)
-
- LC(yi+2 - 2yi+1 + yi)/dt^2 + RC(yi+1 - yi)/dt + yi = xi
-
- yi+2 = (2.0*yi+1 - yi) - (R/L)(y[i+1]-y[i])dt + (1/LC)(x[i] - y[i])dt^2;
-
- xi = Vin(0)
- yi = Vc(0)
- yi+1 = yi + dtI(0)/C
-
- beta = [dt*dt - RCdt + LC]/C(Rdt-2L)
-*/
-
-/*NECESSARY?
-if (musicType != 1)
- {
- outData[0] = VC0;
- rData[0] = inData[0] - outData[0];
- }
- else //Unit Impulse
- {
- outData[0] = inData[0];
- rData[0] = -inData[0];
- }
-*/
-/*
-function getVR(x, y)
-{
- for (var i = 0, l = y.length; i < l; i++)
- {
- y[i] = x[i];
- }
-}
-
-function getVL(x, y)
-{
- for (var i = 0, l = y.length; i < l; i++)
- {
- y[i] = x[i];
- }
-}
-
-function getVC(x, y, R, L, C, VC0, I0, sampleRate)
-{
- var dt = 1.0 / sampleRate;
- var A1 = dt*R/L;
- var A2 = dt*dt/(L*C);
-
- y[0] = VC0;
- y[1] = y[0] + dt*I0/C;
-
- for (var i = 2, l = y.length; i < l; i++)
- {
- y[i+2] = (2.0*y[i+1] - y[i]) - A1*(y[i+1]-y[i]) + A2*(x[i] - y[i]);
- }
-}
-*/
-//###########################################################################
-/*
-vR + vL + vC = vIn
-Ldi/dt + Ri + q/C = vIn
-
-System of ODE, use improved Euler method.
-i = q'
-i' = (1/L)(vIn - Ri - q/C)
-
-Initial conditions given by vC(0) and i0
-
-Then
-q0 = C vC(0)
-i0
-i'0 = (1/L)(vIn - Ri0 - q0/C)
-
-qnew = qold + q'old dt = qold + i'olddt
-inew = iold + i'old dt
-i'new = (1/L)(vIn(n) - Rinew - qnew/C)
-
-qnew = qold + (q'old + q'new)dt/2 = qold + (iold + inew)dt/2
-inew = iold + (i'old + i'new)dt/2
-i'new = (1/L)(vIn(n) - Rinew - qnew/C)
-*/
-
-function getSeriesRLC(x, yi, yr, yl, yc, R, L, C, VC0, I0, sampleRate) //x input, yi, yr, yl, yc outputs
-{
- var dt = 1.0/sampleRate;
- var dtdiv2 = dt/2.0;
- var cte = 1.0/L;
- var qold = C*VC0;
- var qnew;
- var iold = I0;
- var inew;
- var diold = cte*(x[0] - R*iold - qold/C);
- var dinew;
- //Fill out our initial conditions on all 4 outputs
- yc[0] = qold/C;
- yi[0] = iold;
- yr[0] = R*iold;
- yl[0] = L*diold;
-
- for (var k = 1, l = x.length; k < l; k++)
- {
- qnew = qold + iold*dt;
- inew = iold + diold*dt;
- dinew = cte*(x[k] - R*inew - qnew/C);
- //Improved Euler method follows
- qnew = qold + (iold + inew)*dtdiv2;
- inew = iold + (diold + dinew)*dtdiv2;
- dinew = cte*(x[k] - R*inew - qnew/C);
- //Got all we need, fill up our 4 outputs
- yc[k] = qnew/C;
- yi[k] = inew;
- yr[k] = R*inew;
- yl[k] = L*dinew;
-
- qold = qnew;
- iold = inew;
- diold = dinew;
- }
-}
-
-function radToDeg(angle)
-{
- return angle*180.0/Math.PI;
-}
-
-function degToRad(angle)
-{
- return angle*Math.PI/180.0;
-}
-
-//db for voltages: 20*log(|gain|)
-
-//Gain and phase for vR
-//Complex Gain is R/(R +j(Lw - 1/(Cw)))
-function getGainR(w, R, L, C)
-{
- var re = R;
- var im = L*w - 1.0/(C*w);
- return 20.0*(Plotter.Utils.log10(R) - Plotter.Utils.log10(Math.sqrt(re*re + im*im)));
-}
-
-function getPhaseR(w, R, L, C)
-{
- var re = R;
- var im = L*w - 1.0/(C*w);
- return radToDeg(-Math.atan2(im, re));
-}
-
-//Gain and phase for vL
-//Complex Gain is jLw/(R +j(Lw - 1/(Cw)))
-function getGainL(w, R, L, C)
-{
- var re = R;
- var im = L*w - 1.0/(C*w);
- return 20.0*(Plotter.Utils.log10(L*w) - Plotter.Utils.log10(Math.sqrt(re*re + im*im)));
-}
-
-function getPhaseL(w, R, L, C)
-{
- var re = R;
- var im = L*w - 1.0/(C*w);
- return radToDeg(Plotter.Utils.PI_DIV_2 - Math.atan2(im, re));
-}
-
-//Gain and phase for vC
-//Complex Gain is (-j/Cw)/(R +j(Lw - 1/(Cw)))
-function getGainC(w, R, L, C)
-{
- var re = R;
- var im = L*w - 1.0/(C*w);
- return 20.0*(-Plotter.Utils.log10(C*w) - Plotter.Utils.log10(Math.sqrt(re*re + im*im)));
-}
-
-function getPhaseC(w, R, L, C)
-{
- var re = R;
- var im = L*w - 1.0/(C*w);
- return radToDeg(-Plotter.Utils.PI_DIV_2 - Math.atan2(im, re));
-}
diff --git a/common/test/data/full/static/js/sound_labs/sound.js b/common/test/data/full/static/js/sound_labs/sound.js
deleted file mode 100644
index a3bca38092..0000000000
--- a/common/test/data/full/static/js/sound_labs/sound.js
+++ /dev/null
@@ -1,407 +0,0 @@
-var Sound = (function() {
-
- //////////PRIVATE FIELDS AND METHODS//////////
- var TWO_PI = 2.0*Math.PI;
- var PI_DIV_2 = Math.PI/2.0;
-
- function Player()
- {
- this.isChrome = false;
- this.isMoz = false;
- this.audioChrome;
- this.audioMoz;
- this.dir = "/static/courses/6002/sounds/";
- this.inSignal;
- this.outSignals = [];
- this.numberChannels = 1;
- this.soundLength = 1; //In seconds
- this.sampleRate = 44100; //In Hertz
- this.numberSamples = 44100;
- this.isPlaying = false;
- this.chromeTimer;
- this.mozTimer;
- this.audioData ;
- this.playAudio;
- this.outSrc;
-
- //Test for Web Audio API --> Webkit browsers ie Chrome & Safari
- //https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
- if (!!window.webkitAudioContext)
- {
- this.audioChrome = new webkitAudioContext();
- this.isChrome = true;
- }
- //Test for Audio Data API --> Firefox 4 and ulterior
- //https://wiki.mozilla.org/Audio_Data_API
- else if (!!new Audio().mozSetup)
- {
- this.audioMoz = new Audio();
- this.isMoz = true;
- }
- else //Sound libraries are not supported, exit.
- throw "Neither Web Audio API nor Audio Data API is supported in this browser.";
-
- //To be overriden
- this.soundStarted = function()
- {
- }
-
- this.soundStopped = function()
- {
- }
-
- this.load = function(url, callback)
- {
- var request;
- var file = this.dir + url;
- var self = this;
- request = new XMLHttpRequest();
- request.open('GET', file, true); //Asynchronous
- request.responseType = 'arraybuffer';
-
- request.onload = function()
- {
- var arrayBuffer = request.response;
- if (arrayBuffer)
- {
- var audioDataTmp = new Int16Array(arrayBuffer, 44);
- self.audioData = new Float32Array(audioDataTmp);
- //The music has been loaded, continue execution
- callback();
- }
- }
- request.send();
- }
-
- this.getAudioHeader = function(audioHeaderData)
- {
- //44 first bytes of file are the header
- return { // OFFS SIZE NOTES
- chunkId : bytesToStr(audioHeaderData, 0, 4), // 0 4 "RIFF" = 0x52494646
- chunkSize : bytesToNum(audioHeaderData, 4, 4), // 4 4 36+SubChunk2Size = 4+(8+SubChunk1Size)+(8+SubChunk2Size)
- format : bytesToStr(audioHeaderData, 8, 4), // 8 4 "WAVE" = 0x57415645
- subChunk1Id : bytesToStr(audioHeaderData, 12, 4), // 12 4 "fmt " = 0x666d7420
- subChunk1Size: bytesToNum(audioHeaderData, 16, 4), // 16 4 16 for PCM
- audioFormat : bytesToNum(audioHeaderData, 20, 2), // 20 2 PCM = 1
- numChannels : bytesToNum(audioHeaderData, 22, 2), // 22 2 Mono = 1, Stereo = 2, etc.
- sampleRate : bytesToNum(audioHeaderData, 24, 4), // 24 4 8000, 44100, etc
- byteRate : bytesToNum(audioHeaderData, 28, 4), // 28 4 SampleRate*NumChannels*BitsPerSample/8
- blockAlign : bytesToNum(audioHeaderData, 32, 2), // 32 2 NumChannels*BitsPerSample/8
- bitsPerSample: bytesToNum(audioHeaderData, 34, 2), // 34 2 8 bits = 8, 16 bits = 16, etc...
- subChunk2Id : bytesToStr(audioHeaderData, 36, 4), // 36 4 "data" = 0x64617461
- subChunk2Size: bytesToNum(audioHeaderData, 40, 4) // 40 4 data size = NumSamples*NumChannels*BitsPerSample/8
- };
- }
-
- this.bytesToStr = function(arr, offset, len)
- {
- var result = "";
- var l = 0;
- var i = offset;
-
- while (l < len)
- {
- result += String.fromCharCode(arr[i]);
- i++;
- l++;
- }
-
- return result;
- }
-
- //Bytes are stored as little endians
- this.bytesToNum = function(arr, offset, len)
- {
- var result = 0;
- var l = 0;;
- var i = offset + len - 1;
- var hexstr = "0x";
- var tmpstr;
-
- while (l < len)
- {
- if (arr[i] >= 0 && arr[i] <= 15)
- tmpstr = "0" + arr[i].toString(16);
- else
- tmpstr = arr[i].toString(16);
-
- hexstr += tmpstr;
- i--;
- l++;
- }
-
- return parseInt(hexstr, 16);
- }
-
- this.createBuffers = function(nOut)
- {
- this.numberSamples = this.sampleRate*this.soundLength;
-
- if (this.isChrome)
- {
- var b, d;
-
- b = this.audioChrome.createBuffer(this.numberChannels, this.numberSamples, this.sampleRate);
- d = b.getChannelData(0); //Float32Array
- this.inSignal = {buffer: b, data: d, listen: true};
-
- for (var i = 0; i < nOut; i++)
- {
-
- b = this.audioChrome.createBuffer(this.numberChannels, this.numberSamples, this.sampleRate);
- d = b.getChannelData(0); //Float32Array
- this.outSignals[i] = {buffer: b, data: d, listen: false};
- }
- }
- else if (this.isMoz)
- {
- this.inSignal = {data: new Float32Array(this.numberSamples), listen: true};
- for (var i = 0; i < nOut; i++)
- {
- this.outSignals[i] = {data: new Float32Array(this.numberSamples), listen: false};
- }
- this.audioMoz.mozSetup(this.numberChannels, this.sampleRate);
- }
- }
-
- this.generateZero = function()
- {
- for (var i = 0, l = this.inSignal.data.length; i < l; i++)
- {
- this.inSignal.data[i] = 0;
- }
- }
-
- this.generateUnitImpulse = function()
- {
- this.inSignal.data[0] = 1000;
- for (var i = 1, l = this.inSignal.data.length; i < l; i++)
- {
- this.inSignal.data[i] = 0.0;
- }
- }
-
- this.generateUnitStep = function()
- {
- for (var i = 0, l = this.inSignal.data.length; i < l; i++)
- {
- this.inSignal.data[i] = 1.0;
- }
- }
-
- this.generateSineWave = function(peakToPeak, frequency, vOffset)
- {
- var amp = 0.5*peakToPeak;
-
- if (vOffset != 0)
- {
- for (var i = 0, l = this.inSignal.data.length; i < l; i++)
- {
- this.inSignal.data[i] = amp * Math.sin(TWO_PI*frequency*i/this.sampleRate) + vOffset;
- }
- }
- else
- {
- for (var i = 0, l = this.inSignal.data.length; i < l; i++)
- {
- this.inSignal.data[i] = amp * Math.sin(TWO_PI*frequency*i/this.sampleRate);
- }
- }
- }
-
- this.generateSquareWave = function(peakToPeak, frequency, vOffset)
- {
- var amp = 0.5*peakToPeak;
- var period = 1/frequency;
- var halfPeriod = period/2;
- var itmp, sgn;
-
-
- if (vOffset != 0)
- {
- for (var i = 0, l = this.inSignal.data.length; i < l; i++)
- {
- itmp = (i/this.sampleRate) % period;
- if (itmp < halfPeriod)
- sgn = sgn = 1;
- else
- sgn = -1;
- this.inSignal.data[i] = amp * sgn + vOffset;
- }
- }
- else
- {
- for (var i = 0, l = this.inSignal.data.length; i < l; i++)
- {
- itmp = (i/this.sampleRate) % period;
- if (itmp < halfPeriod)
- sgn = sgn = 1;
- else
- sgn = -1;
- this.inSignal.data[i] = amp * sgn;
- }
- }
- }
-
- this.normalizeSound = function(arr)
- {
- var min = Number.POSITIVE_INFINITY;
- var max = Number.NEGATIVE_INFINITY;
- var vInMaxLocal = 10.0;
- var maxVol = 1/vInMaxLocal;
-
- //Find the min and max
- for (var i = 0, l = arr.length; i < l; i++)
- {
- if (arr[i] > max)
- max = arr[i];
- if (arr[i] < min)
- min = arr[i];
- }
-
- var vPeakToPeak = Math.abs(max - min);
- var maxVol = vPeakToPeak / vInMaxLocal; //If we have a peak to peak voltage of 10 V, we want max sound, normalize to [-1, 1]
- var norm = Math.max(Math.abs(min), Math.abs(max));
-
- if (max != 0.0)
- {
- for (var i = 0, l = arr.length; i < l; i++)
- {
- arr[i] = maxVol*arr[i] / norm;
- }
- }
- else //Fill in with zeros
- {
- for (var i = 0, l = arr.length; i < l; i++)
- {
- arr[i] = 0.0;
- }
- }
- }
-
- this.normalizeAllSounds = function()
- {
- //Normalize the sound buffer that will be heard
- this.normalizeSound(this.inSignal.data);
- for (var i = 0; i < this.outSignals.length; i++)
- {
- this.normalizeSound(this.outSignals[i].data);
- }
- }
-
- this.playTone = function()
- {
- this.soundStarted();
- var self = this;
- if (this.isChrome)
- {
- this.outSrc = this.audioChrome.createBufferSource();
-
- if (this.inSignal.listen)
- this.outSrc.buffer = this.inSignal.buffer;
- else
- {
- for (var i = 0; i < this.outSignals.length; i++)
- {
- if (this.outSignals[i].listen)
- this.outSrc.buffer = this.outSignals[i].buffer;
- }
- }
-
- this.outSrc.connect(this.audioChrome.destination);
- this.outSrc.noteOn(0);
- this.isPlaying = true;
- this.chromeTimer = setTimeout(function(){
- self.isPlaying = false;
- self.soundStopped();
- }, this.outSrc.buffer.duration * 1000);
-
- }
- else if (this.isMoz)
- {
- var playedAudioData;
- var currentWritePosition = 0;
- var currentPlayPosition = 0;
- var prebufferSize = 22050 / 2; // buffer 500ms
- var tail = null;
-
- if (this.inSignal.listen)
- playedAudioData = this.inSignal.data;
- else
- {
- for (var i = 0; i < this.outSignals.length; i++)
- {
- if (this.outSignals[i].listen)
- playedAudioData = this.outSignals[i].data;
- }
- }
-
- this.isPlaying = true;
-
- // The function called with regular interval to populate the audio output buffer.
- this.playAudio = setInterval(function()
- {
- var written;
- currentPlayPosition = self.audioMoz.mozCurrentSampleOffset();
-
- // Check if some data was not written in previous attempts.
- if (tail)
- {
- written = self.audioMoz.mozWriteAudio(tail);
- currentWritePosition += written;
- if (written < tail.length)
- {
- // Not all the data was written, saving the tail...
- tail = tail.subarray(written);
- return; //... and exit the function.
- }
- tail = null;
- }
-
- // Check if we need add some data to the audio output
- var available = Math.floor(currentPlayPosition + prebufferSize - currentWritePosition);
- if (available > 0)
- {
- var data = playedAudioData.subarray(currentWritePosition);
- // Writting the data
- written = self.audioMoz.mozWriteAudio(data);
- // Not all the data was written, saving the tail
- if(written <= data.length)
- tail = data.subarray(written);
- currentWritePosition += written;
- }
- }, 100);
-
- this.mozTimer = setTimeout(function(){
- clearInterval(self.playAudio);
- self.isPlaying = false;
- self.soundStopped();
- }, this.soundLength*1000);
- }
- }
-
- this.stopTone = function()
- {
- if (this.isPlaying)
- {
- if (this.isChrome)
- {
- clearTimeout(this.chromeTimer);
- this.outSrc.noteOff(0);
- }
- else if (this.isMoz)
- {
- clearTimeout(this.mozTimer);
- clearInterval(this.playAudio);
- }
- this.isPlaying = false;
- }
- this.soundStopped();
- }
- }
-
- //////////PUBLIC FIELDS AND METHODS//////////
- return {
- Player: Player
- };
-}());
diff --git a/doc/xml-format.md b/doc/xml-format.md
index e1439ee97d..29c60fea99 100644
--- a/doc/xml-format.md
+++ b/doc/xml-format.md
@@ -299,7 +299,7 @@ This is a sketch ("tue" is not a valid start date), that should help illustrate
## Specifying metadata in the xml file
-Metadata can also live in the xml files, but anything defined in the policy file overrides anything in the xml. This is primarily for backwards compatibility, and you should probably not use both. If you do leave some metadata tags in the xml, you should be consistent (e.g. if `display_name`s stay in xml, they should all stay in xml).
+Metadata can also live in the xml files, but anything defined in the policy file overrides anything in the xml. This is primarily for backwards compatibility, and you should probably not use both. If you do leave some metadata tags in the xml, you should be consistent (e.g. if `display_name`s stay in xml, they should all stay in xml. Note `display_name` should be specified in the problem xml definition itself, ie, Problem Text , in file ProblemFoo.xml).
- note, some xml attributes are not metadata. e.g. in ` `, the `youtube` attribute specifies what video this is, and is logically part of the content, not the policy, so it should stay in the xml.
Another example policy file:
diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py
index 88368c4a80..b033660c17 100644
--- a/lms/djangoapps/courseware/module_render.py
+++ b/lms/djangoapps/courseware/module_render.py
@@ -16,6 +16,7 @@ from capa.xqueue_interface import XQueueInterface
from courseware.access import has_access
from mitxmako.shortcuts import render_to_string
from models import StudentModule, StudentModuleCache
+from psychometrics.psychoanalyze import make_psychometrics_data_update_handler
from static_replace import replace_urls
from xmodule.errortracker import exc_info_to_str
from xmodule.exceptions import NotFoundError
@@ -230,6 +231,9 @@ def _get_module(user, request, location, student_module_cache, course_id, positi
# pass position specified in URL to module through ModuleSystem
system.set('position', position)
system.set('DEBUG', settings.DEBUG)
+ if settings.MITX_FEATURES.get('ENABLE_PSYCHOMETRICS') and instance_module is not None:
+ system.set('psychometrics_handler', # set callback for updating PsychometricsData
+ make_psychometrics_data_update_handler(instance_module))
try:
module = descriptor.xmodule_constructor(system)(instance_state, shared_state)
diff --git a/lms/djangoapps/courseware/tests/tests.py b/lms/djangoapps/courseware/tests/tests.py
index 62456d65d5..f3b086748d 100644
--- a/lms/djangoapps/courseware/tests/tests.py
+++ b/lms/djangoapps/courseware/tests/tests.py
@@ -222,16 +222,9 @@ class PageLoader(ActivateLoginTestCase):
handling.
"""
resp = self.client.get(url)
- # HACK: workaround the bug that returns 200 instead of 404.
- # TODO (vshnayder): once we're returning 404s, get rid of this if.
- if code != 404:
- self.assertEqual(resp.status_code, code)
- # And 'page not found' shouldn't be in the returned page
- self.assertTrue(resp.content.lower().find('page not found') == -1)
- else:
- # look for "page not found" instead of the status code
- #print resp.content
- self.assertTrue(resp.content.lower().find('page not found') != -1)
+ self.assertEqual(resp.status_code, code,
+ "got code {0} for url '{1}'. Expected code {2}"
+ .format(resp.status_code, url, code))
def check_pages_load(self, course_name, data_dir, modstore):
diff --git a/lms/djangoapps/django_comment_client/utils.py b/lms/djangoapps/django_comment_client/utils.py
index 79911a38bc..48d59436d9 100644
--- a/lms/djangoapps/django_comment_client/utils.py
+++ b/lms/djangoapps/django_comment_client/utils.py
@@ -9,7 +9,9 @@ from django.utils import simplejson
from django.db import connection
from django.conf import settings
from django.core.urlresolvers import reverse
+from django.contrib.auth.models import User
from django_comment_client.permissions import check_permissions_by_view
+from django_comment_client.models import Role
from mitxmako import middleware
import logging
@@ -226,11 +228,15 @@ def permalink(content):
args=[content['course_id'], content['commentable_id'], content['thread_id']]) + '#' + content['id']
def extend_content(content):
+ user = User.objects.get(pk=content['user_id'])
+ roles = dict(('name', role.name.lower()) for role in user.roles.filter(course_id=content['course_id']))
content_info = {
'displayed_title': content.get('highlighted_title') or content.get('title', ''),
'displayed_body': content.get('highlighted_body') or content.get('body', ''),
'raw_tags': ','.join(content.get('tags', [])),
'permalink': permalink(content),
+ 'roles': roles,
+ 'updated': content['created_at']!=content['updated_at'],
}
return merge_dict(content, content_info)
diff --git a/lms/djangoapps/instructor/views.py b/lms/djangoapps/instructor/views.py
index f4e9c27991..d812791c3d 100644
--- a/lms/djangoapps/instructor/views.py
+++ b/lms/djangoapps/instructor/views.py
@@ -27,6 +27,7 @@ from django.views.decorators.cache import cache_control
from courseware import grades
from courseware.access import has_access, get_access_group_name
from courseware.courses import (get_course_with_access, get_courses_by_university)
+from psychometrics import psychoanalyze
from student.models import UserProfile
from student.models import UserTestGroup, CourseEnrollment
@@ -51,7 +52,18 @@ def instructor_dashboard(request, course_id):
instructor_access = has_access(request.user, course, 'instructor') # an instructor can manage staff lists
msg = ''
- # msg += ('POST=%s' % dict(request.POST)).replace('<','<')
+ #msg += ('POST=%s' % dict(request.POST)).replace('<','<')
+
+ problems = []
+ plots = []
+
+ # the instructor dashboard page is modal: grades, psychometrics, admin
+ # keep that state in request.session (defaults to grades mode)
+ idash_mode = request.POST.get('idash_mode','')
+ if idash_mode:
+ request.session['idash_mode'] = idash_mode
+ else:
+ idash_mode = request.session.get('idash_mode','Grades')
def escape(s):
"""escape HTML special characters in string"""
@@ -149,6 +161,9 @@ def instructor_dashboard(request, course_id):
track.views.server_track(request, 'dump-answer-dist-csv', {}, page='idashboard')
return return_csv('answer_dist_%s.csv' % course_id, get_answers_distribution(request, course_id))
+ #----------------------------------------
+ # Admin
+
elif 'List course staff' in action:
group = get_staff_group(course)
msg += 'Staff group = %s' % group.name
@@ -187,14 +202,31 @@ def instructor_dashboard(request, course_id):
user.groups.remove(group)
track.views.server_track(request, 'remove-staff %s' % user, {}, page='idashboard')
- # For now, mostly a static page
+ #----------------------------------------
+ # psychometrics
+
+ elif action == 'Generate Histogram and IRT Plot':
+ problem = request.POST['Problem']
+ nmsg, plots = psychoanalyze.generate_plots_for_problem(problem)
+ msg += nmsg
+ track.views.server_track(request, 'psychometrics %s' % problem, {}, page='idashboard')
+
+ if idash_mode=='Psychometrics':
+ problems = psychoanalyze.problems_with_psychometric_data(course_id)
+
+ #----------------------------------------
+ # context for rendering
context = {'course': course,
'staff_access': True,
'admin_access': request.user.is_staff,
'instructor_access': instructor_access,
'datatable': datatable,
'msg': msg,
+ 'modeflag': {idash_mode: 'selectedmode'},
+ 'problems': problems, # psychometrics
+ 'plots': plots, # psychometrics
'course_errors': modulestore().get_item_errors(course.location),
+ 'djangopid' : os.getpid(),
}
return render_to_response('courseware/instructor_dashboard.html', context)
diff --git a/lms/djangoapps/lms_migration/migrate.py b/lms/djangoapps/lms_migration/migrate.py
index a3a1c595be..ecde31d6dd 100644
--- a/lms/djangoapps/lms_migration/migrate.py
+++ b/lms/djangoapps/lms_migration/migrate.py
@@ -35,7 +35,17 @@ def getip(request):
ip = request.META.get('REMOTE_ADDR','None')
return ip
-def manage_modulestores(request,reload_dir=None):
+
+def get_commit_id(course):
+ return course.metadata.get('GIT_COMMIT_ID','No commit id')
+ # getattr(def_ms.courses[reload_dir], 'GIT_COMMIT_ID','No commit id')
+
+
+def set_commit_id(course,commit_id):
+ course.metadata['GIT_COMMIT_ID'] = commit_id
+ # setattr(def_ms.courses[reload_dir], 'GIT_COMMIT_ID', new_commit_id)
+
+def manage_modulestores(request, reload_dir=None, commit_id=None):
'''
Manage the static in-memory modulestores.
@@ -52,8 +62,9 @@ def manage_modulestores(request,reload_dir=None):
ip = getip(request)
if LOCAL_DEBUG:
- html += 'IP address: %s ' % ip
- html += 'User: %s ' % request.user
+ html += 'IP address: %s ' % ip
+ html += 'User: %s ' % request.user
+ html += 'My pid: %s ' % os.getpid()
log.debug('request from ip=%s, user=%s' % (ip,request.user))
if not (ip in ALLOWED_IPS or 'any' in ALLOWED_IPS):
@@ -66,14 +77,36 @@ def manage_modulestores(request,reload_dir=None):
return HttpResponse(html, status=403)
#----------------------------------------
- # reload course if specified
+ # reload course if specified; handle optional commit_id
if reload_dir is not None:
if reload_dir not in def_ms.courses:
html += 'Error: "%s" is not a valid course directory ' % reload_dir
else:
- html += 'Reloaded course directory "%s" ' % reload_dir
- def_ms.try_load_course(reload_dir)
+ # reloading based on commit_id is needed when running mutiple worker threads,
+ # so that a given thread doesn't reload the same commit multiple times
+ current_commit_id = get_commit_id(def_ms.courses[reload_dir])
+ log.debug('commit_id="%s"' % commit_id)
+ log.debug('current_commit_id="%s"' % current_commit_id)
+
+ if (commit_id is not None) and (commit_id==current_commit_id):
+ html += "Already at commit id %s for %s " % (commit_id, reload_dir)
+ track.views.server_track(request,
+ 'reload %s skipped already at %s (pid=%s)' % (reload_dir,
+ commit_id,
+ os.getpid(),
+ ),
+ {}, page='migrate')
+ else:
+ html += 'Reloaded course directory "%s" ' % reload_dir
+ def_ms.try_load_course(reload_dir)
+ gdir = settings.DATA_DIR / reload_dir
+ new_commit_id = os.popen('cd %s; git log -n 1 | head -1' % gdir).read().strip().split(' ')[1]
+ set_commit_id(def_ms.courses[reload_dir], new_commit_id)
+ html += ' commit_id=%s
' % new_commit_id
+ track.views.server_track(request, 'reloaded %s now at %s (pid=%s)' % (reload_dir,
+ new_commit_id,
+ os.getpid()), {}, page='migrate')
#----------------------------------------
@@ -94,6 +127,8 @@ def manage_modulestores(request,reload_dir=None):
html += ' '
html += 'Course: %s (%s) ' % (course.display_name,cdir)
+ html += 'commit_id=%s
' % get_commit_id(course)
+
for field in dumpfields:
data = getattr(course,field)
html += '%s ' % field
diff --git a/lms/djangoapps/psychometrics/__init__.py b/lms/djangoapps/psychometrics/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/lms/djangoapps/psychometrics/admin.py b/lms/djangoapps/psychometrics/admin.py
new file mode 100644
index 0000000000..ff1a14d722
--- /dev/null
+++ b/lms/djangoapps/psychometrics/admin.py
@@ -0,0 +1,8 @@
+'''
+django admin pages for courseware model
+'''
+
+from psychometrics.models import *
+from django.contrib import admin
+
+admin.site.register(PsychometricData)
diff --git a/lms/djangoapps/psychometrics/management/__init__.py b/lms/djangoapps/psychometrics/management/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/lms/djangoapps/psychometrics/management/commands/__init__.py b/lms/djangoapps/psychometrics/management/commands/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/lms/djangoapps/psychometrics/management/commands/init_psychometrics.py b/lms/djangoapps/psychometrics/management/commands/init_psychometrics.py
new file mode 100644
index 0000000000..5e782df595
--- /dev/null
+++ b/lms/djangoapps/psychometrics/management/commands/init_psychometrics.py
@@ -0,0 +1,70 @@
+#!/usr/bin/python
+#
+# generate pyschometrics data from tracking logs and student module data
+
+import os, sys, string
+import datetime
+import json
+
+from courseware.models import *
+from track.models import *
+from psychometrics.models import *
+from xmodule.modulestore import Location
+
+from django.conf import settings
+from django.core.management.base import BaseCommand
+
+#db = "ocwtutor" # for debugging
+#db = "default"
+
+db = getattr(settings,'DATABASE_FOR_PSYCHOMETRICS','default')
+
+
+class Command(BaseCommand):
+ help = "initialize PsychometricData tables from StudentModule instances (and tracking data, if in SQL)."
+ help += "Note this is done for all courses for which StudentModule instances exist."
+
+ def handle(self, *args, **options):
+
+ # delete all pmd
+
+ #PsychometricData.objects.all().delete()
+ #PsychometricData.objects.using(db).all().delete()
+
+ smset = StudentModule.objects.using(db).exclude(max_grade=None)
+
+ for sm in smset:
+ url = sm.module_state_key
+ location = Location(url)
+ if not location.category=="problem":
+ continue
+ try:
+ state = json.loads(sm.state)
+ done = state['done']
+ except:
+ print "Oops, failed to eval state for %s (state=%s)" % (sm,sm.state)
+ continue
+
+ if done: # only keep if problem completed
+ try:
+ pmd = PsychometricData.objects.using(db).get(studentmodule=sm)
+ except PsychometricData.DoesNotExist:
+ pmd = PsychometricData(studentmodule=sm)
+
+ pmd.done = done
+ pmd.attempts = state['attempts']
+
+ # get attempt times from tracking log
+ uname = sm.student.username
+ tset = TrackingLog.objects.using(db).filter(username=uname, event_type__contains='save_problem_check')
+ tset = tset.filter(event_source='server')
+ tset = tset.filter(event__contains="'%s'" % url)
+ checktimes = [x.dtcreated for x in tset]
+ pmd.checktimes = checktimes
+ if not len(checktimes)==pmd.attempts:
+ print "Oops, mismatch in number of attempts and check times for %s" % pmd
+
+ #print pmd
+ pmd.save(using=db)
+
+ print "%d PMD entries" % PsychometricData.objects.using(db).all().count()
diff --git a/lms/djangoapps/psychometrics/models.py b/lms/djangoapps/psychometrics/models.py
new file mode 100644
index 0000000000..4ffdf59120
--- /dev/null
+++ b/lms/djangoapps/psychometrics/models.py
@@ -0,0 +1,45 @@
+#
+# db model for psychometrics data
+#
+# this data is collected in real time
+#
+
+from django.db import models
+from courseware.models import StudentModule
+
+class PsychometricData(models.Model):
+ """
+ This data is a table linking student, module, and module performance,
+ including number of attempts, grade, max grade, and time of checks.
+
+ Links to instances of StudentModule, but only those for capa problems.
+
+ Note that StudentModule.module_state_key is nominally a Location instance (url string).
+ That means it is of the form {tag}://{org}/{course}/{category}/{name}[@{revision}]
+ and for capa problems, category = "problem".
+
+ checktimes is extracted from tracking logs, or added by capa module via psychometrics callback.
+ """
+
+ studentmodule = models.ForeignKey(StudentModule, db_index=True, unique=True) # contains student, module_state_key, course_id
+
+ done = models.BooleanField(default=False)
+ attempts = models.IntegerField(default=0) # extracted from studentmodule.state
+ checktimes = models.TextField(null=True, blank=True) # internally stored as list of datetime objects
+
+ # keep in mind
+ # grade = studentmodule.grade
+ # max_grade = studentmodule.max_grade
+ # student = studentmodule.student
+ # course_id = studentmodule.course_id
+ # location = studentmodule.module_state_key
+
+ def __unicode__(self):
+ sm = self.studentmodule
+ return "[PsychometricData] %s url=%s, grade=%s, max=%s, attempts=%s, ct=%s" % (sm.student,
+ sm.module_state_key,
+ sm.grade,
+ sm.max_grade,
+ self.attempts,
+ self.checktimes)
+
diff --git a/lms/djangoapps/psychometrics/psychoanalyze.py b/lms/djangoapps/psychometrics/psychoanalyze.py
new file mode 100644
index 0000000000..dd7d328278
--- /dev/null
+++ b/lms/djangoapps/psychometrics/psychoanalyze.py
@@ -0,0 +1,338 @@
+#
+# File: psychometrics/psychoanalyze.py
+#
+# generate pyschometrics plots from PsychometricData
+
+from __future__ import division
+
+import datetime
+import logging
+import json
+import math
+import numpy as np
+from scipy.optimize import curve_fit
+
+from django.conf import settings
+from django.db.models import Sum, Max
+from psychometrics.models import *
+from xmodule.modulestore import Location
+
+log = logging.getLogger("mitx.psychometrics")
+
+#db = "ocwtutor" # for debugging
+#db = "default"
+
+db = getattr(settings, 'DATABASE_FOR_PSYCHOMETRICS', 'default')
+
+#-----------------------------------------------------------------------------
+# fit functions
+
+
+def func_2pl(x, a, b):
+ """
+ 2-parameter logistic function
+ """
+ D = 1.7
+ edax = np.exp(D * a * (x - b))
+ return edax / (1 + edax)
+
+#-----------------------------------------------------------------------------
+# statistics class
+
+
+class StatVar(object):
+ """
+ Simple statistics on floating point numbers: avg, sdv, var, min, max
+ """
+ def __init__(self, unit=1):
+ self.sum = 0
+ self.sum2 = 0
+ self.cnt = 0
+ self.unit = unit
+ self.min = None
+ self.max = None
+
+ def add(self, x):
+ if x is None:
+ return
+ if self.min is None:
+ self.min = x
+ else:
+ if x < self.min:
+ self.min = x
+ if self.max is None:
+ self.max = x
+ else:
+ if x > self.max:
+ self.max = x
+ self.sum += x
+ self.sum2 += x**2
+ self.cnt += 1
+
+ def avg(self):
+ if self.cnt is None:
+ return 0
+ return self.sum / 1.0 / self.cnt / self.unit
+
+ def var(self):
+ if self.cnt is None:
+ return 0
+ return (self.sum2 / 1.0 / self.cnt / (self.unit**2)) - (self.avg()**2)
+
+ def sdv(self):
+ v = self.var()
+ if v>0:
+ return math.sqrt(v)
+ else:
+ return 0
+
+ def __str__(self):
+ return 'cnt=%d, avg=%f, sdv=%f' % (self.cnt, self.avg(), self.sdv())
+
+ def __add__(self, x):
+ self.add(x)
+ return self
+
+#-----------------------------------------------------------------------------
+# histogram generator
+
+
+def make_histogram(ydata, bins=None):
+ '''
+ Generate histogram of ydata using bins provided, or by default bins
+ from 0 to 100 by 10. bins should be ordered in increasing order.
+
+ returns dict with keys being bins, and values being counts.
+ special: hist['bins'] = bins
+ '''
+ if bins is None:
+ bins = range(0, 100, 10)
+
+ nbins = len(bins)
+ hist = dict(zip(bins, [0] * nbins))
+ for y in ydata:
+ for b in bins[::-1]: # in reverse order
+ if y>b:
+ hist[b] += 1
+ break
+ # hist['bins'] = bins
+ return hist
+
+#-----------------------------------------------------------------------------
+
+
+def problems_with_psychometric_data(course_id):
+ '''
+ Return dict of {problems (location urls): count} for which psychometric data is available.
+ Does this for a given course_id.
+ '''
+ pmdset = PsychometricData.objects.using(db).filter(studentmodule__course_id=course_id)
+ plist = [p['studentmodule__module_state_key'] for p in pmdset.values('studentmodule__module_state_key').distinct()]
+ problems = dict( (p, pmdset.filter(studentmodule__module_state_key=p).count()) for p in plist )
+
+ return problems
+
+#-----------------------------------------------------------------------------
+
+
+def generate_plots_for_problem(problem):
+
+ pmdset = PsychometricData.objects.using(db).filter(studentmodule__module_state_key=problem)
+ nstudents = pmdset.count()
+ msg = ""
+ plots = []
+
+ if nstudents < 2:
+ msg += "%s nstudents=%d --> skipping, too few" % (problem, nstudents)
+ return msg, plots
+
+ max_grade = pmdset[0].studentmodule.max_grade
+
+ agdat = pmdset.aggregate(Sum('attempts'), Max('attempts'))
+ max_attempts = agdat['attempts__max']
+ total_attempts = agdat['attempts__sum'] # not used yet
+
+ msg += "max attempts = %d" % max_attempts
+
+ xdat = range(1, max_attempts + 1)
+ dataset = {'xdat': xdat}
+
+ # compute grade statistics
+ grades = [pmd.studentmodule.grade for pmd in pmdset]
+ gsv = StatVar()
+ for g in grades:
+ gsv += g
+ msg += "Grade distribution: %s
" % gsv
+
+ # generate grade histogram
+ ghist = []
+
+ axisopts = """{
+ xaxes: [{
+ axisLabel: 'Grade'
+ }],
+ yaxes: [{
+ position: 'left',
+ axisLabel: 'Count'
+ }]
+ }"""
+
+ if gsv.max > max_grade:
+ msg += "Something is wrong: max_grade=%s, but max(grades)=%s
" % (max_grade, gsv.max)
+ max_grade = gsv.max
+
+ if max_grade > 1:
+ ghist = make_histogram(grades, np.linspace(0, max_grade, max_grade + 1))
+ ghist_json = json.dumps(ghist.items())
+
+ plot = {'title': "Grade histogram for %s" % problem,
+ 'id': 'histogram',
+ 'info': '',
+ 'data': "var dhist = %s;\n" % ghist_json,
+ 'cmd': '[ {data: dhist, bars: { show: true, align: "center" }} ], %s' % axisopts,
+ }
+ plots.append(plot)
+ else:
+ msg += " Not generating histogram: max_grade=%s" % max_grade
+
+ # histogram of time differences between checks
+ # Warning: this is inefficient - doesn't scale to large numbers of students
+ dtset = [] # time differences in minutes
+ dtsv = StatVar()
+ for pmd in pmdset:
+ try:
+ checktimes = eval(pmd.checktimes) # update log of attempt timestamps
+ except:
+ continue
+ if len(checktimes) < 2:
+ continue
+ ct0 = checktimes[0]
+ for ct in checktimes[1:]:
+ dt = (ct - ct0).total_seconds() / 60.0
+ if dt < 20: # ignore if dt too long
+ dtset.append(dt)
+ dtsv += dt
+ ct0 = ct
+ if dtsv.cnt > 2:
+ msg += "Time differences between checks: %s
" % dtsv
+ bins = np.linspace(0, 1.5 * dtsv.sdv(), 30)
+ dbar = bins[1] - bins[0]
+ thist = make_histogram(dtset, bins)
+ thist_json = json.dumps(sorted(thist.items(), key=lambda(x): x[0]))
+
+ axisopts = """{ xaxes: [{ axisLabel: 'Time (min)'}], yaxes: [{position: 'left',axisLabel: 'Count'}]}"""
+
+ plot = {'title': "Histogram of time differences between checks",
+ 'id': 'thistogram',
+ 'info': '',
+ 'data': "var thist = %s;\n" % thist_json,
+ 'cmd': '[ {data: thist, bars: { show: true, align: "center", barWidth:%f }} ], %s' % (dbar, axisopts),
+ }
+ plots.append(plot)
+
+ # one IRT plot curve for each grade received (TODO: this assumes integer grades)
+ for grade in range(1, int(max_grade) + 1):
+ yset = {}
+ gset = pmdset.filter(studentmodule__grade=grade)
+ ngset = gset.count()
+ if ngset == 0:
+ continue
+ ydat = []
+ ylast = 0
+ for x in xdat:
+ y = gset.filter(attempts=x).count() / ngset
+ ydat.append( y + ylast )
+ ylast = y + ylast
+ yset['ydat'] = ydat
+
+ if len(ydat) > 3: # try to fit to logistic function if enough data points
+ cfp = curve_fit(func_2pl, xdat, ydat, [1.0, max_attempts / 2.0])
+ yset['fitparam'] = cfp
+ yset['fitpts'] = func_2pl(np.array(xdat), *cfp[0])
+ yset['fiterr'] = [yd - yf for (yd, yf) in zip(ydat, yset['fitpts'])]
+ fitx = np.linspace(xdat[0], xdat[-1], 100)
+ yset['fitx'] = fitx
+ yset['fity'] = func_2pl(np.array(fitx), *cfp[0])
+
+ dataset['grade_%d' % grade] = yset
+
+ axisopts = """{
+ xaxes: [{
+ axisLabel: 'Number of Attempts'
+ }],
+ yaxes: [{
+ max:1.0,
+ position: 'left',
+ axisLabel: 'Probability of correctness'
+ }]
+ }"""
+
+ # generate points for flot plot
+ for grade in range(1, int(max_grade) + 1):
+ jsdata = ""
+ jsplots = []
+ gkey = 'grade_%d' % grade
+ if gkey in dataset:
+ yset = dataset[gkey]
+ jsdata += "var d%d = %s;\n" % (grade, json.dumps(zip(xdat, yset['ydat'])))
+ jsplots.append('{ data: d%d, lines: { show: false }, points: { show: true}, color: "red" }' % grade)
+ if 'fitpts' in yset:
+ jsdata += 'var fit = %s;\n' % (json.dumps(zip(yset['fitx'], yset['fity'])))
+ jsplots.append('{ data: fit, lines: { show: true }, color: "blue" }')
+ (a, b) = yset['fitparam'][0]
+ irtinfo = "(2PL: D=1.7, a=%6.3f, b=%6.3f)" % (a, b)
+ else:
+ irtinfo = ""
+
+ plots.append({'title': 'IRT Plot for grade=%s %s' % (grade, irtinfo),
+ 'id': "irt%s" % grade,
+ 'info': '',
+ 'data': jsdata,
+ 'cmd': '[%s], %s' % (','.join(jsplots), axisopts),
+ })
+
+ #log.debug('plots = %s' % plots)
+ return msg, plots
+
+#-----------------------------------------------------------------------------
+
+
+def make_psychometrics_data_update_handler(studentmodule):
+ """
+ Construct and return a procedure which may be called to update
+ the PsychometricsData instance for the given StudentModule instance.
+ """
+ sm = studentmodule
+ try:
+ pmd = PsychometricData.objects.using(db).get(studentmodule=sm)
+ except PsychometricData.DoesNotExist:
+ pmd = PsychometricData(studentmodule=sm)
+
+ def psychometrics_data_update_handler(state):
+ """
+ This function may be called each time a problem is successfully checked
+ (eg on save_problem_check events in capa_module).
+
+ state = instance state (a nice, uniform way to interface - for more future psychometric feature extraction)
+ """
+ try:
+ state = json.loads(sm.state)
+ done = state['done']
+ except:
+ log.exception("Oops, failed to eval state for %s (state=%s)" % (sm, sm.state))
+ return
+
+ pmd.done = done
+ pmd.attempts = state['attempts']
+ try:
+ checktimes = eval(pmd.checktimes) # update log of attempt timestamps
+ except:
+ checktimes = []
+ checktimes.append(datetime.datetime.now())
+ pmd.checktimes = checktimes
+ try:
+ pmd.save()
+ except:
+ log.exception("Error in updating psychometrics data for %s" % sm)
+
+ return psychometrics_data_update_handler
diff --git a/lms/djangoapps/static_template_view/views.py b/lms/djangoapps/static_template_view/views.py
index 90087e06d6..8ab6216eda 100644
--- a/lms/djangoapps/static_template_view/views.py
+++ b/lms/djangoapps/static_template_view/views.py
@@ -6,6 +6,7 @@
from mitxmako.shortcuts import render_to_response, render_to_string
from django.shortcuts import redirect
from django.conf import settings
+from django.http import HttpResponseNotFound, HttpResponseServerError
from django_future.csrf import ensure_csrf_cookie
from util.cache import cache_if_anonymous
@@ -40,9 +41,9 @@ def render(request, template):
def render_404(request):
- return render_to_response('static_templates/404.html', {})
+ return HttpResponseNotFound(render_to_string('static_templates/404.html', {}))
def render_500(request):
- return render_to_response('static_templates/server-error.html', {})
+ return HttpResponseServerError(render_to_string('static_templates/server-error.html', {}))
diff --git a/lms/envs/common.py b/lms/envs/common.py
index 55fa5b457c..3cfaae940d 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -71,6 +71,8 @@ MITX_FEATURES = {
'ENABLE_DISCUSSION' : False,
'ENABLE_DISCUSSION_SERVICE': True,
+ 'ENABLE_PSYCHOMETRICS': False, # real-time psychometrics (eg item response theory analysis in instructor dashboard)
+
'ENABLE_SQL_TRACKING_LOGS': False,
'ENABLE_LMS_MIGRATION': False,
'ENABLE_MANUAL_GIT_RELOAD': False,
@@ -267,12 +269,22 @@ STATICFILES_DIRS = [
PROJECT_ROOT / "askbot" / "skins",
]
if os.path.isdir(DATA_DIR):
+ # Add the full course repo if there is no static directory
STATICFILES_DIRS += [
# TODO (cpennington): When courses are stored in a database, this
# should no longer be added to STATICFILES
(course_dir, DATA_DIR / course_dir)
for course_dir in os.listdir(DATA_DIR)
- if os.path.isdir(DATA_DIR / course_dir)
+ if (os.path.isdir(DATA_DIR / course_dir) and
+ not os.path.isdir(DATA_DIR / course_dir / 'static'))
+ ]
+ # Otherwise, add only the static directory from the course dir
+ STATICFILES_DIRS += [
+ # TODO (cpennington): When courses are stored in a database, this
+ # should no longer be added to STATICFILES
+ (course_dir, DATA_DIR / course_dir / 'static')
+ for course_dir in os.listdir(DATA_DIR)
+ if (os.path.isdir(DATA_DIR / course_dir / 'static'))
]
# Locale/Internationalization
@@ -609,6 +621,7 @@ INSTALLED_APPS = (
'util',
'certificates',
'instructor',
+ 'psychometrics',
#For the wiki
'wiki', # The new django-wiki from benjaoming
diff --git a/lms/envs/dev.py b/lms/envs/dev.py
index 50befeb875..a9f1454193 100644
--- a/lms/envs/dev.py
+++ b/lms/envs/dev.py
@@ -20,6 +20,8 @@ MITX_FEATURES['SUBDOMAIN_COURSE_LISTINGS'] = False # Enable to test subdomains-
MITX_FEATURES['SUBDOMAIN_BRANDING'] = True
MITX_FEATURES['FORCE_UNIVERSITY_DOMAIN'] = None # show all university courses if in dev (ie don't use HTTP_HOST)
MITX_FEATURES['ENABLE_MANUAL_GIT_RELOAD'] = True
+MITX_FEATURES['ENABLE_PSYCHOMETRICS'] = False # real-time psychometrics (eg item response theory analysis in instructor dashboard)
+
WIKI_ENABLED = True
diff --git a/lms/envs/test.py b/lms/envs/test.py
index b55cf2af5f..34108256e9 100644
--- a/lms/envs/test.py
+++ b/lms/envs/test.py
@@ -46,6 +46,7 @@ DATA_DIR = COURSES_ROOT
LOGGING = get_logger_config(TEST_ROOT / "log",
logging_env="dev",
tracking_filename="tracking.log",
+ dev_env=True,
debug=True)
COMMON_TEST_DATA_ROOT = COMMON_ROOT / "test" / "data"
diff --git a/lms/static/coffee/src/discussion/content.coffee b/lms/static/coffee/src/discussion/content.coffee
index 73c3688c2a..9f95c201f4 100644
--- a/lms/static/coffee/src/discussion/content.coffee
+++ b/lms/static/coffee/src/discussion/content.coffee
@@ -374,6 +374,9 @@ if Backbone?
MathJax.Hub.Queue ["Typeset", MathJax.Hub, $contentBody.attr("id")]
initTimeago: ->
+ @$("span.timeago").each (index, element) ->
+ elem = $(element)
+ elem.html("posted on #{$.timeago.parse(elem.html()).toLocaleString()}")
@$("span.timeago").timeago()
renderPartial: ->
diff --git a/lms/static/images/press/baltsun_logo_178x138.jpg b/lms/static/images/press/baltsun_logo_178x138.jpg
new file mode 100755
index 0000000000..3dc619ac7b
Binary files /dev/null and b/lms/static/images/press/baltsun_logo_178x138.jpg differ
diff --git a/lms/static/images/press/bostinno_logo_178x138.jpg b/lms/static/images/press/bostinno_logo_178x138.jpg
new file mode 100644
index 0000000000..dadddecbf9
Binary files /dev/null and b/lms/static/images/press/bostinno_logo_178x138.jpg differ
diff --git a/lms/static/images/press/bostonmag_logo_178x138.jpg b/lms/static/images/press/bostonmag_logo_178x138.jpg
new file mode 100755
index 0000000000..aa4f8a3577
Binary files /dev/null and b/lms/static/images/press/bostonmag_logo_178x138.jpg differ
diff --git a/lms/static/images/press/csmonitor_logo_178x138.jpg b/lms/static/images/press/csmonitor_logo_178x138.jpg
new file mode 100755
index 0000000000..d389b7a6f1
Binary files /dev/null and b/lms/static/images/press/csmonitor_logo_178x138.jpg differ
diff --git a/lms/static/images/press/insidehighered_logo_178x138.jpg b/lms/static/images/press/insidehighered_logo_178x138.jpg
new file mode 100755
index 0000000000..ad23f11a96
Binary files /dev/null and b/lms/static/images/press/insidehighered_logo_178x138.jpg differ
diff --git a/lms/static/images/press/itbriefing_logo_178x138.jpg b/lms/static/images/press/itbriefing_logo_178x138.jpg
new file mode 100755
index 0000000000..c8380ada13
Binary files /dev/null and b/lms/static/images/press/itbriefing_logo_178x138.jpg differ
diff --git a/lms/static/images/press/radioboston_logo_178x138.jpg b/lms/static/images/press/radioboston_logo_178x138.jpg
new file mode 100755
index 0000000000..e29949a31f
Binary files /dev/null and b/lms/static/images/press/radioboston_logo_178x138.jpg differ
diff --git a/lms/static/images/press/smartplanet_logo_178x138.jpg b/lms/static/images/press/smartplanet_logo_178x138.jpg
new file mode 100755
index 0000000000..534677017e
Binary files /dev/null and b/lms/static/images/press/smartplanet_logo_178x138.jpg differ
diff --git a/lms/static/images/press/techreview_logo_178x138.jpg b/lms/static/images/press/techreview_logo_178x138.jpg
new file mode 100755
index 0000000000..ffbd3756c9
Binary files /dev/null and b/lms/static/images/press/techreview_logo_178x138.jpg differ
diff --git a/lms/static/images/press/thetech_logo_178x138.jpg b/lms/static/images/press/thetech_logo_178x138.jpg
new file mode 100755
index 0000000000..024fe11769
Binary files /dev/null and b/lms/static/images/press/thetech_logo_178x138.jpg differ
diff --git a/lms/static/images/press/time_logo_178x138.jpg b/lms/static/images/press/time_logo_178x138.jpg
new file mode 100755
index 0000000000..5d884fe0fe
Binary files /dev/null and b/lms/static/images/press/time_logo_178x138.jpg differ
diff --git a/lms/static/sass/_discussion.scss b/lms/static/sass/_discussion.scss
index b00dc069f1..6d44ebdb68 100644
--- a/lms/static/sass/_discussion.scss
+++ b/lms/static/sass/_discussion.scss
@@ -390,6 +390,14 @@ $tag-text-color: #5b614f;
color: #dea03e;
}
}
+
+ .author-moderator:after{
+ content: " (moderator)"
+ }
+
+ .author-administrator:after{
+ content: " (instructor)"
+ }
}
.discussion-content {
@@ -415,6 +423,13 @@ $tag-text-color: #5b614f;
}
}
+ // Role based styles
+ .role-moderator{
+ background-color: #eafcfc;
+ }
+ .role-administrator{
+ background-color: #eafcea;
+ }
//COMMENT STYLES
.comments {
overflow: hidden;
diff --git a/lms/static/sass/course/_info.scss b/lms/static/sass/course/_info.scss
index e25bb9d8c4..80db054afd 100644
--- a/lms/static/sass/course/_info.scss
+++ b/lms/static/sass/course/_info.scss
@@ -25,11 +25,6 @@ div.info-wrapper {
margin-bottom: lh();
padding-bottom: lh(.5);
- &:first-child {
- margin: 0 (-(lh(.5))) lh();
- padding: lh(.5);
- }
-
ol, ul {
margin: 0;
list-style-type: disk;
diff --git a/lms/static/sass/course/courseware/_courseware.scss b/lms/static/sass/course/courseware/_courseware.scss
index 0532f04b42..0b79ec6a6b 100644
--- a/lms/static/sass/course/courseware/_courseware.scss
+++ b/lms/static/sass/course/courseware/_courseware.scss
@@ -80,7 +80,6 @@ div.course-wrapper {
}
.histogram {
- display: none;
width: 200px;
height: 150px;
}
@@ -117,6 +116,7 @@ div.course-wrapper {
margin: 0;
@include clearfix();
padding: 0;
+ list-style: none;
li {
width: flex-grid(3, 9);
diff --git a/lms/templates/courseware/courseware.html b/lms/templates/courseware/courseware.html
index 29b9be3789..67719eacc9 100644
--- a/lms/templates/courseware/courseware.html
+++ b/lms/templates/courseware/courseware.html
@@ -21,6 +21,7 @@
##
##
+
<%static:js group='courseware'/>
<%static:js group='discussion'/>
@@ -35,6 +36,22 @@
%block>
diff --git a/lms/templates/courseware/instructor_dashboard.html b/lms/templates/courseware/instructor_dashboard.html
index 2d9ab853eb..e822f05f92 100644
--- a/lms/templates/courseware/instructor_dashboard.html
+++ b/lms/templates/courseware/instructor_dashboard.html
@@ -4,6 +4,8 @@
<%block name="headextra">
<%static:css group='course'/>
+
+
%block>
<%include file="/courseware/course_navigation.html" args="active_page='instructor'" />
@@ -31,54 +33,112 @@ table.stat_table td {
border-color: #666666;
background-color: #ffffff;
}
+
+a.selectedmode { background-color: yellow; }
+
+
+
+
Instructor Dashboard
-
+##-----------------------------------------------------------------------------
+%if modeflag.get('Psychometrics') is None:
+
@@ -99,24 +159,61 @@ table.stat_table td {
%endfor
-
-%if msg:
- ${msg}
%endif
-% if course_errors is not UNDEFINED:
+##-----------------------------------------------------------------------------
+%if modeflag.get('Psychometrics'):
+
+ %for plot in plots:
+
+ ${plot['title']}
+
+ ${plot['info']}
+
+
+
+
+
+ %endfor
+
+%endif
+
+##-----------------------------------------------------------------------------
+## always show msg
+
+%if msg:
+ ${msg}
+%endif
+
+##-----------------------------------------------------------------------------
+%if modeflag.get('Admin'):
+ % if course_errors is not UNDEFINED:
Course errors
+ %if not course_errors:
+ None
+ %else:
% for (summary, err) in course_errors:
${summary | h}
+ % if err:
+ % else:
+
+ % endif
% endfor
+ %endif
-% endif
-
+ % endif
+%endif
+
diff --git a/lms/templates/courseware/welcome-back.html b/lms/templates/courseware/welcome-back.html
index a817d303a9..5d4e0fe1e3 100644
--- a/lms/templates/courseware/welcome-back.html
+++ b/lms/templates/courseware/welcome-back.html
@@ -1,3 +1,3 @@
${chapter_module.display_name}
-You were last in ${prev_section.display_name} . If you're done with that, choose another section on the left.
\ No newline at end of file
+You were most recently in ${prev_section.display_name} . If you're done with that, choose another section on the left.
diff --git a/lms/templates/discussion/mustache/_content.mustache b/lms/templates/discussion/mustache/_content.mustache
index 88f53a23ac..8f2ebca2a9 100644
--- a/lms/templates/discussion/mustache/_content.mustache
+++ b/lms/templates/discussion/mustache/_content.mustache
@@ -1,4 +1,4 @@
-
+
diff --git a/lms/templates/press.json b/lms/templates/press.json
index 16942f06fb..f523f430af 100644
--- a/lms/templates/press.json
+++ b/lms/templates/press.json
@@ -1,4 +1,187 @@
[
+
+ {
+ "title": "Is MIT Giving Away the Farm?",
+ "url": "http://www.technologyreview.com/mitnews/428698/is-mit-giving-away-the-farm/",
+ "author": "Larry Hardesty",
+ "image": "techreview_logo_178x138.jpg",
+ "deck": "The surprising logic of MIT's free online education program.",
+ "publication": "Technology Review",
+ "publish_date": "September/October 2012"
+ },
+ {
+ "title": "School’s Out, Forever",
+ "url": "http://www.bostonmagazine.com/articles/2012/08/edx-online-classes-schools-ou
t-forever/",
+ "author": "Chris Vogel",
+ "image": "bostonmag_logo_178x138.jpg",
+ "deck": "A new online education program from Harvard and MIT is poised to transform what it means to go to college.",
+ "publication": "Boston Magazine",
+ "publish_date": "September 2012"
+ },
+ {
+ "title": "Q&A: Anant Agarwal, edX’s president and first professor",
+ "url": "http://www.bostonmagazine.com/articles/2012/08/edx-online-classes-schools-ou
t-forever/",
+ "author": " Molly Petrilla ",
+ "image": "smartplanet_logo_178x138.jpg",
+ "deck": "",
+ "publication": "Smart Planet",
+ "publish_date": "September 3, 2012"
+ },
+
+ {
+ "title": "EdX To Offer Proctored Final Exam For One Course",
+ "url": "http://www.thecrimson.com/article/2012/9/7/edx-offer-proctored-exams/",
+ "author": "Samuel Y. Weinstock",
+ "image": "harvardcrimson_logo_178x138.jpeg",
+ "deck": "",
+ "publication": "Harvard Crimson",
+ "publish_date": "September 7, 2012"
+ },
+ {
+ "title": "MOOCing On Site",
+ "url": "http://www.insidehighered.com/news/2012/09/07/site-based-testing-deals-strengthen-case-granting-credit-mooc-students",
+ "author": "Steve Kolowich ",
+ "image": "insidehighered_logo_178x138.jpg",
+ "deck": "",
+ "publication": "Inside Higher Education",
+ "publish_date": "September 7, 2012"
+ },
+ {
+ "title": "edX Curbs the Downfalls of Online Education By Announcing Supervised Final Exams",
+ "url": "http://bostinno.com/2012/09/07/edx-pearson-proctored-exams/",
+ "author": "Lauren Landry",
+ "image": "bostinno_logo_178x138.jpg",
+ "deck": "",
+ "publication": "Bostinno",
+ "publish_date": "September 7, 2012"
+ },
+ {
+ "title": "Harvard and MIT online courses get 'real world' exams",
+ "url": "http://www.bbc.co.uk/news/education-19505776",
+ "author": "Sean Coughlan",
+ "image": "bbc_logo_178x138.jpeg",
+ "deck": "",
+ "publication": "BBC",
+ "publish_date": "September 6, 2012"
+ },
+ {
+ "title": "Harvard-MIT Online School EdX to Offer Supervised Final Exams",
+ "url": "http://www.businessweek.com/news/2012-09-06/harvard-mit-online-school-edx-to-offer-supervised-final-exams",
+ "author": "Oliver Staley",
+ "image": "bloomberg_logo_178x138.jpeg",
+ "deck": "",
+ "publication": "Bloomberg Business Week",
+ "publish_date": "September 6, 2012"
+ },
+ {
+ "title": "Colorado State to Offer Credits for Online Class",
+ "url": "http://www.nytimes.com/2012/09/07/education/colorado-state-to-offer-credits-for-online-class.html?_r=3",
+ "author": "Tamar Lewin",
+ "image": "nyt_logo_178x138.jpeg",
+ "deck": "",
+ "publication": "New York Times",
+ "publish_date": "September 6, 2012"
+ },
+ {
+ "title": "edX Offers Proctored Exams for Open Online Course",
+ "url": "http://chronicle.com/blogs/wiredcampus/edx-offers-proctored-exams-for-open-online-course/39656",
+ "author": " Marc Parry",
+ "image": "chroniclehighered_logo_178x138.jpeg",
+ "deck": "",
+ "publication": "Chronicle of Higher Education",
+ "publish_date": "September 6, 2012"
+ },
+ {
+ "title": "edX Offers Proctored Exams for Open Online Course",
+ "url": "http://itbriefing.net/modules.php?op=modload&name=News&file=article&sid=323229&newlang=eng&topic=15&catid=37",
+ "author": "",
+ "image": "itbriefing_logo_178x138.jpg",
+ "deck": "",
+ "publication": "ITBriefing.net",
+ "publish_date": "September 6, 2012"
+ },
+
+ {
+ "title": "Student Loans: Debt for Life",
+ "url": "http://www.businessweek.com/articles/2012-09-06/student-loans-debt-for-life#p3",
+ "author": "Peter Coy",
+ "image": "bloomberg_logo_178x138.jpeg",
+ "deck": "",
+ "publication": "Bloomberg Business Week",
+ "publish_date": "September 6, 2012"
+ },
+ {
+ "title": "Straighterline wants to help professors expand reach, while students save",
+ "url": "http://www.baltimoresun.com/business/technology/blog/bs-bz-straighterline-college-professors-20120904,0,6114022.story",
+ "author": "Gus G. Sentementes",
+ "image": "baltsun_logo_178x138.jpg",
+ "deck": "",
+ "publication": "The Baltimore Sun",
+ "publish_date": "September 4, 2012"
+ },
+ {
+ "title": "Want to be a reporter? Learn to code",
+ "url": "http://gigaom.com/cloud/want-to-be-a-reporter-learn-to-code/",
+ "author": "Barb Darrow",
+ "image": "gigaom_logo_178x138.jpeg",
+ "deck": "",
+ "publication": "GigaOM",
+ "publish_date": "September 4, 2012"
+ },
+ {
+ "title": "MOOC Brigade: Will Massive, Open Online Courses Revolutionize Higher Education?",
+ "url": "http://nation.time.com/2012/09/04/mooc-brigade-will-massive-open-online-courses-revolutionize-higher-education/",
+ "author": "Kayla Webley",
+ "image": "time_logo_178x138.jpg",
+ "deck": "",
+ "publication": "Time",
+ "publish_date": "September 4, 2012"
+ },
+ {
+ "title": "Ivy walls lower with free online classes from Coursera and edX ",
+ "url": "http://www.csmonitor.com/Innovation/Pioneers/2012/0903/Ivy-walls-lower-with-free-online-classes-from-Coursera-and-edX",
+ "author": "Chris Gaylord",
+ "image": "csmonitor_logo_178x138.jpg",
+ "deck": "",
+ "publication": "Christian Science Monitor",
+ "publish_date": "September 3, 2012"
+ },
+ {
+ "title": "Summer recap. RLADs, new edX partner, Institute files amicus brief",
+ "url": "http://tech.mit.edu/V132/N34/summer.html",
+ "author": "",
+ "image": "thetech_logo_178x138.jpg",
+ "deck": "",
+ "publication": "The Tech",
+ "publish_date": "September 4, 2012"
+ },
+ {
+ "title": "Into the Future With MOOC's",
+ "url": "http://chronicle.com/article/Into-the-Future-With-MOOCs/134080/",
+ "author": "Kevin Carey",
+ "image": "chroniclehighered_logo_178x138.jpeg",
+ "deck": "",
+ "publication": "The Chronicle of Higher Education",
+ "publish_date": "September 3, 2012"
+ },
+ {
+ "title": "The Future Of Higher Education",
+ "url": "http://radioboston.wbur.org/2012/08/20/higher-education-online",
+ "author": "",
+ "image": "radioboston_logo_178x138.jpg",
+ "deck": "",
+ "publication": "NPR/Radio Boston",
+ "publish_date": "August 20, 2012"
+ },
+ {
+ "title": "Berkeley Joins edX",
+ "url": "http://www.insidehighered.com/quicktakes/2012/07/24/berkeley-joins-edx",
+ "author": "Tamar Lewin",
+ "image": "insidehighered_logo_178x138.jpg",
+ "deck": "",
+ "publication": "Inside Higher Ed",
+ "publish_date": "July 24, 2012"
+ },
{
"title": "Berkeley to Join the Free Online Learning Partnership EdX",
"url": "http://www.nytimes.com/2012/07/24/education/berkeley-to-offer-free-online-classes-on-edx.html?_r=1",
diff --git a/lms/templates/static_templates/press.html b/lms/templates/static_templates/press.html
index 6294b346a9..277cb91bd2 100644
--- a/lms/templates/static_templates/press.html
+++ b/lms/templates/static_templates/press.html
@@ -37,4 +37,3 @@
% endfor
-
diff --git a/lms/urls.py b/lms/urls.py
index 8484ccc40b..49febaf84e 100644
--- a/lms/urls.py
+++ b/lms/urls.py
@@ -237,6 +237,7 @@ if settings.MITX_FEATURES.get('ENABLE_LMS_MIGRATION'):
urlpatterns += (
url(r'^migrate/modules$', 'lms_migration.migrate.manage_modulestores'),
url(r'^migrate/reload/(?P[^/]+)$', 'lms_migration.migrate.manage_modulestores'),
+ url(r'^migrate/reload/(?P[^/]+)/(?P[^/]+)$', 'lms_migration.migrate.manage_modulestores'),
url(r'^gitreload$', 'lms_migration.migrate.gitreload'),
url(r'^gitreload/(?P[^/]+)$', 'lms_migration.migrate.gitreload'),
)
diff --git a/rakefile b/rakefile
index 053abf56a8..9e0bbcbfa4 100644
--- a/rakefile
+++ b/rakefile
@@ -227,3 +227,13 @@ namespace :cms do
end
end
end
+
+desc "Build a properties file used to trigger autodeploy builds"
+task :autodeploy_properties do
+ File.open("autodeploy.properties", "w") do |file|
+ file.puts("UPSTREAM_NOOP=false")
+ file.puts("UPSTREAM_BRANCH=#{BRANCH}")
+ file.puts("UPSTREAM_JOB=#{PACKAGE_NAME}")
+ file.puts("UPSTREAM_REVISION=#{COMMIT}")
+ end
+end