[LON-CAPA-cvs] cvs: loncom /homework chemresponse.pm /html/res/adm/pages/reactionresponse reaction_editor.html reaction_frame.html reaction_help.html reaction_preview.js reaction_viewer.html reaction_window.html
damieng
damieng at source.lon-capa.org
Wed Jan 4 15:09:17 EST 2017
damieng Wed Jan 4 20:09:17 2017 EDT
Added files:
/loncom/html/res/adm/pages/reactionresponse reaction_preview.js
Removed files:
/loncom/html/res/adm/pages/reactionresponse reaction_editor.html
reaction_frame.html
reaction_viewer.html
reaction_window.html
Modified files:
/loncom/homework chemresponse.pm
/loncom/html/res/adm/pages/reactionresponse reaction_help.html
Log:
replaced reaction editor by a real-time preview
-------------- next part --------------
Index: loncom/homework/chemresponse.pm
diff -u loncom/homework/chemresponse.pm:1.98 loncom/homework/chemresponse.pm:1.99
--- loncom/homework/chemresponse.pm:1.98 Mon Sep 21 14:24:54 2015
+++ loncom/homework/chemresponse.pm Wed Jan 4 20:09:08 2017
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# chemical equation style response
#
-# $Id: chemresponse.pm,v 1.98 2015/09/21 14:24:54 raeburn Exp $
+# $Id: chemresponse.pm,v 1.99 2017/01/04 20:09:08 damieng Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -630,37 +630,32 @@
return $result;
}
-sub edit_reaction_button {
- my ($id,$field,$reaction)=@_;
- my $id_es=&escape($id);
- my $field_es=&escape($field);
- my $reaction_es=&escape($reaction);
- my $docopen=&Apache::lonhtmlcommon::javascript_docopen();
- my $iconpath=$Apache::lonnet::perlvar{'lonIconsURL'};
- my $display=&mt('Edit Answer');
- my $start_page =
- &Apache::loncommon::start_page('LON-CAPA Reaction Editor',undef,
- {'frameset' => 1,
- 'js_ready' => 1,
- 'add_entries' => {
- 'rows' => "30%,*",
- 'border' => "0",}},);
- my $end_page =
- &Apache::loncommon::end_page({'frameset' => 1,
- 'js_ready' => 1});
- my $result=<<EDITREACTION;
+sub reaction_preview {
+ my ($field, $reaction) = @_;
+
+ # NOTE: $reaction should be encoded if the document was sent as XHTML
+ $reaction =~ s/"//g;
+ my $result=<<JS_PREVIEW;
+<input type="button" value="Help" onclick = "window.open('/adm/reactionresponse/reaction_help.html','','scrollbars=yes,resizable=yes,width=550,height=600')" />
<script type="text/javascript">
-// <!--
- function create_reaction_window_${id}_${field} () {
- editor=window.open('','','width=500,height=270,scrollbars=no,resizable=yes');
- editor.$docopen;
- editor.document.writeln('$start_page <frame src="/adm/reactionresponse/reaction_viewer.html?inhibitmenu=yes" name="viewer" scrolling="no" /> <frame src="/adm/reactionresponse/reaction_editor.html?inhibitmenu=yes&reaction=$reaction_es&id=$id_es&field=$field_es" name="editor" scrolling="no" /> $end_page');
- editor.document.close();
- }
-// -->
+ if (typeof reaction_preview_started === 'undefined') {
+ var script = document.createElement('script');
+ script.type = 'text/javascript';
+ script.src = '/adm/reactionresponse/reaction_preview.js';
+ document.body.appendChild(script);
+ reaction_preview_started = true;
+ }
+ window.addEventListener('load', function(e) {
+ var input = document.forms.lonhomework.$field;
+ input.readonly = '';
+ input.value = "$reaction";
+ if (!input.id)
+ input.id = "$field";
+ var preview = new LC.HW.ReactionPreview(input.id);
+ preview.start_preview();
+ }, false);
</script>
-<a href="javascript:create_reaction_window_${id}_${field}();void(0);"><img class="stift" src='$iconpath/stift.gif' alt='$display' title='$display' /></a>
-EDITREACTION
+JS_PREVIEW
return $result;
}
@@ -687,11 +682,11 @@
$safeeval);
$result .='<span class="LC_nobreak">'.
&Apache::edit::text_arg('Answer:','answer',$token,40);
- $result .=&edit_reaction_button($id,&Apache::edit::html_element_name('answer'),$answer).'</span>';
+ $result .= &reaction_preview(&Apache::edit::html_element_name('answer'), $answer).'</span>';
my $initial=&Apache::lonxml::get_param('initial',$parstack,$safeeval);
$result.='<span class="LC_nobreak">'.
&Apache::edit::text_arg('Initial Reaction:','initial',$token,40);
- $result .=&edit_reaction_button($id,&Apache::edit::html_element_name('initial'),$initial).'</span>';
+ $result .= &reaction_preview(&Apache::edit::html_element_name('initial'), $initial).'</span>';
$result .=&Apache::edit::end_row().&Apache::edit::start_spanning_row();
} elsif ($target eq 'modified') {
my $constructtag=&Apache::edit::get_new_args($token,$parstack,
@@ -779,7 +774,7 @@
if (($target eq 'web') && ($Apache::lonhomework::type ne 'exam') && ($status eq 'CAN_ANSWER')) {
my $reaction=$Apache::lonhomework::history{"resource.$partid.$id.submission"};
if ($reaction eq '') { $reaction=&Apache::lonxml::get_param('initial',$parstack,$safeeval); }
- $result.=&edit_reaction_button($id,"HWVAL_$id",$reaction);
+ $result .= &reaction_preview("HWVAL_$id", $reaction);
}
&Apache::response::end_response();
return $result;
Index: loncom/html/res/adm/pages/reactionresponse/reaction_help.html
diff -u loncom/html/res/adm/pages/reactionresponse/reaction_help.html:1.3 loncom/html/res/adm/pages/reactionresponse/reaction_help.html:1.4
--- loncom/html/res/adm/pages/reactionresponse/reaction_help.html:1.3 Mon Jun 30 20:52:43 2003
+++ loncom/html/res/adm/pages/reactionresponse/reaction_help.html Wed Jan 4 20:09:17 2017
@@ -1,6 +1,6 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<!--
-$Id: reaction_help.html,v 1.3 2003/06/30 20:52:43 albertel Exp $
+$Id: reaction_help.html,v 1.4 2017/01/04 20:09:17 damieng Exp $
Copyright Michigan State University Board of Trustees
@@ -25,11 +25,11 @@
http://www.lon-capa.org/
-->
<html> <head>
-<title>Chemical Reaction Editor Help</title>
+<title>Chemical Reaction Syntax Help</title>
</head>
<body>
-<h1>Chemical Reaction Editor</h1>
+<h1>Chemical Reaction Syntax</h1>
<h3>Description</h3>
<ul>
<li>Reactants are separated from products by '->'.</li>
@@ -39,15 +39,17 @@
<li>Superscripts are initialized by '^' and terminated by a space.</li>
<li>Ionic charges are superscripts composed of a number followed by a sign (i.e. '^2+').</li>
</ul>
-<br /><br />
-<i>Press the 'Check' button to view your reaction before you submit it!</i>
<h3>Examples</h3>
<ul>
-<li>Ca(NO<sub>3</sub>)<sub>2</sub> is written as:<br> Ca(NO3)2</li>
-<li>OH<sup>-</sup> + H<sub>3</sub>O<sup>+</sup> <m>$\rightarrow$</m> 2H<sub>2</sub>O is written as:<br> OH^- + H3O^+ -> 2H2O</li>
-<li>[Co(H<sub>2</sub>O)<sub>6</sub>]<sup>2+</sup> + 4Cl<sup>-</sup> <m>$\rightarrow$</m> [CoCl<sub>4</sub>]<sup>2-</sup> + 6H<sub>2</sub>O is written as:<br> [Co(H2O)6]^2+ + 4Cl^- -> [CoCl4]^2- + 6H2O</li>
-<li><sup>3</sup><sub>1</sub>H + <sup>2</sup><sub>1</sub>H <m>$\rightarrow$</m> <sup>4</sup><sub>2</sub>He + <sup>1</sup><sub>0</sub>n is written as:<br> ^3 1H + ^2 1H -> ^4 2He + ^1 0n</li>
+<li>Ca(NO<sub>3</sub>)<sub>2</sub> is written as:<br>
+<tt>Ca(NO3)2</tt></li>
+<li>OH<sup>-</sup> + H<sub>3</sub>O<sup>+</sup> → 2H<sub>2</sub>O is written as:<br>
+<tt>OH^- + H3O^+ -> 2H2O</tt></li>
+<li>[Co(H<sub>2</sub>O)<sub>6</sub>]<sup>2+</sup> + 4Cl<sup>-</sup> → [CoCl<sub>4</sub>]<sup>2-</sup> + 6H<sub>2</sub>O is written as:<br>
+<tt>[Co(H2O)6]^2+ + 4Cl^- -> [CoCl4]^2- + 6H2O</tt></li>
+<li><sup>3</sup><sub>1</sub>H + <sup>2</sup><sub>1</sub>H → <sup>4</sup><sub>2</sub>He + <sup>1</sup><sub>0</sub>n is written as:<br>
+<tt>^3 1H + ^2 1H -> ^4 2He + ^1 0n</tt></li>
</ul>
<hr />
<center>
Index: loncom/html/res/adm/pages/reactionresponse/reaction_preview.js
+++ loncom/html/res/adm/pages/reactionresponse/reaction_preview.js
/*
Copyright Michigan State University Board of Trustees
This file is part of the LearningOnline Network with CAPA (LON-CAPA).
LON-CAPA is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
LON-CAPA is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with LON-CAPA; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/home/httpd/html/adm/gpl.txt
http://www.lon-capa.org/
*/
'use strict';
// initialize namespaces if necessary
var LC = LC || {};
LC.HW = LC.HW || {};
/**
* This is implementing the reaction response real-time preview.
* Some of the code comes from the old reaction editor at
* loncom/html/res/adm/pages/reactionresponse/reaction_editor.html
*
* @constructor
*
* @param {string} field_id - the input field id
*/
LC.HW.ReactionPreview = function(field_id) {
/** @type {string} */
this.field_id = field_id;
/** @type {number} */
this.level = null;
/** @type {Array.<string>} */
this.reactants = null;
/** @type {Array.<string>} */
this.products = null;
/** @type {string} */
this.old_string = null;
}
/**
* Parses the reaction string
* @param {string} s - the user input
*/
LC.HW.ReactionPreview.prototype.parse_reaction = function(s) {
var reaction_array = s.split(/\s*->\s*/);
this.reactants = new Array(0);
this.products = new Array(0);
if (reaction_array.length > 0)
this.reactants = reaction_array[0].split(/\s+\+\s*/);
if (reaction_array.length > 1)
this.products = reaction_array[1].split(/\s+\+\s*/);
}
/**
* Converts the reaction string to the CAPA syntax.
* @param {string} s - the user input
* @returns {string}
*/
LC.HW.ReactionPreview.prototype.to_capa = function(s) {
var reaction = '';
var i;
this.parse_reaction(s);
for (i = 0; i < this.reactants.length; i++)
this.reactants[i] = this.capa_component(this.reactants[i]);
for (i = 0; i < this.products.length; i++)
this.products[i] = this.capa_component(this.products[i]);
// this.reactants.sort();
// this.products.sort();
for (i = 0; i < this.reactants.length-1; i++) {
reaction += this.reactants[i];
reaction += ' + ';
}
if (i < this.reactants.length)
reaction += this.reactants[i];
if (this.products.length > 0) {
reaction += ' -> ';
for (i = 0; i < this.products.length-1; i++) {
reaction += this.products[i];
reaction += ' + ';
}
if (i < this.products.length)
reaction += this.products[i];
}
return reaction;
}
/**
* Converts a component string to the CAPA syntax.
* @param {string} s - the component string
*/
LC.HW.ReactionPreview.prototype.capa_component = function(s) {
var reactant = '';
var i = 0;
this.level = 0;
for ( ; s.substring(i, i+1) == ' '; i++)
;
for ( ; LC.HW.ReactionPreview.isDigit(s.substring(i, i+1)); i++)
reactant += s.substring(i, i+1);
for ( ; i < s.length; i++)
reactant += this.capa_char(s.substring(i, i+1));
return reactant;
}
/**
* Processes a character for the CAPA syntax.
* @param {string} chr - a character
* @returns {string}
*/
LC.HW.ReactionPreview.prototype.capa_char = function(chr) {
if (this.level == 0) { // baseline
if (chr == '^')
this.level = 1;
if (chr == ' ')
return '';
return chr;
}
if (this.level == 1) { // superscript
if (LC.HW.ReactionPreview.isDigit(chr))
return chr;
this.level = 0;
return chr;
}
}
/**
* Converts a reaction string to HTML for preview.
* @param {string} string - the user input
* @returns {string}
*/
LC.HW.ReactionPreview.prototype.to_html = function(string) {
var reaction = '';
var i;
this.parse_reaction(string);
for (i = 0; i < this.reactants.length-1; i++) {
reaction += this.html_component(this.reactants[i]);
reaction += ' + ';
}
reaction += this.html_component(this.reactants[i]);
if (this.products.length > 0) {
reaction += ' → ';
for (i = 0; i < this.products.length-1; i++) {
reaction += this.html_component(this.products[i]);
reaction += ' + ';
}
reaction += this.html_component(this.products[i]);
}
return reaction;
}
/**
* Converts a component to HTML.
* @param {string} string - the user string for the component
* @returns {string}
*/
LC.HW.ReactionPreview.prototype.html_component = function(string) {
var reactant = '';
var i = 0;
this.level = -1;
var hydrate = string.split('.');
if (hydrate.length > 1)
return this.html_component(hydrate[0]) + '·' + this.html_component(hydrate[1]);
for ( ; i < string.length; i++)
reactant += this.html_char(string.substring(i, i+1));
return reactant;
}
/**
* Converts a character to HTML, updating the level.
* @param {string} string - the user input character
* @returns {string}
*/
LC.HW.ReactionPreview.prototype.html_char = function(chr) {
if (this.level == -1) { // stoichiometric coefficient
if (LC.HW.ReactionPreview.isDigit(chr) || chr == '/')
return chr;
if (chr == ' ')
return '';
else
this.level = 0;
}
if (this.level == 0) { // baseline
if (LC.HW.ReactionPreview.isDigit(chr))
return chr.sub();
if (chr == '^') {
this.level = 1;
return '';
}
if (chr == '+') // baseline or superscript?
return '?';
if (chr == ' ')
return '';
if (chr == '(' || chr == '|') // coefficient
this.level = -1;
return chr;
}
if (this.level == 1) { // superscript
if (LC.HW.ReactionPreview.isDigit(chr))
return chr.sup();
if (chr == '+') {
this.level = 0;
return chr.sup();
}
if (chr == '-') {
this.level = 0;
return '<sup>−</sup>';
}
if (chr == ' ') {
this.level = 0;
return '';
}
this.level = 0;
return chr;
}
}
/**
* Starts listening to input changes to display the preview.
*/
LC.HW.ReactionPreview.prototype.start_preview = function() {
var ta = document.getElementById(this.field_id);
// The old reaction editor was suggesting using a readonly textline.
// To let users edit the field directly with problems using readonly=yes,
// the input's readOnly has to be set to false.
ta.readOnly = false;
var output_node = document.createElement('span');
output_node.id = this.field_id + '_preview';
output_node.style.display = 'none';
output_node.style.position = 'absolute';
output_node.style.backgroundColor = 'rgba(255,255,224,0.9)';
output_node.style.color = 'black';
output_node.style.border = '1px solid #A0A0A0';
output_node.style.padding = '5px';
output_node.style.zIndex = '1';
if (ta.nextSibling)
ta.parentNode.insertBefore(output_node, ta.nextSibling);
else
ta.parentNode.appendChild(output_node);
var hide_node = function(e) {
output_node.style.display = 'none';
}
output_node.addEventListener('mouseenter', hide_node, false);
var that = this;
var blur = function(e) {
output_node.style.display = 'none';
ta.value = that.to_capa(ta.value);
};
ta.addEventListener('blur', blur, false);
var focus = function(e) {
if (ta.value != '') {
if (ta.value != this.old_string) {
that.handleChange();
} else {
output_node.style.display = 'block';
LC.HW.ReactionPreview.place(ta, output_node);
}
}
};
ta.addEventListener('focus', focus, false);
this.old_string = '';
var startChange = function(e) {
that.handleChange();
};
ta.addEventListener('change', startChange, false);
ta.addEventListener('keyup', startChange, false);
}
/**
* Handle a change in the user input
*/
LC.HW.ReactionPreview.prototype.handleChange = function() {
var ta = document.getElementById(this.field_id);
if (document.activeElement == ta) {
var output_node = document.getElementById(this.field_id + '_preview');
var s = ta.value;
this.old_string = s;
if (s != '') {
output_node.innerHTML = this.to_html(s);
output_node.style.display = 'block';
LC.HW.ReactionPreview.place(ta, output_node);
} else {
output_node.style.display = 'none';
}
}
}
/**
* Returns an element absolute position.
* @static
* @param {Element} el
* @returns {Object} - keys: top, left
*/
LC.HW.ReactionPreview.getCSSAbsolutePosition = function(el) {
var x = 0;
var y = 0;
while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
var scrollLeft;
if (el.nodeName == 'INPUT')
scrollLeft = 0;
else
scrollLeft = el.scrollLeft;
x += el.offsetLeft - scrollLeft;
y += el.offsetTop - el.scrollTop;
el = el.offsetParent;
if (el) {
var style = window.getComputedStyle(el);
if (style.position == 'absolute' || style.position == 'relative')
break;
}
}
return {top: y, left: x};
}
/**
* Positions the output_node below or on top of the input field
* @static
* @param {Element} ta - the input field
* @param {Element} output_node - the preview node
*/
LC.HW.ReactionPreview.place = function(ta, output_node) {
var ta_rect = ta.getBoundingClientRect();
var root = document.documentElement;
var docTop = (window.pageYOffset || root.scrollTop) - (root.clientTop || 0);
var docLeft = (window.pageXOffset || root.scrollLeft) - (root.clientLeft || 0);
var ta_pos = LC.HW.ReactionPreview.getCSSAbsolutePosition(ta);
output_node.style.left = ta_pos.left + 'px';
if (window.innerHeight > ta_rect.bottom + output_node.offsetHeight)
output_node.style.top = (ta_pos.top + ta.offsetHeight) + 'px';
else
output_node.style.top = (ta_pos.top - output_node.offsetHeight) + 'px';
}
/**
* Returns true if the given character is a digit.
* @static
* @param {string} chr - a character
* @returns {boolean}
*/
LC.HW.ReactionPreview.isDigit = function(chr) {
return (chr >= '0' && chr <= '9');
}
More information about the LON-CAPA-cvs
mailing list