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

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) {
1.4     ! damieng    33:     if (firstChild is DNText && firstChild.nextSibling == null)
        !            34:       firstChild.replaceWith(new ChemText(firstChild.nodeValue));
1.1       damieng    35:     preview = previewIsPossible();
                     36:   }
                     37:   
                     38:   bool previewIsPossible() {
                     39:     return (firstChild is DNText && !firstChild.nodeValue.trim().startsWith('\$'));
                     40:   }
                     41:   
                     42:   @override
                     43:   h.Element html() {
                     44:     if (!preview) {
                     45:       if (listener == null)
                     46:         listener = h.document.onClick.listen((h.MouseEvent e) => onClick(e));
                     47:       return super.html();
                     48:     }
                     49:     h.SpanElement span = new h.SpanElement();
                     50:     span.id = "$id";
                     51:     span.classes.add('dn');
                     52:     if (!valid)
                     53:       span.classes.add('invalid');
                     54:     String text = firstChild.nodeValue.trim();
                     55:     span.setInnerHtml(Chem.textToHTML(text));
                     56:     span.onClick.listen((h.MouseEvent e) {
                     57:       preview = false;
                     58:       updateHTML();
1.3       damieng    59:       // prevent calling onClick(e):
                     60:       // (it could switch back to preview mode if the element takes more
                     61:       // space and moves to the next line after switching to non-preview).
                     62:       e.stopPropagation();
                     63:       e.preventDefault();
                     64:       // but set cursor inside:
                     65:       page.moveCursorTo(new Position(this, this.offsetLength));
1.1       damieng    66:     });
1.2       damieng    67:     setupDrag(span);
1.1       damieng    68:     return(span);
                     69:   }
                     70:   
                     71:   void onClick(h.MouseEvent e) {
                     72:     if (!previewIsPossible())
                     73:       return; // NOTE: that will generate a lot of events for nothing when variables are used...
                     74:     h.Element node = getHTMLNode();
                     75:     if (node == null) {
                     76:       listener.cancel();
                     77:       listener = null;
                     78:       return;
                     79:     }
                     80:     h.Rectangle r = node.getBoundingClientRect(); // could be more precise with getClientRects
1.3       damieng    81:     if (!r.containsPoint(e.client)) {
                     82:       listener.cancel();
                     83:       listener = null;
                     84:       preview = true;
                     85:       updateHTML();
                     86:       e.stopPropagation();
                     87:       e.preventDefault();
                     88:       page.cursor.refresh();
                     89:     }
1.1       damieng    90:   }
                     91:   
                     92:   @override
1.4     ! damieng    93:   void updateHTML() {
        !            94:     if (preview && !previewIsPossible())
        !            95:       preview = false;
        !            96:     super.updateHTML();
        !            97:   }
        !            98:   
        !            99:   @override
        !           100:   void updateHTMLAfterChildrenChange(List<DaxeNode> changed) {
        !           101:     if (preview)
        !           102:       updateHTML();
        !           103:     else
        !           104:       super.updateHTMLAfterChildrenChange(changed);
        !           105:   }
        !           106:   
        !           107:   @override
1.1       damieng   108:   h.Element getHTMLContentsNode() {
                    109:     if (!preview)
                    110:       return super.getHTMLContentsNode();
                    111:     return(getHTMLNode());
                    112:   }
                    113: 
                    114:   @override
                    115:   Position firstCursorPositionInside() {
                    116:     if (!preview)
                    117:       return super.firstCursorPositionInside();
                    118:     return(null);
                    119:   }
                    120:   
                    121:   @override
                    122:   Position lastCursorPositionInside() {
                    123:     if (!preview)
                    124:       return super.lastCursorPositionInside();
                    125:     return(null);
                    126:   }
                    127:   
1.4     ! damieng   128:   @override
        !           129:   bool get needsSpecialDNText {
        !           130:     return true;
        !           131:   }
        !           132:   
        !           133:   @override
        !           134:   DNText specialDNTextConstructor(String text) {
        !           135:     return new ChemText(text);
        !           136:   }
        !           137:   
