| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library eval_box_element; |
| |
| import 'dart:async'; |
| |
| import 'package:web/web.dart'; |
| |
| import 'package:observatory/models.dart' as M; |
| import 'package:observatory/src/elements/helpers/any_ref.dart'; |
| import 'package:observatory/src/elements/helpers/custom_element.dart'; |
| import 'package:observatory/src/elements/helpers/element_utils.dart'; |
| import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; |
| |
| class EvalBoxElement extends CustomElement implements Renderable { |
| late RenderingScheduler<EvalBoxElement> _r; |
| |
| Stream<RenderedEvent<EvalBoxElement>> get onRendered => _r.onRendered; |
| |
| late M.IsolateRef _isolate; |
| late M.ObjectRef _context; |
| late M.ObjectRepository _objects; |
| late M.EvalRepository _eval; |
| final _results = <_ExpressionDescription>[]; |
| String? _expression = ''; |
| late bool _multiline; |
| late Iterable<String> _quickExpressions; |
| |
| M.IsolateRef get isolate => _isolate; |
| M.ObjectRef get context => _context; |
| |
| factory EvalBoxElement(M.IsolateRef isolate, M.ObjectRef context, |
| M.ObjectRepository objects, M.EvalRepository eval, |
| {bool multiline = false, |
| Iterable<String> quickExpressions = const [], |
| RenderingQueue? queue}) { |
| EvalBoxElement e = new EvalBoxElement.created(); |
| e._r = new RenderingScheduler<EvalBoxElement>(e, queue: queue); |
| e._isolate = isolate; |
| e._context = context; |
| e._objects = objects; |
| e._eval = eval; |
| e._multiline = multiline; |
| e._quickExpressions = new List.unmodifiable(quickExpressions); |
| return e; |
| } |
| |
| EvalBoxElement.created() : super.created('eval-box'); |
| |
| @override |
| void attached() { |
| super.attached(); |
| _r.enable(); |
| } |
| |
| @override |
| void detached() { |
| super.detached(); |
| _r.disable(notify: true); |
| removeChildren(); |
| _results.clear(); |
| } |
| |
| void render() { |
| setChildren(<HTMLElement>[ |
| new HTMLDivElement() |
| ..className = 'quicks' |
| ..appendChildren( |
| _quickExpressions.map<HTMLElement>((q) => new HTMLButtonElement() |
| ..textContent = q |
| ..onClick.listen((_) { |
| _expression = q; |
| _run(); |
| }))), |
| new HTMLDivElement() |
| ..className = 'heading' |
| ..appendChildren(<HTMLElement>[ |
| new HTMLFormElement() |
| ..autocomplete = 'on' |
| ..appendChildren(<HTMLElement>[ |
| _multiline ? _createEvalTextArea() : _createEvalTextBox(), |
| new HTMLSpanElement() |
| ..className = 'buttons' |
| ..appendChildren(<HTMLElement>[ |
| _createEvalButton(), |
| _createMultilineCheckbox(), |
| new HTMLSpanElement()..textContent = 'Multi-line' |
| ]) |
| ]) |
| ]), |
| new HTMLTableElement() |
| ..appendChildren(_results.reversed |
| .map<HTMLElement>((result) => new HTMLTableRowElement() |
| ..appendChildren(<HTMLElement>[ |
| new HTMLTableCellElement.td() |
| ..className = 'historyExpr' |
| ..appendChildren(<HTMLElement>[ |
| new HTMLButtonElement() |
| ..textContent = result.expression |
| ..onClick.listen((_) { |
| _expression = result.expression; |
| _r.dirty(); |
| }) |
| ]), |
| new HTMLTableCellElement.td() |
| ..className = 'historyValue' |
| ..appendChildren(<HTMLElement>[ |
| result.isPending |
| ? (new HTMLSpanElement()..textContent = 'Pending...') |
| : anyRef(_isolate, result.value, _objects, |
| queue: _r.queue) |
| ]), |
| new HTMLTableCellElement.td() |
| ..className = 'historyDelete' |
| ..appendChildren(<HTMLElement>[ |
| new HTMLButtonElement() |
| ..textContent = '✖ Remove' |
| ..onClick.listen((_) { |
| _results.remove(result); |
| _r.dirty(); |
| }) |
| ]) |
| ])) |
| .toList()) |
| ]); |
| } |
| |
| HTMLTextAreaElement _createEvalTextArea() { |
| var area = new HTMLTextAreaElement() |
| ..className = 'textbox' |
| ..placeholder = 'evaluate an expression' |
| ..value = _expression ?? '' |
| ..onKeyUp.where((e) => e.key == '\n').listen((e) { |
| e.preventDefault(); |
| _run(); |
| }); |
| area.onInput.listen((e) { |
| _expression = area.value; |
| }); |
| return area; |
| } |
| |
| HTMLInputElement _createEvalTextBox() { |
| final expression = (_expression ?? '').split('\n')[0]; |
| _expression = expression; |
| var textbox = new HTMLInputElement() |
| ..className = 'textbox' |
| ..placeholder = 'evaluate an expression' |
| ..value = expression |
| ..onKeyUp.where((e) => e.key == '\n').listen((e) { |
| e.preventDefault(); |
| _run(); |
| }); |
| textbox.onInput.listen((e) { |
| _expression = textbox.value; |
| }); |
| return textbox; |
| } |
| |
| HTMLButtonElement _createEvalButton() { |
| final button = new HTMLButtonElement() |
| ..textContent = 'Evaluate' |
| ..onClick.listen((e) { |
| e.preventDefault(); |
| _run(); |
| }); |
| return button; |
| } |
| |
| HTMLInputElement _createMultilineCheckbox() { |
| final checkbox = new HTMLInputElement() |
| ..type = 'checkbox' |
| ..checked = _multiline; |
| checkbox.onClick.listen((e) { |
| e.preventDefault(); |
| _multiline = checkbox.checked; |
| _r.dirty(); |
| }); |
| return checkbox; |
| } |
| |
| Future _run() async { |
| final expression = _expression; |
| if (expression == null || expression.isEmpty) return; |
| _expression = null; |
| final result = new _ExpressionDescription.pending(expression); |
| _results.add(result); |
| _r.dirty(); |
| final index = _results.indexOf(result); |
| _results[index] = new _ExpressionDescription( |
| expression, await _eval.evaluate(_isolate, _context, expression)); |
| _r.dirty(); |
| } |
| } |
| |
| class _ExpressionDescription { |
| final String expression; |
| final M.ObjectRef? value; |
| bool get isPending => value == null; |
| |
| _ExpressionDescription(this.expression, this.value); |
| _ExpressionDescription.pending(this.expression) : value = null; |
| } |