| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 #library('pond_ui'); | |
| 6 | |
| 7 #import('dart:html', prefix:'html'); | |
| 8 #import('dart:isolate', prefix: 'isolate'); | |
| 9 #import('editors.dart'); | |
| 10 | |
| 11 class PondUI { | |
| 12 Editor dartEditor; | |
| 13 final List<Marker> markers; | |
| 14 | |
| 15 Editor warningEditor; | |
| 16 Editor jsEditor; | |
| 17 Editor htmlEditor; | |
| 18 OutConsole outConsole; | |
| 19 isolate.SendPort compilerIsolate; | |
| 20 | |
| 21 bool _compiling = false; | |
| 22 bool _compileAgain = false; | |
| 23 | |
| 24 TabGroup outTabs; | |
| 25 Tab errorTab; | |
| 26 Tab webTab; | |
| 27 | |
| 28 PondUI() : markers = [] { | |
| 29 compilerIsolate = isolate.spawnUri("compiler.dart.js"); | |
| 30 } | |
| 31 | |
| 32 void setupAndRun(EditorFactory editors) { | |
| 33 // forward output messages to the out-console. | |
| 34 outConsole = new OutConsole(); | |
| 35 | |
| 36 editors.newEditor("dartEditor", "dart", _scheduleCompilation) | |
| 37 .then((Editor e) { | |
| 38 dartEditor = e; | |
| 39 editors.newEditor("htmlEditor", "htmlmixed").then((Editor e) { | |
| 40 htmlEditor = e; | |
| 41 editors.newEditor("jsEditor", "javascript").then((Editor e) { | |
| 42 jsEditor = e; | |
| 43 editors.newEditor("warningEditor", "diff").then((Editor e) { | |
| 44 warningEditor = e; | |
| 45 setup(); | |
| 46 }); | |
| 47 }); | |
| 48 }); | |
| 49 }); | |
| 50 | |
| 51 } | |
| 52 | |
| 53 void setup() { | |
| 54 dartEditor.setText(SampleCode.DART); | |
| 55 htmlEditor.setText(SampleCode.HTML); | |
| 56 | |
| 57 html.document.query('#clearButton').on.click.add((e) { | |
| 58 _clearOutput(); | |
| 59 }); | |
| 60 | |
| 61 html.document.query('#runButton').on.click.add((e) { | |
| 62 _scheduleCompilation(); | |
| 63 }); | |
| 64 | |
| 65 new TabGroup([ | |
| 66 new Tab('tab-dart', 'dartEditor', dartEditor), | |
| 67 new Tab('tab-html', 'htmlEditor', htmlEditor)]).addListeners(); | |
| 68 | |
| 69 outTabs = new TabGroup([ | |
| 70 new Tab('tab-warnings', 'warningEditor', warningEditor), | |
| 71 new Tab('tab-console', 'console'), | |
| 72 new Tab('tab-web', 'results'), | |
| 73 new Tab('tab-js', 'jsEditor', jsEditor)]); | |
| 74 outTabs.addListeners(); | |
| 75 errorTab = outTabs.tabs[0]; | |
| 76 webTab = outTabs.tabs[2]; | |
| 77 } | |
| 78 | |
| 79 void _clearOutput() { | |
| 80 warningEditor.setText(''); | |
| 81 jsEditor.setText(''); | |
| 82 html.document.query('#resultFrame').attributes['src'] = 'about:blank'; | |
| 83 for (Marker marker in markers) { | |
| 84 marker.clear(); | |
| 85 } | |
| 86 markers.clear(); | |
| 87 } | |
| 88 | |
| 89 void _scheduleCompilation() { | |
| 90 if (_compiling) { | |
| 91 _compileAgain = true; | |
| 92 } else { | |
| 93 _compile(); | |
| 94 } | |
| 95 } | |
| 96 | |
| 97 void _compile() { | |
| 98 _compiling = true; | |
| 99 final totalWatch = new Stopwatch.start(); | |
| 100 _clearOutput(); | |
| 101 // TODO(sigmund): cleanup using 'await' as follows: | |
| 102 // String userCode = await dartEditor.getText(); | |
| 103 dartEditor.getText().then((userCode) { | |
| 104 final compileWatch = new Stopwatch.start(); | |
| 105 compilerIsolate.call({ | |
| 106 'code': userCode, | |
| 107 'warningsAsErrors': | |
| 108 html.document.query('#warningCheckbox').dynamic.checked | |
| 109 }).then((reply) { | |
| 110 String warnings = ''; | |
| 111 for (final warning in reply['warnings']) { | |
| 112 String prefix = warning["prefix"]; | |
| 113 warnings = '${warnings}$prefix ${warning["msg"]}'; | |
| 114 warnings = '${warnings}[${warning["locationText"]}]\n'; | |
| 115 if (warning['filename'] == 'user.dart') { | |
| 116 final start = new Position(warning['line'], warning['column']); | |
| 117 final end = new Position( | |
| 118 warning['endLine'], warning['endColumn']); | |
| 119 int kind = Marks.NONE; | |
| 120 String prefix = warning['prefix']; | |
| 121 if (prefix.startsWith('error') || prefix.startsWith('fatal')) { | |
| 122 kind = Marks.ERROR; | |
| 123 } else if (prefix.startsWith('warning')) { | |
| 124 kind = Marks.WARNING; | |
| 125 } | |
| 126 dartEditor.mark(start, end, kind).then( | |
| 127 (Marker m) { markers.add(m); }); | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 if (reply['success']) { | |
| 132 outConsole.clear(); | |
| 133 String code = reply['code']; | |
| 134 int lastLine = code.lastIndexOf(";", code.lastIndexOf(";") - 1); | |
| 135 String injectedCode = | |
| 136 '${OutConsole.CODE_PATCH}${code.substring(lastLine)}'; | |
| 137 code = "${code.substring(0, lastLine)}$injectedCode"; | |
| 138 jsEditor.setText(code); | |
| 139 htmlEditor.getText().then((htmlText) { | |
| 140 var start = htmlText.indexOf("{{DART}}"); | |
| 141 htmlText = htmlText.substring(0, start) + code | |
| 142 + htmlText.substring(start + "{{DART}}".length); | |
| 143 htmlText = htmlText.replaceAll( | |
| 144 "application/dart", "text/javascript"); | |
| 145 html.document.query("#resultFrame").attributes["src"] = | |
| 146 _toDataURL(htmlText); | |
| 147 }); | |
| 148 } | |
| 149 compileWatch.stop(); | |
| 150 totalWatch.stop(); | |
| 151 int time = reply["time"]; | |
| 152 warnings = '$warnings\ncompile time: ${time}ms\n'; | |
| 153 time = compileWatch.elapsedInMs(); | |
| 154 warnings = '$warnings\ncompile + isolate time: ${time}ms\n'; | |
| 155 time = totalWatch.elapsedInMs(); | |
| 156 warnings = '${warnings}total time: ${time}ms\n'; | |
| 157 warningEditor.setText(warnings); | |
| 158 if (_compileAgain) { | |
| 159 _compileAgain = false; | |
| 160 _compile(); | |
| 161 } else { | |
| 162 _compiling = false; | |
| 163 } | |
| 164 }); | |
| 165 }); | |
| 166 } | |
| 167 | |
| 168 // TODO(sigmund): remove use of 'native'. | |
| 169 String _toDataURL(text) native ''' | |
| 170 var preamble = "data:text/html;charset=utf-8,"; | |
| 171 var escaped = window.encodeURIComponent(text); | |
| 172 return preamble + escaped; | |
| 173 '''; | |
| 174 } | |
| 175 | |
| 176 class Tab { | |
| 177 html.Element tab; | |
| 178 html.Element contents; | |
| 179 Editor editor; | |
| 180 | |
| 181 Tab(tabId, contentsId, [this.editor = null]) { | |
| 182 tab = html.document.query("#$tabId"); | |
| 183 contents = html.document.query("#$contentsId"); | |
| 184 } | |
| 185 | |
| 186 void show() { | |
| 187 tab.classes.add("tab-selected"); | |
| 188 contents.classes.remove("hidden"); | |
| 189 if (editor != null) editor.refresh(); | |
| 190 } | |
| 191 | |
| 192 void hide() { | |
| 193 tab.classes.remove("tab-selected"); | |
| 194 contents.classes.add("hidden"); | |
| 195 } | |
| 196 } | |
| 197 | |
| 198 class TabGroup { | |
| 199 List<Tab> tabs; | |
| 200 | |
| 201 TabGroup(this.tabs); | |
| 202 | |
| 203 void addListeners() { | |
| 204 tabs.forEach((t) { t.tab.on.click.add((e) => selectTab(t)); }); | |
| 205 } | |
| 206 | |
| 207 void selectTab(Tab tab) { | |
| 208 tabs.forEach((t) => t == tab ? t.show() : t.hide()); | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 /** Controls the console output. */ | |
| 213 class OutConsole { | |
| 214 html.Element _root; | |
| 215 | |
| 216 OutConsole() { | |
| 217 _root = html.document.query("#console"); | |
| 218 html.window.on.message.add((e) { // output is forwarded using postMessage | |
| 219 var msg = e.data; | |
| 220 if (msg is List && msg[0] == 'app-to-pond-print') { | |
| 221 addLine(msg[1]); | |
| 222 } | |
| 223 }); | |
| 224 } | |
| 225 | |
| 226 void addLine(String line) { | |
| 227 _root.nodes.add(new html.Element.html( | |
| 228 '<span class="console-line">${_htmlEscape(line)}</pre>')); | |
| 229 } | |
| 230 | |
| 231 void clear() { | |
| 232 _root.nodes.clear(); | |
| 233 } | |
| 234 | |
| 235 String _htmlEscape(String s) { | |
| 236 return s.replaceAll('&', '&') | |
| 237 .replaceAll('<','<') | |
| 238 .replaceAll('>','>'); | |
| 239 } | |
| 240 | |
| 241 /** | |
| 242 * A patch (extra code added) to the code generated by of frog so that pond | |
| 243 * can display printed messages correctly. | |
| 244 */ | |
| 245 static String CODE_PATCH = @''' | |
| 246 | |
| 247 /* Code added by pond to forward output messages to the UI out console. */ | |
| 248 var $original_print = print$; | |
| 249 print$ = function (e) { | |
| 250 window.parent.postMessage(['app-to-pond-print', e], '*'); | |
| 251 $original_print(e); | |
| 252 } | |
| 253 '''; | |
| 254 } | |
| 255 | |
| 256 class SampleCode { | |
| 257 final static String DART = ''' | |
| 258 #import("dart:html"); | |
| 259 void main() { | |
| 260 window.on.load.add((Event e) { | |
| 261 Element element = document.query("#status"); | |
| 262 if (element == null) { | |
| 263 throw "can't find status element"; | |
| 264 } | |
| 265 element.innerHTML = "hello, dart, click me"; | |
| 266 print("hello dart!"); | |
| 267 element.on.click.add( | |
| 268 (Event) { | |
| 269 if (element.classes.remove("clicked")) { | |
| 270 return; | |
| 271 } | |
| 272 element.classes.add("clicked"); | |
| 273 }); | |
| 274 }); | |
| 275 } | |
| 276 '''; | |
| 277 | |
| 278 final static String HTML = ''' | |
| 279 <html> | |
| 280 <head> | |
| 281 <style type="text/css"> | |
| 282 .clicked { | |
| 283 background: #003300; | |
| 284 } | |
| 285 </style> | |
| 286 <script type="application/dart"> | |
| 287 {{DART}} | |
| 288 </script> | |
| 289 </head> | |
| 290 <body> | |
| 291 <h2 id="status">not running</h2> | |
| 292 </body> | |
| 293 </html> | |
| 294 '''; | |
| 295 } | |
| OLD | NEW |