mirror of https://github.com/apache/nifi.git
NIFI-13114: (#8713)
- Code clean up and removing TODO regarding enforcing isClustered to see the Primary Node indicator. This closes #8713
This commit is contained in:
parent
2c43a706f8
commit
be4c003cd7
|
@ -157,33 +157,32 @@ export class ProcessorManager {
|
||||||
return processor;
|
return processor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateProcessors(updated: any) {
|
private updateProcessors(updated: d3.Selection<any, any, any, any>) {
|
||||||
if (updated.empty()) {
|
if (updated.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const self: ProcessorManager = this;
|
|
||||||
|
|
||||||
// processor border authorization
|
// processor border authorization
|
||||||
updated
|
updated
|
||||||
.select('rect.border')
|
.select('rect.border')
|
||||||
.classed('unauthorized', function (d: any) {
|
.classed('unauthorized', (d: any) => {
|
||||||
return d.permissions.canRead === false;
|
return d.permissions.canRead === false;
|
||||||
})
|
})
|
||||||
.classed('ghost', function (d: any) {
|
.classed('ghost', (d: any) => {
|
||||||
return d.permissions.canRead === true && d.component.extensionMissing === true;
|
return d.permissions.canRead === true && d.component.extensionMissing === true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// processor body authorization
|
// processor body authorization
|
||||||
updated.select('rect.body').classed('unauthorized', function (d: any) {
|
updated.select('rect.body').classed('unauthorized', (d: any) => {
|
||||||
return d.permissions.canRead === false;
|
return d.permissions.canRead === false;
|
||||||
});
|
});
|
||||||
|
|
||||||
updated.each(function (this: any, processorData: any) {
|
updated.each((processorData: any, i, nodes) => {
|
||||||
const processor: any = d3.select(this);
|
const processor: d3.Selection<any, any, any, any> = d3.select(nodes[i]);
|
||||||
let details: any = processor.select('g.processor-canvas-details');
|
let details: any = processor.select('g.processor-canvas-details');
|
||||||
|
|
||||||
// update the component behavior as appropriate
|
// update the component behavior as appropriate
|
||||||
self.editableBehavior.editable(processor);
|
this.editableBehavior.editable(processor);
|
||||||
|
|
||||||
// if this processor is visible, render everything
|
// if this processor is visible, render everything
|
||||||
if (processor.classed('visible')) {
|
if (processor.classed('visible')) {
|
||||||
|
@ -227,7 +226,7 @@ export class ProcessorManager {
|
||||||
details
|
details
|
||||||
.append('rect')
|
.append('rect')
|
||||||
.attr('class', 'processor-stats-in-out odd')
|
.attr('class', 'processor-stats-in-out odd')
|
||||||
.attr('width', function () {
|
.attr('width', () => {
|
||||||
return processorData.dimensions.width;
|
return processorData.dimensions.width;
|
||||||
})
|
})
|
||||||
.attr('height', 19)
|
.attr('height', 19)
|
||||||
|
@ -238,7 +237,7 @@ export class ProcessorManager {
|
||||||
details
|
details
|
||||||
.append('rect')
|
.append('rect')
|
||||||
.attr('class', 'processor-stats-border')
|
.attr('class', 'processor-stats-border')
|
||||||
.attr('width', function () {
|
.attr('width', () => {
|
||||||
return processorData.dimensions.width;
|
return processorData.dimensions.width;
|
||||||
})
|
})
|
||||||
.attr('height', 1)
|
.attr('height', 1)
|
||||||
|
@ -249,7 +248,7 @@ export class ProcessorManager {
|
||||||
details
|
details
|
||||||
.append('rect')
|
.append('rect')
|
||||||
.attr('class', 'processor-read-write-stats even')
|
.attr('class', 'processor-read-write-stats even')
|
||||||
.attr('width', function () {
|
.attr('width', () => {
|
||||||
return processorData.dimensions.width;
|
return processorData.dimensions.width;
|
||||||
})
|
})
|
||||||
.attr('height', 19)
|
.attr('height', 19)
|
||||||
|
@ -260,7 +259,7 @@ export class ProcessorManager {
|
||||||
details
|
details
|
||||||
.append('rect')
|
.append('rect')
|
||||||
.attr('class', 'processor-stats-border')
|
.attr('class', 'processor-stats-border')
|
||||||
.attr('width', function () {
|
.attr('width', () => {
|
||||||
return processorData.dimensions.width;
|
return processorData.dimensions.width;
|
||||||
})
|
})
|
||||||
.attr('height', 1)
|
.attr('height', 1)
|
||||||
|
@ -271,7 +270,7 @@ export class ProcessorManager {
|
||||||
details
|
details
|
||||||
.append('rect')
|
.append('rect')
|
||||||
.attr('class', 'processor-stats-in-out odd')
|
.attr('class', 'processor-stats-in-out odd')
|
||||||
.attr('width', function () {
|
.attr('width', () => {
|
||||||
return processorData.dimensions.width;
|
return processorData.dimensions.width;
|
||||||
})
|
})
|
||||||
.attr('height', 20)
|
.attr('height', 20)
|
||||||
|
@ -282,7 +281,7 @@ export class ProcessorManager {
|
||||||
details
|
details
|
||||||
.append('rect')
|
.append('rect')
|
||||||
.attr('class', 'processor-stats-border')
|
.attr('class', 'processor-stats-border')
|
||||||
.attr('width', function () {
|
.attr('width', () => {
|
||||||
return processorData.dimensions.width;
|
return processorData.dimensions.width;
|
||||||
})
|
})
|
||||||
.attr('height', 1)
|
.attr('height', 1)
|
||||||
|
@ -293,7 +292,7 @@ export class ProcessorManager {
|
||||||
details
|
details
|
||||||
.append('rect')
|
.append('rect')
|
||||||
.attr('class', 'processor-read-write-stats even')
|
.attr('class', 'processor-read-write-stats even')
|
||||||
.attr('width', function () {
|
.attr('width', () => {
|
||||||
return processorData.dimensions.width;
|
return processorData.dimensions.width;
|
||||||
})
|
})
|
||||||
.attr('height', 19)
|
.attr('height', 19)
|
||||||
|
@ -460,7 +459,7 @@ export class ProcessorManager {
|
||||||
details
|
details
|
||||||
.append('rect')
|
.append('rect')
|
||||||
.attr('class', 'bulletin-background')
|
.attr('class', 'bulletin-background')
|
||||||
.attr('x', function () {
|
.attr('x', () => {
|
||||||
return processorData.dimensions.width - 24;
|
return processorData.dimensions.width - 24;
|
||||||
})
|
})
|
||||||
.attr('width', 24)
|
.attr('width', 24)
|
||||||
|
@ -470,7 +469,7 @@ export class ProcessorManager {
|
||||||
details
|
details
|
||||||
.append('text')
|
.append('text')
|
||||||
.attr('class', 'bulletin-icon')
|
.attr('class', 'bulletin-icon')
|
||||||
.attr('x', function () {
|
.attr('x', () => {
|
||||||
return processorData.dimensions.width - 17;
|
return processorData.dimensions.width - 17;
|
||||||
})
|
})
|
||||||
.attr('y', 17)
|
.attr('y', 17)
|
||||||
|
@ -481,60 +480,60 @@ export class ProcessorManager {
|
||||||
// update the processor name
|
// update the processor name
|
||||||
processor
|
processor
|
||||||
.select('text.processor-name')
|
.select('text.processor-name')
|
||||||
.each(function (this: any, d: any) {
|
.each((d: any, i, nodes) => {
|
||||||
const processorName = d3.select(this);
|
const processorName = d3.select(nodes[i]);
|
||||||
|
|
||||||
// reset the processor name to handle any previous state
|
// reset the processor name to handle any previous state
|
||||||
processorName.text(null).selectAll('title').remove();
|
processorName.text(null).selectAll('title').remove();
|
||||||
|
|
||||||
// apply ellipsis to the processor name as necessary
|
// apply ellipsis to the processor name as necessary
|
||||||
self.canvasUtils.ellipsis(processorName, d.component.name, 'processor-name');
|
this.canvasUtils.ellipsis(processorName, d.component.name, 'processor-name');
|
||||||
})
|
})
|
||||||
.append('title')
|
.append('title')
|
||||||
.text(function (d: any) {
|
.text((d: any) => {
|
||||||
return d.component.name;
|
return d.component.name;
|
||||||
});
|
});
|
||||||
|
|
||||||
// update the processor type
|
// update the processor type
|
||||||
processor
|
processor
|
||||||
.select('text.processor-type')
|
.select('text.processor-type')
|
||||||
.each(function (this: any, d: any) {
|
.each((d: any, i, nodes) => {
|
||||||
const processorType = d3.select(this);
|
const processorType = d3.select(nodes[i]);
|
||||||
|
|
||||||
// reset the processor type to handle any previous state
|
// reset the processor type to handle any previous state
|
||||||
processorType.text(null).selectAll('title').remove();
|
processorType.text(null).selectAll('title').remove();
|
||||||
|
|
||||||
// apply ellipsis to the processor type as necessary
|
// apply ellipsis to the processor type as necessary
|
||||||
self.canvasUtils.ellipsis(
|
this.canvasUtils.ellipsis(
|
||||||
processorType,
|
processorType,
|
||||||
self.nifiCommon.formatType(d.component),
|
this.nifiCommon.formatType(d.component),
|
||||||
'processor-type'
|
'processor-type'
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.append('title')
|
.append('title')
|
||||||
.text(function (d: any) {
|
.text((d: any) => {
|
||||||
return self.nifiCommon.formatType(d.component);
|
return this.nifiCommon.formatType(d.component);
|
||||||
});
|
});
|
||||||
|
|
||||||
// update the processor bundle
|
// update the processor bundle
|
||||||
processor
|
processor
|
||||||
.select('text.processor-bundle')
|
.select('text.processor-bundle')
|
||||||
.each(function (this: any, d: any) {
|
.each((d: any, i, nodes) => {
|
||||||
const processorBundle = d3.select(this);
|
const processorBundle = d3.select(nodes[i]);
|
||||||
|
|
||||||
// reset the processor type to handle any previous state
|
// reset the processor type to handle any previous state
|
||||||
processorBundle.text(null).selectAll('title').remove();
|
processorBundle.text(null).selectAll('title').remove();
|
||||||
|
|
||||||
// apply ellipsis to the processor type as necessary
|
// apply ellipsis to the processor type as necessary
|
||||||
self.canvasUtils.ellipsis(
|
this.canvasUtils.ellipsis(
|
||||||
processorBundle,
|
processorBundle,
|
||||||
self.nifiCommon.formatBundle(d.component.bundle),
|
this.nifiCommon.formatBundle(d.component.bundle),
|
||||||
'processor-bundle'
|
'processor-bundle'
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.append('title')
|
.append('title')
|
||||||
.text(function (d: any) {
|
.text((d: any) => {
|
||||||
return self.nifiCommon.formatBundle(d.component.bundle);
|
return this.nifiCommon.formatBundle(d.component.bundle);
|
||||||
});
|
});
|
||||||
|
|
||||||
// update the processor comments
|
// update the processor comments
|
||||||
|
@ -542,11 +541,11 @@ export class ProcessorManager {
|
||||||
.select('path.component-comments')
|
.select('path.component-comments')
|
||||||
.style(
|
.style(
|
||||||
'visibility',
|
'visibility',
|
||||||
self.nifiCommon.isBlank(processorData.component.config.comments) ? 'hidden' : 'visible'
|
this.nifiCommon.isBlank(processorData.component.config.comments) ? 'hidden' : 'visible'
|
||||||
)
|
)
|
||||||
.each(function (this: any) {
|
.each((d: any, i, nodes) => {
|
||||||
if (!self.nifiCommon.isBlank(processorData.component.config.comments)) {
|
if (!this.nifiCommon.isBlank(processorData.component.config.comments)) {
|
||||||
self.canvasUtils.canvasTooltip(TextTip, d3.select(this), {
|
this.canvasUtils.canvasTooltip(TextTip, d3.select(nodes[i]), {
|
||||||
text: processorData.component.config.comments
|
text: processorData.component.config.comments
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -566,11 +565,11 @@ export class ProcessorManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// populate the stats
|
// populate the stats
|
||||||
self.updateProcessorStatus(processor);
|
this.updateProcessorStatus(processor);
|
||||||
} else {
|
} else {
|
||||||
if (processorData.permissions.canRead) {
|
if (processorData.permissions.canRead) {
|
||||||
// update the processor name
|
// update the processor name
|
||||||
processor.select('text.processor-name').text(function (d: any) {
|
processor.select('text.processor-name').text((d: any) => {
|
||||||
const name = d.component.name;
|
const name = d.component.name;
|
||||||
if (name.length > ProcessorManager.PREVIEW_NAME_LENGTH) {
|
if (name.length > ProcessorManager.PREVIEW_NAME_LENGTH) {
|
||||||
return name.substring(0, ProcessorManager.PREVIEW_NAME_LENGTH) + String.fromCharCode(8230);
|
return name.substring(0, ProcessorManager.PREVIEW_NAME_LENGTH) + String.fromCharCode(8230);
|
||||||
|
@ -612,22 +611,22 @@ export class ProcessorManager {
|
||||||
const color = processorData.component.style['background-color'];
|
const color = processorData.component.style['background-color'];
|
||||||
|
|
||||||
//update the processor icon container
|
//update the processor icon container
|
||||||
processor.select('rect.processor-icon-container').style('fill', function () {
|
processor.select('rect.processor-icon-container').style('fill', () => {
|
||||||
return color;
|
return color;
|
||||||
});
|
});
|
||||||
|
|
||||||
//update the processor border
|
//update the processor border
|
||||||
processor.select('rect.border').style('stroke', function () {
|
processor.select('rect.border').style('stroke', () => {
|
||||||
return color;
|
return color;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (color) {
|
if (color) {
|
||||||
processor.select('text.processor-icon').attr('class', function () {
|
processor.select('text.processor-icon').attr('class', () => {
|
||||||
return 'processor-icon';
|
return 'processor-icon';
|
||||||
});
|
});
|
||||||
|
|
||||||
// update the processor color
|
// update the processor color
|
||||||
processor.style('fill', function (d: any) {
|
processor.style('fill', (d: any) => {
|
||||||
let color = 'unset';
|
let color = 'unset';
|
||||||
|
|
||||||
if (!d.permissions.canRead) {
|
if (!d.permissions.canRead) {
|
||||||
|
@ -638,8 +637,8 @@ export class ProcessorManager {
|
||||||
if (d.component.style['background-color']) {
|
if (d.component.style['background-color']) {
|
||||||
color = d.component.style['background-color'];
|
color = d.component.style['background-color'];
|
||||||
|
|
||||||
color = self.canvasUtils.determineContrastColor(
|
color = this.canvasUtils.determineContrastColor(
|
||||||
self.nifiCommon.substringAfterLast(color, '#')
|
this.nifiCommon.substringAfterLast(color, '#')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,32 +646,39 @@ export class ProcessorManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
processor.select('text.processor-icon').attr('class', function () {
|
processor.select('text.processor-icon').attr('class', () => {
|
||||||
return 'processor-icon accent-color';
|
return 'processor-icon accent-color';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// restricted component indicator
|
// restricted component indicator
|
||||||
processor.select('circle.restricted-background').style('visibility', self.showRestricted);
|
processor.select('circle.restricted-background').style('visibility', (d: any) => {
|
||||||
processor.select('text.restricted').style('visibility', self.showRestricted);
|
return this.showRestricted(d);
|
||||||
|
});
|
||||||
|
processor.select('text.restricted').style('visibility', (d: any) => {
|
||||||
|
return this.showRestricted(d);
|
||||||
|
});
|
||||||
|
|
||||||
// is primary component indicator
|
// is primary component indicator
|
||||||
processor.select('circle.is-primary-background').style('visibility', self.showIsPrimary);
|
processor.select('circle.is-primary-background').style('visibility', (d: any) => {
|
||||||
processor.select('text.is-primary').style('visibility', self.showIsPrimary);
|
return this.showIsPrimary(d);
|
||||||
|
});
|
||||||
|
processor.select('text.is-primary').style('visibility', (d: any) => {
|
||||||
|
return this.showIsPrimary(d);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateProcessorStatus(updated: any) {
|
private updateProcessorStatus(updated: d3.Selection<any, any, any, any>) {
|
||||||
if (updated.empty()) {
|
if (updated.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const self: ProcessorManager = this;
|
|
||||||
|
|
||||||
// update the run status
|
// update the run status
|
||||||
updated
|
updated
|
||||||
.select('text.run-status-icon')
|
.select('text.run-status-icon')
|
||||||
.attr('class', function (d: any) {
|
.attr('class', (d: any) => {
|
||||||
let clazz = 'primary-color';
|
let clazz = 'primary-color';
|
||||||
|
|
||||||
if (d.status.aggregateSnapshot.runStatus === 'Validating') {
|
if (d.status.aggregateSnapshot.runStatus === 'Validating') {
|
||||||
|
@ -687,17 +693,17 @@ export class ProcessorManager {
|
||||||
|
|
||||||
return `run-status-icon ${clazz}`;
|
return `run-status-icon ${clazz}`;
|
||||||
})
|
})
|
||||||
.attr('font-family', function (d: any) {
|
.attr('font-family', (d: any) => {
|
||||||
let family = 'FontAwesome';
|
let family = 'FontAwesome';
|
||||||
if (d.status.aggregateSnapshot.runStatus === 'Disabled') {
|
if (d.status.aggregateSnapshot.runStatus === 'Disabled') {
|
||||||
family = 'flowfont';
|
family = 'flowfont';
|
||||||
}
|
}
|
||||||
return family;
|
return family;
|
||||||
})
|
})
|
||||||
.classed('fa-spin', function (d: any) {
|
.classed('fa-spin', (d: any) => {
|
||||||
return d.status.aggregateSnapshot.runStatus === 'Validating';
|
return d.status.aggregateSnapshot.runStatus === 'Validating';
|
||||||
})
|
})
|
||||||
.text(function (d: any) {
|
.text((d: any) => {
|
||||||
let img = '';
|
let img = '';
|
||||||
if (d.status.aggregateSnapshot.runStatus === 'Disabled') {
|
if (d.status.aggregateSnapshot.runStatus === 'Disabled') {
|
||||||
img = '\ue802';
|
img = '\ue802';
|
||||||
|
@ -712,10 +718,10 @@ export class ProcessorManager {
|
||||||
}
|
}
|
||||||
return img;
|
return img;
|
||||||
})
|
})
|
||||||
.each(function (this: any, d: any) {
|
.each((d: any, i, nodes) => {
|
||||||
// if there are validation errors generate a tooltip
|
// if there are validation errors generate a tooltip
|
||||||
if (self.needsTip(d)) {
|
if (this.needsTip(d)) {
|
||||||
self.canvasUtils.canvasTooltip(ValidationErrorsTip, d3.select(this), {
|
this.canvasUtils.canvasTooltip(ValidationErrorsTip, d3.select(nodes[i]), {
|
||||||
isValidating: d.status.aggregateSnapshot.runStatus === 'Validating',
|
isValidating: d.status.aggregateSnapshot.runStatus === 'Validating',
|
||||||
validationErrors: d.component.validationErrors
|
validationErrors: d.component.validationErrors
|
||||||
});
|
});
|
||||||
|
@ -723,49 +729,49 @@ export class ProcessorManager {
|
||||||
});
|
});
|
||||||
|
|
||||||
// in count value
|
// in count value
|
||||||
updated.select('text.processor-in tspan.count').text(function (d: any) {
|
updated.select('text.processor-in tspan.count').text((d: any) => {
|
||||||
return self.nifiCommon.substringBeforeFirst(d.status.aggregateSnapshot.input, ' ');
|
return this.nifiCommon.substringBeforeFirst(d.status.aggregateSnapshot.input, ' ');
|
||||||
});
|
});
|
||||||
|
|
||||||
// in size value
|
// in size value
|
||||||
updated.select('text.processor-in tspan.size').text(function (d: any) {
|
updated.select('text.processor-in tspan.size').text((d: any) => {
|
||||||
return ' ' + self.nifiCommon.substringAfterFirst(d.status.aggregateSnapshot.input, ' ');
|
return ' ' + this.nifiCommon.substringAfterFirst(d.status.aggregateSnapshot.input, ' ');
|
||||||
});
|
});
|
||||||
|
|
||||||
// read/write value
|
// read/write value
|
||||||
updated.select('text.processor-read-write').text(function (d: any) {
|
updated.select('text.processor-read-write').text((d: any) => {
|
||||||
return d.status.aggregateSnapshot.read + ' / ' + d.status.aggregateSnapshot.written;
|
return d.status.aggregateSnapshot.read + ' / ' + d.status.aggregateSnapshot.written;
|
||||||
});
|
});
|
||||||
|
|
||||||
// out count value
|
// out count value
|
||||||
updated.select('text.processor-out tspan.count').text(function (d: any) {
|
updated.select('text.processor-out tspan.count').text((d: any) => {
|
||||||
return self.nifiCommon.substringBeforeFirst(d.status.aggregateSnapshot.output, ' ');
|
return this.nifiCommon.substringBeforeFirst(d.status.aggregateSnapshot.output, ' ');
|
||||||
});
|
});
|
||||||
|
|
||||||
// out size value
|
// out size value
|
||||||
updated.select('text.processor-out tspan.size').text(function (d: any) {
|
updated.select('text.processor-out tspan.size').text((d: any) => {
|
||||||
return ' ' + self.nifiCommon.substringAfterFirst(d.status.aggregateSnapshot.output, ' ');
|
return ' ' + this.nifiCommon.substringAfterFirst(d.status.aggregateSnapshot.output, ' ');
|
||||||
});
|
});
|
||||||
|
|
||||||
// tasks/time value
|
// tasks/time value
|
||||||
updated.select('text.processor-tasks-time').text(function (d: any) {
|
updated.select('text.processor-tasks-time').text((d: any) => {
|
||||||
return d.status.aggregateSnapshot.tasks + ' / ' + d.status.aggregateSnapshot.tasksDuration;
|
return d.status.aggregateSnapshot.tasks + ' / ' + d.status.aggregateSnapshot.tasksDuration;
|
||||||
});
|
});
|
||||||
|
|
||||||
updated.each(function (this: any, d: any) {
|
updated.each((d: any, i, nodes) => {
|
||||||
const processor = d3.select(this);
|
const processor = d3.select(nodes[i]);
|
||||||
|
|
||||||
// -------------------
|
// -------------------
|
||||||
// active thread count
|
// active thread count
|
||||||
// -------------------
|
// -------------------
|
||||||
|
|
||||||
self.canvasUtils.activeThreadCount(processor, d);
|
this.canvasUtils.activeThreadCount(processor, d);
|
||||||
|
|
||||||
// ---------
|
// ---------
|
||||||
// bulletins
|
// bulletins
|
||||||
// ---------
|
// ---------
|
||||||
|
|
||||||
self.canvasUtils.bulletins(processor, d.bulletins);
|
this.canvasUtils.bulletins(processor, d.bulletins);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -805,8 +811,6 @@ export class ProcessorManager {
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
private showIsPrimary(d: any): string {
|
private showIsPrimary(d: any): string {
|
||||||
// TODO
|
|
||||||
// return nfClusterSummary.isClustered() && d.status.aggregateSnapshot.executionNode === 'PRIMARY' ? 'visible' : 'hidden';
|
|
||||||
return d.status.aggregateSnapshot.executionNode === 'PRIMARY' ? 'visible' : 'hidden';
|
return d.status.aggregateSnapshot.executionNode === 'PRIMARY' ? 'visible' : 'hidden';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue