Files
formipay/node_modules/lighthouse/report/renderer/crc-details-renderer.js
dwindown e8fbfb14c1 fix: prevent asset conflicts between React and Grid.js versions
Add coexistence checks to all enqueue methods to prevent loading
both React and Grid.js assets simultaneously.

Changes:
- ReactAdmin.php: Only enqueue React assets when ?react=1
- Init.php: Skip Grid.js when React active on admin pages
- Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0
- Customer.php, Product.php, License.php: Add coexistence checks

Now the toggle between Classic and React versions works correctly.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 17:02:14 +07:00

204 lines
7.0 KiB
JavaScript

/**
* @license
* Copyright 2017 The Lighthouse Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview This file contains helpers for constructing and rendering the
* critical request chains network tree.
*/
import {Globals} from './report-globals.js';
/** @typedef {import('./dom.js').DOM} DOM */
/** @typedef {import('./details-renderer.js').DetailsRenderer} DetailsRenderer */
/**
* @typedef CRCSegment
* @property {LH.Audit.Details.SimpleCriticalRequestNode[string]} node
* @property {boolean} isLastChild
* @property {boolean} hasChildren
* @property {number} startTime
* @property {number} transferSize
* @property {boolean[]} treeMarkers
*/
class CriticalRequestChainRenderer {
/**
* Create render context for critical-request-chain tree display.
* @param {LH.Audit.Details.SimpleCriticalRequestNode} tree
* @return {{tree: LH.Audit.Details.SimpleCriticalRequestNode, startTime: number, transferSize: number}}
*/
static initTree(tree) {
let startTime = 0;
const rootNodes = Object.keys(tree);
if (rootNodes.length > 0) {
const node = tree[rootNodes[0]];
startTime = node.request.startTime;
}
return {tree, startTime, transferSize: 0};
}
/**
* Helper to create context for each critical-request-chain node based on its
* parent. Calculates if this node is the last child, whether it has any
* children itself and what the tree looks like all the way back up to the root,
* so the tree markers can be drawn correctly.
* @param {LH.Audit.Details.SimpleCriticalRequestNode} parent
* @param {string} id
* @param {number} startTime
* @param {number} transferSize
* @param {Array<boolean>=} treeMarkers
* @param {boolean=} parentIsLastChild
* @return {CRCSegment}
*/
static createSegment(parent, id, startTime, transferSize, treeMarkers, parentIsLastChild) {
const node = parent[id];
const siblings = Object.keys(parent);
const isLastChild = siblings.indexOf(id) === (siblings.length - 1);
const hasChildren = !!node.children && Object.keys(node.children).length > 0;
// Copy the tree markers so that we don't change by reference.
const newTreeMarkers = Array.isArray(treeMarkers) ? treeMarkers.slice(0) : [];
// Add on the new entry.
if (typeof parentIsLastChild !== 'undefined') {
newTreeMarkers.push(!parentIsLastChild);
}
return {
node,
isLastChild,
hasChildren,
startTime,
transferSize: transferSize + node.request.transferSize,
treeMarkers: newTreeMarkers,
};
}
/**
* Creates the DOM for a tree segment.
* @param {DOM} dom
* @param {CRCSegment} segment
* @param {DetailsRenderer} detailsRenderer
* @return {Node}
*/
static createChainNode(dom, segment, detailsRenderer) {
const chainEl = dom.createComponent('crcChain');
// Hovering over request shows full URL.
dom.find('.lh-crc-node', chainEl).setAttribute('title', segment.node.request.url);
const treeMarkeEl = dom.find('.lh-crc-node__tree-marker', chainEl);
// Construct lines and add spacers for sub requests.
segment.treeMarkers.forEach(separator => {
const classSeparator = separator ?
'lh-tree-marker lh-vert' :
'lh-tree-marker';
treeMarkeEl.append(
dom.createElement('span', classSeparator),
dom.createElement('span', 'lh-tree-marker')
);
});
const classLastChild = segment.isLastChild ?
'lh-tree-marker lh-up-right' :
'lh-tree-marker lh-vert-right';
const classHasChildren = segment.hasChildren ?
'lh-tree-marker lh-horiz-down' :
'lh-tree-marker lh-right';
treeMarkeEl.append(
dom.createElement('span', classLastChild),
dom.createElement('span', 'lh-tree-marker lh-right'),
dom.createElement('span', classHasChildren)
);
// Fill in url, host, and request size information.
const url = segment.node.request.url;
const linkEl = detailsRenderer.renderTextURL(url);
const treevalEl = dom.find('.lh-crc-node__tree-value', chainEl);
treevalEl.append(linkEl);
if (!segment.hasChildren) {
const {startTime, endTime, transferSize} = segment.node.request;
const span = dom.createElement('span', 'lh-crc-node__chain-duration');
span.textContent =
' - ' + Globals.i18n.formatMilliseconds((endTime - startTime) * 1000) + ', ';
const span2 = dom.createElement('span', 'lh-crc-node__chain-duration');
span2.textContent = Globals.i18n.formatBytesToKiB(transferSize, 0.01);
treevalEl.append(span, span2);
}
return chainEl;
}
/**
* Recursively builds a tree from segments.
* @param {DOM} dom
* @param {DocumentFragment} tmpl
* @param {CRCSegment} segment
* @param {Element} elem Parent element.
* @param {LH.Audit.Details.CriticalRequestChain} details
* @param {DetailsRenderer} detailsRenderer
*/
static buildTree(dom, tmpl, segment, elem, details, detailsRenderer) {
elem.append(CRCRenderer.createChainNode(dom, segment, detailsRenderer));
if (segment.node.children) {
for (const key of Object.keys(segment.node.children)) {
const childSegment = CRCRenderer.createSegment(segment.node.children, key,
segment.startTime, segment.transferSize, segment.treeMarkers, segment.isLastChild);
CRCRenderer.buildTree(dom, tmpl, childSegment, elem, details, detailsRenderer);
}
}
}
/**
* @param {DOM} dom
* @param {LH.Audit.Details.CriticalRequestChain} details
* @param {DetailsRenderer} detailsRenderer
* @return {Element}
*/
static render(dom, details, detailsRenderer) {
const tmpl = dom.createComponent('crc');
const containerEl = dom.find('.lh-crc', tmpl);
// Fill in top summary.
dom.find('.lh-crc-initial-nav', tmpl).textContent = Globals.strings.crcInitialNavigation;
dom.find('.lh-crc__longest_duration_label', tmpl).textContent =
Globals.strings.crcLongestDurationLabel;
dom.find('.lh-crc__longest_duration', tmpl).textContent =
Globals.i18n.formatMilliseconds(details.longestChain.duration);
// Construct visual tree.
const root = CRCRenderer.initTree(details.chains);
for (const key of Object.keys(root.tree)) {
const segment = CRCRenderer.createSegment(root.tree, key, root.startTime, root.transferSize);
CRCRenderer.buildTree(dom, tmpl, segment, containerEl, details, detailsRenderer);
}
return dom.find('.lh-crc-container', tmpl);
}
}
// Alias b/c the name is really long.
const CRCRenderer = CriticalRequestChainRenderer;
export {
CriticalRequestChainRenderer,
};