| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688 |
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>Markdown Test</title>
- <link rel="icon" href="data:;base64,iVBORw0KGgo=">
- <style type="text/css">
- /* Structural style */
- :root, body {
- width: 100%; height: 100%;
- padding: 0;
- margin: 0;
- }
- .pane-container {
- position: absolute;
- left: 0; top: 0;
- width: 100%; height: 100%;
- box-sizing: border-box;
-
- display: grid;
- grid-template-columns: auto 1fr;
- }
- .left-pane {
- grid-row: 1; grid-column: 1;
- resize: horizontal;
- overflow-x: auto;
- box-sizing: border-box;
- min-width: 40vw;
- max-width: 80vw;
- border-right: 4px solid var(--color-ui-divider);
-
- display: grid;
- grid-template-rows: 32px 1fr;
- grid-template-columns: 1fr;
- }
- .right-pane {
- grid-row: 1; grid-column: 2;
- overflow-x: auto;
- overflow-y: scroll;
- }
- .toolbar-pane {
- grid-row: 1; grid-column: 1;
- box-sizing: border-box;
- display: grid;
- grid-template-columns: 1fr 1fr;
- }
- .toolbar-left {
- grid-column: 1;
- }
- .toolbar-right {
- grid-column: 2;
- }
- .editor-pane {
- grid-row: 2; grid-column: 1;
- }
- #markdown-editor {
- box-sizing: border-box;
- position: relative;
- width: 100%;
- height: 100%;
- resize: none;
- }
- @media screen and (max-width: 800px) {
- .pane-container {
- grid-template-columns: 1fr;
- grid-template-rows: auto 1fr;
- column-gap: 0;
- }
- .left-pane {
- grid-row: 2; grid-column: 1;
- resize: none;
- overflow-x: hidden;
- min-width: none;
- max-width: none;
- width: auto !important; /* undo wide mode resizing */
- border-right: none;
- border-top: 4px solid var(--color-ui-divider);
- }
- .right-pane {
- grid-row: 1; grid-column: 1;
- resize: vertical;
- min-height: 40vw;
- max-height: 80vw;
- }
- }
- @media screen and (min-width: 801px) {
- .right-pane {
- height: auto !important; /* undo narrow mode resizing */
- }
- }
-
- /* UI aesthetic */
-
- :root {
- --color-editor-background: #00a;
- --color-editor-text: white;
- --color-toolbar-background: #ccc;
- --color-toolbar-text: #000;
- --color-ui-divider: #000;
- }
- @media (prefers-color-scheme: dark) {
- :root {
- --toolbar-background: #aaa;
- --color-ui-divider: #999;
- }
- }
- .toolbar-pane,
- dialog {
- font-family: sans-serif;
- }
-
- .toolbar-pane {
- background-color: var(--color-toolbar-background);
- color: var(--color-toolbar-text);
- border-bottom: 1px solid var(--color-ui-divider);
- padding: 4px 20px;
- }
- .toolbar-left {
- text-align: left;
- }
- .toolbar-right {
- text-align: right;
- }
- #markdown-editor {
- padding: 1em 2em;
- border: none;
- outline: none;
- background-color: var(--color-editor-background);
- color: var(--color-editor-text);
- }
- .preview-container {
- padding: 1em;
- margin: 0 auto;
- max-width: 600px;
- background-color: var(--color-background);
- color: var(--color-text);
- }
-
- dialog#readers-dialog {
- max-width: 400px;
- max-height: 400px;
- }
- .dialog-layout {
- position: relative;
- width: 100%; height: 100%;
-
- display: grid;
- grid-template-rows: 32px 368px; /* can't get overflow to work without explicit height :( */
- }
- .dialog-titlebar {
- grid-row: 1;
-
- display: grid;
- grid-template-columns: 1fr auto;
- }
- .dialog-title {
- grid-column: 1;
- font-weight: bold;
- }
- .dialog-close {
- grid-column: 2;
- text-align: right;
- }
- .dialog-content {
- grid-row: 2;
- overflow-y: scroll;
- }
- .reader-list {
- position: relative;
- width: 100%; height: 100%;
- }
-
-
- /* --- old ----------------------------------------- */
-
- .reader-header {
- margin-top: 0.25em;
- font-weight: bold;
- }
- .reader-check {
- display: inline-block;
- padding: 4px 8px;
- border: 1px dotted gray;
- border-radius: 8px;
- margin: 4px;
- }
- .calculated {
- background-color: var(--color-calculated-background);
- font-style: italic;
- }
- .spreadsheet-type-number,
- .spreadsheet-type-currency,
- .spreadsheet-type-percent {
- text-align: right;
- }
- </style>
- <style type="text/css">
- /* Document styling */
- /* From https://github.com/edwardtufte/tufte-css/blob/gh-pages/tufte.css */
- :root {
- --color-background: #fffff8;
- --color-text: #111;
- --color-text-secondary: #666;
- --color-table-header: #ddd;
- --color-table-border: black;
- --color-calculated-background: #eee;
- --color-highlight: #ff0;
- --color-highlight-text: #111;
- --color-rule: #111;
- }
- @media (prefers-color-scheme: dark) {
- :root {
- --color-background: #151515;
- --color-text: #ddd;
- --color-text-secondary: #999;
- --color-table-header: #333;
- --color-table-border: #ccc;
- --color-calculated-background: #444;
- --color-highlight: #88f;
- --color-highlight-text: #ddd;
- --color-rule: #ddd;
- }
- }
-
- html {
- font-size: 15px;
- }
-
- body {
- font-family: et-book, Palatino, "Palatino Linotype", "Palatino LT STD", "Book Antiqua", Georgia, serif;
- background-color: var(--color-background);
- color: var(--color-text);
- counter-reset: sidenote-counter;
- }
-
- h1 {
- font-weight: 400;
- margin-top: 4rem;
- margin-bottom: 1.5rem;
- font-size: 3.2rem;
- line-height: 1;
- }
- h2 {
- font-style: italic;
- font-weight: 400;
- margin-top: 2.1rem;
- margin-bottom: 1.4rem;
- font-size: 2.2rem;
- line-height: 1;
- }
- h3 {
- font-style: italic;
- font-weight: 400;
- font-size: 1.7rem;
- margin-top: 2rem;
- margin-bottom: 1.4rem;
- line-height: 1;
- }
- .subtext {
- font-size: 1rem;
- color: var(--color-text-secondary);
- margin-top: 0.25em;
- margin-bottom: 0.5em;
- }
- mark {
- background-color: var(--color-highlight);
- color: var(--color-highlight-text);
- }
- table {
- margin-top: 1em;
- margin-bottom: 1em;
- border-collapse: collapse;
- }
- td, th {
- padding: 0.4em 0.8em;
- font-size: 1rem;
- }
- th {
- background-color: var(--color-table-header);
- border-bottom: 2px solid var(--color-text);
- }
- tr:not(:last-child) td {
- border-bottom: 1px solid var(--color-text);
- }
- tr td:not(:last-child) {
- border-right: 1px solid var(--color-table-header);
- }
- hr {
- display: block;
- height: 1px;
- width: 55%;
- border: 0;
- border-top: 1px solid var(--color-rule);
- margin: 1em 0;
- padding: 0;
- }
- article {
- padding: 5rem 0rem;
- }
- section {
- padding-top: 1rem;
- padding-bottom: 1rem;
- }
- p,
- ol,
- ul {
- font-size: 1.4rem;
- line-height: 2rem;
- }
- p.subtitle {
- font-style: italic;
- margin-top: 1rem;
- margin-bottom: 1rem;
- font-size: 1.8rem;
- display: block;
- line-height: 1;
- }
- code, pre > code {
- font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
- font-size: 1.0rem;
- line-height: 1.42;
- -webkit-text-size-adjust: 100%; /* Prevent adjustments of font size after orientation changes in iOS. See https://github.com/edwardtufte/tufte-css/issues/81#issuecomment-261953409 */
- }
- pre > code {
- font-size: 0.9rem;
- width: 52.5%;
- margin-left: 2.5%;
- overflow-x: auto;
- display: block;
- }
- a:link,
- a:visited {
- color: inherit;
- text-underline-offset: 0.1em;
- text-decoration-thickness: 0.05em;
- }
- dt:not(:first-child),
- li:not(:first-child) {
- margin-top: 0.25rem;
- }
- dt, dd {
- font-size: 1.2rem;
- }
- dt {
- font-weight: bold;
- }
- .footnotes, .footnotes li {
- font-size: 80%;
- }
- .footnotes li:target, sup:target a {
- background-color: var(--color-highlight);
- color: var(--color-highlight-text);
- }
-
- </style>
- <script src="js/markdown.js"></script>
- <script src="js/spreadsheet.js"></script>
- <script>
- const sampleMarkdown = `
- ## Block Formats {#top}
-
- A regular paragraph.
-
- -# Sub text
-
- * Unordered
- * Lists
- * Sub list
-
- 1. Ordered
- 2. Lists
- 6. Sub list
-
- A blockquote:
-
- > "The only thing we have to fear is fear itself—nameless, unreasoning,
- > unjustified terror which paralyzes needed efforts to convert retreat into
- > advance." - Franklin D. Roosevelt's First Inaugural Address
-
- Some definitions:
-
- word
- : a unit of meaning in a sentence
- sentence
- : a collection of words
-
- Code:
-
- \`\`\`javascript
- function foo() {
- return 'Hello world';
- }
- \`\`\`
-
- function bar() {
- return 'Indented code';
- }
-
- ### Heading, hash style
-
- Heading, underline style
- ---
-
- ### Modified Heading {style=color:red;}
-
- | Unit Price | Qty | Discount | Subtotal |
- | ---: | ---: | ---: | ---: |
- | $1.23 | 2 | 10% | =A\\*B*(1-C) FILL |
- | $4.99 | 6 | | |
- | $0.99 | 1 | 25% | |
- | Total | | | =SUM(D:D) |
-
- ---
-
- ## Inline Formats
-
- Normal, **double asterisk,** __double underscore,__ *asterisks,* _underscores,_
- ~~double tildes,~~ ~single tildes,~ ^carets,^ ==double equals==, \`\`double backticks\`\`,
- \`single backticks\`.
-
- Lorem ipsum dolor sit amet,[^1] consectetur adipiscing elit.[^abc] Sed sodales
- in odio eget porta. Proin et erat sit amet erat semper mollis vitae ut
- turpis.[^foo] Ut ipsum dui, maximus sit amet suscipit at, dictum id nulla.[^bar]
- Aenean efficitur rhoncus nulla non fermentum.
-
- [^1]: Donec ut felis volutpat, gravida ipsum scelerisque, accumsan est.
- [^abc]: Cras dictum rutrum quam.
- [^foo]: Donec maximus bibendum lorem.
- [^bar]: Praesent consectetur tristique leo. Morbi nec nisi sit amet quam
- imperdiet vehicula eu feugiat tortor.
-
- The HTML on the WWW is often full of JS and CSS.
-
- *[HTML]: Hypertext Markup Language
- *[WWW]: World Wide Web
- *[JS]: JavaScript
- *[CSS]: Cascading Style Sheets
-
- Click [here](#top) to return to the top. Referenced link to [Google][google].
-
-  
- ![][testimage]
-
- [testimage]: https://picsum.photos/102/75
- [google]: https://google.com "I prefer Duck Duck Go"
-
- Some verbatim <span style="color:green;">HTML</span>. A script tag
- will be ignored. <scr` + `ipt></sc` + `ript>
- `.trim();
- </script>
- <script>
- const blockReaderClasses = {
- 'Heading (underline)': MDUnderlinedHeadingReader,
- 'Heading (hash)': MDHashHeadingReader,
- 'Subtext': MDSubtextReader,
- 'Block quote': MDBlockQuoteReader,
- 'Unordered list': MDUnorderedListReader,
- 'Ordered list': MDOrderedListReader,
- 'Code block (fenced)': MDFencedCodeBlockReader,
- 'Code block (indented)': MDIndentedCodeBlockReader,
- 'Horizontal rule': MDHorizontalRuleReader,
- 'Table': MDTableReader,
- 'Definition list': MDDefinitionListReader,
- 'Paragraph': MDParagraphReader,
- 'Spreadsheets': MDSpreadsheetReader,
- };
- const inlineReaderClasses = {
- 'Emphasis': MDEmphasisReader,
- 'Strong': MDStrongReader,
- 'Strikethrough': MDStrikethroughReader,
- 'Underline': MDUnderlineReader,
- 'Highlight': MDHighlightReader,
- 'Links': MDLinkReader,
- 'Referenced links': MDReferencedLinkReader,
- 'Images': MDImageReader,
- 'Referenced images': MDReferencedImageReader,
- 'Code span': MDCodeSpanReader,
- 'Subscript': MDSubscriptReader,
- 'Superscript': MDSuperscriptReader,
- 'Footnotes': MDFootnoteReader,
- 'Abbrevations': MDAbbreviationReader,
- 'HTML tags': MDHTMLTagReader,
- 'Modifiers': MDModifierReader,
- 'Line breaks': MDLineBreakReader,
- };
- var activeReaders = [];
- var parser = null;
-
- function markdownEditorNode() { return document.getElementById('markdown-editor'); }
- function previewNode() { return document.getElementById('preview-container'); }
- function codeJSNode() { return document.getElementById('code-js'); }
- function codePHPNode() { return document.getElementById('code-php'); }
- function readersButtonNode() { return document.getElementById('readers-button'); }
- function readersDialogNode() { return document.getElementById('readers-dialog'); }
- function readerListNode() { return document.getElementById('reader-list'); }
-
- function onDocumentLoad() {
- if (markdownEditorNode().value === '') {
- markdownEditorNode().value = sampleMarkdown;
- }
- activeReaders = [ ...Markdown.allReaders, new MDSpreadsheetReader() ];
- parser = new Markdown(activeReaders);
- markdownEditorNode().addEventListener('input', handleMarkdownChange);
- populateReaderCheckboxes();
- readersButtonNode().addEventListener('click', handleReadersButtonClicked);
- codeJSNode().addEventListener('change', handleMarkdownChange);
- codePHPNode().addEventListener('change', handleMarkdownChange);
- readersDialogNode().getElementsByClassName("dialog-title")[0].innerHTML = "Readers";
- readersDialogNode().getElementsByClassName("dialog-titlebar")[0].getElementsByTagName("button")[0].addEventListener('click', () => {
- readersDialogNode().close();
- });
- setTimeout(handleMarkdownChange, 0);
- }
- function updatePreviewLocally() {
- let markdown = markdownEditorNode().value;
- let html = parser.toHTML(markdown, 'foo-');
- previewNode().innerHTML = html;
- }
- var debounceTimer = null;
- var needsUpdate = false;
- function debouncedSendPreviewRequest() {
- if (debounceTimer) {
- needsUpdate = true;
- } else {
- debounceTimer = setInterval(() => {
- if (needsUpdate) {
- needsUpdate = false;
- sendPreviewRequest();
- } else {
- clearTimeout(debounceTimer);
- debounceTimer = null;
- }
- }, 500);
- sendPreviewRequest();
- }
- }
- function sendPreviewRequest() {
- const markdown = markdownEditorNode().value;
- const readers = activeReaders.map((r) => r.constructor.name);
- const request = new XMLHttpRequest();
- var formData = encodeForm({
- 'markdown': markdown,
- 'readers': readers,
- });
- request.open('POST', 'playgroundapi.php', true);
- request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
- request.onreadystatechange = () => {
- if (request.readyState == 4 && request.status == 200) {
- previewNode().innerHTML = request.responseText;
- }
- };
- request.send(formData);
- }
- function encodeForm(values) {
- var pairs = [];
- for (const [key, value] of Object.entries(values)) {
- if (value instanceof Array) {
- for (const v of value) {
- pairs.push(`${encodeURIComponent(key + '[]')}=${encodeURIComponent(v)}`);
- }
- } else {
- pairs.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
- }
- }
- return pairs.join('&');
- }
- function populateReaderCheckboxes() {
- const container = document.getElementById('reader-list');
- var header = document.createElement('div');
- header.classList.add('reader-header');
- header.append(document.createTextNode('Block Readers'));
- container.append(header);
- const blockContainer = document.createElement('div');
- container.append(blockContainer);
- for (const name of Object.keys(blockReaderClasses).sort()) {
- const readerClass = blockReaderClasses[name];
- const checkbox = makeReaderCheckbox(name, readerClass);
- blockContainer.append(checkbox);
- }
- header = document.createElement('div');
- header.classList.add('reader-header');
- header.append(document.createTextNode('Inline Readers'));
- container.append(header);
- const inlineContainer = document.createElement('div');
- container.append(inlineContainer);
- for (const name of Object.keys(inlineReaderClasses).sort()) {
- const readerClass = inlineReaderClasses[name];
- const checkbox = makeReaderCheckbox(name, readerClass);
- inlineContainer.append(checkbox);
- }
- }
- function makeReaderCheckbox(name, readerClass) {
- var isSelected = false;
- for (const elem of activeReaders) {
- if (elem.constructor === readerClass) {
- isSelected = true;
- break;
- }
- }
- const label = document.createElement('label');
- label.classList.add('reader-check');
- const check = document.createElement('input');
- check.type = 'checkbox';
- check.checked = isSelected;
- check.addEventListener('change', () => {
- handleCheckChanged(readerClass, check);
- });
- check.id = `reader-${readerClass.name}`;
- label.append(check);
- label.append(document.createTextNode(name));
- return label;
- }
-
- function handleMarkdownChange() {
- if (codePHPNode().checked) {
- debouncedSendPreviewRequest();
- } else if (codeJSNode().checked) {
- updatePreviewLocally();
- }
- }
- function handleReadersButtonClicked() {
- const dlg = readersDialogNode();
- if (dlg.open) {
- dlg.close();
- } else {
- dlg.showModal();
- }
- }
- function handleCheckChanged(readerClass, check) {
- if (check.checked) {
- activeReaders.push(new readerClass());
- } else {
- activeReaders = activeReaders.filter((reader) => reader.constructor.name !== readerClass.name);
- }
- parser = new Markdown(activeReaders);
- handleMarkdownChange();
- }
- document.addEventListener('DOMContentLoaded', onDocumentLoad);
- </script>
- </head>
-
- <body>
- <div class="pane-container">
- <div class="left-pane pane">
- <div class="toolbar-pane">
- <div class="toolbar-left">
- <button id="readers-button">Readers</button>
- </div>
- <div class="toolbar-right">
- <label>
- <input type="radio" id="code-js" name="code" value="js" checked>
- Javascript
- </label>
- <label>
- <input type="radio" id="code-php" name="code" value="php">
- PHP
- </label>
- </div>
- </div>
- <div class="editor-pane">
- <textarea id="markdown-editor"></textarea>
- </div>
- </div>
- <div class="right-pane pane">
- <div class="preview-container" id="preview-container">
- <h1>Preview Here</h1>
- <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed turpis. Integer tincidunt eu nibh.</p>
- <p>Phasellus viverra nulla ut metus varius laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies velit. Nam eget dui et libero pharetra dapibus.</p>
- <p>Vestibulum in vulputate velit. Cras egestas, urna quis dignissim convallis, arcu felis sagittis diam, non fermentum massa justo ac neque. In ornare do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
- <p>Duis phasellus maecenas vestibulum mollis facilisis. Nulla sit amet erat. Suspendisse in justo eu odio suscipit rhoncus. Praesent id massa for ante varius ultrices. Cras sed leo at felis sagittis cursus. Integer turpis est, vulputate ut, elementum ac, condimentum eget, diam.</p>
- <p>Aliquam nec enim. Nulla tempus sem et risus. Proin mi. Etiam sit amet orci eget eros faucibus tincidunt. Duis volutpat nunc vel velit. Suspendisse potenti. Integer sollicitudin dictum est. Donec ut dolor. Nam malesuada magna.</p>
- <p>Sed non lectus. Vivamus consectetuer purus vitae massa. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel augue. Curabitur ullamcorper ultricies velit. Nam eget dui et libero pharetra dapibus</p>
- </div>
- </div>
- </div>
- <dialog id="readers-dialog">
- <div class="dialog-layout">
- <div class="dialog-titlebar">
- <div class="dialog-title"></div>
- <div class="dialog-close"><button>✕</button></div>
- </div>
- <div class="dialog-content">
- <div class="reader-list" id="reader-list"></div>
- </div>
- </div>
- </dialog>
- </body>
- </html>
|