|
1 | 1 | /*!
|
2 |
| - * netStack v2.1.0 |
| 2 | + * netStack v2.1.1 |
3 | 3 | * A simple and easy JavaScript library for highlighting .NET stack traces
|
4 | 4 | * License: Apache 2
|
5 | 5 | * Author: https://elmah.io
|
|
34 | 34 | // Default values for classes
|
35 | 35 | this.settings = extend({
|
36 | 36 | prettyprint: false,
|
| 37 | + multilanguage: false, |
37 | 38 | frame: 'st-frame',
|
38 | 39 | type: 'st-type',
|
39 | 40 | method: 'st-method',
|
|
76 | 77 | return null;
|
77 | 78 | };
|
78 | 79 |
|
| 80 | + netStack.prototype.allEqual = arr => arr.every(val => val === arr[0]); |
| 81 | + |
79 | 82 | netStack.prototype.replacer = function(args, at_language) {
|
80 | 83 | if (args[0].substring(0).match(/(-{3}>)/)) {
|
81 | 84 | return '\r\n ' + args[0];
|
|
86 | 89 | }
|
87 | 90 | };
|
88 | 91 |
|
89 |
| - netStack.prototype.formatException = function(exceptionMessage, at_language) { |
| 92 | + netStack.prototype.formatException = function(exceptionMessage, at_language, loop, position) { |
90 | 93 | var result = exceptionMessage || '';
|
91 | 94 | var searchReplaces = [
|
92 | 95 | {
|
|
104 | 107 | ];
|
105 | 108 |
|
106 | 109 | var self = this;
|
107 |
| - searchReplaces.forEach(function(item) { |
| 110 | + searchReplaces.forEach(function(item, index) { |
| 111 | + // multilanguage, skip --- lines |
| 112 | + if (loop === true && position > 0 && index === 1) { |
| 113 | + return; |
| 114 | + } |
| 115 | + |
108 | 116 | if (item.repl == null) {
|
109 | 117 | result = result.replace(item.find, function() {
|
110 | 118 | return self.replacer(arguments, at_language);
|
|
116 | 124 | return result;
|
117 | 125 | };
|
118 | 126 |
|
| 127 | + netStack.prototype.detectLanguagesInOrder = function(input, regexes) { |
| 128 | + let matches = []; |
| 129 | + |
| 130 | + for (let [language, regex] of Object.entries(regexes)) { |
| 131 | + let match; |
| 132 | + while ((match = regex.exec(input)) !== null) { |
| 133 | + matches.push({ language, index: match.index }); |
| 134 | + } |
| 135 | + regex.lastIndex = 0; |
| 136 | + } |
| 137 | + |
| 138 | + matches.sort((a, b) => a.index - b.index); |
| 139 | + |
| 140 | + return matches.map((match) => match.language); |
| 141 | + }; |
| 142 | + |
119 | 143 | netStack.prototype.init = function() {
|
120 | 144 | // Get the stacktrace, sanitize it, and split it into lines
|
121 | 145 | var stacktrace = this.element.textContent,
|
|
124 | 148 | lang = '',
|
125 | 149 | clone = '';
|
126 | 150 |
|
127 |
| - // look for the language |
128 |
| - for (var i = 0; i < lines.length; ++i) { |
129 |
| - if (lang === '') { |
130 |
| - var regexes = { |
131 |
| - english: /\s+at .*\)/, |
132 |
| - danish: /\s+ved .*\)/, |
133 |
| - german: /\s+bei .*\)/, |
134 |
| - spanish: /\s+en .*\)/, |
135 |
| - russian: /\s+в .*\)/, |
136 |
| - chinese: /\s+在 .*\)/ |
137 |
| - }; |
138 |
| - |
139 |
| - for (var key in regexes) { |
140 |
| - if (regexes[key].test(lines[i])) { |
141 |
| - lang = key; |
142 |
| - break; |
| 151 | + var languagesRegex = { |
| 152 | + english: /\s+at .*?\)/g, |
| 153 | + danish: /\s+ved .*?\)/g, |
| 154 | + german: /\s+bei .*?\)/g, |
| 155 | + spanish: /\s+en .*?\)/g, |
| 156 | + russian: /\s+в .*?\)/g, |
| 157 | + chinese: /\s+在 .*?\)/g |
| 158 | + }; |
| 159 | + |
| 160 | + // look for the language(s) in the stack trace |
| 161 | + if (this.settings.multilanguage) { |
| 162 | + lang = this.detectLanguagesInOrder(lines, languagesRegex); |
| 163 | + } else { |
| 164 | + for (var i = 0; i < lines.length; ++i) { |
| 165 | + if (lang === '') { |
| 166 | + for (var key in languagesRegex) { |
| 167 | + if (languagesRegex[key].test(lines[i])) { |
| 168 | + lang = key; |
| 169 | + break; |
| 170 | + } |
143 | 171 | }
|
144 | 172 | }
|
145 | 173 | }
|
146 | 174 | }
|
147 | 175 |
|
148 | 176 | if (lang === '') return;
|
149 | 177 |
|
150 |
| - var selectedLanguage = this.search(lang, this.languages); |
151 |
| - this.language = selectedLanguage.name; |
| 178 | + // if multiline option is true, check if the language is the same for all lines |
| 179 | + if (typeof lang === 'object') { |
| 180 | + if (this.allEqual(lang)) { |
| 181 | + lang = lang[0]; |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + // if lang is an array, we have multiple languages |
| 186 | + if (Array.isArray(lang)) { |
| 187 | + var selectedLanguage = []; |
| 188 | + for (var i = 0; i < lang.length; ++i) { |
| 189 | + selectedLanguage.push(this.search(lang[i], this.languages)); |
| 190 | + } |
| 191 | + this.language = 'multilanguage'; |
| 192 | + } else if (typeof lang === 'string') { |
| 193 | + var selectedLanguage = this.search(lang, this.languages); |
| 194 | + this.language = selectedLanguage.name; |
| 195 | + } |
152 | 196 |
|
153 | 197 | // Pritty print result if is set to true
|
154 | 198 | if (this.settings.prettyprint) {
|
155 |
| - sanitizedStack = this.formatException(sanitizedStack, selectedLanguage.at); |
| 199 | + if (Array.isArray(selectedLanguage)) { |
| 200 | + var sanitizedStacks = sanitizedStack; |
| 201 | + const selectedLanguages = [...new Set(selectedLanguage)]; |
| 202 | + selectedLanguages.forEach((language, index) => { |
| 203 | + sanitizedStacks = this.formatException(sanitizedStacks, language.at, true, index); |
| 204 | + }); |
| 205 | + sanitizedStack = sanitizedStacks; |
| 206 | + } else { |
| 207 | + sanitizedStack = this.formatException(sanitizedStack, selectedLanguage.at); |
| 208 | + } |
| 209 | + |
156 | 210 | lines = sanitizedStack.split('\n');
|
157 | 211 | }
|
158 | 212 |
|
| 213 | + if (Array.isArray(selectedLanguage)) { |
| 214 | + var langContor = 0; |
| 215 | + } |
| 216 | + |
159 | 217 | for (var i = 0; i < lines.length; ++i) {
|
160 | 218 | var li = lines[i],
|
161 |
| - hli = new RegExp('(\\S*)' + selectedLanguage.at + ' .*\\)'); |
| 219 | + hli = new RegExp('(\\S*)' + selectedLanguage.at + ' .*\\)'), |
| 220 | + languageSet = selectedLanguage; |
| 221 | + |
| 222 | + if (Array.isArray(selectedLanguage)) { |
| 223 | + hli = new RegExp('(\\S*)' + selectedLanguage[langContor].at + ' .*\\)'); |
| 224 | + languageSet = selectedLanguage[langContor]; |
| 225 | + hli.test(lines[i]) ? langContor++ : langContor; |
| 226 | + } |
162 | 227 |
|
163 | 228 | if (hli.test(lines[i])) {
|
164 | 229 |
|
165 | 230 | // Frame
|
166 |
| - var regFrame = new RegExp('(\\S*)' + selectedLanguage.at + ' .*?\\)'), |
| 231 | + var regFrame = new RegExp('(\\S*)' + languageSet.at + ' .*?\\)'), |
167 | 232 | partsFrame = String(regFrame.exec(lines[i]));
|
168 | 233 |
|
169 | 234 | if (partsFrame.substring(partsFrame.length - 1) == ',') {
|
170 | 235 | partsFrame = partsFrame.slice(0, -1);
|
171 | 236 | }
|
172 | 237 |
|
173 |
| - partsFrame = partsFrame.replace(selectedLanguage.at + ' ', ''); |
| 238 | + partsFrame = partsFrame.replace(languageSet.at + ' ', ''); |
174 | 239 |
|
175 | 240 | // Frame -> ParameterList
|
176 | 241 | var regParamList = /\(.*\)/,
|
|
206 | 271 | var newPartsFrame = partsFrame.replace(partsParamList, stringParam).replace(partsTypeMethod, stringTypeMethod);
|
207 | 272 |
|
208 | 273 | // Line
|
209 |
| - var regLine = new RegExp('\\b:' + selectedLanguage.line + ' \\d+'), |
| 274 | + var regLine = new RegExp('\\b:' + languageSet.line + ' \\d+'), |
210 | 275 | partsLine = String(regLine.exec(lines[i]));
|
211 | 276 |
|
212 | 277 | partsLine = partsLine.replace(':', '').trim();
|
213 | 278 |
|
214 |
| - var fileLi = li.replace(selectedLanguage.at + " " + partsFrame, '').trim(); |
| 279 | + var fileLi = li.replace(languageSet.at + " " + partsFrame, '').trim(); |
215 | 280 |
|
216 | 281 | // File => (!) text requires multiline to exec regex, otherwise it will return null.
|
217 |
| - var regFile = new RegExp(selectedLanguage.in + '\\s.*$', 'm'), |
| 282 | + var regFile = new RegExp(languageSet.in + '\\s.*$', 'm'), |
218 | 283 | partsFile = String(regFile.exec(fileLi));
|
219 | 284 |
|
220 |
| - partsFile = partsFile.replace(selectedLanguage.in + ' ', '').replace(':' + partsLine, '').replace('<---', ''); |
| 285 | + partsFile = partsFile.replace(languageSet.in + ' ', '').replace(':' + partsLine, '').replace('<---', ''); |
221 | 286 |
|
222 | 287 | li = li.replace(partsFrame, '<span class="' + this.settings.frame + '">' + newPartsFrame + '</span>')
|
223 | 288 | .replace(partsFile, '<span class="' + this.settings.file + '">' + partsFile + '</span>')
|
|
0 commit comments