blob: ace4fa3fe33934c6daa8ce691c8fc7a5dc7a1d01 [file] [log] [blame]
// Copyright (c) 2020, 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.
import 'dart:async';
import 'dart:html';
import 'package:markdown/markdown.dart' as md;
import 'highlight.dart';
final markdownInput = querySelector('#markdown') as TextAreaElement;
final htmlDiv = querySelector('#html') as DivElement;
final versionSpan = querySelector('.version') as SpanElement;
final nullSanitizer = NullTreeSanitizer();
const typing = Duration(milliseconds: 150);
final introText = r'''Markdown is the **best**!
* It has lists.
* It has [links](https://dart.dev).
* It has _so much more_...''';
// Flavor support.
final basicRadio = querySelector('#basic-radio') as HtmlElement;
final commonmarkRadio = querySelector('#commonmark-radio') as HtmlElement;
final gfmRadio = querySelector('#gfm-radio') as HtmlElement;
md.ExtensionSet? extensionSet;
final extensionSets = {
'basic-radio': md.ExtensionSet.none,
'commonmark-radio': md.ExtensionSet.commonMark,
'gfm-radio': md.ExtensionSet.gitHubWeb,
};
void main() {
versionSpan.text = 'v${md.version}';
markdownInput.onKeyUp.listen(_renderMarkdown);
var savedMarkdown = window.localStorage['markdown'];
if (savedMarkdown != null &&
savedMarkdown.isNotEmpty &&
savedMarkdown != introText) {
markdownInput.value = savedMarkdown;
markdownInput.focus();
_renderMarkdown();
} else {
_typeItOut(introText, 82);
}
// GitHub is the default extension set.
gfmRadio.attributes['checked'] = '';
gfmRadio.querySelector('.glyph')!.text = 'radio_button_checked';
extensionSet = extensionSets[gfmRadio.id];
_renderMarkdown();
basicRadio.onClick.listen(_switchFlavor);
commonmarkRadio.onClick.listen(_switchFlavor);
gfmRadio.onClick.listen(_switchFlavor);
}
void _renderMarkdown([Event? event]) {
var markdown = markdownInput.value!;
htmlDiv.setInnerHtml(md.markdownToHtml(markdown, extensionSet: extensionSet),
treeSanitizer: nullSanitizer);
for (var block in htmlDiv.querySelectorAll('pre code')) {
try {
highlightElement(block);
} catch (e) {
window.console.error('Error highlighting markdown:');
window.console.error(e);
}
}
if (event != null) {
// Not simulated typing. Store it.
window.localStorage['markdown'] = markdown;
}
}
void _typeItOut(String msg, int pos) {
late Timer timer;
markdownInput.onKeyUp.listen((_) {
timer.cancel();
});
void addCharacter() {
if (pos > msg.length) {
return;
}
markdownInput.value = msg.substring(0, pos);
markdownInput.focus();
_renderMarkdown();
pos++;
timer = Timer(typing, addCharacter);
}
timer = Timer(typing, addCharacter);
}
void _switchFlavor(Event e) {
var target = e.currentTarget as HtmlElement;
if (!target.attributes.containsKey('checked')) {
if (basicRadio != target) {
basicRadio.attributes.remove('checked');
basicRadio.querySelector('.glyph')!.text = 'radio_button_unchecked';
}
if (commonmarkRadio != target) {
commonmarkRadio.attributes.remove('checked');
commonmarkRadio.querySelector('.glyph')!.text = 'radio_button_unchecked';
}
if (gfmRadio != target) {
gfmRadio.attributes.remove('checked');
gfmRadio.querySelector('.glyph')!.text = 'radio_button_unchecked';
}
target.attributes['checked'] = '';
target.querySelector('.glyph')!.text = 'radio_button_checked';
extensionSet = extensionSets[target.id];
_renderMarkdown();
}
}
class NullTreeSanitizer implements NodeTreeSanitizer {
@override
void sanitizeTree(Node node) {}
}