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.

- -
- -
-
- -
-
- - - -
- -
-
-

Graph:

-
    -
  • -
  • -
  • -
-
- -
-

Listen to:

-
    -
  • -
  • -
  • -
-
-
- -
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Your browser must support the Canvas element and have JavaScript enabled to view this tool. - Your browser must support the Canvas element and have JavaScript enabled to view this tool. -
- -
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. -

- -
- -
-
-
-
- - -
- -
-
-

Graph:

-
    -
  • -
  • -
  • -
-
- -
-

Listen to:

-
    -
  • -
  • -
  • -
-
-
-
-
-
fC =
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - Your browser must support the Canvas element and have JavaScript enabled to view this tool. - Your browser must support the Canvas element and have JavaScript enabled to view this tool. - Your browser must support the Canvas element and have JavaScript enabled to view this tool. -
- - Your browser must support the Canvas element and have JavaScript enabled to view this tool. -
- -
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}\)

- -
- -
-
- -
-
- - -
- -
-
-

Graph:

-
    -
  • -
  • -
  • -
  • -
-
- -
-

Listen to:

-
    -
  • -
  • -
  • -
  • -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - Your browser must support the Canvas element and have JavaScript enabled to view this tool. - Your browser must support the Canvas element and have JavaScript enabled to view this tool. - Your browser must support the Canvas element and have JavaScript enabled to view this tool. -
- - Your browser must support the Canvas element and have JavaScript enabled to view this tool. - -
- -
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 "