|
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