[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