escape html updates
This commit is contained in:
parent
5a4ed98aaf
commit
c851ab1394
144
.eslintrc.js
144
.eslintrc.js
@ -49,6 +49,150 @@ module.exports = {
|
||||
],
|
||||
'@typescript-eslint/restrict-template-expressions': 'off',
|
||||
'@typescript-eslint/lines-between-class-members': 'off',
|
||||
'@typescript-eslint/member-ordering': [
|
||||
'warn',
|
||||
{
|
||||
default: [
|
||||
// Index signature
|
||||
'signature',
|
||||
'call-signature',
|
||||
|
||||
// Fields
|
||||
'#private-static-field',
|
||||
'private-static-field',
|
||||
'protected-static-field',
|
||||
'public-static-field',
|
||||
|
||||
'#private-instance-field',
|
||||
'private-instance-field',
|
||||
'protected-instance-field',
|
||||
'public-instance-field',
|
||||
|
||||
'private-decorated-field',
|
||||
'protected-decorated-field',
|
||||
'public-decorated-field',
|
||||
|
||||
'protected-abstract-field',
|
||||
'public-abstract-field',
|
||||
|
||||
'#private-field',
|
||||
'private-field',
|
||||
'protected-field',
|
||||
'public-field',
|
||||
|
||||
'static-field',
|
||||
'instance-field',
|
||||
'abstract-field',
|
||||
|
||||
'decorated-field',
|
||||
|
||||
'field',
|
||||
|
||||
// Static initialization
|
||||
'static-initialization',
|
||||
|
||||
// Constructors
|
||||
'public-constructor',
|
||||
'protected-constructor',
|
||||
'private-constructor',
|
||||
|
||||
'constructor',
|
||||
|
||||
// Getters
|
||||
'public-static-get',
|
||||
'protected-static-get',
|
||||
'private-static-get',
|
||||
'#private-static-get',
|
||||
|
||||
'public-decorated-get',
|
||||
'protected-decorated-get',
|
||||
'private-decorated-get',
|
||||
|
||||
'public-instance-get',
|
||||
'protected-instance-get',
|
||||
'private-instance-get',
|
||||
'#private-instance-get',
|
||||
|
||||
'public-abstract-get',
|
||||
'protected-abstract-get',
|
||||
|
||||
'public-get',
|
||||
'protected-get',
|
||||
'private-get',
|
||||
'#private-get',
|
||||
|
||||
'static-get',
|
||||
'instance-get',
|
||||
'abstract-get',
|
||||
|
||||
'decorated-get',
|
||||
|
||||
'get',
|
||||
|
||||
// Setters
|
||||
'public-static-set',
|
||||
'protected-static-set',
|
||||
'private-static-set',
|
||||
'#private-static-set',
|
||||
|
||||
'public-decorated-set',
|
||||
'protected-decorated-set',
|
||||
'private-decorated-set',
|
||||
|
||||
'public-instance-set',
|
||||
'protected-instance-set',
|
||||
'private-instance-set',
|
||||
'#private-instance-set',
|
||||
|
||||
'public-abstract-set',
|
||||
'protected-abstract-set',
|
||||
|
||||
'public-set',
|
||||
'protected-set',
|
||||
'private-set',
|
||||
'#private-set',
|
||||
|
||||
'static-set',
|
||||
'instance-set',
|
||||
'abstract-set',
|
||||
|
||||
'decorated-set',
|
||||
|
||||
'set',
|
||||
|
||||
// Methods
|
||||
'public-static-method',
|
||||
'protected-static-method',
|
||||
'private-static-method',
|
||||
'#private-static-method',
|
||||
|
||||
'public-decorated-method',
|
||||
'protected-decorated-method',
|
||||
'private-decorated-method',
|
||||
|
||||
'public-instance-method',
|
||||
'protected-instance-method',
|
||||
'private-instance-method',
|
||||
'#private-instance-method',
|
||||
|
||||
'public-abstract-method',
|
||||
'protected-abstract-method',
|
||||
|
||||
'public-method',
|
||||
'protected-method',
|
||||
'private-method',
|
||||
'#private-method',
|
||||
|
||||
'static-method',
|
||||
'instance-method',
|
||||
'abstract-method',
|
||||
|
||||
'decorated-method',
|
||||
|
||||
'method',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, inject, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { MatTooltip, MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { CircleButtonType, CircleButtonTypes } from '../types/circle-button.type';
|
||||
import { IqserTooltipPosition, IqserTooltipPositions, randomString } from '../../utils';
|
||||
import { NgIf } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, inject, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { StopPropagationDirective } from '../../directives';
|
||||
import { MatTooltip, MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { StopPropagationDirective } from '../../directives';
|
||||
import { IqserTooltipPosition, IqserTooltipPositions, randomString } from '../../utils';
|
||||
import { CircleButtonType, CircleButtonTypes } from '../types/circle-button.type';
|
||||
|
||||
@Component({
|
||||
selector: 'iqser-circle-button [icon]',
|
||||
selector: 'iqser-circle-button',
|
||||
templateUrl: './circle-button.component.html',
|
||||
styleUrls: ['./circle-button.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
@ -17,8 +17,12 @@ import { RouterLink } from '@angular/router';
|
||||
imports: [MatTooltipModule, MatIconModule, NgIf, MatButtonModule, StopPropagationDirective],
|
||||
})
|
||||
export class CircleButtonComponent implements OnInit {
|
||||
readonly #elementRef = inject(ElementRef<HTMLElement>);
|
||||
@ViewChild(MatTooltip) private readonly _matTooltip!: MatTooltip;
|
||||
protected readonly _circleButtonTypes = CircleButtonTypes;
|
||||
protected readonly _hasRouterLink = !!inject(RouterLink, { optional: true, host: true });
|
||||
@Input() buttonId = `${randomString()}-circle-button`;
|
||||
@Input() icon!: string;
|
||||
@Input({ required: true }) icon!: string;
|
||||
@Input() tooltip?: string;
|
||||
@Input() tooltipClass?: string;
|
||||
@Input() showDot = false;
|
||||
@ -32,12 +36,8 @@ export class CircleButtonComponent implements OnInit {
|
||||
@Input() size = 34;
|
||||
@Input() iconSize = 14;
|
||||
@Output() readonly action = new EventEmitter<MouseEvent>();
|
||||
protected readonly _circleButtonTypes = CircleButtonTypes;
|
||||
protected readonly _hasRouterLink = !!inject(RouterLink, { optional: true, host: true });
|
||||
@ViewChild(MatTooltip) private readonly _matTooltip!: MatTooltip;
|
||||
readonly #elementRef = inject(ElementRef<HTMLElement>);
|
||||
|
||||
ngOnInit(): void {
|
||||
ngOnInit() {
|
||||
this.#elementRef.nativeElement.style.setProperty('--circle-button-size', `${this.size}px`);
|
||||
this.#elementRef.nativeElement.style.setProperty('--circle-button-icon-size', `${this.iconSize}px`);
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import type { Id, ITrackable } from '../listing';
|
||||
import { UntypedFormGroup } from '@angular/forms';
|
||||
import { forOwn, has, isEqual, isPlainObject, transform } from 'lodash-es';
|
||||
import dayjs, { type Dayjs } from 'dayjs';
|
||||
import { inject } from '@angular/core';
|
||||
import { UntypedFormGroup } from '@angular/forms';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import dayjs, { type Dayjs } from 'dayjs';
|
||||
import { forOwn, has, isEqual, isPlainObject, transform } from 'lodash-es';
|
||||
import type { Id, ITrackable } from '../listing';
|
||||
|
||||
export function capitalize(value: string | string): string {
|
||||
if (!value) {
|
||||
@ -37,7 +37,49 @@ export function size(value: number): string {
|
||||
return `${(value / 1000).toFixed(2)} KB`;
|
||||
}
|
||||
|
||||
interface IReplaceOptions {
|
||||
readonly searchValue: string | RegExp;
|
||||
readonly replaceValue: string;
|
||||
}
|
||||
|
||||
const replacements: { searchValue: string; replaceValue: string }[] = [
|
||||
{ searchValue: '&', replaceValue: '&' },
|
||||
{ searchValue: ' ', replaceValue: ' ' },
|
||||
{ searchValue: '<', replaceValue: '<' },
|
||||
{ searchValue: '>', replaceValue: '>' },
|
||||
{ searchValue: '"', replaceValue: '"' },
|
||||
{ searchValue: "'", replaceValue: ''' },
|
||||
];
|
||||
const escapeReplacements: IReplaceOptions[] = replacements.map(({ searchValue, replaceValue }) => ({
|
||||
searchValue: new RegExp(searchValue, 'g'),
|
||||
replaceValue,
|
||||
}));
|
||||
const unescapeReplacements: IReplaceOptions[] = replacements.map(({ searchValue, replaceValue }) => ({
|
||||
searchValue: new RegExp(replaceValue, 'g'),
|
||||
replaceValue: searchValue,
|
||||
}));
|
||||
|
||||
export function escapeHtml<T extends unknown | string>(unsafe: T, options?: { ignoreTags: string[] }) {
|
||||
return replaceHtml(unsafe, {
|
||||
ignoreTags: options?.ignoreTags,
|
||||
replacements: escapeReplacements,
|
||||
});
|
||||
}
|
||||
|
||||
export function unescapeHtml<T extends unknown | string>(unsafe: T, options?: { ignoreTags: string[] }) {
|
||||
return replaceHtml(unsafe, {
|
||||
ignoreTags: options?.ignoreTags,
|
||||
replacements: unescapeReplacements,
|
||||
});
|
||||
}
|
||||
|
||||
export function replaceHtml<T extends unknown | string>(
|
||||
unsafe: T,
|
||||
options: {
|
||||
ignoreTags?: string[];
|
||||
replacements: IReplaceOptions[];
|
||||
},
|
||||
) {
|
||||
if (typeof unsafe !== 'string') {
|
||||
return unsafe;
|
||||
}
|
||||
@ -57,12 +99,10 @@ export function escapeHtml<T extends unknown | string>(unsafe: T, options?: { ig
|
||||
_unsafe = _unsafe.replaceAll(key, value);
|
||||
});
|
||||
|
||||
let escaped = _unsafe
|
||||
.replaceAll(/&/g, '&')
|
||||
.replaceAll(/ /g, ' ')
|
||||
.replaceAll(/</g, '<')
|
||||
.replaceAll(/>/g, '>')
|
||||
.replaceAll(/"/g, '"');
|
||||
let escaped = _unsafe;
|
||||
for (const replacement of options.replacements) {
|
||||
escaped = escaped.replaceAll(replacement.searchValue, replacement.replaceValue);
|
||||
}
|
||||
|
||||
if (ignoredTags) {
|
||||
Object.entries(ignoredTags).forEach(([key, value]) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user