Embed remote resources in html report (#574)

* Embed remote resources in html report

Fix #450

* Replace D3+C3 with Charts.Js

* Restore static 200px width to charts.

* Add Datalabels to charts

* Fix tabs on summary page

Fix for new bootstrap mechanisms

* Fix Source Code Modal for BootStrap 5

* Swap Ace for Prism highlighting

* Make modal a bit wider

* Unescape HTML sequences to render in pre tag
This commit is contained in:
Gabe Stocco 2024-02-23 02:19:06 +00:00 коммит произвёл GitHub
Родитель 6d539ef691
Коммит 044dc12a29
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
11 изменённых файлов: 538 добавлений и 136 удалений

Просмотреть файл

@ -47,16 +47,28 @@
<ItemGroup>
<None Update="html\index.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="html\resources\css\appinspector.css">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="html\resources\css\bootstrap.min.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="html\resources\css\fa-all.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="html\resources\css\prism.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="html\resources\js\appinspector.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="html\resources\js\deps\deps.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="preferences\tagreportgroups.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

Просмотреть файл

@ -66,21 +66,15 @@ public class AnalyzeHtmlWriter : CommandResultsWriter
var allCSS = "<style>\n" +
MergeResourceFiles(Path.Combine(Utils.GetPath(Utils.AppPath.basePath), "html", "resources",
"css"));
var allJS = "<script type=\"text/javascript\">\n" +
MergeResourceFiles(Path.Combine(Utils.GetPath(Utils.AppPath.basePath), "html", "resources", "js"));
var depsJs = "<script type=\"text/javascript\">\n" +
MergeResourceFiles(Path.Combine(Utils.GetPath(Utils.AppPath.basePath), "html", "resources", "js", "deps")) + "</script>";
var baseJs = "<script type =\"text/javascript\">\n" + File.ReadAllText(Path.Combine(Utils.GetPath(Utils.AppPath.basePath), "html", "resources", "js", "appinspector.js"));
//Prepare html template merge
var htmlTemplateText = File.ReadAllText(Path.Combine(Utils.GetPath(Utils.AppPath.basePath), "html/index.html"));
Template.FileSystem = new EmbeddedFileSystem(Assembly.GetEntryAssembly(),
"Microsoft.ApplicationInspector.CLI.html.partials");
//Update template with local aggregated code for easy relocation of output file
htmlTemplateText = htmlTemplateText.Replace("<script type=\"text/javascript\">", allJS);
htmlTemplateText =
htmlTemplateText.Replace(
"<link rel=\"stylesheet\" type=\"text/css\" href=\"html/resources/css/appinspector.css\" />",
allCSS + "</style>");
RegisterSafeType(typeof(MetaData));
//Prepare data for use in appinspector.js and html partials resources
@ -121,8 +115,17 @@ public class AnalyzeHtmlWriter : CommandResultsWriter
hashData["filetypes"] = _appMetaData?.FileExtensions ?? new List<string>();
hashData["tagcounters"] = ConvertTagCounters(_appMetaData?.TagCounters ?? new List<MetricTagCounter>());
//final render and close
// Liquid render and close
var htmlResult = htmlTemplate.Render(hashData);
// Update template with local aggregated code
// Liquid will break the embedded JS if this replacement is performed before render
htmlResult = htmlResult.Replace("<DEPSJS/>", depsJs);
htmlResult = htmlResult.Replace("<REPLACEJS/>", baseJs);
htmlResult =
htmlResult.Replace(
"<REPLACECSS/>",
allCSS + "</style>");
TextWriter?.Write(htmlResult);
FlushAndClose();
}

Просмотреть файл

