Annotation of modules/damieng/graphical_editor/loncapa_daxe/web/nodes/chem.dart, revision 1.1
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: });
! 58: return(span);
! 59: }
! 60:
! 61: void onClick(h.MouseEvent e) {
! 62: if (!previewIsPossible())
! 63: return; // NOTE: that will generate a lot of events for nothing when variables are used...
! 64: h.Element node = getHTMLNode();
! 65: if (node == null) {
! 66: listener.cancel();
! 67: listener = null;
! 68: return;
! 69: }
! 70: h.Rectangle r = node.getBoundingClientRect(); // could be more precise with getClientRects
! 71: if (!r.containsPoint(e.client)) {
! 72: listener.cancel();
! 73: listener = null;
! 74: preview = true;
! 75: updateHTML();
! 76: e.stopPropagation();
! 77: e.preventDefault();
! 78: page.cursor.refresh();
! 79: }
! 80: }
! 81:
! 82: @override
! 83: h.Element getHTMLContentsNode() {
! 84: if (!preview)
! 85: return super.getHTMLContentsNode();
! 86: return(getHTMLNode());
! 87: }
! 88:
! 89: @override
! 90: Position firstCursorPositionInside() {
! 91: if (!preview)
! 92: return super.firstCursorPositionInside();
! 93: return(null);
! 94: }
! 95:
! 96: @override
! 97: Position lastCursorPositionInside() {
! 98: if (!preview)
! 99: return super.lastCursorPositionInside();
! 100: return(null);
! 101: }
! 102:
! 103: static String textToHTML(String reaction) {
! 104: // this is doing the same thing as chemparse, except it uses UNICODE characters instead of LaTeX
! 105: //List<String> tokens = reaction.split(new RegExp(r"(\s\+|\->|<=>|<\-|\.)"));
! 106: // this did not work (delimiters are not preserved)
! 107: // using look-ahead/behind does not work either...
! 108: List<String> tokens = getTokensWithDelimiters(reaction, new RegExp(r"(\s\+|\->|<=>|<\-|\.)"));
! 109: String formula = '';
! 110: for (int i=0; i<tokens.length; i++) {
! 111: String token = tokens[i];
! 112: if (token == '->' ) {
! 113: formula += '→ ';
! 114: continue;
! 115: }
! 116: if (token == '<-' ) {
! 117: formula += '← ';
! 118: continue;
! 119: }
! 120: if (token == '<=>') {
! 121: formula += '⇌ ';
! 122: continue;
! 123: }
! 124: if (token == '.') {
! 125: formula = formula.replaceFirst(new RegExp(r" | "), '');
! 126: formula += '·';
! 127: continue;
! 128: }
! 129: Iterable<Match> matches = new RegExp(r"^\s*([\d|\/]*(?:&frac\d\d)?)(.*)").allMatches(token);
! 130: String molecule;
! 131: if (matches.length > 0) {
! 132: Match firstMatch = matches.first;
! 133: if (firstMatch.group(1) != null)
! 134: formula += firstMatch.group(1); // stoichiometric coefficient
! 135: if (firstMatch.group(2) != null)
! 136: molecule = firstMatch.group(2);
! 137: else
! 138: molecule = '';
! 139: } else
! 140: molecule = '';
! 141: // subscripts
! 142: // $molecule =~ s|(?<=[a-zA-Z\)\]\s])(\d+)|<sub>$1</sub>|g;
! 143: // Javascript does not support look-behind like Perl
! 144: molecule = molecule.replaceAllMapped(new RegExp(r"([a-zA-Z\)\]\s])(\d+)"), (Match m) => "${m[1]}<sub>${m[2]}</sub>");
! 145: // superscripts
! 146: molecule = molecule.replaceAllMapped(new RegExp(r"\^(\d*[+\-]*)"), (Match m) => "<sup>${m[1]}</sup>");
! 147: // strip whitespace
! 148: molecule = molecule.replaceAll(new RegExp(r"\s*"), '');
! 149: // forced space
! 150: molecule = molecule.replaceAll('_', ' ');
! 151: molecule = molecule.replaceAll('-', '−');
! 152: formula += molecule + ' ';
! 153: }
! 154: // get rid of trailing space
! 155: formula = formula.replaceFirst(new RegExp(r"( | )$"), '');
! 156: return formula;
! 157: }
! 158:
! 159: static List<String> getTokensWithDelimiters(String s, Pattern p) {
! 160: int start = 0;
! 161: int ind = s.indexOf(p, start);
! 162: List<String> list = new List<String>();
! 163: while (ind >= 0) {
! 164: if (ind > 0)
! 165: list.add(s.substring(start, ind));
! 166: Match m = p.matchAsPrefix(s, ind);
! 167: String delimiter = m.group(0);
! 168: list.add(delimiter);
! 169: start = ind + delimiter.length;
! 170: ind = s.indexOf(p, start);
! 171: }
! 172: if (start < s.length)
! 173: list.add(s.substring(start));
! 174: return list;
! 175: }
! 176: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>