commit 903b090: [WebUI] Add map editor (requires a modern browser)

moisseev moiseev at mezonplus.ru
Tue Jul 14 09:00:07 UTC 2020


Author: moisseev
Date: 2020-07-14 11:47:12 +0300
URL: https://github.com/rspamd/rspamd/commit/903b0905432ca4e160e482c4c9c3157f74762b1a (refs/pull/3434/head)

[WebUI] Add map editor (requires a modern browser)

---
 .stylelintrc.json                   |   3 +-
 interface/css/prism.css             | 161 ++++++++++++++++++++++++++++++++++++
 interface/css/rspamd.css            |  32 ++++++-
 interface/index.html                |   1 +
 interface/js/app/config.js          |  44 ++++++++--
 interface/js/lib/codejar.min.js     |   5 ++
 interface/js/lib/linenumbers.min.js |   5 ++
 interface/js/lib/prism.js           |   5 ++
 interface/js/main.js                |   8 +-
 package.json                        |   3 +-
 10 files changed, 254 insertions(+), 13 deletions(-)

diff --git a/.stylelintrc.json b/.stylelintrc.json
index b25642b8f..be1ea613f 100644
--- a/.stylelintrc.json
+++ b/.stylelintrc.json
@@ -4,7 +4,8 @@
         "**/*.min.css",
         "**/*.min.js",
         "interface/css/d3evolution.css",
-        "interface/css/nprogress.css"
+        "interface/css/nprogress.css",
+        "interface/css/prism.css"
     ],
     "rules": {
         "at-rule-empty-line-before": null,
diff --git a/interface/css/prism.css b/interface/css/prism.css
new file mode 100644
index 000000000..93e23c3b6
--- /dev/null
+++ b/interface/css/prism.css
@@ -0,0 +1,161 @@
+/* PrismJS 1.20.0
+https://prismjs.com/download.html#themes=prism-okaidia&languages=clike&plugins=show-invisibles */
+/**
+ * okaidia theme for JavaScript, CSS and HTML
+ * Loosely based on Monokai textmate theme by http://www.monokai.nl/
+ * @author ocodia
+ */
+
+code[class*="language-"],
+pre[class*="language-"] {
+	color: #f8f8f2;
+	background: none;
+	text-shadow: 0 1px rgba(0, 0, 0, 0.3);
+	font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+	font-size: 1em;
+	text-align: left;
+	white-space: pre;
+	word-spacing: normal;
+	word-break: normal;
+	word-wrap: normal;
+	line-height: 1.5;
+
+	-moz-tab-size: 4;
+	-o-tab-size: 4;
+	tab-size: 4;
+
+	-webkit-hyphens: none;
+	-moz-hyphens: none;
+	-ms-hyphens: none;
+	hyphens: none;
+}
+
+/* Code blocks */
+pre[class*="language-"] {
+	padding: 1em;
+	margin: .5em 0;
+	overflow: auto;
+	border-radius: 0.3em;
+}
+
+:not(pre) > code[class*="language-"],
+pre[class*="language-"] {
+	background: #272822;
+}
+
+/* Inline code */
+:not(pre) > code[class*="language-"] {
+	padding: .1em;
+	border-radius: .3em;
+	white-space: normal;
+}
+
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+	color: #8292a2;
+}
+
+.token.punctuation {
+	color: #f8f8f2;
+}
+
+.token.namespace {
+	opacity: .7;
+}
+
+.token.property,
+.token.tag,
+.token.constant,
+.token.symbol,
+.token.deleted {
+	color: #f92672;
+}
+
+.token.boolean,
+.token.number {
+	color: #ae81ff;
+}
+
+.token.selector,
+.token.attr-name,
+.token.string,
+.token.char,
+.token.builtin,
+.token.inserted {
+	color: #a6e22e;
+}
+
+.token.operator,
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string,
+.token.variable {
+	color: #f8f8f2;
+}
+
+.token.atrule,
+.token.attr-value,
+.token.function,
+.token.class-name {
+	color: #e6db74;
+}
+
+.token.keyword {
+	color: #66d9ef;
+}
+
+.token.regex,
+.token.important {
+	color: #fd971f;
+}
+
+.token.important,
+.token.bold {
+	font-weight: bold;
+}
+.token.italic {
+	font-style: italic;
+}
+
+.token.entity {
+	cursor: help;
+}
+
+.token.tab:not(:empty),
+.token.cr,
+.token.lf,
+.token.space {
+	position: relative;
+}
+
+.token.tab:not(:empty):before,
+.token.cr:before,
+.token.lf:before,
+.token.space:before {
+	color: #808080;
+	opacity: 0.6;
+	position: absolute;
+}
+
+.token.tab:not(:empty):before {
+	content: '\21E5';
+}
+
+.token.cr:before {
+	content: '\240D';
+}
+
+.token.crlf:before {
+	content: '\240D\240A';
+}
+.token.lf:before {
+	content: '\240A';
+}
+
+.token.space:before {
+	content: '\00B7';
+}
+
diff --git a/interface/css/rspamd.css b/interface/css/rspamd.css
index 4330962b0..5fefb4b9a 100644
--- a/interface/css/rspamd.css
+++ b/interface/css/rspamd.css
@@ -285,9 +285,6 @@ table#symbolsTable input[type="number"] {
     background-color: #cddbff;
 }
 
-#map-textarea {
-    height: 360px;
-}
 td.maps-cell {
     vertical-align: middle;
 }
@@ -536,3 +533,32 @@ td.maps-cell {
 #clusterTable tr:last-child td:last-child {
     border-radius: 0 0 calc(.25rem - 1px) 0;
 }
+
+textarea#editor {
+    height: calc(100vh - 178px);
+}
+.codejar-wrap {
+    background: rgb(0, 47, 79);
+    border-radius: 6px;
+    max-height: calc(100vh - 178px);
+    overflow-y: auto;
+}
+.codejar-linenumbers {
+    background: rgba(255, 255, 255, 0.07) !important;
+    bottom: unset !important;
+    color: rgba(120, 120, 120, 1) !important;
+    mix-blend-mode: unset !important;
+    text-align: right;
+    overflow: unset !important;
+}
+.editor {
+    color: #fff;
+    font-family: monospace;
+    font-size: 14px;
+    font-weight: 400;
+    letter-spacing: normal;
+    min-height: 1.5em;
+    resize: unset !important;
+    tab-size: 4;
+    overflow-y: visible !important;
+}
diff --git a/interface/index.html b/interface/index.html
index 312a1956e..c7b941c7e 100644
--- a/interface/index.html
+++ b/interface/index.html
@@ -21,6 +21,7 @@
 	<link rel="stylesheet" type="text/css" href="./css/svg-with-js.min.css">
 	<link rel="stylesheet" type="text/css" href="./css/d3evolution.css">
 	<link rel="stylesheet" type="text/css" href="./css/nprogress.css"/>
+	<link rel="stylesheet" type="text/css" href="./css/prism.css"/>
 	<link href="./css/rspamd.css" rel="stylesheet">
 </head>
 
diff --git a/interface/js/app/config.js b/interface/js/app/config.js
index 986db5748..2c65bfff1 100644
--- a/interface/js/app/config.js
+++ b/interface/js/app/config.js
@@ -22,8 +22,8 @@
  THE SOFTWARE.
  */
 