@ -1,12 +1,9 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.20/c3.css" rel="stylesheet"/>
<link href="html/resources/css/appinspector.css" rel="stylesheet" type="text/css"/>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport" />
<REPLACECSS/>
<title>{{ application_version }}</title>
</head>
@ -53,27 +50,8 @@
{% include "file_listing" %}
<script crossorigin="anonymous"
integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg=="
src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script crossorigin="anonymous"
integrity="sha512-GZ1RIgZaSc8rnco/8CXfRdCpDxRCphenIiZ2ztLy3XQfCbQUSCuk8IudvNHxkRA3oUg6q0qejgN/qqyG1duv5Q=="
src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js"></script>
<script crossorigin="anonymous"
integrity="sha512-M5KW3ztuIICmVIhjSqXe01oV2bpe248gOxqmlcYrEzAvws7Pw3z6BK0iGbrwvdrUQUhi3eXgtxp5I8PDo9YfjQ=="
src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script crossorigin="anonymous"
integrity="sha512-+IpCthlNahOuERYUSnKFjzjdKXIbJ/7Dd6xvUp+7bEw0Jp2dg6tluyxLs+zq9BMzZgrLv8886T4cBSqnKiVgUw=="
src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.7.20/c3.min.js"></script>
<script crossorigin="anonymous"
integrity="sha512-FHsFVKQ/T1KWJDGSbrUhTJyS1ph3eRrxI228ND0EGaEp6v4a/vGwPWd3Dtd/+9cI7ccofZvl/wulICEurHN1pg=="
src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.16.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.5.3/umd/popper.min.js" integrity="sha512-53CQcu9ciJDlqhK7UD8dZZ+TF2PFGZrOngEYM/8qucuQba+a+BXOIRsp9PoMNJI3ZeLMVNIxIfZLbG/CdHI5PA==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.3.3/ext-beautify.js" integrity="sha512-cbnV6WqDWdPXhI/SfbylU8ZOgxe6X7u1cAChSzCEgcixCoBlg0M7pZQgUmFFaStd1Y+pimB65mL9gUE0R2Xpug==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.3.3/ext-modelist.js" integrity="sha512-Cp7JolVvryVA9mLSbTnQbpmfpCZg4VQ8BgKDgRcyNu3yWMitVcxzFj8/fcjxDrNvMjAPCU9Noygzc6eK9rP4Mg==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.3.3/ext-settings_menu.js" integrity="sha512-soczE7f5nUAF4LBvJ+N+h7mDqFWJT0zyhH+F1CiQns+r1EVS5Ze0qPgRm+2rJpuXggx07DMDCGdoaASwEAScrw==" crossorigin="anonymous"></script>
<script type="text/javascript">
<DEPSJS/>
<REPLACEJS/>
let data = {{ json }};
</script>
</body>

Просмотреть файл

@ -1,6 +1,6 @@

<div id="file_listing_modal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document" style="width: 700px;overflow-x:auto;">
<div class="modal-dialog" role="document" style="width: 800px;overflow-x:auto;">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Source File Listing</h5>
@ -16,6 +16,8 @@
<div style="height: 360px; overflow-y:auto;">
<div id="editor-container" style="margin-top: 5px;">
<div id="editor" style="height: 350px; max-height: 350px;">
<pre id="snippet-container">
</pre>
</div>
</div>
</div>

Просмотреть файл

