Skip to content

Commit

Permalink
netStack.js added
Browse files Browse the repository at this point in the history
  • Loading branch information
ZsharE committed Aug 29, 2024
1 parent aa083ca commit 9081d09
Show file tree
Hide file tree
Showing 10 changed files with 364 additions and 15 deletions.
6 changes: 6 additions & 0 deletions assets/css/dark-theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@
color: var(--elmahio-dark-href-color);
border-color: #282828;
}
[data-bs-theme=darkmode] .hljs-wrapper.netstack pre code {
border: 1px solid #282828;
}
[data-bs-theme=darkmode] pre.request-method {
background-color: #333;
color: var(--elmahio-dark-href-color);
Expand All @@ -194,6 +197,9 @@
[data-bs-theme=darkmode] .fullscreen-code.is-open {
background: #333;
}
[data-bs-theme=darkmode] .fullscreen-code.is-open .hljs-wrapper.netstack pre code {
border: none;
}
[data-bs-theme=darkmode] code {
color: #8FBCBB;
background-color: #333;
Expand Down
36 changes: 36 additions & 0 deletions assets/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,38 @@ code.powershell.hljs:not(.language-yml) {
z-index: 10;
display: block;
}
.hljs-wrapper.netstack pre, .hljs-wrapper.netstack code {
background-color: #333;
color: #ffffff;
border-radius: 4px;
}
.hljs-wrapper.netstack code {
border: 1px solid #333;
}
.hljs-wrapper.netstack .st-type {
color: #0a8472;
font-weight: bolder;
}
.hljs-wrapper.netstack .st-method {
color: #70c9ba;
font-weight: bolder;
}
.hljs-wrapper.netstack .st-frame-params {
color: #ffffff;
font-weight: normal;
}
.hljs-wrapper.netstack .st-param-type {
color: #0a8472;
}
.hljs-wrapper.netstack .st-param-name {
color: #ffffff;
}
.hljs-wrapper.netstack .st-file {
color: #f8b068;
}
.hljs-wrapper.netstack .st-line {
color: #ff4f68;
}

.btn-fullscreen-mode {
display: inline-block;
Expand Down Expand Up @@ -1215,6 +1247,10 @@ pre.request-method code .badge {
height: 29px;
}

.is-netstack {
background: #333 !important;
}

.is-console {
background: #0c0c0c !important;
}
Expand Down
4 changes: 2 additions & 2 deletions assets/css/style.min.css

Large diffs are not rendered by default.

33 changes: 26 additions & 7 deletions assets/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,24 +167,38 @@ window.addEventListener('scroll', function() {
// Initialize highlight JS
function initHighlight(wrapperHighlight) {
hljs.configure({languages: []});
hljs.highlightAll();
//hljs.highlightAll();
document.querySelectorAll('pre code').forEach((el) => {
if (el.classList.contains('language-nohighlight')) {
el.classList.add('hljs');
} else if (el.classList.contains('language-stacktrace')) {
new netStack(el, {
prettyprint: true
});
el.classList.add('hljs');
} else {
hljs.highlightElement(el);
}
});

wrapperHighlight();
document.body.insertAdjacentHTML('beforeend', '<div class="fullscreen-code js-fullscreen-code"></div>');
}

// Wrap highlight
function wrapperHighlight() {
const hljsElements = document.querySelectorAll('.hljs:not(.language-console), .language-nohighlight');
const hljsElements = document.querySelectorAll('.hljs:not(.language-console), .language-nohighlight, .language-stacktrace');
hljsElements.forEach((elem) => {
const wrapper = document.createElement('div');
wrapper.className = 'hljs-wrapper';
elem.parentElement.parentNode.insertBefore(wrapper, elem.parentElement);
wrapper.appendChild(elem.parentElement);

// No highlight, but still an hljs component
if (elem.classList.contains('language-nohighlight')) {
elem.classList.add('hljs');
if (elem.classList.contains('language-stacktrace')) {
wrapper.classList.add('netstack');
}

elem.parentElement.parentNode.insertBefore(wrapper, elem.parentElement);
wrapper.appendChild(elem.parentElement);
});

const hljsWrappers = document.querySelectorAll('.hljs-wrapper');
Expand Down Expand Up @@ -212,7 +226,7 @@ function addFullscreenMode() {
e.stopPropagation();
if (isFullScreenModeCodeOn) {
document.body.style.overflow = '';
fullScreenWindow.classList.remove('is-open', 'is-console');
fullScreenWindow.classList.remove('is-open', 'is-console', 'is-netstack');
fullScreenWindow.innerHTML = '';
isFullScreenModeCodeOn = false;
} else {
Expand All @@ -233,6 +247,11 @@ function addFullscreenMode() {
fullScreenWindow.appendChild(codeBlock);
fullScreenWindow.classList.add('is-open', 'is-console');
} else {
// netstack
if (e.currentTarget.parentNode.parentNode.classList.contains('netstack')) {
fullScreenWindow.classList.add('is-open', 'is-netstack');
}

const codeBlock = e.currentTarget.parentNode.parentNode.cloneNode(true);
codeBlock.querySelector('.btn-fullscreen-mode').addEventListener('click', (e) => openCloseCodeWindow(e));
fullScreenWindow.appendChild(codeBlock);
Expand Down
16 changes: 11 additions & 5 deletions assets/js/main.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/js/main.min.js.map

Large diffs are not rendered by default.

257 changes: 257 additions & 0 deletions assets/js/netstack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
/*!
* netStack v2.0.0
* A simple and easy JavaScript library for highlighting .NET stack traces
* License: Apache 2
* Author: https://elmah.io
*/

(function(root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define([], factory);
} else if (typeof module === 'object' && module.exports) {
// Node, CommonJS-like
module.exports = factory();
} else {
// Browser globals (root is window)
root.netStack = factory();
}
}(typeof self !== 'undefined' ? self : this, function() {

function netStack(element, options) {
if (typeof document !== 'undefined') {
if (typeof element === 'string') {
this.element = document.querySelector(element);
} else if (element instanceof HTMLElement) {
this.element = element;
} else {
throw new Error('The element parameter must be a selector string or an HTMLElement.');
}
} else {
throw new Error('netStack requires a DOM environment');
}

// Default values for classes
this.settings = extend({
prettyprint: false,
frame: 'st-frame',
type: 'st-type',
method: 'st-method',
paramsList: 'st-frame-params',
paramType: 'st-param-type',
paramName: 'st-param-name',
file: 'st-file',
line: 'st-line'
}, options);

this.languages = [
{ name: 'english', at: 'at', in: 'in', line: 'line' },
{ name: 'danish', at: 'ved', in: 'i', line: 'linje' },
{ name: 'german', at: 'bei', in: 'in', line: 'Zeile' },
{ name: 'russian', at: 'в', in: 'в', line: 'строка' },
{ name: 'chinese', at: '在', in: '位置', line: '行号' }
];

this.init();
}

function extend() {
for (var i = 1; i < arguments.length; i++) {
for (var key in arguments[i]) {
if (arguments[i].hasOwnProperty(key)) {
arguments[0][key] = arguments[i][key];
}
}
}
return arguments[0];
}

netStack.prototype.search = function(nameKey, myArray) {
for (var i = 0; i < myArray.length; i++) {
if (myArray[i].name === nameKey) {
return myArray[i];
}
}
return null;
};

netStack.prototype.replacer = function(args, at_language) {
if (args[0].substring(0).match(/(-{3}&gt;)/)) {
return '\r\n ' + args[0];
} else if (args[0].substring(0).match(/(-{3})/)) {
return '\r\n ' + args[0];
} else {
return '\r\n ' + at_language + ' ' + args[2] + '(' + args[3] + ')';
}
};

netStack.prototype.formatException = function(exceptionMessage, at_language) {
var result = exceptionMessage || '';
var searchReplaces = [
{
find: new RegExp('(-{3}&gt;\\s)(.*?)(?=\\s-{3}|(\\s)+' + at_language + ')', 'g'),
repl: null
},
{
find: /(-{3}\s)(.*?)(-{3})/gm,
repl: null
},
{
find: new RegExp('(\\s)' + at_language + ' ([^-:]*?)\\((.*?)\\)', 'g'),
repl: null
}
];

var self = this;
searchReplaces.forEach(function(item) {
if (item.repl == null) {
result = result.replace(item.find, function() {
return self.replacer(arguments, at_language);
});
} else {
result = result.replace(item.find, item.repl);
}
});
return result;
};

netStack.prototype.init = function() {
// Get the stacktrace, sanitize it, and split it into lines
var stacktrace = this.element.textContent,
sanitizedStack = stacktrace.replace(/</g, '&lt;').replace(/>/g, '&gt;'),
lines = sanitizedStack.split('\n'),
lang = '',
clone = '';

// look for the language
for (var i = 0; i < lines.length; ++i) {
if (lang === '') {
var regexes = {
english: /\s+at .*\)/,
danish: /\s+ved .*\)/,
german: /\s+bei .*\)/,
russian: /\s+в .*\)/,
chinese: /\s+在 .*\)/
};

for (var key in regexes) {
if (regexes[key].test(lines[i])) {
lang = key;
break;
}
}
}
}

if (lang === '') return;

var selectedLanguage = this.search(lang, this.languages);
this.language = selectedLanguage.name;

// Pritty print result if is set to true
if (this.settings.prettyprint) {
sanitizedStack = this.formatException(sanitizedStack, selectedLanguage.at);
lines = sanitizedStack.split('\n');
}

for (var i = 0; i < lines.length; ++i) {
var li = lines[i],
hli = new RegExp('(\\S*)' + selectedLanguage.at + ' .*\\)');

if (hli.test(lines[i])) {

// Frame
var regFrame = new RegExp('(\\S*)' + selectedLanguage.at + ' .*?\\)'),
partsFrame = String(regFrame.exec(lines[i]));

if (partsFrame.substring(partsFrame.length - 1) == ',') {
partsFrame = partsFrame.slice(0, -1);
}

partsFrame = partsFrame.replace(selectedLanguage.at + ' ', '');

// Frame -> ParameterList
var regParamList = /\(.*\)/,
partsParamList = String(regParamList.exec(lines[i]));

// Frame -> Params
var partsParams = partsParamList.replace('(', '').replace(')', ''),
arrParams = partsParams.split(', '),
stringParam = '';

for (var x = 0; x < arrParams.length; ++x) {
var param = arrParams[x].split(' '),
paramType = param[0],
paramName = param[1];

if (param[0] !== "null" && param[0] !== '') {
var theParam = '<span class="' + this.settings.paramType + '">' + paramType + '</span>' + ' ' + '<span class="' + this.settings.paramName + '">' + paramName + '</span>';
stringParam += String(theParam) + ', ';
}
}

stringParam = stringParam.replace(/,\s*$/, "");
stringParam = '<span class="' + this.settings.paramsList + '">' + '(' + stringParam + ')' + '</span>';

// Frame -> Type & Method
var partsTypeMethod = partsFrame.replace(partsParamList, ''),
arrTypeMethod = partsTypeMethod.split('.'),
method = arrTypeMethod.pop(),
type = partsTypeMethod.replace('.' + method, ''),
stringTypeMethod = '<span class="' + this.settings.type + '">' + type + '</span>.' + '<span class="' + this.settings.method + '">' + method + '</span>';

// Construct Frame
var newPartsFrame = partsFrame.replace(partsParamList, stringParam).replace(partsTypeMethod, stringTypeMethod);

// Line
var regLine = new RegExp('\\b:' + selectedLanguage.line + ' \\d+'),
partsLine = String(regLine.exec(lines[i]));

partsLine = partsLine.replace(':', '').trim();

var fileLi = li.replace(selectedLanguage.at + " " + partsFrame, '').trim();

// File => (!) text requires multiline to exec regex, otherwise it will return null.
var regFile = new RegExp(selectedLanguage.in + '\\s.*$', 'm'),
partsFile = String(regFile.exec(fileLi));

partsFile = partsFile.replace(selectedLanguage.in + ' ', '').replace(':' + partsLine, '').replace('&lt;---', '');

li = li.replace(partsFrame, '<span class="' + this.settings.frame + '">' + newPartsFrame + '</span>')
.replace(partsFile, '<span class="' + this.settings.file + '">' + partsFile + '</span>')
.replace(partsLine, '<span class="' + this.settings.line + '">' + partsLine + '</span>');

li = li.replace(/&lt;/g, '<span>&lt;</span>').replace(/&gt;/g, '<span>&gt;</span>');

if (lines.length - 1 == i) {
clone += li;
} else {
clone += li + '\n';
}
} else {
if ((lines[i].trim()).length) {
li = lines[i];

if (lines.length - 1 == i) {
clone += li;
} else {
clone += li + '\n';
}
}
}
}

this.element.innerHTML = clone;
};

netStack.prototype.getJSON = function() {
return JSON.stringify(this.element.innerHTML);
};

netStack.prototype.getLanguage = function() {
return this.language;
};

return netStack;

}));
Loading

0 comments on commit 9081d09

Please sign in to comment.