Update html/custom-css.ejs
This commit is contained in:
parent
bea7a079a6
commit
2e186b0d25
@ -1,7 +1,4 @@
|
|||||||
<!--
|
|
||||||
GPLv3+ © 2021–2025 POKETUBE (https://codeberg.org/Ashley/poketube)
|
|
||||||
This program is free software—see https://www.gnu.org/licenses/
|
|
||||||
-->
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html class="gradient-bg" lang="en">
|
<html class="gradient-bg" lang="en">
|
||||||
<head>
|
<head>
|
||||||
@ -265,157 +262,207 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
<div>© 2021–2025 PokeTube • Free software under the GNU GPL. Customizations are stored locally on your device.</div>
|
<div>© 2021–2025 PokeTube • Free software under the GNU GPL. Customizations are stored locally on your device.</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- ================= SCRIPTS ================ -->
|
||||||
|
<script>
|
||||||
|
/* ---------- utils ---------- */
|
||||||
|
const $ = s => document.querySelector(s);
|
||||||
|
const on = (el, ev, fn) => el && el.addEventListener(ev, fn, { passive:true });
|
||||||
|
const saveFile = (name, content, type='text/plain') => {
|
||||||
|
const blob = new Blob([content], { type });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url; a.download = name; document.body.appendChild(a); a.click();
|
||||||
|
URL.revokeObjectURL(url); a.remove();
|
||||||
|
};
|
||||||
|
const copyText = async (txt) => { try{ await navigator.clipboard.writeText(txt); toast('Copied'); } catch{ toast('Copy failed', true); } };
|
||||||
|
const toast = (msg,bad=false)=>{
|
||||||
|
const t=document.createElement('div');
|
||||||
|
t.textContent=msg;
|
||||||
|
t.style.cssText='position:fixed;left:50%;bottom:22px;transform:translateX(-50%);background:'+(bad?'#4d1622':'#132a20')+';color:#fff;padding:10px 14px;border:1px solid rgba(255,255,255,.12);border-radius:12px;z-index:9999';
|
||||||
|
document.body.appendChild(t); setTimeout(()=>t.remove(),1500);
|
||||||
|
};
|
||||||
|
const debounce = (fn,ms=200)=>{ let t; return (...a)=>{ clearTimeout(t); t=setTimeout(()=>fn(...a),ms); }; };
|
||||||
|
|
||||||
<!-- ================= SCRIPTS ================ -->
|
/* ---------- basic formatters ---------- */
|
||||||
<script>
|
const basicFormatCSS = s => { try{ return s.replace(/\{/g,'{\n ').replace(/\;/g,';\n ').replace(/\}\s*/g,'\n}\n').replace(/\n\s+\n/g,'\n'); }catch{ return s } };
|
||||||
/* ---------- utils ---------- */
|
const basicFormatJS = s => { try{ return s.replace(/;\s*/g,';\n').replace(/\{\s*/g,'{\n ').replace(/\}\s*/g,'\n}\n').replace(/\)\s*\{/g,') {\n ').replace(/\n\s+\n/g,'\n'); }catch{ return s } };
|
||||||
const $ = s => document.querySelector(s);
|
|
||||||
const on = (el, ev, fn) => el && el.addEventListener(ev, fn, { passive:true });
|
/* ---------- one-time repair for polluted saves ---------- */
|
||||||
const saveFile = (name, content, type='text/plain') => {
|
// Strips any leftover <span class="tok ...">...</span> from previously broken runs
|
||||||
const blob = new Blob([content], { type });
|
// and decodes entities back to plain text.
|
||||||
const url = URL.createObjectURL(blob);
|
function stripHighlightArtifacts(s){
|
||||||
const a = document.createElement('a');
|
if (typeof s !== 'string') return s;
|
||||||
a.href = url; a.download = name; document.body.appendChild(a); a.click();
|
// fast bail if nothing suspicious
|
||||||
URL.revokeObjectURL(url); a.remove();
|
if (!/<span\s+class="tok\s/.test(s) && !/<span\s+class="tok\s/.test(s)) return s;
|
||||||
|
// Remove our token spans
|
||||||
|
let out = s.replace(/<\/span>/g, '')
|
||||||
|
.replace(/<span\s+class="tok\s[^>]*>/g, '');
|
||||||
|
// Decode common entities that may have been saved
|
||||||
|
out = out.replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&');
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- highlighter (safe placeholders; never touches source text) ---------- */
|
||||||
|
const JS_KEYWORDS = /\b(?:await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|from|function|if|import|in|instanceof|let|new|null|return|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/g;
|
||||||
|
const JS_BUILTINS = /\b(?:Array|Object|String|Number|Boolean|Map|Set|WeakMap|WeakSet|Date|Math|JSON|Promise|RegExp|Error|TypeError|URL|Node|Element|document|window|console|fetch)\b/g;
|
||||||
|
const ESC = s => s.replace(/[&<>]/g, c => ({'&':'&','<':'<','>':'>'}[c]));
|
||||||
|
|
||||||
|
function highlightJS(code){
|
||||||
|
let s = ESC(code);
|
||||||
|
const tokens = [];
|
||||||
|
const PL = i => `__PTK${i}__`;
|
||||||
|
|
||||||
|
const protect = (re, cls) => {
|
||||||
|
s = s.replace(re, m => { const i = tokens.push(`<span class="tok ${cls}">${m}</span>`) - 1; return PL(i); });
|
||||||
};
|
};
|
||||||
const copyText = async (txt) => { try{ await navigator.clipboard.writeText(txt); toast('Copied'); } catch{ toast('Copy failed', true); } };
|
|
||||||
const toast = (msg,bad=false)=>{ const t=document.createElement('div'); t.textContent=msg; t.style.cssText='position:fixed;left:50%;bottom:22px;transform:translateX(-50%);background:'+ (bad?'#4d1622':'#132a20') +';color:#fff;padding:10px 14px;border:1px solid rgba(255,255,255,.12);border-radius:12px;z-index:9999'; document.body.appendChild(t); setTimeout(()=>t.remove(),1500); };
|
|
||||||
const debounce = (fn,ms=200)=>{ let t; return (...a)=>{ clearTimeout(t); t=setTimeout(()=>fn(...a),ms); }; };
|
|
||||||
|
|
||||||
/* ---------- basic formatters ---------- */
|
// protect first (so later regexes never see our <span> tags)
|
||||||
const basicFormatCSS = s => { try{ return s.replace(/\{/g,'{\n ').replace(/\;/g,';\n ').replace(/\}\s*/g,'\n}\n').replace(/\n\s+\n/g,'\n'); }catch{ return s } };
|
protect(/\/\*[\s\S]*?\*\//g, 'comment');
|
||||||
const basicFormatJS = s => { try{ return s.replace(/;\s*/g,';\n').replace(/\{\s*/g,'{\n ').replace(/\}\s*/g,'\n}\n').replace(/\)\s*\{/g,') {\n ').replace(/\n\s+\n/g,'\n'); }catch{ return s } };
|
protect(/\/\/.*?$/gm, 'comment');
|
||||||
|
protect(/`[\s\S]*?`/g, 'str');
|
||||||
|
protect(/'[^'\\]*(?:\\.[^'\\]*)*'/g, 'str');
|
||||||
|
protect(/"[^"\\]*(?:\\.[^"\\]*)*"/g, 'str');
|
||||||
|
protect(/\/(?:\\.|[^\\\/\n])+\/[gimsuy]*/g, 'reg');
|
||||||
|
|
||||||
/* ---------- highlighter (safe placeholders) ---------- */
|
// plain text pass
|
||||||
const JS_KEYWORDS = /\b(?:await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|from|function|if|import|in|instanceof|let|new|null|return|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/g;
|
s = s.replace(/\b(0x[\da-fA-F]+|\d+\.\d+|\d+)\b/g, '<span class="tok num">$1</span>');
|
||||||
const JS_BUILTINS = /\b(?:Array|Object|String|Number|Boolean|Map|Set|WeakMap|WeakSet|Date|Math|JSON|Promise|RegExp|Error|TypeError|URL|Node|Element|document|window|console|fetch)\b/g;
|
s = s.replace(/\b(true|false|null)\b/g, '<span class="tok bool">$1</span>');
|
||||||
const ESC = s => s.replace(/[&<>]/g, c => ({'&':'&','<':'<','>':'>'}[c]));
|
s = s.replace(JS_KEYWORDS, '<span class="tok kw">$&</span>');
|
||||||
|
s = s.replace(JS_BUILTINS, '<span class="tok fn">$&</span>');
|
||||||
|
s = s.replace(/([=+\-/*<>!&|%^~?:]+)/g, '<span class="tok op">$1</span>');
|
||||||
|
|
||||||
function highlightJS(code){
|
// restore protected segments
|
||||||
let s = ESC(code);
|
s = s.replace(/__PTK(\d+)__/g, (_,i)=>tokens[+i]);
|
||||||
const tokens = [];
|
return s;
|
||||||
const PL = i => `__PTK${i}__`;
|
}
|
||||||
|
|
||||||
const protect = (re, cls) => {
|
function highlightCSS(code){
|
||||||
s = s.replace(re, m => { const i = tokens.push(`<span class="tok ${cls}">${m}</span>`) - 1; return PL(i); });
|
let s = ESC(code);
|
||||||
};
|
s = s.replace(/\/\*[\s\S]*?\*\//g, '<span class="tok comment">$&</span>');
|
||||||
|
s = s.replace(/(@[a-zA-Z-]+)/g, '<span class="tok at">$1</span>');
|
||||||
|
s = s.replace(/^([^{}@\n][^{\n]+)(?=\s*\{)/gm, '<span class="tok selector">$1</span>');
|
||||||
|
s = s.replace(/([a-zA-Z-]+)(\s*:\s*)([^;}{]+)/g, (m,p,c,v)=>{
|
||||||
|
v = v.replace(/(#[0-9a-fA-F]{3,8}|\b\d+(?:\.\d+)?(?:px|em|rem|%|vh|vw)?\b)/g, '<span class="tok num">$1</span>');
|
||||||
|
v = v.replace(/("[^"]*"|'[^']*')/g, '<span class="tok str">$1</span>');
|
||||||
|
return '<span class="tok prop">'+p+'</span>'+c+v;
|
||||||
|
});
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
// protect first
|
function makeEditor(edSel, hlSel, lnSel, { lang='css', storageKey, defaultValue='' }){
|
||||||
protect(/\/\*[\s\S]*?\*\//g, 'comment');
|
const ed = $(edSel), hl = $(hlSel), ln = $(lnSel);
|
||||||
protect(/\/\/.*?$/gm, 'comment');
|
|
||||||
protect(/`[\s\S]*?`/g, 'str');
|
|
||||||
protect(/'[^'\\]*(?:\\.[^'\\]*)*'/g, 'str');
|
|
||||||
protect(/"[^"\\]*(?:\\.[^"\\]*)*"/g, 'str');
|
|
||||||
protect(/\/(?:\\.|[^\\\/\n])+\/[gimsuy]*/g, 'reg');
|
|
||||||
|
|
||||||
// then style remaining plain text
|
const load = () => {
|
||||||
s = s.replace(/\b(0x[\da-fA-F]+|\d+\.\d+|\d+)\b/g, '<span class="tok num">$1</span>');
|
let saved = localStorage.getItem(storageKey);
|
||||||
s = s.replace(/\b(true|false|null)\b/g, '<span class="tok bool">$1</span>');
|
if (saved === null) saved = defaultValue;
|
||||||
s = s.replace(JS_KEYWORDS, '<span class="tok kw">$&</span>');
|
// one-time cleanup for old corrupted saves
|
||||||
s = s.replace(JS_BUILTINS, '<span class="tok fn">$&</span>');
|
const cleaned = stripHighlightArtifacts(saved);
|
||||||
s = s.replace(/([=+\-/*<>!&|%^~?:]+)/g, '<span class="tok op">$1</span>');
|
if (cleaned !== saved) {
|
||||||
|
localStorage.setItem(storageKey, cleaned);
|
||||||
|
saved = cleaned;
|
||||||
|
}
|
||||||
|
return saved;
|
||||||
|
};
|
||||||
|
|
||||||
// restore
|
const setLines = (text) => {
|
||||||
s = s.replace(/__PTK(\d+)__/g, (_,i)=>tokens[+i]);
|
const lines = text.split('\n').length;
|
||||||
return s;
|
let out = '';
|
||||||
}
|
for (let i=1;i<=lines;i++) out += i + (i<lines ? '\n' : '');
|
||||||
|
ln.textContent = out;
|
||||||
|
};
|
||||||
|
|
||||||
function highlightCSS(code){
|
const render = (text) => {
|
||||||
let s = ESC(code);
|
setLines(text);
|
||||||
s = s.replace(/\/\*[\s\S]*?\*\//g, '<span class="tok comment">$&</span>');
|
hl.innerHTML = (lang==='css') ? highlightCSS(text) : highlightJS(text);
|
||||||
s = s.replace(/(@[a-zA-Z-]+)/g, '<span class="tok at">$1</span>');
|
};
|
||||||
s = s.replace(/^([^{}@\n][^{\n]+)(?=\s*\{)/gm, '<span class="tok selector">$1</span>');
|
|
||||||
s = s.replace(/([a-zA-Z-]+)(\s*:\s*)([^;}{]+)/g, (m,p,c,v)=>{
|
|
||||||
v = v.replace(/(#[0-9a-fA-F]{3,8}|\b\d+(?:\.\d+)?(?:px|em|rem|%|vh|vw)?\b)/g, '<span class="tok num">$1</span>');
|
|
||||||
v = v.replace(/("[^"]*"|'[^']*')/g, '<span class="tok str">$1</span>');
|
|
||||||
return '<span class="tok prop">'+p+'</span>'+c+v;
|
|
||||||
});
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeEditor(edSel, hlSel, lnSel, { lang='css', storageKey, defaultValue='' }){
|
const initial = load();
|
||||||
const ed = $(edSel), hl = $(hlSel), ln = $(lnSel);
|
ed.value = initial; render(initial);
|
||||||
|
|
||||||
const load = () => {
|
const syncScroll = () => { hl.scrollTop = ed.scrollTop; hl.scrollLeft = ed.scrollLeft; ln.scrollTop = ed.scrollTop; };
|
||||||
const saved = localStorage.getItem(storageKey);
|
on(ed, 'scroll', syncScroll);
|
||||||
return saved !== null ? saved : defaultValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
const setLines = (text) => {
|
const update = () => render(ed.value);
|
||||||
const lines = text.split('\n').length;
|
on(ed, 'input', debounce(update, 20));
|
||||||
let out = '';
|
|
||||||
for (let i=1;i<=lines;i++) out += i + (i<lines ? '\n' : '');
|
|
||||||
ln.textContent = out;
|
|
||||||
};
|
|
||||||
|
|
||||||
const render = (text) => {
|
const save = () => { localStorage.setItem(storageKey, ed.value); toast('Saved'); };
|
||||||
setLines(text);
|
const reset = () => { if (confirm('Clear the editor?')) { ed.value=''; update(); save(); } };
|
||||||
hl.innerHTML = (lang==='css') ? highlightCSS(text) : highlightJS(text);
|
const copy = () => copyText(ed.value);
|
||||||
};
|
const exportFile = () => saveFile(storageKey + (lang==='css'?'.css':'.js'), ed.value, 'text/plain');
|
||||||
|
|
||||||
const initial = load();
|
return { ed, hl, ln, save, reset, copy, exportFile, render, setLines,
|
||||||
ed.value = initial; render(initial);
|
setWrap:(onwrap)=>{ ed.style.whiteSpace = onwrap?'pre-wrap':'pre'; ed.style.overflowWrap = onwrap?'anywhere':'normal'; },
|
||||||
|
format:()=>{ if (lang==='css') ed.value = basicFormatCSS(ed.value); else ed.value = basicFormatJS(ed.value); update(); },
|
||||||
|
importFrom:(file)=> new Promise(res=>{
|
||||||
|
const r=new FileReader();
|
||||||
|
r.onload=()=>{ ed.value=String(r.result||''); update(); res(); };
|
||||||
|
r.readAsText(file);
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const syncScroll = () => { hl.scrollTop = ed.scrollTop; hl.scrollLeft = ed.scrollLeft; ln.scrollTop = ed.scrollTop; };
|
/* ---------- init per tab ---------- */
|
||||||
on(ed, 'scroll', syncScroll);
|
<% if (!tab) { %>
|
||||||
|
const cssEditor = makeEditor('#cssEd','#cssHl','#cssLines',{
|
||||||
const update = () => render(ed.value);
|
lang:'css', storageKey:'poke-custom-css',
|
||||||
on(ed, 'input', debounce(update, 20));
|
defaultValue:`/* Write CSS that applies across Poke. Example:
|
||||||
|
|
||||||
const save = () => { localStorage.setItem(storageKey, ed.value); toast('Saved'); };
|
|
||||||
const reset = () => { if (confirm('Clear the editor?')) { ed.value=''; update(); save(); } };
|
|
||||||
const copy = () => copyText(ed.value);
|
|
||||||
const exportFile = () => saveFile(storageKey + (lang==='css'?'.css':'.js'), ed.value, 'text/plain');
|
|
||||||
|
|
||||||
return { ed, hl, ln, save, reset, copy, exportFile, render, setLines,
|
|
||||||
setWrap:(onwrap)=>{ ed.style.whiteSpace = onwrap?'pre-wrap':'pre'; ed.style.overflowWrap = onwrap?'anywhere':'normal'; },
|
|
||||||
format:()=>{ if (lang==='css') ed.value = basicFormatCSS(ed.value); else ed.value = basicFormatJS(ed.value); update(); },
|
|
||||||
importFrom:(file)=> new Promise(res=>{ const r=new FileReader(); r.onload=()=>{ ed.value=String(r.result||''); update(); res(); }; r.readAsText(file); })
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---------- init per tab ---------- */
|
|
||||||
<% if (!tab) { %>
|
|
||||||
const cssEditor = makeEditor('#cssEd','#cssHl','#cssLines',{lang:'css',storageKey:'poke-custom-css',
|
|
||||||
defaultValue:`/* Write CSS that applies across Poke. Example:
|
|
||||||
|
|
||||||
nav { backdrop-filter: blur(16px) saturate(120%); }
|
nav { backdrop-filter: blur(16px) saturate(120%); }
|
||||||
.tab.active { filter: drop-shadow(0 0 10px #7cc7ff); }
|
.tab.active { filter: drop-shadow(0 0 10px #7cc7ff); }
|
||||||
:root { --accent: #9df7c9; }
|
:root { --accent: #9df7c9; }
|
||||||
|
|
||||||
*/`});
|
*/`
|
||||||
const cssStylePreview = document.createElement('style'); cssStylePreview.id='custom-css-preview'; document.body.appendChild(cssStylePreview);
|
});
|
||||||
const applyCssPreview = () => { cssStylePreview.textContent = cssEditor.ed.value || ''; };
|
|
||||||
applyCssPreview();
|
|
||||||
on($('#saveCssBtn'),'click',cssEditor.save);
|
|
||||||
on($('#formatCssBtn'),'click',cssEditor.format);
|
|
||||||
on($('#copyCssBtn'),'click',cssEditor.copy);
|
|
||||||
on($('#exportCssBtn'),'click',cssEditor.exportFile);
|
|
||||||
on($('#resetCssBtn'),'click',cssEditor.reset);
|
|
||||||
on($('#importCssFile'),'change', async e=>{ const f=e.target.files?.[0]; if(f) await cssEditor.importFrom(f); });
|
|
||||||
on($('#wrapCssBtn'),'click', e=>{ const b=e.currentTarget; const onw=b.getAttribute('data-wrap')==='1'; cssEditor.setWrap(!onw); b.setAttribute('data-wrap', onw?'0':'1'); b.textContent='Wrap: ' + (onw?'Off':'On'); });
|
|
||||||
on($('#reloadPreviewCss'),'click',applyCssPreview);
|
|
||||||
on(cssEditor.ed,'input',debounce(applyCssPreview,50));
|
|
||||||
on(document,'keydown',e=>{ if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()==='s'){ e.preventDefault(); cssEditor.save(); } });
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<% if (tab) { %>
|
const cssStylePreview = document.createElement('style'); cssStylePreview.id='custom-css-preview'; document.body.appendChild(cssStylePreview);
|
||||||
const jsEditor = makeEditor('#jsEd','#jsHl','#jsLines',{lang:'js',storageKey:'poke-custom-script',
|
const applyCssPreview = () => { cssStylePreview.textContent = cssEditor.ed.value || ''; };
|
||||||
defaultValue:`// Write JavaScript that will run on Poke pages.
|
applyCssPreview();
|
||||||
|
|
||||||
|
on($('#saveCssBtn'),'click',cssEditor.save);
|
||||||
|
on($('#formatCssBtn'),'click',cssEditor.format);
|
||||||
|
on($('#copyCssBtn'),'click',cssEditor.copy);
|
||||||
|
on($('#exportCssBtn'),'click',cssEditor.exportFile);
|
||||||
|
on($('#resetCssBtn'),'click',cssEditor.reset);
|
||||||
|
on($('#importCssFile'),'change', async e=>{ const f=e.target.files?.[0]; if(f) await cssEditor.importFrom(f); });
|
||||||
|
on($('#wrapCssBtn'),'click', e=>{
|
||||||
|
const b=e.currentTarget; const onw=b.getAttribute('data-wrap')==='1';
|
||||||
|
cssEditor.setWrap(!onw); b.setAttribute('data-wrap', onw?'0':'1'); b.textContent='Wrap: ' + (onw?'Off':'On');
|
||||||
|
});
|
||||||
|
on($('#reloadPreviewCss'),'click',applyCssPreview);
|
||||||
|
on(cssEditor.ed,'input',debounce(applyCssPreview,50));
|
||||||
|
|
||||||
|
on(document,'keydown',e=>{
|
||||||
|
if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()==='s'){ e.preventDefault(); cssEditor.save(); }
|
||||||
|
});
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
<% if (tab) { %>
|
||||||
|
const jsEditor = makeEditor('#jsEd','#jsHl','#jsLines',{
|
||||||
|
lang:'js', storageKey:'poke-custom-script',
|
||||||
|
defaultValue:`// Write JavaScript that will run on Poke pages.
|
||||||
// Tip: wrap in DOMContentLoaded to avoid race conditions:
|
// Tip: wrap in DOMContentLoaded to avoid race conditions:
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
console.log('[Poke Custom] Hello from your script!');
|
console.log('[Poke Custom] Hello from your script!');
|
||||||
});`});
|
});`
|
||||||
on($('#saveJsBtn'),'click',jsEditor.save);
|
});
|
||||||
on($('#formatJsBtn'),'click',jsEditor.format);
|
|
||||||
on($('#copyJsBtn'),'click',jsEditor.copy);
|
on($('#saveJsBtn'),'click',jsEditor.save);
|
||||||
on($('#exportJsBtn'),'click',jsEditor.exportFile);
|
on($('#formatJsBtn'),'click',jsEditor.format);
|
||||||
on($('#resetJsBtn'),'click',jsEditor.reset);
|
on($('#copyJsBtn'),'click',jsEditor.copy);
|
||||||
on($('#importJsFile'),'change', async e=>{ const f=e.target.files?.[0]; if(f) await jsEditor.importFrom(f); });
|
on($('#exportJsBtn'),'click',jsEditor.exportFile);
|
||||||
on($('#wrapJsBtn'),'click', e=>{ const b=e.currentTarget; const onw=b.getAttribute('data-wrap')==='1'; jsEditor.setWrap(!onw); b.setAttribute('data-wrap', onw?'0':'1'); b.textContent='Wrap: ' + (onw?'Off':'On'); });
|
on($('#resetJsBtn'),'click',jsEditor.reset);
|
||||||
on(document,'keydown',e=>{ if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()==='s'){ e.preventDefault(); jsEditor.save(); } });
|
on($('#importJsFile'),'change', async e=>{ const f=e.target.files?.[0]; if(f) await jsEditor.importFrom(f); });
|
||||||
<% } %>
|
on($('#wrapJsBtn'),'click', e=>{
|
||||||
</script>
|
const b=e.currentTarget; const onw=b.getAttribute('data-wrap')==='1';
|
||||||
|
jsEditor.setWrap(!onw); b.setAttribute('data-wrap', onw?'0':'1'); b.textContent='Wrap: ' + (onw?'Off':'On');
|
||||||
|
});
|
||||||
|
|
||||||
|
on(document,'keydown',e=>{
|
||||||
|
if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()==='s'){ e.preventDefault(); jsEditor.save(); }
|
||||||
|
});
|
||||||
|
<% } %>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<!-- site loader to apply saved CSS/JS globally -->
|
<!-- site loader to apply saved CSS/JS globally -->
|
||||||
<script src="/css/custom-css.js"></script>
|
<script src="/css/custom-css.js"></script>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user