mirror of
https://github.com/alecthomas/chroma.git
synced 2025-03-17 20:58:08 +02:00
feat: support sharing in playground
This commit is contained in:
parent
0e2db44744
commit
d330b760dc
1
.gitignore
vendored
1
.gitignore
vendored
@ -17,3 +17,4 @@
|
||||
_models/
|
||||
|
||||
_examples/
|
||||
*.min.*
|
||||
|
6
Makefile
6
Makefile
@ -1,6 +1,8 @@
|
||||
.PHONY: chromad upload all
|
||||
|
||||
VERSION ?= $(shell git describe --tags --dirty --always)
|
||||
export GOOS ?= linux
|
||||
export GOARCH ?= amd64
|
||||
|
||||
all: README.md tokentype_string.go
|
||||
|
||||
@ -12,7 +14,9 @@ tokentype_string.go: types.go
|
||||
|
||||
chromad:
|
||||
rm -f chromad
|
||||
(export CGOENABLED=0 GOOS=linux GOARCH=amd64; cd ./cmd/chromad && go build -ldflags="-X 'main.version=$(VERSION)'" -o ../../chromad .)
|
||||
esbuild --bundle cmd/chromad/static/index.js --minify --outfile=cmd/chromad/static/index.min.js
|
||||
esbuild --bundle cmd/chromad/static/index.css --minify --outfile=cmd/chromad/static/index.min.css
|
||||
(export CGOENABLED=0 ; cd ./cmd/chromad && go build -ldflags="-X 'main.version=$(VERSION)'" -o ../../chromad .)
|
||||
|
||||
upload: chromad
|
||||
scp chromad root@swapoff.org: && \
|
||||
|
1
bin/.esbuild-0.16.16.pkg
Symbolic link
1
bin/.esbuild-0.16.16.pkg
Symbolic link
@ -0,0 +1 @@
|
||||
hermit
|
1
bin/.reflex-0.3.1.pkg
Symbolic link
1
bin/.reflex-0.3.1.pkg
Symbolic link
@ -0,0 +1 @@
|
||||
hermit
|
1
bin/esbuild
Symbolic link
1
bin/esbuild
Symbolic link
@ -0,0 +1 @@
|
||||
.esbuild-0.16.16.pkg
|
1
bin/reflex
Symbolic link
1
bin/reflex
Symbolic link
@ -0,0 +1 @@
|
||||
.reflex-0.3.1.pkg
|
@ -29,7 +29,29 @@ var (
|
||||
//go:embed static
|
||||
staticFiles embed.FS
|
||||
|
||||
htmlTemplate = template.Must(template.New("html").Parse(indexTemplate))
|
||||
htmlTemplate = template.Must(template.New("html").
|
||||
Funcs(template.FuncMap{
|
||||
"JS": func(filename string) template.JS {
|
||||
if version == "devel" {
|
||||
return template.JS(`import "./static/` + filename + "\";\n")
|
||||
}
|
||||
content, err := staticFiles.ReadFile("static/" + strings.TrimSuffix(filename, ".js") + ".min.js")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return template.JS(content)
|
||||
},
|
||||
"CSS": func(filename string) template.CSS {
|
||||
if version == "devel" {
|
||||
return template.CSS(`@import url("./static/` + filename + "\");")
|
||||
}
|
||||
content, err := staticFiles.ReadFile("static/" + strings.TrimSuffix(filename, ".css") + ".min.css")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return template.CSS(content)
|
||||
},
|
||||
}).Parse(indexTemplate))
|
||||
)
|
||||
|
||||
type context struct {
|
||||
|
2
cmd/chromad/reflex.conf
Normal file
2
cmd/chromad/reflex.conf
Normal file
@ -0,0 +1,2 @@
|
||||
-sr '.*' -- \
|
||||
go run .
|
296
cmd/chromad/static/base64.js
Normal file
296
cmd/chromad/static/base64.js
Normal file
@ -0,0 +1,296 @@
|
||||
/**
|
||||
* base64.ts
|
||||
*
|
||||
* Licensed under the BSD 3-Clause License.
|
||||
* http://opensource.org/licenses/BSD-3-Clause
|
||||
*
|
||||
* References:
|
||||
* http://en.wikipedia.org/wiki/Base64
|
||||
*
|
||||
* @author Dan Kogai (https://github.com/dankogai)
|
||||
*/
|
||||
const version = '3.7.4';
|
||||
/**
|
||||
* @deprecated use lowercase `version`.
|
||||
*/
|
||||
const VERSION = version;
|
||||
const _hasatob = typeof atob === 'function';
|
||||
const _hasbtoa = typeof btoa === 'function';
|
||||
const _hasBuffer = typeof Buffer === 'function';
|
||||
const _TD = typeof TextDecoder === 'function' ? new TextDecoder() : undefined;
|
||||
const _TE = typeof TextEncoder === 'function' ? new TextEncoder() : undefined;
|
||||
const b64ch = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
const b64chs = Array.prototype.slice.call(b64ch);
|
||||
const b64tab = ((a) => {
|
||||
let tab = {};
|
||||
a.forEach((c, i) => tab[c] = i);
|
||||
return tab;
|
||||
})(b64chs);
|
||||
const b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
|
||||
const _fromCC = String.fromCharCode.bind(String);
|
||||
const _U8Afrom = typeof Uint8Array.from === 'function'
|
||||
? Uint8Array.from.bind(Uint8Array)
|
||||
: (it, fn = (x) => x) => new Uint8Array(Array.prototype.slice.call(it, 0).map(fn));
|
||||
const _mkUriSafe = (src) => src
|
||||
.replace(/=/g, '').replace(/[+\/]/g, (m0) => m0 == '+' ? '-' : '_');
|
||||
const _tidyB64 = (s) => s.replace(/[^A-Za-z0-9\+\/]/g, '');
|
||||
/**
|
||||
* polyfill version of `btoa`
|
||||
*/
|
||||
const btoaPolyfill = (bin) => {
|
||||
// console.log('polyfilled');
|
||||
let u32, c0, c1, c2, asc = '';
|
||||
const pad = bin.length % 3;
|
||||
for (let i = 0; i < bin.length;) {
|
||||
if ((c0 = bin.charCodeAt(i++)) > 255 ||
|
||||
(c1 = bin.charCodeAt(i++)) > 255 ||
|
||||
(c2 = bin.charCodeAt(i++)) > 255)
|
||||
throw new TypeError('invalid character found');
|
||||
u32 = (c0 << 16) | (c1 << 8) | c2;
|
||||
asc += b64chs[u32 >> 18 & 63]
|
||||
+ b64chs[u32 >> 12 & 63]
|
||||
+ b64chs[u32 >> 6 & 63]
|
||||
+ b64chs[u32 & 63];
|
||||
}
|
||||
return pad ? asc.slice(0, pad - 3) + "===".substring(pad) : asc;
|
||||
};
|
||||
/**
|
||||
* does what `window.btoa` of web browsers do.
|
||||
* @param {String} bin binary string
|
||||
* @returns {string} Base64-encoded string
|
||||
*/
|
||||
const _btoa = _hasbtoa ? (bin) => btoa(bin)
|
||||
: _hasBuffer ? (bin) => Buffer.from(bin, 'binary').toString('base64')
|
||||
: btoaPolyfill;
|
||||
const _fromUint8Array = _hasBuffer
|
||||
? (u8a) => Buffer.from(u8a).toString('base64')
|
||||
: (u8a) => {
|
||||
// cf. https://stackoverflow.com/questions/12710001/how-to-convert-uint8-array-to-base64-encoded-string/12713326#12713326
|
||||
const maxargs = 0x1000;
|
||||
let strs = [];
|
||||
for (let i = 0, l = u8a.length; i < l; i += maxargs) {
|
||||
strs.push(_fromCC.apply(null, u8a.subarray(i, i + maxargs)));
|
||||
}
|
||||
return _btoa(strs.join(''));
|
||||
};
|
||||
/**
|
||||
* converts a Uint8Array to a Base64 string.
|
||||
* @param {boolean} [urlsafe] URL-and-filename-safe a la RFC4648 §5
|
||||
* @returns {string} Base64 string
|
||||
*/
|
||||
const fromUint8Array = (u8a, urlsafe = false) => urlsafe ? _mkUriSafe(_fromUint8Array(u8a)) : _fromUint8Array(u8a);
|
||||
// This trick is found broken https://github.com/dankogai/js-base64/issues/130
|
||||
// const utob = (src: string) => unescape(encodeURIComponent(src));
|
||||
// reverting good old fationed regexp
|
||||
const cb_utob = (c) => {
|
||||
if (c.length < 2) {
|
||||
var cc = c.charCodeAt(0);
|
||||
return cc < 0x80 ? c
|
||||
: cc < 0x800 ? (_fromCC(0xc0 | (cc >>> 6))
|
||||
+ _fromCC(0x80 | (cc & 0x3f)))
|
||||
: (_fromCC(0xe0 | ((cc >>> 12) & 0x0f))
|
||||
+ _fromCC(0x80 | ((cc >>> 6) & 0x3f))
|
||||
+ _fromCC(0x80 | (cc & 0x3f)));
|
||||
}
|
||||
else {
|
||||
var cc = 0x10000
|
||||
+ (c.charCodeAt(0) - 0xD800) * 0x400
|
||||
+ (c.charCodeAt(1) - 0xDC00);
|
||||
return (_fromCC(0xf0 | ((cc >>> 18) & 0x07))
|
||||
+ _fromCC(0x80 | ((cc >>> 12) & 0x3f))
|
||||
+ _fromCC(0x80 | ((cc >>> 6) & 0x3f))
|
||||
+ _fromCC(0x80 | (cc & 0x3f)));
|
||||
}
|
||||
};
|
||||
const re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;
|
||||
/**
|
||||
* @deprecated should have been internal use only.
|
||||
* @param {string} src UTF-8 string
|
||||
* @returns {string} UTF-16 string
|
||||
*/
|
||||
const utob = (u) => u.replace(re_utob, cb_utob);
|
||||
//
|
||||
const _encode = _hasBuffer
|
||||
? (s) => Buffer.from(s, 'utf8').toString('base64')
|
||||
: _TE
|
||||
? (s) => _fromUint8Array(_TE.encode(s))
|
||||
: (s) => _btoa(utob(s));
|
||||
/**
|
||||
* converts a UTF-8-encoded string to a Base64 string.
|
||||
* @param {boolean} [urlsafe] if `true` make the result URL-safe
|
||||
* @returns {string} Base64 string
|
||||
*/
|
||||
const encode = (src, urlsafe = false) => urlsafe
|
||||
? _mkUriSafe(_encode(src))
|
||||
: _encode(src);
|
||||
/**
|
||||
* converts a UTF-8-encoded string to URL-safe Base64 RFC4648 §5.
|
||||
* @returns {string} Base64 string
|
||||
*/
|
||||
const encodeURI = (src) => encode(src, true);
|
||||
// This trick is found broken https://github.com/dankogai/js-base64/issues/130
|
||||
// const btou = (src: string) => decodeURIComponent(escape(src));
|
||||
// reverting good old fationed regexp
|
||||
const re_btou = /[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g;
|
||||
const cb_btou = (cccc) => {
|
||||
switch (cccc.length) {
|
||||
case 4:
|
||||
var cp = ((0x07 & cccc.charCodeAt(0)) << 18)
|
||||
| ((0x3f & cccc.charCodeAt(1)) << 12)
|
||||
| ((0x3f & cccc.charCodeAt(2)) << 6)
|
||||
| (0x3f & cccc.charCodeAt(3)), offset = cp - 0x10000;
|
||||
return (_fromCC((offset >>> 10) + 0xD800)
|
||||
+ _fromCC((offset & 0x3FF) + 0xDC00));
|
||||
case 3:
|
||||
return _fromCC(((0x0f & cccc.charCodeAt(0)) << 12)
|
||||
| ((0x3f & cccc.charCodeAt(1)) << 6)
|
||||
| (0x3f & cccc.charCodeAt(2)));
|
||||
default:
|
||||
return _fromCC(((0x1f & cccc.charCodeAt(0)) << 6)
|
||||
| (0x3f & cccc.charCodeAt(1)));
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @deprecated should have been internal use only.
|
||||
* @param {string} src UTF-16 string
|
||||
* @returns {string} UTF-8 string
|
||||
*/
|
||||
const btou = (b) => b.replace(re_btou, cb_btou);
|
||||
/**
|
||||
* polyfill version of `atob`
|
||||
*/
|
||||
const atobPolyfill = (asc) => {
|
||||
// console.log('polyfilled');
|
||||
asc = asc.replace(/\s+/g, '');
|
||||
if (!b64re.test(asc))
|
||||
throw new TypeError('malformed base64.');
|
||||
asc += '=='.slice(2 - (asc.length & 3));
|
||||
let u24, bin = '', r1, r2;
|
||||
for (let i = 0; i < asc.length;) {
|
||||
u24 = b64tab[asc.charAt(i++)] << 18
|
||||
| b64tab[asc.charAt(i++)] << 12
|
||||
| (r1 = b64tab[asc.charAt(i++)]) << 6
|
||||
| (r2 = b64tab[asc.charAt(i++)]);
|
||||
bin += r1 === 64 ? _fromCC(u24 >> 16 & 255)
|
||||
: r2 === 64 ? _fromCC(u24 >> 16 & 255, u24 >> 8 & 255)
|
||||
: _fromCC(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255);
|
||||
}
|
||||
return bin;
|
||||
};
|
||||
/**
|
||||
* does what `window.atob` of web browsers do.
|
||||
* @param {String} asc Base64-encoded string
|
||||
* @returns {string} binary string
|
||||
*/
|
||||
const _atob = _hasatob ? (asc) => atob(_tidyB64(asc))
|
||||
: _hasBuffer ? (asc) => Buffer.from(asc, 'base64').toString('binary')
|
||||
: atobPolyfill;
|
||||
//
|
||||
const _toUint8Array = _hasBuffer
|
||||
? (a) => _U8Afrom(Buffer.from(a, 'base64'))
|
||||
: (a) => _U8Afrom(_atob(a), c => c.charCodeAt(0));
|
||||
/**
|
||||
* converts a Base64 string to a Uint8Array.
|
||||
*/
|
||||
const toUint8Array = (a) => _toUint8Array(_unURI(a));
|
||||
//
|
||||
const _decode = _hasBuffer
|
||||
? (a) => Buffer.from(a, 'base64').toString('utf8')
|
||||
: _TD
|
||||
? (a) => _TD.decode(_toUint8Array(a))
|
||||
: (a) => btou(_atob(a));
|
||||
const _unURI = (a) => _tidyB64(a.replace(/[-_]/g, (m0) => m0 == '-' ? '+' : '/'));
|
||||
/**
|
||||
* converts a Base64 string to a UTF-8 string.
|
||||
* @param {String} src Base64 string. Both normal and URL-safe are supported
|
||||
* @returns {string} UTF-8 string
|
||||
*/
|
||||
const decode = (src) => _decode(_unURI(src));
|
||||
/**
|
||||
* check if a value is a valid Base64 string
|
||||
* @param {String} src a value to check
|
||||
*/
|
||||
const isValid = (src) => {
|
||||
if (typeof src !== 'string')
|
||||
return false;
|
||||
const s = src.replace(/\s+/g, '').replace(/={0,2}$/, '');
|
||||
return !/[^\s0-9a-zA-Z\+/]/.test(s) || !/[^\s0-9a-zA-Z\-_]/.test(s);
|
||||
};
|
||||
//
|
||||
const _noEnum = (v) => {
|
||||
return {
|
||||
value: v, enumerable: false, writable: true, configurable: true
|
||||
};
|
||||
};
|
||||
/**
|
||||
* extend String.prototype with relevant methods
|
||||
*/
|
||||
const extendString = function () {
|
||||
const _add = (name, body) => Object.defineProperty(String.prototype, name, _noEnum(body));
|
||||
_add('fromBase64', function () { return decode(this); });
|
||||
_add('toBase64', function (urlsafe) { return encode(this, urlsafe); });
|
||||
_add('toBase64URI', function () { return encode(this, true); });
|
||||
_add('toBase64URL', function () { return encode(this, true); });
|
||||
_add('toUint8Array', function () { return toUint8Array(this); });
|
||||
};
|
||||
/**
|
||||
* extend Uint8Array.prototype with relevant methods
|
||||
*/
|
||||
const extendUint8Array = function () {
|
||||
const _add = (name, body) => Object.defineProperty(Uint8Array.prototype, name, _noEnum(body));
|
||||
_add('toBase64', function (urlsafe) { return fromUint8Array(this, urlsafe); });
|
||||
_add('toBase64URI', function () { return fromUint8Array(this, true); });
|
||||
_add('toBase64URL', function () { return fromUint8Array(this, true); });
|
||||
};
|
||||
/**
|
||||
* extend Builtin prototypes with relevant methods
|
||||
*/
|
||||
const extendBuiltins = () => {
|
||||
extendString();
|
||||
extendUint8Array();
|
||||
};
|
||||
const gBase64 = {
|
||||
version: version,
|
||||
VERSION: VERSION,
|
||||
atob: _atob,
|
||||
atobPolyfill: atobPolyfill,
|
||||
btoa: _btoa,
|
||||
btoaPolyfill: btoaPolyfill,
|
||||
fromBase64: decode,
|
||||
toBase64: encode,
|
||||
encode: encode,
|
||||
encodeURI: encodeURI,
|
||||
encodeURL: encodeURI,
|
||||
utob: utob,
|
||||
btou: btou,
|
||||
decode: decode,
|
||||
isValid: isValid,
|
||||
fromUint8Array: fromUint8Array,
|
||||
toUint8Array: toUint8Array,
|
||||
extendString: extendString,
|
||||
extendUint8Array: extendUint8Array,
|
||||
extendBuiltins: extendBuiltins,
|
||||
};
|
||||
// makecjs:CUT //
|
||||
export { version };
|
||||
export { VERSION };
|
||||
export { _atob as atob };
|
||||
export { atobPolyfill };
|
||||
export { _btoa as btoa };
|
||||
export { btoaPolyfill };
|
||||
export { decode as fromBase64 };
|
||||
export { encode as toBase64 };
|
||||
export { utob };
|
||||
export { encode };
|
||||
export { encodeURI };
|
||||
export { encodeURI as encodeURL };
|
||||
export { btou };
|
||||
export { decode };
|
||||
export { isValid };
|
||||
export { fromUint8Array };
|
||||
export { toUint8Array };
|
||||
export { extendString };
|
||||
export { extendUint8Array };
|
||||
export { extendBuiltins };
|
||||
// and finally,
|
||||
export { gBase64 as Base64 };
|
10599
cmd/chromad/static/bulma-0.7.5.css
Normal file
10599
cmd/chromad/static/bulma-0.7.5.css
Normal file
File diff suppressed because it is too large
Load Diff
9
cmd/chromad/static/index.css
Normal file
9
cmd/chromad/static/index.css
Normal file
@ -0,0 +1,9 @@
|
||||
@import url("./bulma-0.7.5.css");
|
||||
|
||||
textarea {
|
||||
font-family: Consolas, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace;
|
||||
}
|
||||
|
||||
#output pre {
|
||||
padding: 0;
|
||||
}
|
@ -1,88 +1,165 @@
|
||||
import * as Base64 from "./base64.js";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
var style = document.createElement('style');
|
||||
var ref = document.querySelector('script');
|
||||
ref.parentNode.insertBefore(style, ref);
|
||||
var style = document.createElement('style');
|
||||
var ref = document.querySelector('script');
|
||||
ref.parentNode.insertBefore(style, ref);
|
||||
|
||||
var form = document.getElementById('chroma');
|
||||
var textArea = form.elements["text"];
|
||||
var styleSelect = form.elements["style"];
|
||||
var languageSelect = form.elements["language"];
|
||||
var csrfToken = form.elements["gorilla.csrf.Token"].value;
|
||||
var output = document.getElementById("output");
|
||||
var htmlCheckbox = document.getElementById("html");
|
||||
var form = document.getElementById('chroma');
|
||||
var textArea = form.elements["text"];
|
||||
var styleSelect = form.elements["style"];
|
||||
var languageSelect = form.elements["language"];
|
||||
var copyButton = form.elements["copy"];
|
||||
var csrfToken = form.elements["gorilla.csrf.Token"].value;
|
||||
var output = document.getElementById("output");
|
||||
var htmlCheckbox = document.getElementById("html");
|
||||
|
||||
(document.querySelectorAll('.notification .delete') || []).forEach(($delete) => {
|
||||
$notification = $delete.parentNode;
|
||||
$delete.addEventListener('click', () => {
|
||||
$notification.parentNode.removeChild($notification);
|
||||
});
|
||||
(document.querySelectorAll('.notification .delete') || []).forEach((el) => {
|
||||
const notification = el.parentNode;
|
||||
el.addEventListener('click', () => {
|
||||
notification.parentNode.removeChild(notification);
|
||||
});
|
||||
});
|
||||
|
||||
// https://stackoverflow.com/a/37697925/7980
|
||||
function handleTab(e) {
|
||||
var after, before, end, lastNewLine, changeLength, re, replace, selection, start, val;
|
||||
if ((e.charCode === 9 || e.keyCode === 9) && !e.altKey && !e.ctrlKey && !e.metaKey) {
|
||||
e.preventDefault();
|
||||
start = this.selectionStart;
|
||||
end = this.selectionEnd;
|
||||
val = this.value;
|
||||
before = val.substring(0, start);
|
||||
after = val.substring(end);
|
||||
replace = true;
|
||||
if (start !== end) {
|
||||
selection = val.substring(start, end);
|
||||
if (~selection.indexOf('\n')) {
|
||||
replace = false;
|
||||
changeLength = 0;
|
||||
lastNewLine = before.lastIndexOf('\n');
|
||||
if (!~lastNewLine) {
|
||||
selection = before + selection;
|
||||
changeLength = before.length;
|
||||
before = '';
|
||||
} else {
|
||||
selection = before.substring(lastNewLine) + selection;
|
||||
changeLength = before.length - lastNewLine;
|
||||
before = before.substring(0, lastNewLine);
|
||||
}
|
||||
if (e.shiftKey) {
|
||||
re = /(\n|^)(\t|[ ]{1,8})/g;
|
||||
if (selection.match(re)) {
|
||||
start--;
|
||||
changeLength--;
|
||||
}
|
||||
selection = selection.replace(re, '$1');
|
||||
} else {
|
||||
selection = selection.replace(/(\n|^)/g, '$1\t');
|
||||
start++;
|
||||
changeLength++;
|
||||
}
|
||||
this.value = before + selection + after;
|
||||
this.selectionStart = start;
|
||||
this.selectionEnd = start + selection.length - changeLength;
|
||||
}
|
||||
}
|
||||
if (replace && !e.shiftKey) {
|
||||
this.value = before + '\t' + after;
|
||||
this.selectionStart = this.selectionEnd = start + 1;
|
||||
}
|
||||
}
|
||||
debouncedEventHandler(e);
|
||||
}
|
||||
|
||||
function debounce(func, wait, immediate) {
|
||||
var timeout;
|
||||
return function () {
|
||||
var context = this, args = arguments;
|
||||
var later = function () {
|
||||
timeout = null;
|
||||
if (!immediate) func.apply(context, args);
|
||||
};
|
||||
var callNow = immediate && !timeout;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow) func.apply(context, args);
|
||||
};
|
||||
}
|
||||
|
||||
function getFormJSON() {
|
||||
return {
|
||||
"language": languageSelect.value,
|
||||
"style": styleSelect.value,
|
||||
"text": textArea.value,
|
||||
"classes": htmlCheckbox.checked,
|
||||
}
|
||||
}
|
||||
|
||||
function update(event) {
|
||||
fetch("api/render", {
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
cache: 'no-cache',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'X-CSRF-Token': csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
redirect: 'follow',
|
||||
referrer: 'no-referrer',
|
||||
body: JSON.stringify(getFormJSON()),
|
||||
}).then(data => {
|
||||
data.json().then(
|
||||
value => {
|
||||
if (value.language) {
|
||||
languageSelect.value = value.language;
|
||||
}
|
||||
style.innerHTML = "#output { " + value.background + "}";
|
||||
if (htmlCheckbox.checked) {
|
||||
output.innerText = value.html;
|
||||
} else {
|
||||
output.innerHTML = value.html;
|
||||
}
|
||||
}
|
||||
);
|
||||
}).catch(reason => {
|
||||
console.log(reason);
|
||||
});
|
||||
|
||||
function debounce(func, wait, immediate) {
|
||||
var timeout;
|
||||
return function () {
|
||||
var context = this, args = arguments;
|
||||
var later = function () {
|
||||
timeout = null;
|
||||
if (!immediate) func.apply(context, args);
|
||||
};
|
||||
var callNow = immediate && !timeout;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow) func.apply(context, args);
|
||||
};
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
function share(event) {
|
||||
let data = JSON.stringify(getFormJSON())
|
||||
data = Base64.encodeURI(data);
|
||||
location.hash = "#" + data;
|
||||
try {
|
||||
navigator.clipboard.writeText(location.href);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
function getFormJSON() {
|
||||
return {
|
||||
"language": languageSelect.value,
|
||||
"style": styleSelect.value,
|
||||
"text": textArea.value,
|
||||
"classes": htmlCheckbox.checked,
|
||||
}
|
||||
}
|
||||
if (location.hash) {
|
||||
let json = Base64.decode(location.hash.substring(1))
|
||||
json = JSON.parse(json);
|
||||
textArea.value = json.text;
|
||||
languageSelect.value = json.language;
|
||||
styleSelect.value = json.style;
|
||||
htmlCheckbox.checked = json.classes;
|
||||
update(new Event('change'));
|
||||
}
|
||||
|
||||
function update(event) {
|
||||
fetch("api/render", {
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
cache: 'no-cache',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'X-CSRF-Token': csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
redirect: 'follow',
|
||||
referrer: 'no-referrer',
|
||||
body: JSON.stringify(getFormJSON()),
|
||||
}).then(data => {
|
||||
data.json().then(
|
||||
value => {
|
||||
if (value.language) {
|
||||
languageSelect.value = value.language;
|
||||
}
|
||||
style.innerHTML = "#output { " + value.background + "}";
|
||||
if (htmlCheckbox.checked) {
|
||||
output.innerText = value.html;
|
||||
} else {
|
||||
output.innerHTML = value.html;
|
||||
}
|
||||
}
|
||||
);
|
||||
}).catch(reason => {
|
||||
console.log(reason);
|
||||
});
|
||||
var eventHandler = (event) => update(event);
|
||||
var debouncedEventHandler = debounce(eventHandler, 250);
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
languageSelect.addEventListener('change', eventHandler);
|
||||
styleSelect.addEventListener('change', eventHandler);
|
||||
htmlCheckbox.addEventListener('change', eventHandler);
|
||||
copyButton.addEventListener('click', share);
|
||||
|
||||
var eventHandler = (event) => update(event);
|
||||
var debouncedEventHandler = debounce(eventHandler, 250);
|
||||
|
||||
languageSelect.addEventListener('change', eventHandler);
|
||||
styleSelect.addEventListener('change', eventHandler);
|
||||
htmlCheckbox.addEventListener('change', eventHandler);
|
||||
|
||||
textArea.addEventListener('input', debouncedEventHandler);
|
||||
textArea.addEventListener('change', debouncedEventHandler);
|
||||
textArea.addEventListener('keydown', handleTab);
|
||||
textArea.addEventListener('change', debouncedEventHandler);
|
||||
});
|
||||
|
@ -2,21 +2,12 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Chroma Playground ({{.Version}})</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.min.css"/>
|
||||
<style>
|
||||
textarea {
|
||||
font-family: Consolas, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace;
|
||||
}
|
||||
|
||||
{{CSS "index.css"}}
|
||||
#output {
|
||||
{{.Background}};
|
||||
}
|
||||
|
||||
#output pre {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
<script src="static/index.js?{{.Version}}"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
@ -24,6 +15,7 @@
|
||||
<h1 class="title">Chroma Playground ({{.Version}})</h1>
|
||||
|
||||
<div class="notification">
|
||||
<button class="delete"></button>
|
||||
<a href="https://github.com/alecthomas/chroma">Chroma</a> is a general purpose syntax highlighter in pure Go.
|
||||
It takes source code and other structured text and converts it into syntax highlighted HTML, ANSI-coloured text,
|
||||
etc. Chroma is based heavily on Pygments, and includes translators for Pygments lexers and styles.
|
||||
@ -31,40 +23,54 @@
|
||||
|
||||
<form id="chroma" method="post">
|
||||
{{ .CSRFField }}
|
||||
<div class="columns">
|
||||
<div class="column field">
|
||||
<label class="label">Language</label>
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select name="language" id="language">
|
||||
<option value="" disabled{{if eq "" $.SelectedLanguage}} selected{{end}}>Language</option>
|
||||
{{- range .Languages}}
|
||||
<option value="{{.}}"{{if eq . $.SelectedLanguage}} selected{{end}}>{{.}}</option>
|
||||
{{- end}}
|
||||
</select>
|
||||
|
||||
<nav class="level">
|
||||
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<div class="label">Code</div>
|
||||
</div>
|
||||
<div class="level-item">
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select name="language" id="language">
|
||||
<option value="" disabled{{if eq "" $.SelectedLanguage}} selected{{end}}>Language</option>
|
||||
{{- range .Languages}}
|
||||
<option value="{{.}}"{{if eq . $.SelectedLanguage}} selected{{end}}>{{.}}</option>
|
||||
{{- end}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="level-item">
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select name="style" id="style">
|
||||
<option value="" disabled{{if eq "" $.SelectedStyle}} selected{{end}}>Style</option>
|
||||
{{- range .Styles}}
|
||||
<option value="{{.}}"{{if eq . $.SelectedStyle}} selected{{end}}>{{.}}</option>
|
||||
{{- end}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column field">
|
||||
<label class="label">Style</label>
|
||||
<div class="control">
|
||||
<div class="select">
|
||||
<select name="style" id="style">
|
||||
<option value="" disabled{{if eq "" $.SelectedStyle}} selected{{end}}>Style</option>
|
||||
{{- range .Styles}}
|
||||
<option value="{{.}}"{{if eq . $.SelectedStyle}} selected{{end}}>{{.}}</option>
|
||||
{{- end}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<button name="copy" id="copy" class="button">
|
||||
<span class="icon is-small">
|
||||
<ion-icon name="copy-outline"></ion-icon>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Code</label>
|
||||
<div class="control">
|
||||
<textarea autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" class="textarea" id="text" name="text" rows="25" cols="80"></textarea>
|
||||
<textarea autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" class="textarea" id="text" name="text" rows="15" cols="80"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -82,5 +88,10 @@
|
||||
<div class="field box" id="output"></div>
|
||||
</form>
|
||||
</div>
|
||||
<script type="module">
|
||||
{{JS "index.js"}}
|
||||
</script>
|
||||
<script type="module" src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.esm.js"></script>
|
||||
<script nomodule src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.js"></script>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user