@ -6,35 +6,35 @@
<div class="container">
<div class="row">
<div class="col-2">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="#project_info" role="tab" data-toggle="tab">Project Info</a>
<ul class="nav nav-tabs flex-column">
<li class="nav-item" role="presentation">
<a class="nav-link active" id="project_tab" data-bs-toggle="tab" data-bs-target="#project_info" href="#project_info" role="tab" aria-controls="project_info" aria-selected="true">Project Info</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#scan_metadata" role="tab" data-toggle="tab">Metadata</a>
<li class="nav-item" role="presentation">
<a class="nav-link" id="scan_metadata_tab" data-bs-toggle="tab" data-bs-target="#scan_metadata" href="#scan_metadata" role="tab" aria-controls="metadata_tab">Metadata</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#sorted_uniquetags" role="tab" data-toggle="tab">All Tags</a>
<li class="nav-item" role="presentation">
<a class="nav-link" id="sorted_uniquetags_tab" data-bs-toggle="tab" data-bs-target="#sorted_uniquetags" href="#sorted_uniquetags" role="tab" aria-controls="sorted_uniquetags">All Tags</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#select_tagcounters" role="tab" data-toggle="tab">Tag Counters</a>
<li class="nav-item" role="presentation">
<a class="nav-link" id="sorted_uniquetags_tab" data-bs-toggle="tab" data-bs-target="#select_tagcounters" href="#select_tagcounters" role="tab" aria-controls="select_tagcounters">Tag Counters</a>
</li>
</ul>
</div>
<div class="col">
<div class="tab-content">
<div role="tabpanel" class="tab-pane active show" id="project_info">
<div role="tabpanel" class="tab-pane active show" id="project_info" aria-labelledby="project_tab">
<h4>Project Info</h4>
<div class="container">
<div class="row">
<div class="col">
<div id="s_pi_analysis_chart"></div>
<canvas style="width:200px" id="s_pi_analysis_chart"/>
</div>
<div class="col">
<div id="s_pi_patterns_chart"></div>
<canvas style="width:200px" id="s_pi_patterns_chart"/>
</div>
<div class="col">
<div id="s_pi_languages_chart"></div>
<canvas style="width:200px" id="s_pi_languages_chart"/>
</div>
</div>
</div>
@ -66,7 +66,7 @@
</tr>
</table>
</div>
<div role="tabpanel" class="tab-pane" id="scan_metadata">
<div role="tabpanel" class="tab-pane" id="scan_metadata" aria-labelledby="scan_metadata_tab">
<h4>Metadata</h4>
<div>
Application targets or packaging detected.
@ -104,7 +104,7 @@
</tr>
</table>
</div>
<div role="tabpanel" class="tab-pane" id="sorted_uniquetags">
<div role="tabpanel" class="tab-pane" id="sorted_uniquetags" aria-labelledby="sorted_uniquetags_tab">
<h4>Tags List View</h4>
<div>
A list view of the unique tags found and matching rule. Select a tag on the left then select
@ -243,7 +243,7 @@
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="select_tagcounters">
<div role="tabpanel" class="tab-pane" id="select_tagcounters" aria-labelledby="select_tagcounters_tab">
<h4>Tag Counters</h4>
<div>
Tags found that are identified as Metric related matches and therefore counted but not included in detailed results.

6
AppInspector.CLI/html/resources/css/bootstrap.min.css поставляемый Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -0,0 +1,5 @@
/* PrismJS 1.29.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+abap+abnf+actionscript+ada+agda+al+antlr4+apacheconf+apex+apl+applescript+aql+arduino+arff+armasm+arturo+asciidoc+aspnet+asm6502+asmatmel+autohotkey+autoit+avisynth+avro-idl+awk+bash+basic+batch+bbcode+bbj+bicep+birb+bison+bnf+bqn+brainfuck+brightscript+bro+bsl+c+csharp+cpp+cfscript+chaiscript+cil+cilkc+cilkcpp+clojure+cmake+cobol+coffeescript+concurnas+csp+cooklang+coq+crystal+css-extras+csv+cue+cypher+d+dart+dataweave+dax+dhall+diff+django+dns-zone-file+docker+dot+ebnf+editorconfig+eiffel+ejs+elixir+elm+etlua+erb+erlang+excel-formula+fsharp+factor+false+firestore-security-rules+flow+fortran+ftl+gml+gap+gcode+gdscript+gedcom+gettext+gherkin+git+glsl+gn+linker-script+go+go-module+gradle+graphql+groovy+haml+handlebars+haskell+haxe+hcl+hlsl+hoon+http+hpkp+hsts+ichigojam+icon+icu-message-format+idris+ignore+inform7+ini+io+j+java+javadoc+javadoclike+javastacktrace+jexl+jolie+jq+jsdoc+js-extras+json+json5+jsonp+jsstacktrace+js-templates+julia+keepalived+keyman+kotlin+kumir+kusto+latex+latte+less+lilypond+liquid+lisp+livescript+llvm+log+lolcode+lua+magma+makefile+markdown+markup-templating+mata+matlab+maxscript+mel+mermaid+metafont+mizar+mongodb+monkey+moonscript+n1ql+n4js+nand2tetris-hdl+naniscript+nasm+neon+nevod+nginx+nim+nix+nsis+objectivec+ocaml+odin+opencl+openqasm+oz+parigp+parser+pascal+pascaligo+psl+pcaxis+peoplecode+perl+php+phpdoc+php-extras+plant-uml+plsql+powerquery+powershell+processing+prolog+promql+properties+protobuf+pug+puppet+pure+purebasic+purescript+python+qsharp+q+qml+qore+r+racket+cshtml+jsx+tsx+reason+regex+rego+renpy+rescript+rest+rip+roboconf+robotframework+ruby+rust+sas+sass+scss+scala+scheme+shell-session+smali+smalltalk+smarty+sml+solidity+solution-file+soy+sparql+splunk-spl+sqf+sql+squirrel+stan+stata+iecst+stylus+supercollider+swift+systemd+t4-templating+t4-cs+t4-vb+tap+tcl+tt2+textile+toml+tremor+turtle+twig+typescript+typoscript+unrealscript+uorazor+uri+v+vala+vbnet+velocity+verilog+vhdl+vim+visual-basic+warpscript+wasm+web-idl+wgsl+wiki+wolfram+wren+xeora+xml-doc+xojo+xquery+yaml+yang+zig&plugins=line-highlight+line-numbers */
code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;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[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.token.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}
pre[data-line]{position:relative;padding:1em 0 1em 3em}.line-highlight{position:absolute;left:0;right:0;padding:inherit 0;margin-top:1em;background:hsla(24,20%,50%,.08);background:linear-gradient(to right,hsla(24,20%,50%,.1) 70%,hsla(24,20%,50%,0));pointer-events:none;line-height:inherit;white-space:pre}@media print{.line-highlight{-webkit-print-color-adjust:exact;color-adjust:exact}}.line-highlight:before,.line-highlight[data-end]:after{content:attr(data-start);position:absolute;top:.4em;left:.6em;min-width:1em;padding:0 .5em;background-color:hsla(24,20%,50%,.4);color:#f4f1ef;font:bold 65%/1.5 sans-serif;text-align:center;vertical-align:.3em;border-radius:999px;text-shadow:none;box-shadow:0 1px #fff}.line-highlight[data-end]:after{content:attr(data-end);top:auto;bottom:.4em}.line-numbers .line-highlight:after,.line-numbers .line-highlight:before{content:none}pre[id].linkable-line-numbers span.line-numbers-rows{pointer-events:all}pre[id].linkable-line-numbers span.line-numbers-rows>span:before{cursor:pointer}pre[id].linkable-line-numbers span.line-numbers-rows>span:hover:before{background-color:rgba(128,128,128,.2)}
pre[class*=language-].line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right}

