diff --git a/src/js/editor.js b/src/js/editor.js index 0bb88b0..f8e0caf 100644 --- a/src/js/editor.js +++ b/src/js/editor.js @@ -7,90 +7,124 @@ var relative = 'relative'; var insert = (textarea, symbol) => { var s = textarea.value; var pos = textarea.selectionEnd; - textarea.value = s.substring(0,pos) + symbol + s.substring(pos, s.length); + textarea.value = s.substring(0, pos) + symbol + s.substring(pos, s.length); textarea.selectionEnd = pos; -} +}; var auto_close = (key, textarea) => { switch (key) { - case '{': insert(textarea, '}'); break; - case '[': insert(textarea, ']'); break; - case '(': insert(textarea, ')'); break; - case '"': insert(textarea, '"'); break; + case '{': + insert(textarea, '}'); + break; + case '[': + insert(textarea, ']'); + break; + case '(': + insert(textarea, ')'); + break; + case '"': + insert(textarea, '"'); + break; } -} +}; -var zdhl= (elid)=> { +var zdhl = (elid) => { let el = document.getElementById(elid); let code = el.parentElement; let v = el.innerText; code.innerHTML = v - .replace(/:[a-zA-Z0-9][-.:\/_a-zA-Z0-9]+/gi, (x)=> {return `${x}`;}) - .replace(/#[-.:_a-zA-Z0-9]+/gi, (x)=> {return `${x}`;}) - .replace(/\^[-_a-zA-Z0-9]+/gi, (x)=> {return `${x}`;}) - .replace(/(\(\([^\)]+\)\))/gi, (x)=> { return `${x}`; }) - .replace(/(\[\[[^\]]+\]\])/gi, (x)=> { return `${x}`; }); + .replace(/:[a-zA-Z0-9][-.:\/_a-zA-Z0-9]+/gi, (x) => { + return `${x}`; + }) + .replace(/#[-.:_a-zA-Z0-9]+/gi, (x) => { + return `${x}`; + }) + .replace(/\^[-_a-zA-Z0-9]+/gi, (x) => { + return `${x}`; + }) + .replace(/(\(\([^\)]+\)\))/gi, (x) => { + return `${x}`; + }) + .replace(/(\[\[[^\]]+\]\])/gi, (x) => { + return `${x}`; + }); }; -var hl = (ctx, v)=>{ - var t = ctx.editor.els.textarea; - var h = ctx.editor.els.hl; - var c = ctx.editor.els.cursor; - set(t, {style: {opacity: "1"}}); - set(h, {style: {opacity: "0"}}); - h.innerHTML = - v - .replace(/:[a-zA-Z0-9][-.:\/_a-zA-Z0-9]+/gi, (x)=> {return `${x}`;}) - .replace(/#[-.:_a-zA-Z0-9]+/gi, (x)=> {return `${x}`;}) - .replace(/\^[-_a-zA-Z0-9]+/gi, (x)=> {return `${x}`;}) +var hl = (ctx, v) => { + var t = ctx.editorContainer.els.editor.els.textarea; + var h = ctx.editorContainer.els.editor.els.hl; + var c = ctx.editorContainer.els.editor.els.cursor; + set(t, { style: { opacity: '1' } }); + set(h, { style: { opacity: '0' } }); + h.innerHTML = v + .replace(/:[a-zA-Z0-9][-.:\/_a-zA-Z0-9]+/gi, (x) => { + return `${x}`; + }) + .replace(/#[-.:_a-zA-Z0-9]+/gi, (x) => { + return `${x}`; + }) + .replace(/\^[-_a-zA-Z0-9]+/gi, (x) => { + return `${x}`; + }) //.replace(/("[^"]+")/gi, (x)=> { return `${x}`; }) - .replace(/(\(\([^\)]+\)\))/gi, (x)=> { return `${x}`; }) - .replace(/(\[\[[^\]]+\]\])/gi, (x)=> { return `${x}`; }) - ; - set(h, {style: {opacity: "1"}}); - set(t, {style: {opacity: 0.5}}); + .replace(/(\(\([^\)]+\)\))/gi, (x) => { + return `${x}`; + }) + .replace(/(\[\[[^\]]+\]\])/gi, (x) => { + return `${x}`; + }); + set(h, { style: { opacity: '1' } }); + set(t, { style: { opacity: 0.5 } }); - setTimeout(()=> { + setTimeout(() => { var rect = h.getBoundingClientRect(); - set(t, {style: {width: rect.width, height: rect.height + 500}}); - set(c, {style: {width: rect.width, height: rect.height + 500}}); + set(t, { style: { width: rect.width, height: rect.height + 500 } }); + set(c, { style: { width: rect.width, height: rect.height + 500 } }); }, 300); - }; var keys = [':title', ':tags', ':desc', ':role']; var options = { - key: (ctx, token)=> { - if( token.startsWith(':fa-')){ - return ctx.icons.search(token.substring(4)).map((x)=> {return x.item;}); + key: (ctx, token) => { + if (token.startsWith(':fa-')) { + return ctx.icons.search(token.substring(4)).map((x) => { + return x.item; + }); } else { - return ctx.keys.search(token).map((x)=> { return x.item; }); + return ctx.keys.search(token).map((x) => { + return x.item; + }); } }, - symbol: (ctx, token)=>{ - console.log('token',token); - let res = ctx.symbols.search(token).map((x)=> { console.log(x.item); return x.item; }); - if(token == '/'){ - res.unshift({title: '', name: "/\n"}); + symbol: (ctx, token) => { + console.log('token', token); + let res = ctx.symbols.search(token).map((x) => { + console.log(x.item); + return x.item; + }); + if (token == '/') { + res.unshift({ title: '', name: '/\n' }); } else { - res.unshift({name: token, icon: ["fa-solid", "fa-plus"]}); + res.unshift({ name: token, icon: ['fa-solid', 'fa-plus'] }); } - return res; + return res; + }, + annotation: (ctx, token) => { + return ctx.annotations.search(token).map((x) => { + return x.item; + }); }, - annotation: (ctx, token)=>{ - return ctx.annotations.search(token).map((x)=> { return x.item; }); - } }; -var hide_popup = (ctx)=>{ - var els = ctx.editor.els; +var hide_popup = (ctx) => { + var els = ctx.editorContainer.els.editor.els; ctx.items = null; - set(els.pop, {style: {display: 'none'}}); + set(els.pop, { style: { display: 'none' } }); }; -var selection_style = {background: 'blueviolet'}; +var selection_style = { background: 'blueviolet' }; var insert_text = (ctx, text) => { - var textarea = ctx.editor.els.textarea; + var textarea = ctx.editorContainer.els.editor.els.textarea; var v = textarea.value; var start = textarea.selectionStart; var new_v = v.substring(0, ctx.insert_at) + text + v.substring(start, v.length); @@ -98,12 +132,12 @@ var insert_text = (ctx, text) => { textarea.selectionEnd = ctx.insert_at + text.length; hl(ctx, new_v); hide_popup(ctx); - textarea.dispatchEvent(new KeyboardEvent("keypress", {})); -} + textarea.dispatchEvent(new KeyboardEvent('keypress', {})); +}; -var insert_item = (ctx,item) => { +var insert_item = (ctx, item) => { insert_text(ctx, item.name); - // var textarea = ctx.editor.els.textarea; + // var textarea = ctx.editorContainer.els.editor.els.textarea; // var v = textarea.value; // var start = textarea.selectionStart; // var new_v = v.substring(0, ctx.insert_at) + item.name + v.substring(start, v.length); @@ -116,12 +150,12 @@ var insert_item = (ctx,item) => { var insert_selection = (ctx) => { var item = ctx.items[ctx.selection]; - insert_item(ctx,item); + insert_item(ctx, item); }; -var autocompl = (ctx, v)=> { - var els = ctx.editor.els; - var btxt = v.substring(0,els.textarea.selectionStart); +var autocompl = (ctx, v) => { + var els = ctx.editorContainer.els.editor.els; + var btxt = v.substring(0, els.textarea.selectionStart); // var line_start = btxt.lastIndexOf("\n"); // var str_start = btxt.lastIndexOf('"'); @@ -134,42 +168,42 @@ var autocompl = (ctx, v)=> { var j = 0; var lst_idx = btxt.length - 1; var p_aln = is_alphanum(btxt[lst_idx]); - var pos_type = null; + var pos_type = null; var token_start = -1; - for(var i = lst_idx; i > 0; i--){ - j = j+1; + for (var i = lst_idx; i > 0; i--) { + j = j + 1; c = btxt[i]; - if(c == '^' && (p_aln || j == 1) && (i == 0 || btxt[i-1] == '\n' )) { - pos_type='annotation'; + if (c == '^' && (p_aln || j == 1) && (i == 0 || btxt[i - 1] == '\n')) { + pos_type = 'annotation'; token_start = i; break; - } else if(c == ':' ) { + } else if (c == ':') { console.log('here'); pos_type = 'key'; token_start = i; break; - } else if ( j> 1 && c == '#' && p_aln){ + } else if (j > 1 && c == '#' && p_aln) { pos_type = 'symbol'; token_start = i + 1; break; - } else if ( j > 1 && !is_alphanum(c) && c !== '/' && p_aln && is_in_key(btxt)) { + } else if (j > 1 && !is_alphanum(c) && c !== '/' && p_aln && is_in_key(btxt)) { pos_type = 'symbol'; token_start = i + 1; break; } - if(c == '\n') { + if (c == '\n') { break; } - if(p_aln && ! is_alphanum(c)) { + if (p_aln && !is_alphanum(c)) { p_aln = false; } } - if(pos_type) { + if (pos_type) { var token = btxt.substring(token_start); var items = options[pos_type](ctx, token); - if(items.length > 0) { - items=items.slice(0, 100); + if (items.length > 0) { + items = items.slice(0, 100); ctx.items = items; ctx.selection = 0; ctx.insert_at = token_start; @@ -179,120 +213,147 @@ var autocompl = (ctx, v)=> { var cur = els.caret.els.cursor; var item_els = {}; for (let i = 0; i < items.length; i++) { - var item =items[i]; + var item = items[i]; var icon = null; - if(item.logo) { - icon = {tag: 'img', src: item.logo, style: { height: 16, 'border-radius': "1px", 'padding-right': '0.5rem', display: 'inline-block'}}; + if (item.logo) { + icon = { + tag: 'img', + src: item.logo, + style: { + height: 16, + 'border-radius': '1px', + 'padding-right': '0.5rem', + display: 'inline-block', + }, + }; } else if (item.icon) { - icon = {tag: 'span', class: item.icon, style: {'font-size': 12, 'padding-right': '0.5rem'}}; + icon = { tag: 'span', class: item.icon, style: { 'font-size': 12, 'padding-right': '0.5rem' } }; + } + var opts = { + tag: 'div', + class: ['menu-item'], + on: { + click: (ev) => { + ctx.selection = i; + insert_selection(ctx); + }, + }, + els: { + icon: icon, + name: { tag: 'b', style: { 'padding-right': 5 }, text: (item.name || '').trim() }, + title: { tag: 'span', text: item.title }, + }, + style: { padding: 5, 'font-size': 12, cursor: 'point' }, + }; + if (i == 0) { + opts.style = merge(opts.style, selection_style); } - var opts = {tag: 'div', - class: ['menu-item'], - on: {click: (ev)=> { ctx.selection = i; insert_selection(ctx); } }, - els: {icon: icon, - name: {tag: 'b', style: {'padding-right': 5}, text: (item.name || '').trim()}, - title: {tag: 'span', text: item.title}}, - style: {padding: 5, 'font-size': 12, cursor: 'point'}}; - if(i == 0) { opts.style = merge(opts.style, selection_style); } item_els[i] = opts; } - set(els.pop, {style: {display: 'block', top: cur.offsetTop + 18, left: cur.offsetLeft, height: 500, 'overflow-y': 'auto'}, - els: item_els}); - + set(els.pop, { + style: { + display: 'block', + top: cur.offsetTop + 18, + left: cur.offsetLeft, + height: 500, + 'overflow-y': 'auto', + }, + els: item_els, + }); } else { hide_popup(ctx); } } else { hide_popup(ctx); - }}; - + } +}; var get_in = (obj, path) => { var res = obj; for (var i of path) { res = res[i]; - if(!res) { return res; } + if (!res) { + return res; + } } return res; }; - var select = (ctx, dir) => { - var textarea = ctx.editor.els.popup; + var textarea = ctx.editorContainer.els.editor.els.popup; var sel = ctx.selection; var len = ctx.items.length; - if(sel == 0 && dir < 0) { - sel = len -1; - } else if (sel > (len - 2) && dir > 0) { + if (sel == 0 && dir < 0) { + sel = len - 1; + } else if (sel > len - 2 && dir > 0) { sel = 0; - } else if (sel < len ) { + } else if (sel < len) { sel = sel + dir; } - set(get_in(ctx, ['editor', 'els', 'pop', 'els', ctx.selection]), {style: {background: 'transparent'}}); - set(get_in(ctx, ['editor', 'els', 'pop', 'els', sel]), {style: selection_style, intoView: true}); - + set(get_in(ctx, ['editor', 'els', 'pop', 'els', ctx.selection]), { style: { background: 'transparent' } }); + set(get_in(ctx, ['editor', 'els', 'pop', 'els', sel]), { style: selection_style, intoView: true }); ctx.selection = sel; }; -var save = (ctx)=>{ - var value = sanitize(ctx.editor.els.textarea.value); +var save = (ctx) => { + var value = sanitize(ctx.editorContainer.els.editor.els.textarea.value); var doc = ctx.doc; - fetch(`/${doc}`, {method: 'POST', body: value}).then((resp)=> { - if(resp.status == 200){ - resp.text().then((docid)=> { - window.location.href = docid; - }); + fetch(`/${doc}`, { method: 'POST', body: value }).then((resp) => { + if (resp.status == 200) { + resp.text().then((docid) => { + window.location.href = docid; + }); } else { - ctx.container.els.save.style.visibility = "visible"; + ctx.container.els.save.style.visibility = 'visible'; } }); -} +}; -var cancel = (ctx)=>{ +var cancel = (ctx) => { var doc = ctx.doc; window.location.href = `/${doc}`; -} +}; var on_editor_keydown = (ctx, ev) => { - if(ev.ctrlKey && ev.key == 's') { + if (ev.ctrlKey && ev.key == 's') { save(ctx); stop(ev); return false; } else if (ev.ctrlKey && ev.key == 'x') { cancel(ctx); } - if(ctx.items) { - if(ev.key == "Enter") { + if (ctx.items) { + if (ev.key == 'Enter') { stop(ev); insert_selection(ctx); ctx.skip_up = true; return false; } else if (ev.key == 'ArrowUp' || (ev.key == 'p' && ev.ctrlKey)) { stop(ev); - select(ctx,-1); + select(ctx, -1); ctx.skip_up = true; return false; } else if (ev.key == 'ArrowDown' || (ev.key == 'n' && ev.ctrlKey)) { stop(ev); - select(ctx,+1); + select(ctx, +1); ctx.skip_up = true; return false; - } else if ((ev.key == 'c' && ev.ctrlKey)) { + } else if (ev.key == 'c' && ev.ctrlKey) { hide_popup(ctx); } } }; var sanitize = (s) => { - return s.replace(/[\u0020\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/g, " "); + return s.replace(/[\u0020\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/g, ' '); }; -var _render = (content)=> { - fetch(`/${ctx.doc}/preview`, {method: 'POST', body: content}).then((resp)=> { - resp.text().then((txt)=> { +var _render = (content) => { + fetch(`/${ctx.doc}/preview`, { method: 'POST', body: content }).then((resp) => { + resp.text().then((txt) => { ctx.preview.innerHTML = txt; - ctx.preview.querySelectorAll('script').forEach((x)=>{ + ctx.preview.querySelectorAll('script').forEach((x) => { eval(x.innerText); }); // reload widgets @@ -304,18 +365,17 @@ var _render = (content)=> { var render = debounce(_render, 300); var on_editor_keyup = (ctx, ev) => { - - if (!ctx.in_chrome){ + if (!ctx.in_chrome) { render(sanitize(ev.target.value)); } - if(ctx.skip_up) { + if (ctx.skip_up) { ctx.skip_up = false; return; } var t = ev.target; var v = t.value; - if( v !== ctx.prev_value ){ + if (v !== ctx.prev_value) { ctx.prev_value = v; //auto_close(ev.key, t); hl(ctx, v); @@ -323,68 +383,154 @@ var on_editor_keyup = (ctx, ev) => { } }; -var poss = {display: 'block', - border: none, - outline: none, - background: transparent, - "font-size": 14, - padding: 10, - margin: 0, - 'line-height': 20, - "overflow-wrap": 'break-word', - "white-space": 'pre-wrap', - "font-family": 'monospace'}; - -var popup_style = {position: absolute, - display: none, - 'box-shadow': '1px 2px 2px #ddd', - 'min-width': '10em', - 'border-radius': 5, - background: '#555', - border: '1px solid #ddd'}; - - -var caret_style = merge(poss, {color: transparent, - overflow: 'hidden', - position: absolute, margin: 0, top: 0, left: 0}); - -var textarea_style = merge(poss, {overflow: 'hidden', position: absolute, margin: 0, top: 0, left: 0}); +var poss = { + display: 'block', + border: none, + outline: none, + background: transparent, + 'font-size': 14, + padding: 10, + margin: 0, + 'line-height': 20, + 'overflow-wrap': 'break-word', + 'white-space': 'pre-wrap', + 'font-family': 'monospace', +}; + +var popup_style = { + position: absolute, + display: none, + 'box-shadow': '1px 2px 2px #ddd', + 'min-width': '10em', + 'border-radius': 5, + background: '#555', + border: '1px solid #ddd', +}; + +var caret_style = merge(poss, { + color: transparent, + overflow: 'hidden', + position: absolute, + margin: 0, + top: 0, + left: 0, +}); + +var textarea_style = merge(poss, { overflow: 'hidden', position: absolute, margin: 0, top: 0, left: 0 }); + +const toggleConsolePanel = (ctx, shouldExpand) => { + ctx.editorContainer.els.consolePanel.els.expander.els.button.classList.replace( + shouldExpand ? 'fa-angle-up' : 'fa-angle-down', + shouldExpand ? 'fa-angle-down' : 'fa-angle-up', + ); + + ctx.editorContainer.els.editor.style.height = shouldExpand ? 'calc(100vh - 260px)' : 'calc(100vh - 60px)'; + + ctx.editorContainer.els.console.style.display = shouldExpand ? 'block' : 'none'; +}; + +const validateContent = (ctx, zendoc) => async (event) => { + if (event.target.classList.contains('fa-spinner')) { + return; + } + + ctx.editorContainer.els.consolePanel.els.expander.els.errors.textContent = 'Problems (0)'; + + ctx.editorContainer.els.console.innerHTML = ''; + + event.target.classList.replace('fa-square-check', 'fa-spinner'); + + event.target.style.animation = 'spin 1s linear infinite'; + + try { + const sanitizedFshDocumentContent = sanitize(ctx.editorContainer.els.editor.els.textarea.value); + + const response = await fetch(zendoc.validator.endpoint, { + method: 'POST', + mode: 'cors', + + headers: { + 'Content-Type': 'application/json', + }, + + body: JSON.stringify({ + documentName: zendoc.doc, + documentContent: sanitizedFshDocumentContent, + }), + }); + + const { problemsNumber, messageLines, errorMessage } = await response.json(); + + if (!response.ok) { + throw new Error(errorMessage); + } + + if (problemsNumber > 0) { + ctx.editorContainer.els.consolePanel.els.expander.els.errors.textContent = `Problems (${problemsNumber})`; + + toggleConsolePanel(ctx, true); + } + + messageLines.forEach((messageLine) => { + const preElement = document.createElement('pre'); + + preElement.style.marginTop = '0px'; + preElement.style.marginBottom = '0px'; + + preElement.textContent = messageLine.length === 0 ? ' ' : messageLine; + + ctx.editorContainer.els.console.append(preElement); + }); + } catch (error) { + ctx.editorContainer.els.consolePanel.els.expander.els.errors.textContent = `Problems (1)`; + + toggleConsolePanel(ctx, true); + + const preElement = document.createElement('pre'); + + preElement.style.marginTop = '0px'; + preElement.style.marginBottom = '0px'; + + preElement.textContent = error.message; + + ctx.editorContainer.els.console.append(preElement); + } finally { + event.target.classList.replace('fa-spinner', 'fa-square-check'); + + event.target.style.animation = null; + } +}; var editor = (zendoc) => { - var symIdx = new quickScore.QuickScore(zendoc.symbols || [], ["name", "title"]); + var symIdx = new quickScore.QuickScore(zendoc.symbols || [], ['name', 'title']); symIdx.config.maxIterations = 1000; - var keysIdx = new quickScore.QuickScore(zendoc.keys || [], ["name"]); + var keysIdx = new quickScore.QuickScore(zendoc.keys || [], ['name']); keysIdx.config.maxIterations = 1000; - var iconsIdx = new quickScore.QuickScore(zendoc.icons || [], ["name", "title"]); + var iconsIdx = new quickScore.QuickScore(zendoc.icons || [], ['name', 'title']); iconsIdx.config.maxIterations = 1000; - var annotationsIdx = new quickScore.QuickScore(zendoc.annotations || [], ["name"]); + var annotationsIdx = new quickScore.QuickScore(zendoc.annotations || [], ['name']); annotationsIdx.config.maxIterations = 1000; - var ctx = {symbols: symIdx, keys: keysIdx, icons: iconsIdx, annotations: annotationsIdx, doc: zendoc.doc}; - var in_chrome = (window.location.search || '').includes('chrome') || document.body.getBoundingClientRect().width < 800; + var ctx = { symbols: symIdx, keys: keysIdx, icons: iconsIdx, annotations: annotationsIdx, doc: zendoc.doc }; + var in_chrome = + (window.location.search || '').includes('chrome') || document.body.getBoundingClientRect().width < 800; ctx.in_chrome = in_chrome; - var editor_style = {position: relative , - background: black, - color: 'white', - overflow: 'auto', - width: "50%", - height: 'calc(100vh)'}; - - ctx.container = el({tag: 'div', - append: document.body, - style: {display: 'flex', padding: 0}, - els: {save: {tag: 'i', - class: ['fa-solid', 'fa-save'], - style: { position: absolute, - cursor: 'pointer', - 'z-index': "10000", - bottom: 10, left: in_chrome ? 'calc(100vw - 50px)' : 'calc(40vw - 50px)', - 'font-size': 40, color: 'white'}, - on: {click: (ev)=> { - ev.target.style.visibility = "hidden"; - save(ctx);}}}} - }); + var editor_style = { + position: relative, + background: black, + color: 'white', + overflow: 'auto', + height: 'calc(100vh - 60px)', + }; + + var containerConfig = { + tag: 'div', + append: document.body, + style: { display: 'flex', padding: 0 }, + }; + + ctx.container = el(containerConfig); var on_paste = (evt) => { const clipboardItems = evt.clipboardData.items; @@ -394,54 +540,229 @@ var editor = (zendoc) => { return item.type.indexOf('image') !== -1; }); const item = items[0]; - if(item){ + if (item) { const blob = item.getAsFile(); console.log(blob); const file_name = prompt('Give file a uniq name', blob.name); - if(file_name) { + if (file_name) { fetch(`/${ctx.doc}/file`, { method: 'POST', - headers: {'content-type': item.type, - 'file-name': file_name}, - body: blob - }).then((resp)=>{ + headers: { 'content-type': item.type, 'file-name': file_name }, + body: blob, + }).then((resp) => { insert_text(ctx, `((img "${file_name}" {:width 400 :class "screenshot"}))`); }); } } }; - ctx.editor = el({tag: 'div', - append: ctx.container, - style: editor_style, - els: {hl: {tag: 'pre', style: merge(poss,{})}, - caret: {tag: 'pre', style: caret_style, - els: {txt: {tag: 'text'}, - cursor: {tag: 'span', text: ' ', style: {}}}}, - textarea: {tag: 'textarea', - spellcheck: false, - autofocus: true, - on: {keyup: (ev) => { on_editor_keyup(ctx,ev);}, - paste: on_paste, - keydown: (ev) => { on_editor_keydown(ctx ,ev); }, - click: (ev) => { hide_popup(ctx); }}, - value: zendoc.text || "", - style: textarea_style}, - pop: {tag: 'div', style: popup_style }}}); + const hasValidator = !!zendoc.validator; + + if (hasValidator) { + const styleSheet = document.styleSheets[0]; + + styleSheet.insertRule(` + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + `); + } + + ctx.editorContainer = el({ + tag: 'div', + append: ctx.container, + + style: { + display: 'flex', + width: '100%', + height: '100%', + 'flex-direction': 'column', + flex: 1, + }, + + els: { + editor: { + tag: 'div', + style: editor_style, + els: { + hl: { tag: 'pre', style: merge(poss, {}) }, + caret: { + tag: 'pre', + style: caret_style, + els: { txt: { tag: 'text' }, cursor: { tag: 'span', text: ' ', style: {} } }, + }, + textarea: { + tag: 'textarea', + spellcheck: false, + autofocus: true, + on: { + keyup: (ev) => { + on_editor_keyup(ctx, ev); + }, + paste: on_paste, + keydown: (ev) => { + on_editor_keydown(ctx, ev); + }, + click: (ev) => { + hide_popup(ctx); + }, + }, + value: zendoc.text || '', + style: textarea_style, + }, + pop: { tag: 'div', style: popup_style }, + }, + }, + consolePanel: { + tag: 'div', + + style: { + display: 'flex', + 'align-items': 'center', + 'justify-content': 'space-between', + height: '60px', + width: '100%', + 'border-top': '1px solid rgba(255, 255, 255, 0.6)', + background: 'rgb(31, 41, 55)', + }, + + els: { + expander: { + tag: 'div', + + style: { + display: 'flex', + 'align-items': 'center', + 'justify-content': 'space-between', + gap: '16px', + padding: '0 16px', + }, + + els: !hasValidator + ? null + : { + button: { + tag: 'i', + class: ['fa-solid', 'fa-angle-up'], + + style: { + cursor: 'pointer', + color: 'white', + 'font-size': 30, + 'z-index': '10000', + }, + + on: { + click(event) { + const isExpandConsolePanel = + event.target.classList.contains('fa-angle-down'); + + toggleConsolePanel(ctx, !isExpandConsolePanel); + }, + }, + }, + + title: { + tag: 'span', + + style: { + color: 'white', + }, + + text: 'Console', + }, + + errors: { + tag: 'span', + + style: { + color: 'white', + }, + + text: 'Problems (0)', + }, + }, + }, + + buttons: { + tag: 'div', + + style: { + display: 'flex', + 'align-items': 'center', + 'justify-content': 'space-between', + gap: '16px', + padding: '0 16px', + }, + + els: { + validate: !hasValidator + ? null + : { + tag: 'i', + class: ['fa-solid', 'fa-square-check'], + style: { + cursor: 'pointer', + 'z-index': '10000', + 'font-size': 40, + color: 'white', + }, + on: { + click: validateContent(ctx, zendoc), + }, + }, + save: { + tag: 'i', + class: ['fa-solid', 'fa-save'], + style: { + cursor: 'pointer', + color: 'white', + 'font-size': 40, + 'z-index': '10000', + }, + on: { + click: (ev) => { + ev.target.style.visibility = 'hidden'; + save(ctx); + }, + }, + }, + }, + }, + }, + }, + console: { + tag: 'div', + + style: { + display: 'none', + height: '200px', + width: '40vw', + padding: '8px', + 'border-top': '1px solid rgba(255, 255, 255, 0.6)', + background: 'rgb(31, 41, 55)', + color: 'white', + 'font-size': 12, + overflow: 'auto', + }, + }, + }, + }); hl(ctx, zendoc.text); // var preview = new DOMParser().parseFromString(zendoc.preview, "text/html"); // TODO think if in_chrome is still needed - if(! in_chrome) { - ctx.preview = el({tag: 'div', html: 'loading...', // preview.documentElement.textContent, - style: {height: 'calc(100vh - 20px)', - padding: 20, - width: '60vw', - overflow: 'auto'}, - append: ctx.container}); - - ctx.preview.querySelectorAll('script').forEach((x)=>{ + if (!in_chrome) { + ctx.preview = el({ + tag: 'div', + html: 'loading...', // preview.documentElement.textContent, + style: { height: 'calc(100vh - 20px)', padding: 20, width: '60vw', overflow: 'auto' }, + append: ctx.container, + }); + + ctx.preview.querySelectorAll('script').forEach((x) => { eval(x.innerText); }); } @@ -452,8 +773,8 @@ var editor = (zendoc) => { return ctx; }; -main(()=>{ - if(window.zendoc){ +main(() => { + if (window.zendoc) { editor(zendoc); } }); diff --git a/src/zd/view/editor.clj b/src/zd/view/editor.clj index 6bc6daa..36629a8 100644 --- a/src/zd/view/editor.clj +++ b/src/zd/view/editor.clj @@ -5,15 +5,17 @@ [hiccup.util] [zd.schema :as sch] [zd.view.icons :as icons] - [zd.store :as store])) + [zd.store :as store] + [zen.core :as zen])) -(defn editor [ztx ctx {docname :zd/docname :as doc} content] +(defn editor [ztx _ doc content] (let [header (str ":zd/docname " (:zd/docname doc) "\n") text (str header content) template (when (str/blank? content) (sch/get-class-template ztx (:zd/parent doc))) symbols (store/symbols ztx) anns (store/annotations ztx) + zendoc-config (zen/get-symbol ztx (-> @ztx :zen/state :http :state :config :zendoc)) zendoc {:text (if (str/blank? content) (str text "\n" template) text) @@ -21,5 +23,6 @@ :keys (store/props ztx) :icons icons/icons :annotations anns + :validator (:validator zendoc-config) :doc (:zd/docname doc)}] [:script#editor-config (str "var zendoc=" (json/generate-string zendoc) ";")])) diff --git a/zrc/zd.edn b/zrc/zd.edn index 0dc8f25..b82bbea 100644 --- a/zrc/zd.edn +++ b/zrc/zd.edn @@ -4,7 +4,10 @@ zendoc-config {:zen/tags #{zen/tag zen/schema} :type zen/map - :keys {:dir {:type zen/string}}} + :keys {:dir {:type zen/string} + :validator {:type zen/map + :keys {:type {:type zen/string} + :endpoint {:type zen/string}}}}} query {:zen/tags #{zen/op}