// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'chrome://resources/cr_elements/cr_hidden_style.css.js';
import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
import './strings.m.js';
import './shared_vars.css.js';
import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {getTemplate} from './code_section.html.js';
function visibleLineCount(totalCount: number, oppositeCount: number): number {
// We limit the number of lines shown for DOM performance.
const MAX_VISIBLE_LINES = 1000;
const max =
Math.max(MAX_VISIBLE_LINES / 2, MAX_VISIBLE_LINES - oppositeCount);
return Math.min(max, totalCount);
}
const ExtensionsCodeSectionElementBase = I18nMixin(PolymerElement);
export class ExtensionsCodeSectionElement extends
ExtensionsCodeSectionElementBase {
static get is() {
return 'extensions-code-section';
}
static get template() {
return getTemplate();
}
static get properties() {
return {
code: {
type: Object,
value: null,
},
isActive: Boolean,
/** Highlighted code. */
highlighted_: String,
/** Code before the highlighted section. */
before_: String,
/** Code after the highlighted section. */
after_: String,
showNoCode_: {
type: Boolean,
computed: 'computeShowNoCode_(isActive, highlighted_)',
},
/** Description for the highlighted section. */
highlightDescription_: String,
lineNumbers_: String,
truncatedBefore_: Number,
truncatedAfter_: Number,
/**
* The string to display if no |code| is set (e.g. because we couldn't
* load the relevant source file).
*/
couldNotDisplayCode: String,
};
}
code: chrome.developerPrivate.RequestFileSourceResponse|null;
isActive: boolean;
couldNotDisplayCode: string;
private highlighted_: string;
private before_: string;
private after_: string;
private showNoCode_: boolean;
private highlightDescription_: string;
private lineNumbers_: string;
private truncatedBefore_: number;
private truncatedAfter_: number;
static get observers() {
return ['onCodeChanged_(code.*)'];
}
private onCodeChanged_() {
if (!this.code ||
(!this.code.beforeHighlight && !this.code.highlight &&
!this.code.afterHighlight)) {
this.highlighted_ = '';
this.highlightDescription_ = '';
this.before_ = '';
this.after_ = '';
this.lineNumbers_ = '';
return;
}
const before = this.code.beforeHighlight;
const highlight = this.code.highlight;
const after = this.code.afterHighlight;
const linesBefore = before ? before.split('\n') : [];
const linesAfter = after ? after.split('\n') : [];
const visibleLineCountBefore =
visibleLineCount(linesBefore.length, linesAfter.length);
const visibleLineCountAfter =
visibleLineCount(linesAfter.length, linesBefore.length);
const visibleBefore =
linesBefore.slice(linesBefore.length - visibleLineCountBefore)
.join('\n');
let visibleAfter = linesAfter.slice(0, visibleLineCountAfter).join('\n');
// If the last character is a \n, force it to be rendered.
if (visibleAfter.charAt(visibleAfter.length - 1) === '\n') {
visibleAfter += ' ';
}
this.highlighted_ = highlight;
this.highlightDescription_ = this.getAccessibilityHighlightDescription_(
linesBefore.length, highlight.split('\n').length);
this.before_ = visibleBefore;
this.after_ = visibleAfter;
this.truncatedBefore_ = linesBefore.length - visibleLineCountBefore;
this.truncatedAfter_ = linesAfter.length - visibleLineCountAfter;
const visibleCode = visibleBefore + highlight + visibleAfter;
this.setLineNumbers_(
this.truncatedBefore_ + 1,
this.truncatedBefore_ + visibleCode.split('\n').length);
this.scrollToHighlight_(visibleLineCountBefore);
}
private getLinesNotShownLabel_(
lineCount: number, stringSingular: string,
stringPluralTemplate: string): string {
return lineCount === 1 ?
stringSingular :
loadTimeData.substituteString(stringPluralTemplate, lineCount);
}
private setLineNumbers_(start: number, end: number) {
let lineNumbers = '';
for (let i = start; i <= end; ++i) {
lineNumbers += i + '\n';
}
this.lineNumbers_ = lineNumbers;
}
private scrollToHighlight_(linesBeforeHighlight: number) {
const CSS_LINE_HEIGHT = 20;
// Count how many pixels is above the highlighted code.
const highlightTop = linesBeforeHighlight * CSS_LINE_HEIGHT;
// Find the position to show the highlight roughly in the middle.
const targetTop = highlightTop - this.clientHeight * 0.5;
this.$['scroll-container'].scrollTo({top: targetTop});
}
private getAccessibilityHighlightDescription_(
lineStart: number, numLines: number): string {
if (numLines > 1) {
return this.i18n(
'accessibilityErrorMultiLine', lineStart.toString(),
(lineStart + numLines - 1).toString());
} else {
return this.i18n('accessibilityErrorLine', lineStart.toString());
}
}
private computeShowNoCode_(): boolean {
return this.isActive && !this.highlighted_;
}
}
declare global {
interface HTMLElementTagNameMap {
'extensions-code-section': ExtensionsCodeSectionElement;
}
}
customElements.define(
ExtensionsCodeSectionElement.is, ExtensionsCodeSectionElement);