mirror of
https://github.com/iconify/iconify.git
synced 2025-01-07 15:44:05 +00:00
Replace unnecessary complex icon set validation function with basic function
This commit is contained in:
parent
0f21f877b8
commit
19e951cdb7
@ -1,6 +1,7 @@
|
|||||||
import type { IconifyJSON, IconifyIcon } from '@iconify/types';
|
import type { IconifyJSON, IconifyIcon } from '@iconify/types';
|
||||||
import type { FullIconifyIcon } from '@iconify/utils/lib/icon';
|
import type { FullIconifyIcon } from '@iconify/utils/lib/icon';
|
||||||
import { parseIconSet } from '@iconify/utils/lib/icon-set/parse';
|
import { parseIconSet } from '@iconify/utils/lib/icon-set/parse';
|
||||||
|
import { quicklyValidateIconSet } from '@iconify/utils/lib/icon-set/validate-basic';
|
||||||
import type { IconifyIconName } from '@iconify/utils/lib/icon/name';
|
import type { IconifyIconName } from '@iconify/utils/lib/icon/name';
|
||||||
import { stringToIcon, validateIcon } from '@iconify/utils/lib/icon/name';
|
import { stringToIcon, validateIcon } from '@iconify/utils/lib/icon/name';
|
||||||
import {
|
import {
|
||||||
@ -108,21 +109,17 @@ export function addCollection(data: IconifyJSON, provider?: string): boolean {
|
|||||||
) {
|
) {
|
||||||
// Simple names: add icons one by one
|
// Simple names: add icons one by one
|
||||||
let added = false;
|
let added = false;
|
||||||
parseIconSet(
|
|
||||||
data,
|
if (quicklyValidateIconSet(data)) {
|
||||||
(name, icon) => {
|
// Reset prefix
|
||||||
|
data.prefix = '';
|
||||||
|
|
||||||
|
parseIconSet(data, (name, icon) => {
|
||||||
if (icon && addIcon(name, icon)) {
|
if (icon && addIcon(name, icon)) {
|
||||||
added = true;
|
added = true;
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
{
|
}
|
||||||
// Validate icon set and set prefix to empty
|
|
||||||
validate: {
|
|
||||||
fix: true,
|
|
||||||
prefix: '',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return added;
|
return added;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import type { IconifyJSON, IconifyIcon } from '@iconify/types';
|
|||||||
import type { FullIconifyIcon } from '@iconify/utils/lib/icon';
|
import type { FullIconifyIcon } from '@iconify/utils/lib/icon';
|
||||||
import { fullIcon } from '@iconify/utils/lib/icon';
|
import { fullIcon } from '@iconify/utils/lib/icon';
|
||||||
import { parseIconSet } from '@iconify/utils/lib/icon-set/parse';
|
import { parseIconSet } from '@iconify/utils/lib/icon-set/parse';
|
||||||
|
import { quicklyValidateIconSet } from '@iconify/utils/lib/icon-set/validate-basic';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of icons
|
* List of icons
|
||||||
@ -99,6 +100,10 @@ export function getStorage(provider: string, prefix: string): IconStorage {
|
|||||||
* Returns array of added icons
|
* Returns array of added icons
|
||||||
*/
|
*/
|
||||||
export function addIconSet(storage: IconStorage, data: IconifyJSON): string[] {
|
export function addIconSet(storage: IconStorage, data: IconifyJSON): string[] {
|
||||||
|
if (!quicklyValidateIconSet(data)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const t = Date.now();
|
const t = Date.now();
|
||||||
return parseIconSet(data, (name, icon: FullIconifyIcon | null) => {
|
return parseIconSet(data, (name, icon: FullIconifyIcon | null) => {
|
||||||
if (icon) {
|
if (icon) {
|
||||||
|
@ -7,6 +7,7 @@ import type {
|
|||||||
} from '@iconify/utils/lib/customisations';
|
} from '@iconify/utils/lib/customisations';
|
||||||
import { fullIcon } from '@iconify/utils/lib/icon';
|
import { fullIcon } from '@iconify/utils/lib/icon';
|
||||||
import { parseIconSet } from '@iconify/utils/lib/icon-set/parse';
|
import { parseIconSet } from '@iconify/utils/lib/icon-set/parse';
|
||||||
|
import { quicklyValidateIconSet } from '@iconify/utils/lib/icon-set/validate-basic';
|
||||||
import type {
|
import type {
|
||||||
IconifyIconCustomisations,
|
IconifyIconCustomisations,
|
||||||
IconifyIconProps,
|
IconifyIconProps,
|
||||||
@ -116,19 +117,11 @@ export function addCollection(
|
|||||||
: prefix !== false && typeof data.prefix === 'string'
|
: prefix !== false && typeof data.prefix === 'string'
|
||||||
? data.prefix + ':'
|
? data.prefix + ':'
|
||||||
: '';
|
: '';
|
||||||
parseIconSet(
|
|
||||||
data,
|
quicklyValidateIconSet(data) &&
|
||||||
(name, icon) => {
|
parseIconSet(data, (name, icon) => {
|
||||||
if (icon) {
|
if (icon) {
|
||||||
storage[iconPrefix + name] = icon;
|
storage[iconPrefix + name] = icon;
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
{
|
|
||||||
// Allow empty prefix
|
|
||||||
validate: {
|
|
||||||
fix: true,
|
|
||||||
prefix: iconPrefix,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import type { IconifyIcon, IconifyJSON } from '@iconify/types';
|
import type { IconifyIcon, IconifyJSON } from '@iconify/types';
|
||||||
import { fullIcon } from '@iconify/utils/lib/icon';
|
import { fullIcon } from '@iconify/utils/lib/icon';
|
||||||
import { parseIconSet } from '@iconify/utils/lib/icon-set/parse';
|
import { parseIconSet } from '@iconify/utils/lib/icon-set/parse';
|
||||||
|
import { quicklyValidateIconSet } from '@iconify/utils/lib/icon-set/validate-basic';
|
||||||
import { render } from './render';
|
import { render } from './render';
|
||||||
import type { RenderResult } from './render';
|
import type { RenderResult } from './render';
|
||||||
import type { IconProps } from './props';
|
import type { IconProps } from './props';
|
||||||
@ -60,19 +61,10 @@ export function addCollection(
|
|||||||
: prefix !== false && typeof data.prefix === 'string'
|
: prefix !== false && typeof data.prefix === 'string'
|
||||||
? data.prefix + ':'
|
? data.prefix + ':'
|
||||||
: '';
|
: '';
|
||||||
parseIconSet(
|
quicklyValidateIconSet(data) &&
|
||||||
data,
|
parseIconSet(data, (name, icon) => {
|
||||||
(name, icon) => {
|
|
||||||
if (icon) {
|
if (icon) {
|
||||||
storage[iconPrefix + name] = icon;
|
storage[iconPrefix + name] = icon;
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
{
|
|
||||||
// Allow empty prefix
|
|
||||||
validate: {
|
|
||||||
fix: true,
|
|
||||||
prefix: iconPrefix,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,10 @@
|
|||||||
"require": "./lib/icon-set/validate.cjs",
|
"require": "./lib/icon-set/validate.cjs",
|
||||||
"import": "./lib/icon-set/validate.mjs"
|
"import": "./lib/icon-set/validate.mjs"
|
||||||
},
|
},
|
||||||
|
"./lib/icon-set/validate-basic": {
|
||||||
|
"require": "./lib/icon-set/validate-basic.cjs",
|
||||||
|
"import": "./lib/icon-set/validate-basic.mjs"
|
||||||
|
},
|
||||||
"./lib/icon": {
|
"./lib/icon": {
|
||||||
"require": "./lib/icon/index.cjs",
|
"require": "./lib/icon/index.cjs",
|
||||||
"import": "./lib/icon/index.mjs"
|
"import": "./lib/icon/index.mjs"
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import type { IconifyAlias, IconifyJSON } from '@iconify/types';
|
import type { IconifyAlias, IconifyJSON } from '@iconify/types';
|
||||||
import { FullIconifyIcon, iconDefaults } from '../icon';
|
import { FullIconifyIcon, iconDefaults } from '../icon';
|
||||||
import { getIconData } from './get-icon';
|
import { getIconData } from './get-icon';
|
||||||
import { IconSetValidationOptions, validateIconSet } from './validate';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Which aliases to parse:
|
* Which aliases to parse:
|
||||||
@ -35,7 +34,6 @@ export function isVariation(item: IconifyAlias): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ParseIconSetOptions {
|
export interface ParseIconSetOptions {
|
||||||
validate?: boolean | IconSetValidationOptions;
|
|
||||||
aliases?: ParseIconSetAliases;
|
aliases?: ParseIconSetAliases;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,20 +57,6 @@ export function parseIconSet(
|
|||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate icon set
|
|
||||||
const validate = options.validate;
|
|
||||||
if (validate !== false) {
|
|
||||||
// Validate icon set
|
|
||||||
try {
|
|
||||||
validateIconSet(
|
|
||||||
data,
|
|
||||||
typeof validate === 'object' ? validate : { fix: true }
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
return names;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for missing icons list returned by API
|
// Check for missing icons list returned by API
|
||||||
if (data.not_found instanceof Array) {
|
if (data.not_found instanceof Array) {
|
||||||
data.not_found.forEach((name) => {
|
data.not_found.forEach((name) => {
|
||||||
|
99
packages/utils/src/icon-set/validate-basic.ts
Normal file
99
packages/utils/src/icon-set/validate-basic.ts
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import type { IconifyJSON } from '@iconify/types';
|
||||||
|
import { iconDefaults, matchName } from '../icon';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional properties
|
||||||
|
*/
|
||||||
|
const optionalProperties = {
|
||||||
|
provider: 'string',
|
||||||
|
aliases: 'object',
|
||||||
|
not_found: 'object',
|
||||||
|
} as Record<string, string>;
|
||||||
|
|
||||||
|
for (const prop in iconDefaults) {
|
||||||
|
optionalProperties[prop] =
|
||||||
|
typeof iconDefaults[prop as keyof typeof iconDefaults];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate icon set, return it as IconifyJSON on success, null on failure
|
||||||
|
*
|
||||||
|
* Unlike validateIconSet(), this function is very basic.
|
||||||
|
* It does not throw exceptions, it does not check metadata, it does not fix stuff.
|
||||||
|
*/
|
||||||
|
export function quicklyValidateIconSet(obj: unknown): IconifyJSON | null {
|
||||||
|
// Check for object with 'icons' nested object
|
||||||
|
if (typeof obj !== 'object' || obj === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert type
|
||||||
|
const data = obj as IconifyJSON;
|
||||||
|
|
||||||
|
// Check for prefix and icons
|
||||||
|
if (
|
||||||
|
typeof data.prefix !== 'string' ||
|
||||||
|
!(obj as Record<string, unknown>).icons ||
|
||||||
|
typeof (obj as Record<string, unknown>).icons !== 'object'
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for optional properties
|
||||||
|
for (const prop in optionalProperties) {
|
||||||
|
if (
|
||||||
|
(obj as Record<string, unknown>)[prop] !== void 0 &&
|
||||||
|
typeof (obj as Record<string, unknown>)[prop] !==
|
||||||
|
optionalProperties[prop]
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check all icons
|
||||||
|
const icons = data.icons;
|
||||||
|
for (const name in icons) {
|
||||||
|
const icon = icons[name];
|
||||||
|
if (!name.match(matchName) || typeof icon.body !== 'string') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const prop in iconDefaults) {
|
||||||
|
if (
|
||||||
|
icon[prop as keyof typeof icon] !== void 0 &&
|
||||||
|
typeof icon[prop as keyof typeof icon] !==
|
||||||
|
typeof iconDefaults[prop as keyof typeof iconDefaults]
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check all aliases
|
||||||
|
const aliases = data.aliases;
|
||||||
|
if (aliases) {
|
||||||
|
for (const name in aliases) {
|
||||||
|
const icon = aliases[name];
|
||||||
|
const parent = icon.parent;
|
||||||
|
if (
|
||||||
|
!name.match(matchName) ||
|
||||||
|
typeof parent !== 'string' ||
|
||||||
|
(!icons[parent] && !aliases[parent])
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const prop in iconDefaults) {
|
||||||
|
if (
|
||||||
|
icon[prop as keyof typeof icon] !== void 0 &&
|
||||||
|
typeof icon[prop as keyof typeof icon] !==
|
||||||
|
typeof iconDefaults[prop as keyof typeof iconDefaults]
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
@ -27,6 +27,7 @@ export {
|
|||||||
// Icon set functions
|
// Icon set functions
|
||||||
export { parseIconSet, isVariation } from './icon-set/parse';
|
export { parseIconSet, isVariation } from './icon-set/parse';
|
||||||
export { validateIconSet } from './icon-set/validate';
|
export { validateIconSet } from './icon-set/validate';
|
||||||
|
export { quicklyValidateIconSet } from './icon-set/validate-basic';
|
||||||
export { expandIconSet } from './icon-set/expand';
|
export { expandIconSet } from './icon-set/expand';
|
||||||
export { minifyIconSet } from './icon-set/minify';
|
export { minifyIconSet } from './icon-set/minify';
|
||||||
export { getIcons } from './icon-set/get-icons';
|
export { getIcons } from './icon-set/get-icons';
|
||||||
|
152
packages/utils/tests/validate-basic-test.ts
Normal file
152
packages/utils/tests/validate-basic-test.ts
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
import { quicklyValidateIconSet } from '../lib/icon-set/validate-basic';
|
||||||
|
|
||||||
|
describe('Testing validation', () => {
|
||||||
|
test('Not object', () => {
|
||||||
|
expect(quicklyValidateIconSet(void 0)).toBe(null);
|
||||||
|
expect(quicklyValidateIconSet({})).toBe(null);
|
||||||
|
expect(quicklyValidateIconSet(null)).toBe(null);
|
||||||
|
expect(quicklyValidateIconSet([])).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Valid sets', () => {
|
||||||
|
expect(
|
||||||
|
quicklyValidateIconSet({
|
||||||
|
prefix: 'foo',
|
||||||
|
icons: {
|
||||||
|
bar: {
|
||||||
|
body: '<g />',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
})
|
||||||
|
).toEqual({
|
||||||
|
prefix: 'foo',
|
||||||
|
icons: {
|
||||||
|
bar: {
|
||||||
|
body: '<g />',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
quicklyValidateIconSet({
|
||||||
|
prefix: 'foo',
|
||||||
|
icons: {
|
||||||
|
bar: {
|
||||||
|
body: '<g />',
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
rotate: 0,
|
||||||
|
hFlip: false,
|
||||||
|
vFlip: true,
|
||||||
|
// Legacy property
|
||||||
|
verticalAlign: -0.14,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
aliases: {
|
||||||
|
baz: {
|
||||||
|
parent: 'bar',
|
||||||
|
hFlip: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
})
|
||||||
|
).toEqual({
|
||||||
|
prefix: 'foo',
|
||||||
|
icons: {
|
||||||
|
bar: {
|
||||||
|
body: '<g />',
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
rotate: 0,
|
||||||
|
hFlip: false,
|
||||||
|
vFlip: true,
|
||||||
|
verticalAlign: -0.14,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
aliases: {
|
||||||
|
baz: {
|
||||||
|
parent: 'bar',
|
||||||
|
hFlip: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Empty is allowed
|
||||||
|
expect(
|
||||||
|
quicklyValidateIconSet({
|
||||||
|
prefix: 'foo',
|
||||||
|
icons: {},
|
||||||
|
})
|
||||||
|
).toEqual({
|
||||||
|
prefix: 'foo',
|
||||||
|
icons: {},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Missing required properties', () => {
|
||||||
|
expect(
|
||||||
|
quicklyValidateIconSet({
|
||||||
|
prefix: 'foo',
|
||||||
|
})
|
||||||
|
).toBe(null);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
quicklyValidateIconSet({
|
||||||
|
icons: {},
|
||||||
|
})
|
||||||
|
).toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Invalid optional properties', () => {
|
||||||
|
expect(
|
||||||
|
quicklyValidateIconSet({
|
||||||
|
prefix: 'foo',
|
||||||
|
icons: {
|
||||||
|
icon1: {
|
||||||
|
body: '<path d="icon1" />',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
height: 24,
|
||||||
|
// Object
|
||||||
|
rotate: {
|
||||||
|
foo: 1,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
).toBe(null);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
quicklyValidateIconSet({
|
||||||
|
prefix: 'foo',
|
||||||
|
icons: {
|
||||||
|
icon1: {
|
||||||
|
body: '<path d="icon1" />',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
height: 24,
|
||||||
|
// Object
|
||||||
|
hFlip: null,
|
||||||
|
})
|
||||||
|
).toBe(null);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
quicklyValidateIconSet({
|
||||||
|
prefix: 'foo',
|
||||||
|
icons: {
|
||||||
|
icon1: {
|
||||||
|
body: '<path d="icon1" />',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
height: 24,
|
||||||
|
// String
|
||||||
|
width: '32',
|
||||||
|
})
|
||||||
|
).toBe(null);
|
||||||
|
});
|
||||||
|
});
|
@ -16,6 +16,7 @@ import type {
|
|||||||
} from '@iconify/utils/lib/customisations';
|
} from '@iconify/utils/lib/customisations';
|
||||||
import { fullIcon } from '@iconify/utils/lib/icon';
|
import { fullIcon } from '@iconify/utils/lib/icon';
|
||||||
import { parseIconSet } from '@iconify/utils/lib/icon-set/parse';
|
import { parseIconSet } from '@iconify/utils/lib/icon-set/parse';
|
||||||
|
import { quicklyValidateIconSet } from '@iconify/utils/lib/icon-set/validate-basic';
|
||||||
import type {
|
import type {
|
||||||
IconifyIconCustomisations,
|
IconifyIconCustomisations,
|
||||||
IconifyIconProps,
|
IconifyIconProps,
|
||||||
@ -70,21 +71,12 @@ export function addCollection(
|
|||||||
: prefix !== false && typeof data.prefix === 'string'
|
: prefix !== false && typeof data.prefix === 'string'
|
||||||
? data.prefix + ':'
|
? data.prefix + ':'
|
||||||
: '';
|
: '';
|
||||||
parseIconSet(
|
quicklyValidateIconSet(data) &&
|
||||||
data,
|
parseIconSet(data, (name, icon) => {
|
||||||
(name, icon) => {
|
|
||||||
if (icon) {
|
if (icon) {
|
||||||
storage[iconPrefix + name] = icon;
|
storage[iconPrefix + name] = icon;
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
{
|
|
||||||
// Allow empty prefix
|
|
||||||
validate: {
|
|
||||||
fix: true,
|
|
||||||
prefix: iconPrefix,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,6 +9,7 @@ import type {
|
|||||||
} from '@iconify/utils/lib/customisations';
|
} from '@iconify/utils/lib/customisations';
|
||||||
import { fullIcon } from '@iconify/utils/lib/icon';
|
import { fullIcon } from '@iconify/utils/lib/icon';
|
||||||
import { parseIconSet } from '@iconify/utils/lib/icon-set/parse';
|
import { parseIconSet } from '@iconify/utils/lib/icon-set/parse';
|
||||||
|
import { quicklyValidateIconSet } from '@iconify/utils/lib/icon-set/validate-basic';
|
||||||
import type {
|
import type {
|
||||||
IconifyIconCustomisations,
|
IconifyIconCustomisations,
|
||||||
IconifyIconProps,
|
IconifyIconProps,
|
||||||
@ -63,16 +64,10 @@ export function addCollection(
|
|||||||
: prefix !== false && typeof data.prefix === 'string'
|
: prefix !== false && typeof data.prefix === 'string'
|
||||||
? data.prefix + ':'
|
? data.prefix + ':'
|
||||||
: '';
|
: '';
|
||||||
parseIconSet(data, (name, icon) => {
|
quicklyValidateIconSet(data) && parseIconSet(data, (name, icon) => {
|
||||||
if (icon) {
|
if (icon) {
|
||||||
storage[iconPrefix + name] = icon;
|
storage[iconPrefix + name] = icon;
|
||||||
}
|
}
|
||||||
}, {
|
|
||||||
// Allow empty prefix
|
|
||||||
validate: {
|
|
||||||
fix: true,
|
|
||||||
prefix: iconPrefix,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user