Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | pikchrshow: support 4 different preview modes, clipboard copy of previewed content, and markup alignment option (left/center). |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
d330c09135556fc98bbb709d54ae876a |
User & Date: | stephan 2020-09-10 22:31:13.197 |
Context
2020-09-10
| ||
22:56 | Update Pikchr to the latest code from the Pikchr website. ... (check-in: a5c685fa user: drh tags: trunk) | |
22:31 | pikchrshow: support 4 different preview modes, clipboard copy of previewed content, and markup alignment option (left/center). ... (check-in: d330c091 user: stephan tags: trunk) | |
22:15 | Fix typo in the 'admin_sql' page. ... (check-in: e654b300 user: mistachkin tags: trunk) | |
Changes
Changes to src/fossil.dom.js.
︙ | ︙ | |||
95 96 97 98 99 100 101 102 103 104 105 106 107 108 | dom.br = dom.createElemFactory('br'); dom.text = (t)=>document.createTextNode(t||''); dom.button = function(label){ const b = this.create('button'); if(label) b.appendChild(this.text(label)); return b; }; dom.select = dom.createElemFactory('select'); /** Returns an OPTION element with the given value and label text (which defaults to the value). May be called as (value), (selectElement), (selectElement, value), (value, label) or (selectElement, value, | > | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | dom.br = dom.createElemFactory('br'); dom.text = (t)=>document.createTextNode(t||''); dom.button = function(label){ const b = this.create('button'); if(label) b.appendChild(this.text(label)); return b; }; dom.textarea = dom.createElemFactory('textarea'); dom.select = dom.createElemFactory('select'); /** Returns an OPTION element with the given value and label text (which defaults to the value). May be called as (value), (selectElement), (selectElement, value), (value, label) or (selectElement, value, |
︙ | ︙ |
Changes to src/fossil.page.pikchrshow.js.
1 2 3 4 | (function(F/*the fossil object*/){ "use strict"; /** Client-side implementation of the /pikchrshow app. Requires that | | | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | > > > < < | > | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | (function(F/*the fossil object*/){ "use strict"; /** Client-side implementation of the /pikchrshow app. Requires that the fossil JS bootstrapping is complete and that these fossil JS APIs have been installed: fossil.fetch, fossil.dom, fossil.copybutton */ const E = (s)=>document.querySelector(s), D = F.dom, P = F.page; P.previewMode = 0 /*0==rendered SVG, 1==pikchr text markdown, 2==pikchr text fossil, 3==raw SVG. */ P.response = {/*stashed state for the server's preview response*/ isError: false, inputText: undefined /* value of the editor field at render-time */, raw: undefined /* raw response text/HTML from server */ }; F.onPageLoad(function() { document.body.classList.add('pikchrshow'); P.e = { /* various DOM elements we work with... */ previewTarget: E('#pikchrshow-output'), previewModeLabel: E('#pikchrshow-output-wrapper > legend'), btnCopy: E('#pikchrshow-output-wrapper > legend > .copy-button'), btnSubmit: E('#pikchr-submit-preview'), cbDarkMode: E('#flipcolors-wrapper > input[type=checkbox]'), taContent: E('#content'), taPreviewText: D.attr(D.textarea(), 'rows', 20, 'cols', 60, 'readonly', true), divControls: E('#pikchrshow-controls'), btnTogglePreviewMode: D.button("Preview mode"), selectMarkupAlignment: D.select() }; D.append(P.e.divControls, P.e.btnTogglePreviewMode); // Setup markup alignment selection... D.append(P.e.divControls, P.e.selectMarkupAlignment); D.disable(D.option(P.e.selectMarkupAlignment, '', 'Markup Alignment')); ['left', 'center'].forEach(function(val,ndx){ D.option(P.e.selectMarkupAlignment, ndx ? val : '', val); }); // Setup clipboard-copy of markup/SVG... F.copyButton(P.e.btnCopy, {copyFromElement: P.e.taPreviewText}); P.e.btnCopy.addEventListener('text-copied',function(ev){ D.flashOnce(ev.target); },false); // Set up dark mode simulator... P.e.cbDarkMode.addEventListener('change', function(ev){ if(ev.target.checked) D.addClass(P.e.previewTarget, 'dark-mode'); else D.removeClass(P.e.previewTarget, 'dark-mode'); }, false); if(P.e.cbDarkMode.checked) D.addClass(P.e.previewTarget, 'dark-mode'); // Set up preview update and preview mode toggle... P.e.btnSubmit.addEventListener('click', ()=>P.preview(), false); P.e.btnTogglePreviewMode.addEventListener('click', function(){ /* Rotate through the 4 available preview modes */ P.previewMode = ++P.previewMode % 4; P.renderPreview(); }, false); P.e.selectMarkupAlignment.addEventListener('change', function(ev){ /* Update markdown/fossil wiki preview if it's active */ if(P.previewMode==1 || P.previewMode==2){ P.renderPreview(); } }, false); if(P.e.taContent.value/*was pre-filled server-side*/){ /* Fill our "response" state so that renderPreview() can work */ P.response.inputText = P.e.taContent.value; P.response.raw = P.e.previewTarget.innerHTML; P.renderPreview()/*not strictly necessary, but gets all labels/headers in alignment.*/; } }/*F.onPageLoad()*/); /** Updates the preview view based on the current preview mode and error state. */ P.renderPreview = function f(){ if(!f.hasOwnProperty('rxNonce')){ f.rxNonce = /<!--.+-->\r?\n?/g /*nonce comments*/; } const preTgt = this.e.previewTarget; if(this.response.isError){ preTgt.innerHTML = this.response.raw; D.addClass(preTgt, 'error'); this.e.previewModeLabel.innerText = "Error"; return; } D.removeClass(preTgt, 'error'); D.removeClass(this.e.btnTogglePreviewMode, 'hidden'); let label; switch(this.previewMode){ case 0: label = "Rendered SVG"; preTgt.innerHTML = this.response.raw; this.e.taPreviewText.value = this.response.raw.replace(f.rxNonce, '')/*for copy button*/; break; case 1: label = "Markdown"; this.e.taPreviewText.value = [ '```pikchr'+(this.e.selectMarkupAlignment.value ? ' '+this.e.selectMarkupAlignment.value : ''), this.response.inputText, '```' ].join('\n'); D.append(D.clearElement(preTgt), this.e.taPreviewText); break; case 2: label = "Fossil wiki"; this.e.taPreviewText.value = [ '<verbatim type="pikchr', this.e.selectMarkupAlignment.value ? ' '+this.e.selectMarkupAlignment.value : '', '">', this.response.inputText, '</verbatim>' ].join(''); D.append(D.clearElement(preTgt), this.e.taPreviewText); break; case 3: label = "Raw SVG"; this.e.taPreviewText.value = this.response.raw.replace(f.rxNonce, ''); D.append(D.clearElement(preTgt), this.e.taPreviewText); break; } D.append(D.clearElement(this.e.previewModeLabel), label, this.e.btnCopy); }; /** Fetches the preview from the server and updates the preview to the rendered SVG content or error report. */ P.preview = function fp(){ if(!fp.hasOwnProperty('toDisable')){ fp.toDisable = [ /* elements to disable during ajax operations */ this.e.btnSubmit, this.e.taContent, this.e.btnTogglePreviewMode, this.e.selectMarkupAlignment, ]; fp.target = this.e.previewTarget; fp.updateView = function(c,isError){ P.previewMode = 0; P.response.raw = c; P.response.isError = isError; D.enable(fp.toDisable); P.renderPreview(); }; } D.disable(fp.toDisable); D.addClass(this.e.btnTogglePreviewMode, 'hidden'); const content = this.e.taContent.value.trim(); this.response.raw = undefined; this.response.inputText = content; if(!content){ fp.updateView("No pikchr content!",true); return this; } const self = this; const fd = new FormData(); fd.append('ajax', true); |
︙ | ︙ |
Changes to src/pikchrshow.c.
︙ | ︙ | |||
68 69 70 71 72 73 74 | } style_header("PikchrShow"); CX("<style>"); CX("div.content { padding-top: 0.5em }"); CX("#sbs-wrapper {" "display: flex; flex-direction: row; flex-wrap: wrap;" "}"); | | > > > | < | < < < | | > | > > > > < < | > > > > > | | 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | } style_header("PikchrShow"); CX("<style>"); CX("div.content { padding-top: 0.5em }"); CX("#sbs-wrapper {" "display: flex; flex-direction: row; flex-wrap: wrap;" "}"); CX("#sbs-wrapper > * {" "margin: 0 0.25em 0.5em 0; flex: 1 10 auto;" "}"); CX("#sbs-wrapper textarea {max-width: initial}"); CX("#pikchrshow-output, #pikchrshow-form" "{display: flex; flex-direction: column; align-items: stretch;}"); CX("#pikchrshow-form > * {margin: 0.25em 0}"); CX("#pikchrshow-output {flex: 5 1 auto; padding: 0}"); CX("#pikchrshow-output > pre, " "#pikchrshow-output > pre > div, " "#pikchrshow-output > pre > div > pre " "{margin: 0; padding: 0}"); CX("#pikchrshow-controls {" /* where the buttons live */ "display: flex; flex-direction: row; " "align-items: center; flex-wrap: wrap;" "}"); CX("#pikchrshow-controls > * {" "display: inline; margin: 0 0.25em 0.5em 0;" "}"); CX("#pikchrshow-controls > .input-with-label > * {" "cursor: pointer;" "}"); CX("#pikchrshow-output.dark-mode > svg {" /* Flip the colors to approximate a dark theme look */ "filter: invert(1) hue-rotate(180deg);" "}"); CX("#sbs-wrapper > fieldset {" "padding: 0.25em 0.5em; border-radius: 0.25em;" "}"); CX("fieldset > legend > .copy-button {margin-left: 0.25em}"); CX("</style>"); CX("<div>Input pikchr code and tap Preview to render it:</div>"); CX("<div id='sbs-wrapper'>"); CX("<div id='pikchrshow-form'>"); CX("<div id='pikchrshow-controls'>"); CX("<button id='pikchr-submit-preview'>Preview</button>"); style_labeled_checkbox("flipcolors-wrapper", "flipcolors", "Simulate dark theme?", "1", flipColors, 0); CX("</div>"/*#pikchrshow-controls*/); CX("<textarea id='content' name='content' rows='15'>%s</textarea>", zContent/*safe-for-%s*/); CX("</div>"/*#pikchrshow-form*/); CX("<fieldset id='pikchrshow-output-wrapper'>"); CX("<legend>Preview <span class='copy-button'></span></legend>"); CX("<div id='pikchrshow-output'>"); if(*zContent){ int w = 0, h = 0; char *zOut = pikchr(zContent, "pikchr", 0, &w, &h); if( w>0 && h>0 ){ const char *zNonce = safe_html_nonce(1); CX("%s\n%s%s", zNonce, zOut, zNonce); }else{ CX("<pre>\n%s\n</pre>\n", zOut); } fossil_free(zOut); } CX("</div>"/*#pikchrshow-output*/); CX("</fieldset>"); CX("</div>"/*sbs-wrapper*/); if(!builtin_bundle_all_fossil_js_apis()){ builtin_emit_fossil_js_apis("dom", "fetch", "copybutton", 0); } builtin_emit_fossil_js_apis("page.pikchrshow", 0); builtin_fulfill_js_requests(); style_footer(); } |