-define(["jquery"],
-    function ($) {
+define(["jquery", "codejar", "linenumbers", "prism"],
+    function ($, CodeJar, withLineNumbers, Prism) {
         "use strict";
         var ui = {};
 
@@ -155,6 +155,22 @@ define(["jquery"],
         };
 
         ui.setup = function (rspamd) {
+            var jar = {};
+            // CodeJar requires ES6
+            var editor = window.CodeJar &&
+                // Required to restore cursor position
+                (typeof window.getSelection().setBaseAndExtent === "function")
+                ? {
+                    codejar: true,
+                    elt: "div",
+                    class: "editor language-clike",
+                }
+                // Fallback to textarea if the browser does not support ES6
+                : {
+                    elt: "textarea",
+                    class: "form-control map-textarea",
+                };
+
             // Modal form for maps
             $(document).on("click", "[data-toggle=\"modal\"]", function () {
                 var checked_server = rspamd.getSelector("selSrv");
@@ -176,10 +192,19 @@ define(["jquery"],
                         }
                         $("#modalDialog .modal-header").find("[data-fa-i2svg]").addClass(icon);
                         $("#modalTitle").html(item.uri);
-                        $('<textarea id="map-textarea" class="form-control"' + readonly +
+
+                        $("<" + editor.elt + ' id="editor" class="' + editor.class + '"' + readonly +
                             ' data-id="' + item.map + '">' +
                             text +
-                            "</textarea>").appendTo("#modalBody");
+                            "</" + editor.elt + ">").appendTo("#modalBody");
+
+                        if (editor.codejar) {
+                            jar = new CodeJar(
+                                document.querySelector("#editor"),
+                                withLineNumbers(Prism.highlightElement)
+                            );
+                        }
+
                         $("#modalDialog").modal("show");
                     },
                     errorMessage: "Cannot receive maps data",
@@ -188,7 +213,12 @@ define(["jquery"],
                 return false;
             });
             $("#modalDialog").on("hidden.bs.modal", function () {
-                $("#map-textarea").remove();
+                if (editor.codejar) {
+                    jar.destroy();
+                    $(".codejar-wrap").remove();
+                } else {
+                    $("#editor").remove();
+                }
             });
 
             $("#saveActionsBtn").on("click", function () {
@@ -207,10 +237,10 @@ define(["jquery"],
                     errorMessage: "Save map error",
                     method: "POST",
                     headers: {
-                        Map: $("#map-textarea").data("id"),
+                        Map: $("#editor").data("id"),
                     },
                     params: {
-                        data: $("#map-textarea").val(),
+                        data: editor.codejar ? jar.toString() : $("#editor").val(),
                         dataType: "text",
                     },
                     server: server
diff --git a/interface/js/lib/codejar.min.js b/interface/js/lib/codejar.min.js
new file mode 100644
index 000000000..bbd637879
--- /dev/null
+++ b/interface/js/lib/codejar.min.js
@@ -0,0 +1,5 @@
+/*!
+ * CodeJar 3.1.0 (https://github.com/antonmedv/codejar)
+ * Copyright (c) 2020, Anton Medvedev, MIT
+ */
+function CodeJar(t,e,n={}){const o=Object.assign({tab:"\t",indentOn:/{$/},n);let r,s,i=[],c=[],d=-1,a=!1,f=navigator.userAgent.toLowerCase().indexOf("firefox")>-1;t.setAttribute("contentEditable",f?"true":"plaintext-only"),t.setAttribute("spellcheck","false"),t.style.outline="none",t.style.overflowWrap="break-word",t.style.overflowY="auto",t.style.resize="vertical",t.style.whiteSpace="pre-wrap",e(t);const l=E(()=>{const n=y();e(t),m(n)},30);let u=!1;const p=t=>!T(t)&&!v(t)&&"Meta"!==t.key&&"Control"!==t.key&&"Alt"!==t.key&&!t.key.startsWith("Arrow"),g=E(t=>{p(t)&&(O(),u=!1)},300),h=(e,n)=>{i.push([e,n]),t.addEventListener(e,n)};function y(){const e=window.getSelection(),n={start:0,end:0,dir:void 0};return C(t,t=>{if(t===e.anchorNode&&t===e.focusNode)return n.start+=e.anchorOffset,n.end+=e.focusOffset,n.dir=e.anchorOffset<=e.focusOffset?"->":"<-","stop";if(t===e.anchorNode){if(n.start+=e.anchorOffset,n.dir)return"stop";n.dir="->"}else if(t===e.focusNode){if(n.end+=e.focusOffset,n.dir)return"stop";n.dir="<-"}t.nodeType===Node.TEXT_NODE&&("->"!=n.dir&&(n.start+=t.nodeValue.length),"<-"!=n.dir&&(n.end+=t.nodeValue.length))}),n}function m(e){const n=window.getSelection();let o,r,s=0,i=0;if(e.dir||(e.dir="->"),e.start<0&&(e.start=0),e.end<0&&(e.end=0),"<-"==e.dir){const{start:t,end:n}=e;e.start=n,e.end=t}let c=0;C(t,t=>{if(t.nodeType!==Node.TEXT_NODE)return;const n=(t.nodeValue||"").length;if(c+n>=e.start&&(o||(o=t,s=e.start-c),c+n>=e.end))return r=t,i=e.end-c,"stop";c+=n}),o||(o=t),r||(r=t),"<-"==e.dir&&([o,s,r,i]=[r,i,o,s]),n.setBaseAndExtent(o,s,r,i)}function b(){const e=window.getSelection().getRangeAt(0),n=document.createRange();return n.selectNodeContents(t),n.setEnd(e.startContainer,e.startOffset),n.toString()}function w(){const e=window.getSelection().getRangeAt(0),n=document.createRange();return n.selectNodeContents(t),n.setStart(e.endContainer,e.endOffset),n.toString()}function O(){if(!a)return;const e=t.innerHTML,n=y(),o=c[d];if(o&&o.html===e&&o.pos.start===n.start&&o.pos.end===n.end)return;c[++d]={html:e,pos:n},c.splice(d+1);d>300&&(d=300,c.splice(0,1))}function C(t,e){const n=[];t.firstChild&&n.push(t.firstChild);let o=n.pop();for(;o&&"stop"!==e(o);)o.nextSibling&&n.push(o.nextSibling),o.firstChild&&n.push(o.firstChild),o=n.pop()}function k(t){return t.metaKey||t.ctrlKey}function T(t){return k(t)&&!t.shiftKey&&"KeyZ"===t.code}function v(t){return k(t)&&t.shiftKey&&"KeyZ"===t.code}function x(t){t=t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),document.execCommand("insertHTML",!1,t)}function E(t,e){let n=0;return(...o)=>{clearTimeout(n),n=window.setTimeout(()=>t(...o),e)}}function S(t){let e=t.length-1;for(;e>=0&&"\n"!==t[e];)e--;let n=++e;for(;n<t.length&&/[ \t]/.test(t[n]);)n++;return[t.substring(e,n)||"",e,n]}function N(){return t.textContent||""}function A(t){t.preventDefault()}return h("keydown",e=>{e.defaultPrevented||(s=N(),function(t){if("Enter"===t.key){const e=b(),n=w();let[r]=S(e),s=r;if(o.indentOn.test(e)&&(s+=o.tab),f?(A(t),x("\n"+s)):s.length>0&&(A(t),x("\n"+s)),s!==r&&"}"===n[0]){const t=y();x("\n"+r),m(t)}}}(e),function(t){if("Tab"===t.key)if(A(t),t.shiftKey){const t=b();let[e,n]=S(t);if(e.length>0){const t=y(),r=Math.min(o.tab.length,e.length);m({start:n,end:n+r}),document.execCommand("delete"),t.start-=r,t.end-=r,m(t)}}else x(o.tab)}(e),function(t){const e="([{'\"",n=")]}'\"",o=w();if(n.includes(t.key)&&o.substr(0,1)===t.key){const e=y();A(t),e.start=++e.end,m(e)}else if(e.includes(t.key)){const o=y();A(t);const r=t.key+n[e.indexOf(t.key)];x(r),o.start=++o.end,m(o)}}(e),function(e){if(T(e)){A(e);const n=c[--d];n&&(t.innerHTML=n.html,m(n.pos)),d<0&&(d=0)}if(v(e)){A(e);const n=c[++d];n&&(t.innerHTML=n.html,m(n.pos)),d>=c.length&&d--}}(e),p(e)&&!u&&(O(),u=!0))}),h("keyup",t=>{t.defaultPrevented||t.isComposing||(s!==N()&&l(),g(t),r&&r(N()))}),h("focus",t=>{a=!0}),h("blur",t=>{a=!1}),h("paste",n=>{O(),function(n){A(n);const o=(n.originalEvent||n).clipboardData.getData("text/plain"),r=y();x(o),e(t),m({start:r.end+o.length,end:r.end+o.length})}(n),O(),r&&r(N())}),{updateOptions(t){t=Object.assign(Object.assign({},t),t)},updateCode(n){t.textContent=n,e(t)},onUpdate(t){r=t},toString:N,destroy(){for(let[e,n]of i)t.removeEventListener(e,n)}}}
\ No newline at end of file
diff --git a/interface/js/lib/linenumbers.min.js b/interface/js/lib/linenumbers.min.js
new file mode 100644
index 000000000..1b8312009
--- /dev/null
+++ b/interface/js/lib/linenumbers.min.js
@@ -0,0 +1,5 @@
+/*!
+ * CodeJar 3.1.0 helper: lineNumbers (https://github.com/antonmedv/codejar)
+ * Copyright (c) 2020, Anton Medvedev, MIT
+ */
+function withLineNumbers(e,t={}){const o=Object.assign({class:"codejar-linenumbers",wrapClass:"codejar-wrap",width:"35px",backgroundColor:"rgba(128, 128, 128, 0.15)",color:""},t);let l;return function(t){e(t),l||(l=init(t,o));const n=(t.textContent||"").replace(/\n+$/,"\n").split("\n").length+1;let s="";for(let e=1;e<n;e++)s+=`${e}\n`;l.innerText=s}}function init(e,t){const o=getComputedStyle(e),l=document.createElement("div");l.className=t.wrapClass,l.style.position="relative";const n=document.createElement("div");return n.className=t.class,l.appendChild(n),n.style.position="absolute",n.style.top="0px",n.style.left="0px",n.style.bottom="0px",n.style.width=t.width,n.style.overflow="hidden",n.style.backgroundColor=t.backgroundColor,n.style.color=t.color||o.color,n.style.setProperty("mix-blend-mode","difference"),n.style.fontFamily=o.fontFamily,n.style.fontSize=o.fontSize,n.style.lineHeight=o.lineHeight,n.style.paddingTop=o.paddingTop,n.style.paddingLeft=o.paddingLeft,n.style.borderTopLeftRadius=o.borderTopLeftRadius,n.style.borderBottomLeftRadius=o.borderBottomLeftRadius,e.style.paddingLeft=`calc(${t.width} + ${n.style.paddingLeft})`,e.style.whiteSpace="pre",e.parentNode.insertBefore(l,e),l.appendChild(e),n}
\ No newline at end of file
diff --git a/interface/js/lib/prism.js b/interface/js/lib/prism.js
new file mode 100644
index 000000000..dc578925a
--- /dev/null
+++ b/interface/js/lib/prism.js
@@ -0,0 +1,5 @@
+/* PrismJS 1.20.0
+https://prismjs.com/download.html#themes=prism-okaidia&languages=clike&plugins=show-invisibles */
+var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(u){var c=/\blang(?:uage)?-([\w-]+)\b/i,n=0,M={manual:u.Prism&&u.Prism.manual,disableWorkerMessageHandler:u.Prism&&u.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof W?new W(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/</g,"<").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).slice(8,-1)},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++n}),e.__id},clone:function t(e,r){var a,n;switch(r=r||{},M.util.type(e)){case"Object":if(n=M.util.objId(e),r[n])return r[n];for(var i in a={},r[n]=a,e)e.hasOwnProperty(i)&&(a[i]=t(e[i],r));return a;case"Array":return n=M.util.objId(e),r[n]?r[n]:(a=[],r[n]=a,e.forEach(function(e,n){a[n]=t(e,r)}),a);default:return e}},getLanguage:function(e){for(;e&&!c.test(e.className);)e=e.parentElement;return e?(e.className.match(c)||[,"none"])[1].toLowerCase():"none"},currentScript:function(){if("undefined"==typeof document)return null;if("currentScript"in document)return document.currentScript;try{throw new Error}catch(e){var n=(/at [^(\r\n]*\((.*):.+:.+\)$/i.exec(e.stack)||[])[1];if(n){var t=document.getElementsByTagName("script");for(var r in t)if(t[r].src==n)return t[r]}return null}},isActive:function(e,n,t){for(var r="no-"+n;e;){var a=e.classList;if(a.contains(n))return!0;if(a.contains(r))return!1;e=e.parentElement}return!!t}},languages:{extend:function(e,n){var t=M.util.clone(M.languages[e]);for(var r in n)t[r]=n[r];return t},insertBefore:function(t,e,n,r){var a=(r=r||M.languages)[t],i={};for(var l in a)if(a.hasOwnProperty(l)){if(l==e)for(var o in n)n.hasOwnProperty(o)&&(i[o]=n[o]);n.hasOwnProperty(l)||(i[l]=a[l])}var s=r[t];return r[t]=i,M.languages.DFS(M.languages,function(e,n){n===s&&e!=t&&(this[e]=i)}),i},DFS:function e(n,t,r,a){a=a||{};var i=M.util.objId;for(var l in n)if(n.hasOwnProperty(l)){t.call(n,l,n[l],r||l);var o=n[l],s=M.util.type(o);"Object"!==s||a[i(o)]?"Array"!==s||a[i(o)]||(a[i(o)]=!0,e(o,t,l,a)):(a[i(o)]=!0,e(o,t,null,a))}}},plugins:{},highlightAll:function(e,n){M.highlightAllUnder(document,e,n)},highlightAllUnder:function(e,n,t){var r={callback:t,container:e,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};M.hooks.run("before-highlightall",r),r.elements=Array.prototype.slice.apply(r.container.querySelectorAll(r.selector)),M.hooks.run("before-all-elements-highlight",r);for(var a,i=0;a=r.elements[i++];)M.highlightElement(a,!0===n,r.callback)},highlightElement:function(e,n,t){var r=M.util.getLanguage(e),a=M.languages[r];e.className=e.className.replace(c,"").replace(/\s+/g," ")+" language-"+r;var i=e.parentElement;i&&"pre"===i.nodeName.toLowerCase()&&(i.className=i.className.replace(c,"").replace(/\s+/g," ")+" language-"+r);var l={element:e,language:r,grammar:a,code:e.textContent};function o(e){l.highlightedCode=e,M.hooks.run("before-insert",l),l.element.innerHTML=l.highlightedCode,M.hooks.run("after-highlight",l),M.hooks.run("complete",l),t&&t.call(l.element)}if(M.hooks.run("before-sanity-check",l),!l.code)return M.hooks.run("complete",l),void(t&&t.call(l.element));if(M.hooks.run("before-highlight",l),l.grammar)if(n&&u.Worker){var s=new Worker(M.filename);s.onmessage=function(e){o(e.data)},s.postMessage(JSON.stringify({language:l.language,code:l.code,immediateClose:!0}))}else o(M.highlight(l.code,l.grammar,l.language));else o(M.util.encode(l.code))},highlight:function(e,n,t){var r={code:e,grammar:n,language:t};return M.hooks.run("before-tokenize",r),r.tokens=M.tokenize(r.code,r.grammar),M.hooks.run("after-tokenize",r),W.stringify(M.util.encode(r.tokens),r.language)},tokenize:function(e,n){var t=n.rest;if(t){for(var r in t)n[r]=t[r];delete n.rest}var a=new i;return I(a,a.head,e),function e(n,t,r,a,i,l){for(var o in r)if(r.hasOwnProperty(o)&&r[o]){var s=r[o];s=Array.isArray(s)?s:[s];for(var u=0;u<s.length;++u){if(l&&l.cause==o+","+u)return;var c=s[u],g=c.inside,f=!!c.lookbehind,h=!!c.greedy,d=0,v=c.alias;if(h&&!c.pattern.global){var p=c.pattern.toString().match(/[imsuy]*$/)[0];c.pattern=RegExp(c.pattern.source,p+"g")}for(var m=c.pattern||c,y=a.next,k=i;y!==t.tail&&!(l&&k>=l.reach);k+=y.value.length,y=y.next){var b=y.value;if(t.length>n.length)return;if(!(b instanceof W)){var x=1;if(h&&y!=t.tail.prev){m.lastIndex=k;var w=m.exec(n);if(!w)break;var A=w.index+(f&&w[1]?w[1].length:0),P=w.index+w[0].length,S=k;for(S+=y.value.length;S<=A;)y=y.next,S+=y.value.length;if(S-=y.value.length,k=S,y.value instanceof W)continue;for(var E=y;E!==t.tail&&(S<P||"string"==typeof E.value);E=E.next)x++,S+=E.value.length;x--,b=n.slice(k,S),w.index-=k}else{m.lastIndex=0;var w=m.exec(b)}if(w){f&&(d=w[1]?w[1].length:0);var A=w.index+d,O=w[0].slice(d),P=A+O.length,L=b.slice(0,A),N=b.slice(P),j=k+b.length;l&&j>l.reach&&(l.reach=j);var C=y.prev;L&&(C=I(t,C,L),k+=L.length),z(t,C,x);var _=new W(o,g?M.tokenize(O,g):O,v,O);y=I(t,C,_),N&&I(t,y,N),1<x&&e(n,t,r,y.prev,k,{cause:o+","+u,reach:j})}}}}}}(e,a,n,a.head,0),function(e){var n=[],t=e.head.next;for(;t!==e.tail;)n.push(t.value),t=t.next;return n}(a)},hooks:{all:{},add:function(e,n){var t=M.hooks.all;t[e]=t[e]||[],t[e].push(n)},run:function(e,n){var t=M.hooks.all[e];if(t&&t.length)for(var r,a=0;r=t[a++];)r(n)}},Token:W};function W(e,n,t,r){this.type=e,this.content=n,this.alias=t,this.length=0|(r||"").length}function i(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function I(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function z(e,n,t){for(var r=n.next,a=0;a<t&&r!==e.tail;a++)r=r.next;(n.next=r).prev=n,e.length-=a}if(u.Prism=M,W.stringify=function n(e,t){if("string"==typeof e)return e;if(Array.isArray(e)){var r="";return e.forEach(function(e){r+=n(e,t)}),r}var a={type:e.type,content:n(e.content,t),tag:"span",classes:["token",e.type],attributes:{},language:t},i=e.alias;i&&(Array.isArray(i)?Array.prototype.push.apply(a.classes,i):a.classes.push(i)),M.hooks.run("wrap",a);var l="";for(var o in a.attributes)l+=" "+o+'="'+(a.attributes[o]||"").replace(/"/g,""")+'"';return"<"+a.tag+' class="'+a.classes.join(" ")+'"'+l+">"+a.content+"</"+a.tag+">"},!u.document)return u.addEventListener&&(M.disableWorkerMessageHandler||u.addEventListener("message",function(e){var n=JSON.parse(e.data),t=n.language,r=n.code,a=n.immediateClose;u.postMessage(M.highlight(r,M.languages[t],t)),a&&u.close()},!1)),M;var e=M.util.currentScript();function t(){M.manual||M.highlightAll()}if(e&&(M.filename=e.src,e.hasAttribute("data-manual")&&(M.manual=!0)),!M.manual){var r=document.readyState;"loading"===r||"interactive"===r&&e&&e.defer?document.addEventListener("DOMContentLoaded",t):window.requestAnimationFrame?window.requestAnimationFrame(t):window.setTimeout(t,16)}return M}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
+Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|interface|extends|implements|trait|instanceof|new)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/};
+!function(){if(("undefined"==typeof self||self.Prism)&&("undefined"==typeof global||global.Prism)){var i={tab:/\t/,crlf:/\r\n/,lf:/\n/,cr:/\r/,space:/ /};Prism.hooks.add("before-highlight",function(r){s(r.grammar)})}function f(r,e){var i=r[e];switch(Prism.util.type(i)){case"RegExp":var a={};r[e]={pattern:i,inside:a},s(a);break;case"Array":for(var n=0,t=i.length;n<t;n++)f(i,n);break;default:s(a=i.inside||(i.inside={}))}}function s(r){if(r&&!r.tab){for(var e in i)i.hasOwnProperty(e)&&(r[e]=i[e]);for(var e in r)r.hasOwnProperty(e)&&!i[e]&&("rest"===e?s(r.rest):f(r,e))}}}();
diff --git a/interface/js/main.js b/interface/js/main.js
index e0f8fe293..7c6bb6851 100644
--- a/interface/js/main.js
+++ b/interface/js/main.js
@@ -7,21 +7,27 @@ requirejs.config({
         jquery: "jquery-3.5.1.min",
         visibility: "visibility.min",
         bootstrap: "bootstrap.bundle.min",
+        codejar: "codejar.min",
         d3: "d3.min",
         d3evolution: "d3evolution.min",
         d3pie: "d3pie.min",
         fontawesome: "fontawesome.min",
         fontawesome_solid: "solid.min",
         footable: "footable.min",
+        linenumbers: "linenumbers.min",
         nprogress: "nprogress.min",
+        prism: "prism",
         stickytabs: "jquery.stickytabs.min"
     },
     shim: {
+        codejar: {exports: "CodeJar", deps:["linenumbers"]},
         bootstrap: {exports:"bootstrap", deps:["jquery"]},
-        d3pie: {exports:"d3pie", deps:["d3.global", "jquery"]},
         d3evolution: {exports:"D3Evolution", deps:["d3", "jquery"]},
+        d3pie: {exports:"d3pie", deps:["d3.global", "jquery"]},
         fontawesome: {exports: "FontAwesome", deps:["fontawesome_solid"]},
         footable: {deps:["bootstrap", "jquery"]},
+        linenumbers: {exports: "withLineNumbers", deps:["prism"]},
+        prism: {exports: "Prism"},
         stickytabs: {deps:["jquery"]}
     },
     waitSeconds: 30,
diff --git a/package.json b/package.json
index f22abf878..cda030fb0 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,7 @@
         "stylelint-config-standard": "*"
     },
     "eslintIgnore": [
-        "*.min.js"
+        "*.min.js",
+        "prism.js"
     ]
 }


More information about the Commits mailing list