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 += '→ ';
115: continue;
116: }
117: if (token == '<-' ) {
118: formula += '← ';
119: continue;
120: }
121: if (token == '<=>') {
122: formula += '⇌ ';
123: continue;
124: }
125: if (token == '.') {
126: formula = formula.replaceFirst(new RegExp(r" | "), '');
127: formula += '·';
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('-', '−');
153: formula += molecule + ' ';
154: }
155: // get rid of trailing space
156: formula = formula.replaceFirst(new RegExp(r"( | )$"), '');
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>