1.1       damieng   138:   static String textToHTML(String reaction) {
                    139:     // this is doing the same thing as chemparse, except it uses UNICODE characters instead of LaTeX
                    140:     //List<String> tokens = reaction.split(new RegExp(r"(\s\+|\->|<=>|<\-|\.)"));
                    141:     // this did not work (delimiters are not preserved)
                    142:     // using look-ahead/behind does not work either...
                    143:     List<String> tokens = getTokensWithDelimiters(reaction, new RegExp(r"(\s\+|\->|<=>|<\-|\.)"));
                    144:     String formula = '';
                    145:     for (int i=0; i<tokens.length; i++) {
                    146:         String token = tokens[i];
                    147:         if (token == '->' ) {
                    148:             formula += '&#8594; ';
                    149:             continue;
                    150:         }
                    151:         if (token == '<-' ) {
                    152:             formula += '&#8592; ';
                    153:             continue;
                    154:         }  
                    155:         if (token == '<=>') {
                    156:             formula += '&#8652; ';
                    157:             continue;
                    158:         }
                    159:         if (token == '.') {
                    160:           formula = formula.replaceFirst(new RegExp(r"&nbsp;| "), '');
                    161:           formula += '&middot;';
                    162:           continue;
                    163:         }
                    164:         Iterable<Match> matches = new RegExp(r"^\s*([\d|\/]*(?:&frac\d\d)?)(.*)").allMatches(token);
                    165:         String molecule;
                    166:         if (matches.length > 0) {
                    167:           Match firstMatch = matches.first;
                    168:           if (firstMatch.group(1) != null)
                    169:             formula += firstMatch.group(1); // stoichiometric coefficient
                    170:           if (firstMatch.group(2) != null)
                    171:             molecule = firstMatch.group(2);
                    172:           else
                    173:             molecule = '';
                    174:         } else
                    175:           molecule = '';
                    176:         // subscripts
                    177:         // $molecule =~ s|(?<=[a-zA-Z\)\]\s])(\d+)|<sub>$1</sub>|g;
                    178:         // Javascript does not support look-behind like Perl
                    179:         molecule = molecule.replaceAllMapped(new RegExp(r"([a-zA-Z\)\]\s])(\d+)"), (Match m) => "${m[1]}<sub>${m[2]}</sub>");
                    180:         // superscripts
                    181:         molecule = molecule.replaceAllMapped(new RegExp(r"\^(\d*[+\-]*)"), (Match m) => "<sup>${m[1]}</sup>");
                    182:         // strip whitespace
                    183:         molecule = molecule.replaceAll(new RegExp(r"\s*"), '');
                    184:         // forced space
                    185:         molecule = molecule.replaceAll('_', ' ');
                    186:         molecule = molecule.replaceAll('-', '&minus;');
                    187:         formula += molecule + '&nbsp;';
                    188:     }
                    189:     // get rid of trailing space
                    190:     formula = formula.replaceFirst(new RegExp(r"(&nbsp;| )$"), '');
                    191:     return formula;
                    192:   }
                    193:   
                    194:   static List<String> getTokensWithDelimiters(String s, Pattern p) {
                    195:     int start = 0;
                    196:     int ind = s.indexOf(p, start);
                    197:     List<String> list = new List<String>();
                    198:     while (ind >= 0) {
                    199:       if (ind > 0)
                    200:         list.add(s.substring(start, ind));
                    201:       Match m = p.matchAsPrefix(s, ind);
                    202:       String delimiter = m.group(0);
                    203:       list.add(delimiter);
                    204:       start = ind + delimiter.length;
                    205:       ind = s.indexOf(p, start);
                    206:     }
                    207:     if (start < s.length)
                    208:       list.add(s.substring(start));
                    209:     return list;
                    210:   }
                    211: }
1.4     ! damieng   212: 
        !           213: /**
        !           214:  * A DNText which triggers an update for the parent Chem when preview is true.
        !           215:  * This is needed to handle undo of a part of the text when in preview mode.
        !           216:  */
        !           217: class ChemText extends DNText {
        !           218:   
        !           219:   ChemText(String s) : super(s) {
        !           220:   }
        !           221:   
        !           222:   ChemText.fromNode(x.Node node, DaxeNode parent) : super.fromNode(node, parent) {
        !           223:   }
        !           224:   
        !           225:   @override
        !           226:   void updateHTML() {
        !           227:     if (parent is Chem && (parent as Chem).preview)
        !           228:       parent.updateHTML();
        !           229:     else
        !           230:       super.updateHTML();
        !           231:   }
        !           232: }

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