Annotation of modules/damieng/graphical_editor/loncapa_daxe/web/nodes/chem.dart, revision 1.2

1.1       damieng     1: /*
                      2:   This file is part of LONCAPA-Daxe.
                      3: 
                      4:   LONCAPA-Daxe is free software: you can redistribute it and/or modify
                      5:   it under the terms of the GNU General Public License as published by
                      6:   the Free Software Foundation, either version 3 of the License, or
                      7:   (at your option) any later version.
                      8: 
                      9:   LONCAPA-Daxe is distributed in the hope that it will be useful,
                     10:   but WITHOUT ANY WARRANTY; without even the implied warranty of
                     11:   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     12:   GNU General Public License for more details.
                     13: 
                     14:   You should have received a copy of the GNU General Public License
                     15:   along with Daxe.  If not, see <http://www.gnu.org/licenses/>.
                     16: */
                     17: 
                     18: part of loncapa_daxe;
                     19: 
                     20: /**
                     21:  * Display for the chem element.
                     22:  * Jaxe display type: 'chem'.
                     23:  */
                     24: class Chem extends DNString {
                     25:   bool preview;
                     26:   StreamSubscription<h.MouseEvent> listener = null;
                     27:   
                     28:   Chem.fromRef(x.Element elementRef) : super.fromRef(elementRef) {
                     29:     preview = false;
                     30:   }
                     31:   
                     32:   Chem.fromNode(x.Node node, DaxeNode parent) : super.fromNode(node, parent) {
                     33:     preview = previewIsPossible();
                     34:   }
                     35:   
                     36:   bool previewIsPossible() {
                     37:     return (firstChild is DNText && !firstChild.nodeValue.trim().startsWith('\$'));
                     38:   }
                     39:   
                     40:   @override
                     41:   h.Element html() {
                     42:     if (!preview) {
                     43:       if (listener == null)
                     44:         listener = h.document.onClick.listen((h.MouseEvent e) => onClick(e));
                     45:       return super.html();
                     46:     }
                     47:     h.SpanElement span = new h.SpanElement();
                     48:     span.id = "$id";
                     49:     span.classes.add('dn');
                     50:     if (!valid)
                     51:       span.classes.add('invalid');
                     52:     String text = firstChild.nodeValue.trim();
                     53:     span.setInnerHtml(Chem.textToHTML(text));
                     54:     span.onClick.listen((h.MouseEvent e) {
                     55:       preview = false;
                     56:       updateHTML();
                     57:     });
1.2     ! damieng    58:     setupDrag(span);
1.1       damieng    59:     return(span);
                     60:   }
                     61:   
                     62:   void onClick(h.MouseEvent e) {
                     63:     if (!previewIsPossible())
                     64:       return; // NOTE: that will generate a lot of events for nothing when variables are used...
                     65:     h.Element node = getHTMLNode();
                     66:     if (node == null) {
                     67:       listener.cancel();
                     68:       listener = null;
                     69:       return;
                     70:     }
                     71:     h.Rectangle r = node.getBoundingClientRect(); // could be more precise with getClientRects
                     72:    if (!r.containsPoint(e.client)) {
                     73:      listener.cancel();
                     74:      listener = null;
                     75:      preview = true;
                     76:      updateHTML();
                     77:      e.stopPropagation();
                     78:      e.preventDefault();
                     79:      page.cursor.refresh();
                     80:    }
                     81:   }
                     82:   
                     83:   @override
                     84:   h.Element getHTMLContentsNode() {
                     85:     if (!preview)
                     86:       return super.getHTMLContentsNode();
                     87:     return(getHTMLNode());
                     88:   }
                     89: 
                     90:   @override
                     91:   Position firstCursorPositionInside() {
                     92:     if (!preview)
                     93:       return super.firstCursorPositionInside();
                     94:     return(null);
                     95:   }
                     96:   
                     97:   @override
                     98:   Position lastCursorPositionInside() {
                     99:     if (!preview)
                    100:       return super.lastCursorPositionInside();
                    101:     return(null);
                    102:   }
                    103:   
                    104:   static String textToHTML(String reaction) {
                    105:     // this is doing the same thing as chemparse, except it uses UNICODE characters instead of LaTeX
                    106:     //List<String> tokens = reaction.split(new RegExp(r"(\s\+|\->|<=>|<\-|\.)"));
                    107:     // this did not work (delimiters are not preserved)
                    108:     // using look-ahead/behind does not work either...
                    109:     List<String> tokens = getTokensWithDelimiters(reaction, new RegExp(r"(\s\+|\->|<=>|<\-|\.)"));
                    110:     String formula = '';
                    111:     for (int i=0; i<tokens.length; i++) {
                    112:         String token = tokens[i];
                    113:         if (token == '->' ) {
                    114:             formula += '&#8594; ';
                    115:             continue;
                    116:         }
                    117:         if (token == '<-' ) {
                    118:             formula += '&#8592; ';
                    119:             continue;
                    120:         }  
                    121:         if (token == '<=>') {
                    122:             formula += '&#8652; ';
                    123:             continue;
                    124:         }
                    125:         if (token == '.') {
                    126:           formula = formula.replaceFirst(new RegExp(r"&nbsp;| "), '');
                    127:           formula += '&middot;';
                    128:           continue;
                    129:         }
                    130:         Iterable<Match> matches = new RegExp(r"^\s*([\d|\/]*(?:&frac\d\d)?)(.*)").allMatches(token);
                    131:         String molecule;
                    132:         if (matches.length > 0) {
                    133:           Match firstMatch = matches.first;
                    134:           if (firstMatch.group(1) != null)
                    135:             formula += firstMatch.group(1); // stoichiometric coefficient
                    136:           if (firstMatch.group(2) != null)
                    137:             molecule = firstMatch.group(2);
                    138:           else
                    139:             molecule = '';
                    140:         } else
                    141:           molecule = '';
                    142:         // subscripts
                    143:         // $molecule =~ s|(?<=[a-zA-Z\)\]\s])(\d+)|<sub>$1</sub>|g;
                    144:         // Javascript does not support look-behind like Perl
                    145:         molecule = molecule.replaceAllMapped(new RegExp(r"([a-zA-Z\)\]\s])(\d+)"), (Match m) => "${m[1]}<sub>${m[2]}</sub>");
                    146:         // superscripts
                    147:         molecule = molecule.replaceAllMapped(new RegExp(r"\^(\d*[+\-]*)"), (Match m) => "<sup>${m[1]}</sup>");
                    148:         // strip whitespace
                    149:         molecule = molecule.replaceAll(new RegExp(r"\s*"), '');
                    150:         // forced space
                    151:         molecule = molecule.replaceAll('_', ' ');
                    152:         molecule = molecule.replaceAll('-', '&minus;');
                    153:         formula += molecule + '&nbsp;';
                    154:     }
                    155:     // get rid of trailing space
                    156:     formula = formula.replaceFirst(new RegExp(r"(&nbsp;| )$"), '');
                    157:     return formula;
                    158:   }
                    159:   
                    160:   static List<String> getTokensWithDelimiters(String s, Pattern p) {
                    161:     int start = 0;
                    162:     int ind = s.indexOf(p, start);
                    163:     List<String> list = new List<String>();
                    164:     while (ind >= 0) {
                    165:       if (ind > 0)
                    166:         list.add(s.substring(start, ind));
                    167:       Match m = p.matchAsPrefix(s, ind);
                    168:       String delimiter = m.group(0);
                    169:       list.add(delimiter);
                    170:       start = ind + delimiter.length;
                    171:       ind = s.indexOf(p, start);
                    172:     }
                    173:     if (start < s.length)
                    174:       list.add(s.substring(start));
                    175:     return list;
                    176:   }
                    177: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>