Просмотреть файл

@ -31,39 +31,42 @@
$('#file_listing_modal').modal();
})
$('button.close').on('click', (e) => {
var fileListingModal = document.getElementById('file_listing_modal');
var modal = bootstrap.Modal.getInstance(fileListingModal);
modal.hide();
})
/*
* When a user clicks on a file listing filename, load the data
* to show into the Ace editor in the popup dialog.
*/
// Method to fix html encoded &quot/&lt/&gt etc. before rendinging in pre tag
function htmlDecode(input) {
var doc = new DOMParser().parseFromString(input, "text/html");
return doc.documentElement.textContent;
}
$('#file_listing_modal').on('click', 'a.content-link', (e) => {
const content = $(e.target).data('excerpt');
const startLocationLine = $(e.target).data('startLocationLine');
const endLocationLine = $(e.target).data('endLocationLine');
const editor = ace.edit("editor");
const language = $(e.target).data('language');
let snippetElement = $('#snippet-container');
snippetElement.empty();
snippetElement.removeClass();
snippetElement.addClass('line-numbers');
snippetElement.addClass('language-'+language);
// Assume that the default context of 3 is used, the minimum line number is 1
// TODO: Better handle context that isn't 3 length
const actualStartNumber = Math.max(1, startLocationLine - 3);
editor.setOption('firstLineNumber', actualStartNumber);
// Decode the content (HTML encoded) for Ace to display
// Disabled, needs better testing, since it's prone to XSS if content contains JS.
// TODO: Can this be rewritten to properly escape in a more limited fashion and use something like a <pre> tag?
// if (false) {
// const htmlEntityDecoder = (content) => {
// const textArea = document.createElement('textarea');
// textArea.innerHTML = content;
// return textArea.value;
// }
// content = htmlEntityDecoder(content);
// }
editor.getSession().setValue(content);
editor.resize();
editor.scrollToLine(0);
// We need to calculate the number relative to the number of lines in the content available
// This assumes the first line is 1, even though we have explicitly numbered the lines otherwise
editor.gotoLine(startLocationLine - actualStartNumber + 1);
$('editor-container').removeClass('d-none');
snippetElement.attr('data-line', startLocationLine);
snippetElement.attr('data-line-offset', actualStartNumber);
snippetElement.attr('data-start', actualStartNumber);
let codeBlock = $('<code>');
codeBlock.addClass('language-'+language);
codeBlock.text(htmlDecode(content));
snippetElement.append(codeBlock);
Prism.highlightAll();
const locationString = startLocationLine < endLocationLine ? (startLocationLine.toString() + " - " + endLocationLine.toString()) : startLocationLine.toString();
$('#match-line-number').text('Line number: ' + locationString);
});
@ -80,68 +83,81 @@ class TemplateInsertion {
}
processSummaryPage() {
c3.generate({
bindto: '#s_pi_analysis_chart',
size: {
width: 200
},
data: {
columns: [
['Analyzed', this.mt.filesAnalyzed],
['Skipped', this.mt.filesSkipped],
],
type: 'donut'
},
donut: {
title: "Analyzed Files",
label: {
format: function (value) {
return value;
Chart.register(ChartDataLabels);
const analysisChartData ={
labels:['Analyzed', 'Skipped'],
datasets:[{data:[this.mt.filesAnalyzed, this.mt.filesSkipped]}]
};
const analysisChartConfig = {
type: 'doughnut',
data: analysisChartData,
options:{
plugins:{
title:{
display:true,
text:"Analyzed Files"
},
datalabels: {
display: function(context) {
return context.dataset.data[context.dataIndex] !== 0;
},
color: '#fff'
}
}
}
});
};
const analysisChartContext = $('#s_pi_analysis_chart').get(0).getContext('2d');
new Chart(analysisChartContext, analysisChartConfig);
c3.generate({
bindto: '#s_pi_patterns_chart',
size: {
width: 200
},
data: {
columns: [
['Unique Matches', this.mt.uniqueMatchesCount],
['Repeats', this.mt.totalMatchesCount - this.mt.uniqueMatchesCount],
],
type: 'donut'
},
donut: {
title: "Results",
label: {
format: function (value) {
return value;
const patternsChartData ={
labels:['Unique Matches', 'Repeats'],
datasets:[{data:[this.mt.uniqueMatchesCount, this.mt.totalMatchesCount - this.mt.uniqueMatchesCount]}]
};
const patternsChartConfig = {
type: 'doughnut',
data: patternsChartData,
options:{
plugins:{
title:{
display:true,
text:"Results"
},
datalabels: {
display: function(context) {
return context.dataset.data[context.dataIndex] !== 0;
},
color: '#fff'
}
}
}
});
};
const patternsChartContext = $('#s_pi_patterns_chart').get(0).getContext('2d');
new Chart(patternsChartContext, patternsChartConfig);
c3.generate({
bindto: '#s_pi_languages_chart',
size: {
width: 200
},
data: {
columns: Object.entries(this.mt.languages),
type: 'donut'
},
donut: {
title: "Source Types",
label: {
format: function (value) {
return value;
const languagesChartData ={
labels:Object.keys(this.mt.languages),
datasets:[{data:Object.values(this.mt.languages)}]
};
const languagesChartConfig = {
type: 'doughnut',
data: languagesChartData,
options:{
plugins:{
title:{
display:true,
text:"Languages"
},
datalabels: {
display: function(context) {
return context.dataset.data[context.dataIndex] !== 0;
},
color: '#fff'
}
}
}
});
};
const languagesChartContext = $('#s_pi_languages_chart').get(0).getContext('2d');
new Chart(languagesChartContext, languagesChartConfig);
$('#s_pi_application_name').text(this.mt.applicationName);
$('#s_pi_version').text(this.mt.sourceVersion);
@ -193,6 +209,7 @@ class TemplateInsertion {
.data('excerpt', excerpt)
.data('startLocationLine', $l)
.data('endLocationLine', $e)
.data('language', match.language)
.text(removePrefix(match.fileName));
$li.append(matchCount++);
$li.append(". ");
@ -204,9 +221,12 @@ class TemplateInsertion {
}
$('#file_listing_modal').on('shown.bs.modal', function (e) {
$('a.content-link').first().trigger('click');
Prism.highlightAllUnder($('#file_listing_modal')[0]);
});
$('#file_listing_modal').modal();
var fileListingModal = new bootstrap.Modal(document.getElementById('file_listing_modal'), {
keyboard: false
});
fileListingModal.show();
}
/*

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны