import{CoverageType,URLCoverageInfo}from'./CoverageModel.js';export class CoverageListView extends UI.VBox{constructor(filterCallback){super(true);this._nodeForCoverageInfo=new Map();this._filterCallback=filterCallback;this._highlightRegExp=null;this.registerRequiredCSS('coverage/coverageListView.css');const columns=[{id:'url',title:Common.UIString('URL'),width:'250px',fixedWidth:false,sortable:true},{id:'type',title:Common.UIString('Type'),width:'45px',fixedWidth:true,sortable:true},{id:'size',title:Common.UIString('Total Bytes'),width:'60px',fixedWidth:true,sortable:true,align:DataGrid.DataGrid.Align.Right},{id:'unusedSize',title:Common.UIString('Unused Bytes'),width:'100px',fixedWidth:true,sortable:true,align:DataGrid.DataGrid.Align.Right,sort:DataGrid.DataGrid.Order.Descending},{id:'bars',title:ls`Usage Visualization`,width:'250px',fixedWidth:false,sortable:true}];this._dataGrid=new DataGrid.SortableDataGrid({displayName:ls`Code Coverage`,columns});this._dataGrid.setResizeMethod(DataGrid.DataGrid.ResizeMethod.Last);this._dataGrid.element.classList.add('flex-auto');this._dataGrid.element.addEventListener('keydown',this._onKeyDown.bind(this),false);this._dataGrid.addEventListener(DataGrid.DataGrid.Events.OpenedNode,this._onOpenedNode,this);this._dataGrid.addEventListener(DataGrid.DataGrid.Events.SortingChanged,this._sortingChanged,this);const dataGridWidget=this._dataGrid.asWidget();dataGridWidget.show(this.contentElement);}
update(coverageInfo){let hadUpdates=false;const maxSize=coverageInfo.reduce((acc,entry)=>Math.max(acc,entry.size()),0);const rootNode=this._dataGrid.rootNode();for(const entry of coverageInfo){let node=this._nodeForCoverageInfo.get(entry);if(node){if(this._filterCallback(node._coverageInfo)){hadUpdates=node._refreshIfNeeded(maxSize)||hadUpdates;}
continue;}
node=new GridNode(entry,maxSize);this._nodeForCoverageInfo.set(entry,node);if(this._filterCallback(node._coverageInfo)){rootNode.appendChild(node);hadUpdates=true;}}
if(hadUpdates){this._sortingChanged();}}
reset(){this._nodeForCoverageInfo.clear();this._dataGrid.rootNode().removeChildren();}
updateFilterAndHighlight(highlightRegExp){this._highlightRegExp=highlightRegExp;let hadTreeUpdates=false;for(const node of this._nodeForCoverageInfo.values()){const shouldBeVisible=this._filterCallback(node._coverageInfo);const isVisible=!!node.parent;if(shouldBeVisible){node._setHighlight(this._highlightRegExp);}
if(shouldBeVisible===isVisible){continue;}
hadTreeUpdates=true;if(!shouldBeVisible){node.remove();}else{this._dataGrid.rootNode().appendChild(node);}}
if(hadTreeUpdates){this._sortingChanged();}}
selectByUrl(url){for(const[info,node]of this._nodeForCoverageInfo.entries()){if(info.url()===url){node.revealAndSelect();break;}}}
_onOpenedNode(){this._revealSourceForSelectedNode();}
_onKeyDown(event){if(!isEnterKey(event)){return;}
event.consume(true);this._revealSourceForSelectedNode();}
async _revealSourceForSelectedNode(){const node=this._dataGrid.selectedNode;if(!node){return;}
const coverageInfo=(node)._coverageInfo;let sourceCode=self.Workspace.workspace.uiSourceCodeForURL(coverageInfo.url());if(!sourceCode){return;}
const formatData=await Formatter.sourceFormatter.format(sourceCode);sourceCode=formatData.formattedSourceCode;if(this._dataGrid.selectedNode!==node){return;}
Common.Revealer.reveal(sourceCode);}
_sortingChanged(){const columnId=this._dataGrid.sortColumnId();if(!columnId){return;}
let sortFunction;switch(columnId){case'url':sortFunction=compareURL;break;case'type':sortFunction=compareType;break;case'size':sortFunction=compareNumericField.bind(null,'size');break;case'bars':case'unusedSize':sortFunction=compareNumericField.bind(null,'unusedSize');break;default:console.assert(false,'Unknown sort field: '+columnId);return;}
this._dataGrid.sortNodes(sortFunction,!this._dataGrid.isSortOrderAscending());function compareURL(a,b){const nodeA=(a);const nodeB=(b);return nodeA._url.localeCompare(nodeB._url);}
function compareNumericField(fieldName,a,b){const nodeA=(a);const nodeB=(b);return nodeA._coverageInfo[fieldName]()-nodeB._coverageInfo[fieldName]()||compareURL(a,b);}
function compareType(a,b){const nodeA=(a);const nodeB=(b);const typeA=CoverageListView._typeToString(nodeA._coverageInfo.type());const typeB=CoverageListView._typeToString(nodeB._coverageInfo.type());return typeA.localeCompare(typeB)||compareURL(a,b);}}
static _typeToString(type){const types=[];if(type&CoverageType.CSS){types.push(Common.UIString('CSS'));}
if(type&CoverageType.JavaScriptPerFunction){types.push(Common.UIString('JS (per function)'));}else if(type&CoverageType.JavaScript){types.push(Common.UIString('JS (per block)'));}
return types.join('+');}}
export class GridNode extends DataGrid.SortableDataGridNode{constructor(coverageInfo,maxSize){super();this._coverageInfo=coverageInfo;this._lastUsedSize;this._url=coverageInfo.url();this._maxSize=maxSize;this._highlightDOMChanges=[];this._highlightRegExp=null;}
_setHighlight(highlightRegExp){if(this._highlightRegExp===highlightRegExp){return;}
this._highlightRegExp=highlightRegExp;this.refresh();}
_refreshIfNeeded(maxSize){if(this._lastUsedSize===this._coverageInfo.usedSize()&&maxSize===this._maxSize){return false;}
this._lastUsedSize=this._coverageInfo.usedSize();this._maxSize=maxSize;this.refresh();return true;}
createCell(columnId){const cell=this.createTD(columnId);switch(columnId){case'url':cell.title=this._url;const outer=cell.createChild('div','url-outer');const prefix=outer.createChild('div','url-prefix');const suffix=outer.createChild('div','url-suffix');const splitURL=/^(.*)(\/[^/]*)$/.exec(this._url);prefix.textContent=splitURL?splitURL[1]:this._url;suffix.textContent=splitURL?splitURL[2]:'';if(this._highlightRegExp){this._highlight(outer,this._url);}
this.setCellAccessibleName(this._url,cell,columnId);break;case'type':cell.textContent=CoverageListView._typeToString(this._coverageInfo.type());if(this._coverageInfo.type()&CoverageType.JavaScriptPerFunction){cell.title=ls`JS coverage with per function granularity: Once a function was executed, the whole function is marked as covered.`;}else if(this._coverageInfo.type()&CoverageType.JavaScript){cell.title=ls`JS coverage with per block granularity: Once a block of JavaScript was executed, that block is marked as covered.`;}
break;case'size':const sizeSpan=cell.createChild('span');sizeSpan.textContent=Number.withThousandsSeparator(this._coverageInfo.size()||0);const sizeAccessibleName=(this._coverageInfo.size()===1)?ls`1 byte`:ls`${this._coverageInfo.size() || 0} bytes`;this.setCellAccessibleName(sizeAccessibleName,cell,columnId);break;case'unusedSize':const unusedSize=this._coverageInfo.unusedSize()||0;const unusedSizeSpan=cell.createChild('span');const unusedPercentsSpan=cell.createChild('span','percent-value');unusedSizeSpan.textContent=Number.withThousandsSeparator(unusedSize);const unusedPercentFormatted=ls`${this._percentageString(this._coverageInfo.unusedPercentage())} %`;unusedPercentsSpan.textContent=unusedPercentFormatted;const unusedAccessibleName=(unusedSize===1)?ls`1 byte, ${unusedPercentFormatted}`:ls`${unusedSize} bytes, ${unusedPercentFormatted}`;this.setCellAccessibleName(unusedAccessibleName,cell,columnId);break;case'bars':const barContainer=cell.createChild('div','bar-container');const unusedPercent=this._percentageString(this._coverageInfo.unusedPercentage());const usedPercent=this._percentageString(this._coverageInfo.usedPercentage());if(this._coverageInfo.unusedSize()>0){const unusedSizeBar=barContainer.createChild('div','bar bar-unused-size');unusedSizeBar.style.width=((this._coverageInfo.unusedSize()/this._maxSize)*100||0)+'%';if(this._coverageInfo.type()&CoverageType.JavaScriptPerFunction){unusedSizeBar.title=ls`${this._coverageInfo.unusedSize()} bytes (${
                unusedPercent} %) belong to functions that have not (yet) been executed.`;}else if(this._coverageInfo.type()&CoverageType.JavaScript){unusedSizeBar.title=ls`${this._coverageInfo.unusedSize()} bytes (${
                unusedPercent} %) belong to blocks of JavaScript that have not (yet) been executed.`;}}
if(this._coverageInfo.usedSize()>0){const usedSizeBar=barContainer.createChild('div','bar bar-used-size');usedSizeBar.style.width=((this._coverageInfo.usedSize()/this._maxSize)*100||0)+'%';if(this._coverageInfo.type()&CoverageType.JavaScriptPerFunction){usedSizeBar.title=ls`${this._coverageInfo.usedSize()} bytes (${
                usedPercent} %) belong to functions that have executed at least once.`;}else if(this._coverageInfo.type()&CoverageType.JavaScript){usedSizeBar.title=ls`${this._coverageInfo.usedSize()} bytes (${
                usedPercent} %) belong to blocks of JavaScript that have executed at least once.`;}}
this.setCellAccessibleName(ls`${unusedPercent} % of file unused, ${usedPercent} % of file used`,cell,columnId);}
return cell;}
_percentageString(value){return value.toFixed(1);}
_highlight(element,textContent){const matches=this._highlightRegExp.exec(textContent);if(!matches||!matches.length){return;}
const range=new TextUtils.SourceRange(matches.index,matches[0].length);UI.highlightRangesWithStyleClass(element,[range],'filter-highlight');}}