diff --git a/application/pom.xml b/application/pom.xml
index 7fa427cd89..adc3f1e801 100644
--- a/application/pom.xml
+++ b/application/pom.xml
@@ -20,7 +20,7 @@
4.0.0
org.thingsboard
- 3.6.3-SNAPSHOT
+ 3.7.0-SNAPSHOT
thingsboard
application
@@ -49,7 +49,6 @@
io.netty
netty-transport-native-epoll
- ${netty.version}
linux-x86_64
@@ -212,7 +211,15 @@
com.sun.mail
- javax.mail
+ jakarta.mail
+
+
+ jakarta.xml.bind
+ jakarta.xml.bind-api
+
+
+ javax.xml.bind
+ jaxb-api
com.twilio.sdk
@@ -255,8 +262,8 @@
opensmpp-core
- org.thingsboard
- springfox-boot-starter
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
com.sun.winsw
diff --git a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json
index c84faa359e..83a96c6fe6 100644
--- a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json
+++ b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json
@@ -5,104 +5,50 @@
"mobileOrder": null,
"configuration": {
"widgets": {
- "81987f19-3eac-e4ce-b790-d96e9b54d9a0": {
+ "5eb79712-5c24-3060-7e4f-6af36b8f842d": {
"type": "timeseries",
- "sizeX": 12,
- "sizeY": 7,
+ "sizeX": 24,
+ "sizeY": 5,
"config": {
"datasources": [
{
"type": "entity",
"dataKeys": [
{
- "name": "successfulMsgs",
+ "name": "ruleEngineException",
"type": "timeseries",
- "label": "${entityName} Successful",
- "color": "#4caf50",
+ "label": "Rule Chain",
+ "color": "#2196f3",
"settings": {
- "excludeFromStacking": false,
- "hideDataByDefault": false,
- "disableDataHiding": false,
- "removeFromLegend": false,
- "showLines": true,
- "fillLines": false,
- "showPoints": false,
- "showPointShape": "circle",
- "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
- "showPointsLineWidth": 5,
- "showPointsRadius": 3,
- "showSeparateAxis": false,
- "axisPosition": "left",
- "thresholds": [
- {
- "thresholdValueSource": "predefinedValue"
- }
- ],
- "comparisonSettings": {
- "showValuesForComparison": true
- }
+ "useCellStyleFunction": false,
+ "useCellContentFunction": true,
+ "cellContentFunction": "return JSON.parse(value).ruleChainName;"
},
- "_hash": 0.15490750967648736
+ "_hash": 0.9954481282345906
},
{
- "name": "failedMsgs",
+ "name": "ruleEngineException",
"type": "timeseries",
- "label": "${entityName} Permanent Failures",
- "color": "#ef5350",
+ "label": "Rule Node",
+ "color": "#4caf50",
"settings": {
- "excludeFromStacking": false,
- "hideDataByDefault": false,
- "disableDataHiding": false,
- "removeFromLegend": false,
- "showLines": true,
- "fillLines": false,
- "showPoints": false,
- "showPointShape": "circle",
- "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
- "showPointsLineWidth": 5,
- "showPointsRadius": 3,
- "showSeparateAxis": false,
- "axisPosition": "left",
- "thresholds": [
- {
- "thresholdValueSource": "predefinedValue"
- }
- ],
- "comparisonSettings": {
- "showValuesForComparison": true
- }
+ "useCellStyleFunction": false,
+ "useCellContentFunction": true,
+ "cellContentFunction": "return JSON.parse(value).ruleNodeName;"
},
- "_hash": 0.4186621166514697
+ "_hash": 0.18580357036589978
},
{
- "name": "tmpFailed",
+ "name": "ruleEngineException",
"type": "timeseries",
- "label": "${entityName} Processing Failures",
- "color": "#ffc107",
+ "label": "Latest Error",
+ "color": "#f44336",
"settings": {
- "excludeFromStacking": false,
- "hideDataByDefault": false,
- "disableDataHiding": false,
- "removeFromLegend": false,
- "showLines": true,
- "fillLines": false,
- "showPoints": false,
- "showPointShape": "circle",
- "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
- "showPointsLineWidth": 5,
- "showPointsRadius": 3,
- "showSeparateAxis": false,
- "axisPosition": "left",
- "thresholds": [
- {
- "thresholdValueSource": "predefinedValue"
- }
- ],
- "comparisonSettings": {
- "showValuesForComparison": true
- }
+ "useCellStyleFunction": false,
+ "useCellContentFunction": true,
+ "cellContentFunction": "return JSON.parse(value).message;"
},
- "_hash": 0.49891007198715376
+ "_hash": 0.7255162989552142
}
],
"entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018"
@@ -111,312 +57,905 @@
"timewindow": {
"realtime": {
"interval": 1000,
- "timewindowMs": 300000
+ "timewindowMs": 86400000
},
"aggregation": {
"type": "NONE",
- "limit": 8640
- },
- "hideInterval": false,
- "hideAggregation": false,
- "hideAggInterval": false
+ "limit": 200
+ }
},
"showTitle": true,
- "backgroundColor": "#fff",
+ "backgroundColor": "rgb(255, 255, 255)",
"color": "rgba(0, 0, 0, 0.87)",
"padding": "8px",
"settings": {
- "shadowSize": 4,
- "fontColor": "#545454",
- "fontSize": 10,
- "xaxis": {
- "showLabels": true,
- "color": "#545454"
- },
- "yaxis": {
- "showLabels": true,
- "color": "#545454"
- },
- "grid": {
- "color": "#545454",
- "tickColor": "#DDDDDD",
- "verticalLines": true,
- "horizontalLines": true,
- "outlineWidth": 1
- },
- "stack": false,
- "tooltipIndividual": false,
- "timeForComparison": "months",
- "xaxisSecond": {
- "axisPosition": "top",
- "showLabels": true
- },
- "showLegend": true,
- "legendConfig": {
- "direction": "column",
- "position": "bottom",
- "showMin": true,
- "showMax": true,
- "showAvg": false,
- "showTotal": true
- }
+ "showTimestamp": true,
+ "displayPagination": true,
+ "defaultPageSize": 10
},
- "title": "Queue Stats",
+ "title": "Exceptions",
"dropShadow": true,
"enableFullscreen": true,
"titleStyle": {
"fontSize": "16px",
"fontWeight": 400
},
- "mobileHeight": null,
+ "useDashboardTimewindow": false,
+ "showLegend": false,
+ "widgetStyle": {},
+ "actions": {},
"showTitleIcon": false,
"titleIcon": null,
"iconColor": "rgba(0, 0, 0, 0.87)",
"iconSize": "24px",
"titleTooltip": "",
- "widgetStyle": {},
- "useDashboardTimewindow": false,
- "displayTimewindow": true,
- "actions": {}
+ "displayTimewindow": true
},
- "id": "81987f19-3eac-e4ce-b790-d96e9b54d9a0",
- "typeFullFqn": "system.charts.basic_timeseries"
+ "id": "5eb79712-5c24-3060-7e4f-6af36b8f842d",
+ "typeFullFqn": "system.cards.timeseries_table"
},
- "5eb79712-5c24-3060-7e4f-6af36b8f842d": {
+ "42face47-730d-f930-fef5-2a1ef6304b16": {
+ "typeFullFqn": "system.time_series_chart",
"type": "timeseries",
- "sizeX": 24,
+ "sizeX": 8,
"sizeY": 5,
"config": {
"datasources": [
{
"type": "entity",
+ "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018",
"dataKeys": [
{
- "name": "ruleEngineException",
+ "name": "successfulMsgs",
"type": "timeseries",
- "label": "Rule Chain",
- "color": "#2196f3",
+ "label": "{i18n:api-usage.successful}",
+ "color": "#4caf50",
"settings": {
- "useCellStyleFunction": false,
- "useCellContentFunction": true,
- "cellContentFunction": "return JSON.parse(value).ruleChainName;"
+ "yAxisId": "default",
+ "showInLegend": true,
+ "dataHiddenByDefault": false,
+ "type": "line",
+ "lineSettings": {
+ "showLine": true,
+ "step": false,
+ "stepType": "start",
+ "smooth": false,
+ "lineType": "solid",
+ "lineWidth": 2.5,
+ "showPoints": false,
+ "showPointLabel": false,
+ "pointLabelPosition": "top",
+ "pointLabelFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "pointLabelColor": "rgba(0, 0, 0, 0.76)",
+ "pointShape": "circle",
+ "pointSize": 12,
+ "fillAreaSettings": {
+ "type": "none",
+ "opacity": 0.4,
+ "gradient": {
+ "start": 100,
+ "end": 0
+ }
+ }
+ },
+ "barSettings": {
+ "showBorder": false,
+ "borderWidth": 2,
+ "borderRadius": 0,
+ "showLabel": false,
+ "labelPosition": "top",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.76)",
+ "backgroundSettings": {
+ "type": "none",
+ "opacity": 0.4,
+ "gradient": {
+ "start": 100,
+ "end": 0
+ }
+ }
+ }
},
- "_hash": 0.9954481282345906
+ "_hash": 0.15490750967648736,
+ "aggregationType": null,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
},
{
- "name": "ruleEngineException",
+ "name": "failedMsgs",
"type": "timeseries",
- "label": "Rule Node",
- "color": "#4caf50",
+ "label": "{i18n:api-usage.permanent-failures}",
+ "color": "#ef5350",
"settings": {
- "useCellStyleFunction": false,
- "useCellContentFunction": true,
- "cellContentFunction": "return JSON.parse(value).ruleNodeName;"
+ "yAxisId": "default",
+ "showInLegend": true,
+ "dataHiddenByDefault": false,
+ "type": "line",
+ "lineSettings": {
+ "showLine": true,
+ "step": false,
+ "stepType": "start",
+ "smooth": false,
+ "lineType": "solid",
+ "lineWidth": 2.5,
+ "showPoints": false,
+ "showPointLabel": false,
+ "pointLabelPosition": "top",
+ "pointLabelFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "pointLabelColor": "rgba(0, 0, 0, 0.76)",
+ "pointShape": "circle",
+ "pointSize": 12,
+ "fillAreaSettings": {
+ "type": "none",
+ "opacity": 0.4,
+ "gradient": {
+ "start": 100,
+ "end": 0
+ }
+ }
+ },
+ "barSettings": {
+ "showBorder": false,
+ "borderWidth": 2,
+ "borderRadius": 0,
+ "showLabel": false,
+ "labelPosition": "top",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.76)",
+ "backgroundSettings": {
+ "type": "none",
+ "opacity": 0.4,
+ "gradient": {
+ "start": 100,
+ "end": 0
+ }
+ }
+ }
},
- "_hash": 0.18580357036589978
+ "_hash": 0.4186621166514697,
+ "aggregationType": null,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
},
{
- "name": "ruleEngineException",
+ "name": "tmpFailed",
"type": "timeseries",
- "label": "Latest Error",
- "color": "#f44336",
+ "label": "{i18n:api-usage.processing-failures}",
+ "color": "#ffc107",
"settings": {
- "useCellStyleFunction": false,
- "useCellContentFunction": true,
- "cellContentFunction": "return JSON.parse(value).message;"
+ "yAxisId": "default",
+ "showInLegend": true,
+ "dataHiddenByDefault": false,
+ "type": "line",
+ "lineSettings": {
+ "showLine": true,
+ "step": false,
+ "stepType": "start",
+ "smooth": false,
+ "lineType": "solid",
+ "lineWidth": 2.5,
+ "showPoints": false,
+ "showPointLabel": false,
+ "pointLabelPosition": "top",
+ "pointLabelFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "pointLabelColor": "rgba(0, 0, 0, 0.76)",
+ "pointShape": "circle",
+ "pointSize": 12,
+ "fillAreaSettings": {
+ "type": "none",
+ "opacity": 0.4,
+ "gradient": {
+ "start": 100,
+ "end": 0
+ }
+ }
+ },
+ "barSettings": {
+ "showBorder": false,
+ "borderWidth": 2,
+ "borderRadius": 0,
+ "showLabel": false,
+ "labelPosition": "top",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.76)",
+ "backgroundSettings": {
+ "type": "none",
+ "opacity": 0.4,
+ "gradient": {
+ "start": 100,
+ "end": 0
+ }
+ }
+ }
},
- "_hash": 0.7255162989552142
+ "_hash": 0.49891007198715376,
+ "aggregationType": null,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
}
],
- "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018"
+ "alarmFilterConfig": {
+ "statusList": [
+ "ACTIVE"
+ ]
+ }
}
],
"timewindow": {
+ "hideInterval": false,
+ "hideLastInterval": false,
+ "hideQuickInterval": false,
+ "hideAggregation": false,
+ "hideAggInterval": false,
+ "hideTimezone": false,
+ "selectedTab": 0,
"realtime": {
- "interval": 1000,
- "timewindowMs": 86400000
+ "realtimeType": 0,
+ "timewindowMs": 300000,
+ "quickInterval": "CURRENT_DAY",
+ "interval": 1000
},
"aggregation": {
"type": "NONE",
- "limit": 200
+ "limit": 8640
}
},
"showTitle": true,
- "backgroundColor": "rgb(255, 255, 255)",
+ "backgroundColor": "#FFFFFF",
"color": "rgba(0, 0, 0, 0.87)",
- "padding": "8px",
+ "padding": "0px",
"settings": {
- "showTimestamp": true,
- "displayPagination": true,
- "defaultPageSize": 10
+ "yAxes": {
+ "default": {
+ "units": null,
+ "decimals": 0,
+ "show": true,
+ "label": "",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "600",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.54)",
+ "position": "left",
+ "showTickLabels": true,
+ "tickLabelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "tickLabelColor": "rgba(0, 0, 0, 0.54)",
+ "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;",
+ "showTicks": true,
+ "ticksColor": "rgba(0, 0, 0, 0.54)",
+ "showLine": true,
+ "lineColor": "rgba(0, 0, 0, 0.54)",
+ "showSplitLines": true,
+ "splitLinesColor": "rgba(0, 0, 0, 0.12)",
+ "id": "default",
+ "order": 0,
+ "min": null,
+ "max": null
+ }
+ },
+ "thresholds": [],
+ "dataZoom": true,
+ "stack": false,
+ "xAxis": {
+ "show": true,
+ "label": "",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "600",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.54)",
+ "position": "bottom",
+ "showTickLabels": true,
+ "tickLabelFont": {
+ "family": "Roboto",
+ "size": 10,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "tickLabelColor": "rgba(0, 0, 0, 0.54)",
+ "showTicks": true,
+ "ticksColor": "rgba(0, 0, 0, 0.54)",
+ "showLine": true,
+ "lineColor": "rgba(0, 0, 0, 0.54)",
+ "showSplitLines": true,
+ "splitLinesColor": "rgba(0, 0, 0, 0.12)"
+ },
+ "noAggregationBarWidthSettings": {
+ "strategy": "group",
+ "groupWidth": {
+ "relative": false,
+ "relativeWidth": 2,
+ "absoluteWidth": 1800000
+ },
+ "barWidth": {
+ "relative": true,
+ "relativeWidth": 2,
+ "absoluteWidth": 1000
+ }
+ },
+ "showLegend": true,
+ "legendLabelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "16px"
+ },
+ "legendLabelColor": "rgba(0, 0, 0, 0.76)",
+ "legendConfig": {
+ "direction": "column",
+ "position": "bottom",
+ "sortDataKeys": false,
+ "showMin": true,
+ "showMax": true,
+ "showAvg": false,
+ "showTotal": true,
+ "showLatest": false
+ },
+ "showTooltip": true,
+ "tooltipTrigger": "axis",
+ "tooltipValueFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "500",
+ "lineHeight": "16px"
+ },
+ "tooltipValueColor": "rgba(0, 0, 0, 0.76)",
+ "tooltipShowDate": true,
+ "tooltipDateFormat": {
+ "format": "yyyy-MM-dd HH:mm:ss",
+ "lastUpdateAgo": false,
+ "custom": false
+ },
+ "tooltipDateFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "16px"
+ },
+ "tooltipDateColor": "rgba(0, 0, 0, 0.76)",
+ "tooltipDateInterval": true,
+ "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)",
+ "tooltipBackgroundBlur": 4,
+ "animation": {
+ "animation": true,
+ "animationThreshold": 2000,
+ "animationDuration": 300,
+ "animationEasing": "cubicOut",
+ "animationDelay": 0,
+ "animationDurationUpdate": 300,
+ "animationEasingUpdate": "cubicOut",
+ "animationDelayUpdate": 0
+ },
+ "background": {
+ "type": "color",
+ "color": "#fff",
+ "overlay": {
+ "enabled": false,
+ "color": "rgba(255,255,255,0.72)",
+ "blur": 3
+ }
+ },
+ "padding": "12px"
},
- "title": "Exceptions",
+ "title": "{i18n:api-usage.queue-stats}",
"dropShadow": true,
"enableFullscreen": true,
- "titleStyle": {
- "fontSize": "16px",
- "fontWeight": 400
- },
- "useDashboardTimewindow": false,
- "showLegend": false,
- "widgetStyle": {},
+ "titleStyle": null,
+ "configMode": "basic",
"actions": {},
"showTitleIcon": false,
- "titleIcon": null,
- "iconColor": "rgba(0, 0, 0, 0.87)",
- "iconSize": "24px",
+ "titleIcon": "thermostat",
+ "iconColor": "#1F6BDD",
+ "useDashboardTimewindow": false,
+ "displayTimewindow": true,
+ "titleFont": {
+ "size": 16,
+ "sizeUnit": "px",
+ "family": "Roboto",
+ "weight": "500",
+ "style": "normal",
+ "lineHeight": "24px"
+ },
+ "titleColor": "rgba(0, 0, 0, 0.87)",
"titleTooltip": "",
- "displayTimewindow": true
+ "widgetStyle": {},
+ "widgetCss": "",
+ "pageSize": 1024,
+ "units": "",
+ "decimals": null,
+ "noDataDisplayMessage": "",
+ "timewindowStyle": {
+ "showIcon": false,
+ "iconSize": "24px",
+ "icon": null,
+ "iconPosition": "left",
+ "font": {
+ "size": 12,
+ "sizeUnit": "px",
+ "family": "Roboto",
+ "weight": "400",
+ "style": "normal",
+ "lineHeight": "16px"
+ },
+ "color": "rgba(0, 0, 0, 0.38)",
+ "displayTypePrefix": true
+ },
+ "margin": "0px",
+ "borderRadius": "0px",
+ "iconSize": "0px"
},
- "id": "5eb79712-5c24-3060-7e4f-6af36b8f842d",
- "typeFullFqn": "system.cards.timeseries_table"
+ "row": 0,
+ "col": 0,
+ "id": "42face47-730d-f930-fef5-2a1ef6304b16"
},
- "ad3f1417-87a8-750e-fc67-49a2de1466d4": {
+ "6a74ab56-cb36-e75e-c094-a51a896da94a": {
+ "typeFullFqn": "system.time_series_chart",
"type": "timeseries",
- "sizeX": 12,
- "sizeY": 7,
+ "sizeX": 8,
+ "sizeY": 5,
"config": {
"datasources": [
{
"type": "entity",
+ "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018",
"dataKeys": [
{
"name": "timeoutMsgs",
"type": "timeseries",
- "label": "${entityName} Permanent Timeouts",
+ "label": "{i18n:api-usage.permanent-timeouts}",
"color": "#4caf50",
"settings": {
- "excludeFromStacking": false,
- "hideDataByDefault": false,
- "disableDataHiding": false,
- "removeFromLegend": false,
- "showLines": true,
- "fillLines": false,
- "showPoints": false,
- "showPointShape": "circle",
- "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
- "showPointsLineWidth": 5,
- "showPointsRadius": 3,
- "showSeparateAxis": false,
- "axisPosition": "left",
- "thresholds": [
- {
- "thresholdValueSource": "predefinedValue"
+ "yAxisId": "default",
+ "showInLegend": true,
+ "dataHiddenByDefault": false,
+ "type": "line",
+ "lineSettings": {
+ "showLine": true,
+ "step": false,
+ "stepType": "start",
+ "smooth": false,
+ "lineType": "solid",
+ "lineWidth": 2.5,
+ "showPoints": false,
+ "showPointLabel": false,
+ "pointLabelPosition": "top",
+ "pointLabelFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "pointLabelColor": "rgba(0, 0, 0, 0.76)",
+ "pointShape": "circle",
+ "pointSize": 12,
+ "fillAreaSettings": {
+ "type": "none",
+ "opacity": 0.4,
+ "gradient": {
+ "start": 100,
+ "end": 0
+ }
+ }
+ },
+ "barSettings": {
+ "showBorder": false,
+ "borderWidth": 2,
+ "borderRadius": 0,
+ "showLabel": false,
+ "labelPosition": "top",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.76)",
+ "backgroundSettings": {
+ "type": "none",
+ "opacity": 0.4,
+ "gradient": {
+ "start": 100,
+ "end": 0
+ }
}
- ],
- "comparisonSettings": {
- "showValuesForComparison": true
}
},
- "_hash": 0.565222981550328
+ "_hash": 0.565222981550328,
+ "aggregationType": null,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
},
{
"name": "tmpTimeout",
"type": "timeseries",
- "label": "${entityName} Processing Timeouts",
+ "label": "{i18n:api-usage.processing-timeouts}",
"color": "#9c27b0",
"settings": {
- "excludeFromStacking": false,
- "hideDataByDefault": false,
- "disableDataHiding": false,
- "removeFromLegend": false,
- "showLines": true,
- "fillLines": false,
- "showPoints": false,
- "showPointShape": "circle",
- "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
- "showPointsLineWidth": 5,
- "showPointsRadius": 3,
- "showSeparateAxis": false,
- "axisPosition": "left",
- "thresholds": [
- {
- "thresholdValueSource": "predefinedValue"
+ "yAxisId": "default",
+ "showInLegend": true,
+ "dataHiddenByDefault": false,
+ "type": "line",
+ "lineSettings": {
+ "showLine": true,
+ "step": false,
+ "stepType": "start",
+ "smooth": false,
+ "lineType": "solid",
+ "lineWidth": 2.5,
+ "showPoints": false,
+ "showPointLabel": false,
+ "pointLabelPosition": "top",
+ "pointLabelFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "pointLabelColor": "rgba(0, 0, 0, 0.76)",
+ "pointShape": "circle",
+ "pointSize": 12,
+ "fillAreaSettings": {
+ "type": "none",
+ "opacity": 0.4,
+ "gradient": {
+ "start": 100,
+ "end": 0
+ }
+ }
+ },
+ "barSettings": {
+ "showBorder": false,
+ "borderWidth": 2,
+ "borderRadius": 0,
+ "showLabel": false,
+ "labelPosition": "top",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.76)",
+ "backgroundSettings": {
+ "type": "none",
+ "opacity": 0.4,
+ "gradient": {
+ "start": 100,
+ "end": 0
+ }
}
- ],
- "comparisonSettings": {
- "showValuesForComparison": true
}
},
- "_hash": 0.2679547062508352
+ "_hash": 0.2679547062508352,
+ "aggregationType": null,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
}
],
- "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018"
+ "alarmFilterConfig": {
+ "statusList": [
+ "ACTIVE"
+ ]
+ }
}
],
"timewindow": {
+ "hideInterval": false,
+ "hideLastInterval": false,
+ "hideQuickInterval": false,
+ "hideAggregation": false,
+ "hideAggInterval": false,
+ "hideTimezone": false,
+ "selectedTab": 0,
"realtime": {
- "interval": 1000,
- "timewindowMs": 300000
+ "realtimeType": 0,
+ "timewindowMs": 300000,
+ "quickInterval": "CURRENT_DAY",
+ "interval": 1000
},
"aggregation": {
"type": "NONE",
"limit": 8640
- },
- "hideInterval": false,
- "hideAggregation": false,
- "hideAggInterval": false
+ }
},
"showTitle": true,
- "backgroundColor": "#fff",
+ "backgroundColor": "#FFFFFF",
"color": "rgba(0, 0, 0, 0.87)",
- "padding": "8px",
+ "padding": "0px",
"settings": {
- "shadowSize": 4,
- "fontColor": "#545454",
- "fontSize": 10,
- "xaxis": {
- "showLabels": true,
- "color": "#545454"
- },
- "yaxis": {
- "showLabels": true,
- "color": "#545454"
- },
- "grid": {
- "color": "#545454",
- "tickColor": "#DDDDDD",
- "verticalLines": true,
- "horizontalLines": true,
- "outlineWidth": 1
+ "yAxes": {
+ "default": {
+ "units": null,
+ "decimals": 0,
+ "show": true,
+ "label": "",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "600",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.54)",
+ "position": "left",
+ "showTickLabels": true,
+ "tickLabelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "tickLabelColor": "rgba(0, 0, 0, 0.54)",
+ "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;",
+ "showTicks": true,
+ "ticksColor": "rgba(0, 0, 0, 0.54)",
+ "showLine": true,
+ "lineColor": "rgba(0, 0, 0, 0.54)",
+ "showSplitLines": true,
+ "splitLinesColor": "rgba(0, 0, 0, 0.12)",
+ "id": "default",
+ "order": 0,
+ "min": null,
+ "max": null
+ }
},
+ "thresholds": [],
+ "dataZoom": true,
"stack": false,
- "tooltipIndividual": false,
- "timeForComparison": "months",
- "xaxisSecond": {
- "axisPosition": "top",
- "showLabels": true
+ "xAxis": {
+ "show": true,
+ "label": "",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "600",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.54)",
+ "position": "bottom",
+ "showTickLabels": true,
+ "tickLabelFont": {
+ "family": "Roboto",
+ "size": 10,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "tickLabelColor": "rgba(0, 0, 0, 0.54)",
+ "showTicks": true,
+ "ticksColor": "rgba(0, 0, 0, 0.54)",
+ "showLine": true,
+ "lineColor": "rgba(0, 0, 0, 0.54)",
+ "showSplitLines": true,
+ "splitLinesColor": "rgba(0, 0, 0, 0.12)"
+ },
+ "noAggregationBarWidthSettings": {
+ "strategy": "group",
+ "groupWidth": {
+ "relative": false,
+ "relativeWidth": 2,
+ "absoluteWidth": 1800000
+ },
+ "barWidth": {
+ "relative": true,
+ "relativeWidth": 2,
+ "absoluteWidth": 1000
+ }
},
"showLegend": true,
+ "legendLabelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "16px"
+ },
+ "legendLabelColor": "rgba(0, 0, 0, 0.76)",
"legendConfig": {
"direction": "column",
"position": "bottom",
+ "sortDataKeys": false,
"showMin": true,
"showMax": true,
"showAvg": false,
- "showTotal": true
- }
+ "showTotal": true,
+ "showLatest": false
+ },
+ "showTooltip": true,
+ "tooltipTrigger": "axis",
+ "tooltipValueFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "500",
+ "lineHeight": "16px"
+ },
+ "tooltipValueColor": "rgba(0, 0, 0, 0.76)",
+ "tooltipShowDate": true,
+ "tooltipDateFormat": {
+ "format": "yyyy-MM-dd HH:mm:ss",
+ "lastUpdateAgo": false,
+ "custom": false
+ },
+ "tooltipDateFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "16px"
+ },
+ "tooltipDateColor": "rgba(0, 0, 0, 0.76)",
+ "tooltipDateInterval": true,
+ "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)",
+ "tooltipBackgroundBlur": 4,
+ "animation": {
+ "animation": true,
+ "animationThreshold": 2000,
+ "animationDuration": 300,
+ "animationEasing": "cubicOut",
+ "animationDelay": 0,
+ "animationDurationUpdate": 300,
+ "animationEasingUpdate": "cubicOut",
+ "animationDelayUpdate": 0
+ },
+ "background": {
+ "type": "color",
+ "color": "#fff",
+ "overlay": {
+ "enabled": false,
+ "color": "rgba(255,255,255,0.72)",
+ "blur": 3
+ }
+ },
+ "padding": "12px"
},
- "title": "Processing Failures and Timeouts",
+ "title": "{i18n:api-usage.processing-failures-and-timeouts}",
"dropShadow": true,
"enableFullscreen": true,
- "titleStyle": {
- "fontSize": "16px",
- "fontWeight": 400
- },
- "mobileHeight": null,
+ "titleStyle": null,
+ "configMode": "basic",
+ "actions": {},
"showTitleIcon": false,
- "titleIcon": null,
- "iconColor": "rgba(0, 0, 0, 0.87)",
- "iconSize": "24px",
- "titleTooltip": "",
- "widgetStyle": {},
+ "titleIcon": "thermostat",
+ "iconColor": "#1F6BDD",
"useDashboardTimewindow": false,
"displayTimewindow": true,
- "actions": {}
+ "titleFont": {
+ "size": 16,
+ "sizeUnit": "px",
+ "family": "Roboto",
+ "weight": "500",
+ "style": "normal",
+ "lineHeight": "24px"
+ },
+ "titleColor": "rgba(0, 0, 0, 0.87)",
+ "titleTooltip": "",
+ "widgetStyle": {},
+ "widgetCss": "",
+ "pageSize": 1024,
+ "units": "",
+ "decimals": null,
+ "noDataDisplayMessage": "",
+ "timewindowStyle": {
+ "showIcon": false,
+ "iconSize": "24px",
+ "icon": null,
+ "iconPosition": "left",
+ "font": {
+ "size": 12,
+ "sizeUnit": "px",
+ "family": "Roboto",
+ "weight": "400",
+ "style": "normal",
+ "lineHeight": "16px"
+ },
+ "color": "rgba(0, 0, 0, 0.38)",
+ "displayTypePrefix": true
+ },
+ "margin": "0px",
+ "borderRadius": "0px",
+ "iconSize": "0px"
},
- "id": "ad3f1417-87a8-750e-fc67-49a2de1466d4",
- "typeFullFqn": "system.charts.basic_timeseries"
+ "row": 0,
+ "col": 0,
+ "id": "6a74ab56-cb36-e75e-c094-a51a896da94a"
}
},
"states": {
@@ -426,23 +965,21 @@
"layouts": {
"main": {
"widgets": {
- "81987f19-3eac-e4ce-b790-d96e9b54d9a0": {
- "sizeX": 12,
- "sizeY": 7,
- "mobileHeight": null,
- "row": 0,
- "col": 0
- },
"5eb79712-5c24-3060-7e4f-6af36b8f842d": {
"sizeX": 24,
"sizeY": 5,
"row": 7,
"col": 0
},
- "ad3f1417-87a8-750e-fc67-49a2de1466d4": {
+ "42face47-730d-f930-fef5-2a1ef6304b16": {
+ "sizeX": 12,
+ "sizeY": 7,
+ "row": 0,
+ "col": 0
+ },
+ "6a74ab56-cb36-e75e-c094-a51a896da94a": {
"sizeX": 12,
"sizeY": 7,
- "mobileHeight": null,
"row": 0,
"col": 12
}
@@ -511,6 +1048,5 @@
},
"filters": {}
},
- "externalId": null,
"name": "Rule Engine Statistics"
}
\ No newline at end of file
diff --git a/application/src/main/data/json/demo/dashboards/thermostats.json b/application/src/main/data/json/demo/dashboards/thermostats.json
index 967c1cb253..223722fee1 100644
--- a/application/src/main/data/json/demo/dashboards/thermostats.json
+++ b/application/src/main/data/json/demo/dashboards/thermostats.json
@@ -325,240 +325,6 @@
"id": "7943196b-eedb-d422-f9c3-b32d379ad172",
"typeFullFqn": "system.alarm_widgets.alarms_table"
},
- "14a19183-f0b2-d6be-0f62-9863f0a51111": {
- "type": "timeseries",
- "sizeX": 18,
- "sizeY": 6,
- "config": {
- "datasources": [
- {
- "type": "entity",
- "dataKeys": [
- {
- "name": "temperature",
- "type": "timeseries",
- "label": "Temperature",
- "color": "#ef5350",
- "settings": {
- "excludeFromStacking": false,
- "hideDataByDefault": false,
- "disableDataHiding": false,
- "removeFromLegend": false,
- "showLines": true,
- "fillLines": true,
- "showPoints": false,
- "showPointShape": "circle",
- "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
- "showPointsLineWidth": 5,
- "showPointsRadius": 3,
- "showSeparateAxis": false,
- "axisPosition": "left",
- "thresholds": [
- {
- "thresholdValueSource": "predefinedValue"
- }
- ],
- "comparisonSettings": {
- "showValuesForComparison": true
- }
- },
- "_hash": 0.7852346160709658,
- "units": "°C",
- "decimals": 1
- }
- ],
- "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547"
- }
- ],
- "timewindow": {
- "realtime": {
- "interval": 30000,
- "timewindowMs": 3600000
- },
- "aggregation": {
- "type": "AVG",
- "limit": 25000
- }
- },
- "showTitle": true,
- "backgroundColor": "#fff",
- "color": "rgba(0, 0, 0, 0.87)",
- "padding": "8px",
- "settings": {
- "shadowSize": 4,
- "fontColor": "#545454",
- "fontSize": 10,
- "xaxis": {
- "showLabels": true,
- "color": "#545454"
- },
- "yaxis": {
- "showLabels": true,
- "color": "#545454"
- },
- "grid": {
- "color": "#545454",
- "tickColor": "#DDDDDD",
- "verticalLines": true,
- "horizontalLines": true,
- "outlineWidth": 1
- },
- "stack": false,
- "tooltipIndividual": false,
- "timeForComparison": "months",
- "xaxisSecond": {
- "axisPosition": "top",
- "showLabels": true
- },
- "smoothLines": true,
- "showLegend": true,
- "legendConfig": {
- "direction": "column",
- "position": "bottom",
- "showMin": true,
- "showMax": true,
- "showAvg": true,
- "showTotal": false
- }
- },
- "title": "Temperature",
- "dropShadow": true,
- "enableFullscreen": true,
- "titleStyle": {
- "fontSize": "16px",
- "fontWeight": 400
- },
- "mobileHeight": null,
- "showTitleIcon": false,
- "titleIcon": null,
- "iconColor": "rgba(0, 0, 0, 0.87)",
- "iconSize": "24px",
- "titleTooltip": "",
- "widgetStyle": {},
- "useDashboardTimewindow": false,
- "displayTimewindow": true,
- "actions": {}
- },
- "id": "14a19183-f0b2-d6be-0f62-9863f0a51111",
- "typeFullFqn": "system.charts.basic_timeseries"
- },
- "07f49fd5-a73b-d74c-c220-362c20af81f4": {
- "type": "timeseries",
- "sizeX": 18,
- "sizeY": 6,
- "config": {
- "datasources": [
- {
- "type": "entity",
- "dataKeys": [
- {
- "name": "humidity",
- "type": "timeseries",
- "label": "Humidity",
- "color": "#2196f3",
- "settings": {
- "excludeFromStacking": false,
- "hideDataByDefault": false,
- "disableDataHiding": false,
- "removeFromLegend": false,
- "showLines": true,
- "fillLines": true,
- "showPoints": false,
- "showPointShape": "circle",
- "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);",
- "showPointsLineWidth": 5,
- "showPointsRadius": 3,
- "showSeparateAxis": false,
- "axisPosition": "left",
- "thresholds": [
- {
- "thresholdValueSource": "predefinedValue"
- }
- ],
- "comparisonSettings": {
- "showValuesForComparison": true
- }
- },
- "_hash": 0.28640715926957183,
- "units": "%",
- "decimals": 0
- }
- ],
- "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547"
- }
- ],
- "timewindow": {
- "realtime": {
- "interval": 30000,
- "timewindowMs": 3600000
- },
- "aggregation": {
- "type": "AVG",
- "limit": 25000
- }
- },
- "showTitle": true,
- "backgroundColor": "#fff",
- "color": "rgba(0, 0, 0, 0.87)",
- "padding": "8px",
- "settings": {
- "shadowSize": 4,
- "fontColor": "#545454",
- "fontSize": 10,
- "xaxis": {
- "showLabels": true,
- "color": "#545454"
- },
- "yaxis": {
- "showLabels": true,
- "color": "#545454"
- },
- "grid": {
- "color": "#545454",
- "tickColor": "#DDDDDD",
- "verticalLines": true,
- "horizontalLines": true,
- "outlineWidth": 1
- },
- "stack": false,
- "tooltipIndividual": false,
- "timeForComparison": "months",
- "xaxisSecond": {
- "axisPosition": "top",
- "showLabels": true
- },
- "smoothLines": true,
- "showLegend": true,
- "legendConfig": {
- "direction": "column",
- "position": "bottom",
- "showMin": true,
- "showMax": true,
- "showAvg": true,
- "showTotal": false
- }
- },
- "title": "Humidity",
- "dropShadow": true,
- "enableFullscreen": true,
- "titleStyle": {
- "fontSize": "16px",
- "fontWeight": 400
- },
- "mobileHeight": null,
- "showTitleIcon": false,
- "titleIcon": null,
- "iconColor": "rgba(0, 0, 0, 0.87)",
- "iconSize": "24px",
- "titleTooltip": "",
- "widgetStyle": {},
- "useDashboardTimewindow": false,
- "displayTimewindow": true,
- "actions": {}
- },
- "id": "07f49fd5-a73b-d74c-c220-362c20af81f4",
- "typeFullFqn": "system.charts.basic_timeseries"
- },
"c4631f94-2db3-523b-4d09-2a1a0a75d93f": {
"type": "latest",
"sizeX": 6,
@@ -668,7 +434,9 @@
"fieldsAlignment": "column",
"fieldsInRow": 2,
"groupTitle": "${entityName}",
- "widgetTitle": "Termostat settings"
+ "widgetTitle": "Termostat settings",
+ "columnGap": 10,
+ "rowGap": 5
},
"title": "New Update Multiple Attributes",
"dropShadow": true,
@@ -787,8 +555,8 @@
"markerImageSize": 48,
"useColorFunction": false,
"markerImages": [
- "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+",
- "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg=="
+ "tb-image:dGhlcm1vc3RhdHNfZGFzaGJvYXJkX3dpZGdldF90aGVybW9zdGF0X21hcHNfbWFya2VyX2ltYWdlXzAuc3Zn:IlRoZXJtb3N0YXRzIiBkYXNoYm9hcmQgd2lkZ2V0ICJUaGVybW9zdGF0IG1hcHMiIG1hcmtlciBpbWFnZSAw;data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+",
+ "tb-image:dGhlcm1vc3RhdHNfZGFzaGJvYXJkX3dpZGdldF90aGVybW9zdGF0X21hcHNfbWFya2VyX2ltYWdlXzEuc3Zn:IlRoZXJtb3N0YXRzIiBkYXNoYm9hcmQgd2lkZ2V0ICJUaGVybW9zdGF0IG1hcHMiIG1hcmtlciBpbWFnZSAx;data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg=="
],
"useMarkerImageFunction": true,
"colorFunction": "\n",
@@ -942,8 +710,8 @@
"markerImageSize": 34,
"useColorFunction": false,
"markerImages": [
- "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+",
- "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg=="
+ "tb-image:dGhlcm1vc3RhdHNfZGFzaGJvYXJkX3dpZGdldF90aGVybW9zdGF0X21hcHNfbWFya2VyX2ltYWdlXzAuc3Zn:IlRoZXJtb3N0YXRzIiBkYXNoYm9hcmQgd2lkZ2V0ICJUaGVybW9zdGF0IG1hcHMiIG1hcmtlciBpbWFnZSAw;data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+",
+ "tb-image:dGhlcm1vc3RhdHNfZGFzaGJvYXJkX3dpZGdldF90aGVybW9zdGF0X21hcHNfbWFya2VyX2ltYWdlXzEuc3Zn:IlRoZXJtb3N0YXRzIiBkYXNoYm9hcmQgd2lkZ2V0ICJUaGVybW9zdGF0IG1hcHMiIG1hcmtlciBpbWFnZSAx;data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg=="
],
"useMarkerImageFunction": true,
"color": "#fe7569",
@@ -1093,8 +861,8 @@
"markerImageSize": 34,
"useColorFunction": false,
"markerImages": [
- "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+",
- "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg=="
+ "tb-image:dGhlcm1vc3RhdHNfZGFzaGJvYXJkX3dpZGdldF90aGVybW9zdGF0X21hcHNfbWFya2VyX2ltYWdlXzAuc3Zn:IlRoZXJtb3N0YXRzIiBkYXNoYm9hcmQgd2lkZ2V0ICJUaGVybW9zdGF0IG1hcHMiIG1hcmtlciBpbWFnZSAw;data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+",
+ "tb-image:dGhlcm1vc3RhdHNfZGFzaGJvYXJkX3dpZGdldF90aGVybW9zdGF0X21hcHNfbWFya2VyX2ltYWdlXzEuc3Zn:IlRoZXJtb3N0YXRzIiBkYXNoYm9hcmQgd2lkZ2V0ICJUaGVybW9zdGF0IG1hcHMiIG1hcmtlciBpbWFnZSAx;data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg=="
],
"useMarkerImageFunction": true,
"color": "#fe7569",
@@ -1151,6 +919,792 @@
},
"id": "0a430429-9078-9ae6-2b67-e4a15a2bf8bf",
"typeFullFqn": "system.input_widgets.markers_placement_openstreetmap"
+ },
+ "eda8a397-0959-690c-405c-11e2c9b2bc7e": {
+ "typeFullFqn": "system.time_series_chart",
+ "type": "timeseries",
+ "sizeX": 8,
+ "sizeY": 5,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "name": "",
+ "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547",
+ "dataKeys": [
+ {
+ "name": "temperature",
+ "type": "timeseries",
+ "label": "Temperature",
+ "color": "#EF5350",
+ "settings": {
+ "yAxisId": "default",
+ "showInLegend": true,
+ "dataHiddenByDefault": false,
+ "type": "line",
+ "lineSettings": {
+ "showLine": true,
+ "step": false,
+ "stepType": "start",
+ "smooth": true,
+ "lineType": "solid",
+ "lineWidth": 2.5,
+ "showPoints": false,
+ "showPointLabel": false,
+ "pointLabelPosition": "top",
+ "pointLabelFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "pointLabelColor": "rgba(0, 0, 0, 0.76)",
+ "pointShape": "circle",
+ "pointSize": 14,
+ "fillAreaSettings": {
+ "type": "gradient",
+ "opacity": 0.4,
+ "gradient": {
+ "start": 60,
+ "end": 10
+ }
+ }
+ },
+ "barSettings": {
+ "showBorder": false,
+ "borderWidth": 2,
+ "borderRadius": 0,
+ "showLabel": false,
+ "labelPosition": "top",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.76)",
+ "backgroundSettings": {
+ "type": "none",
+ "opacity": 0.4,
+ "gradient": {
+ "start": 100,
+ "end": 0
+ }
+ }
+ }
+ },
+ "_hash": 0.5973804076994531,
+ "units": "°C",
+ "decimals": 1,
+ "aggregationType": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ],
+ "alarmFilterConfig": {
+ "statusList": [
+ "ACTIVE"
+ ]
+ },
+ "latestDataKeys": [
+ {
+ "name": "temperatureAlarmThreshold",
+ "type": "attribute",
+ "label": "temperatureAlarmThreshold",
+ "color": "#4caf50",
+ "settings": {
+ "__thresholdKey": true
+ },
+ "_hash": 0.7120450032526351
+ }
+ ]
+ }
+ ],
+ "timewindow": {
+ "hideInterval": false,
+ "hideLastInterval": false,
+ "hideQuickInterval": false,
+ "hideAggregation": false,
+ "hideAggInterval": false,
+ "hideTimezone": false,
+ "selectedTab": 0,
+ "realtime": {
+ "realtimeType": 0,
+ "timewindowMs": 3600000,
+ "quickInterval": "CURRENT_DAY",
+ "interval": 30000
+ },
+ "aggregation": {
+ "type": "AVG",
+ "limit": 25000
+ },
+ "timezone": null
+ },
+ "showTitle": true,
+ "backgroundColor": "rgba(0, 0, 0, 0)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "0px",
+ "settings": {
+ "showLegend": true,
+ "legendConfig": {
+ "direction": "column",
+ "position": "bottom",
+ "sortDataKeys": false,
+ "showMin": true,
+ "showMax": true,
+ "showAvg": true,
+ "showTotal": false,
+ "showLatest": false
+ },
+ "thresholds": [
+ {
+ "type": "latestKey",
+ "yAxisId": "default",
+ "units": "°C",
+ "decimals": 0,
+ "lineColor": "rgb(233, 30, 99)",
+ "lineType": "solid",
+ "lineWidth": 2,
+ "startSymbol": "none",
+ "startSymbolSize": 5,
+ "endSymbol": "arrow",
+ "endSymbolSize": 8,
+ "showLabel": true,
+ "labelPosition": "end",
+ "labelFont": {
+ "size": 14,
+ "family": "Roboto",
+ "weight": "500",
+ "style": "normal"
+ },
+ "labelColor": "rgb(233, 30, 99)",
+ "latestKey": "temperatureAlarmThreshold",
+ "latestKeyType": "attribute"
+ }
+ ],
+ "dataZoom": true,
+ "stack": false,
+ "yAxis": {
+ "show": true,
+ "label": "",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "600",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.54)",
+ "position": "left",
+ "showTickLabels": true,
+ "tickLabelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "tickLabelColor": "rgba(0, 0, 0, 0.54)",
+ "showTicks": true,
+ "ticksColor": "rgba(0, 0, 0, 0.54)",
+ "showLine": true,
+ "lineColor": "rgba(0, 0, 0, 0.54)",
+ "showSplitLines": true,
+ "splitLinesColor": "rgba(0, 0, 0, 0.12)"
+ },
+ "xAxis": {
+ "show": true,
+ "label": "",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "600",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.54)",
+ "position": "bottom",
+ "showTickLabels": true,
+ "tickLabelFont": {
+ "family": "Roboto",
+ "size": 10,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "tickLabelColor": "rgba(0, 0, 0, 0.54)",
+ "showTicks": true,
+ "ticksColor": "rgba(0, 0, 0, 0.54)",
+ "showLine": true,
+ "lineColor": "rgba(0, 0, 0, 0.54)",
+ "showSplitLines": true,
+ "splitLinesColor": "rgba(0, 0, 0, 0.12)",
+ "ticksFormat": {}
+ },
+ "legendLabelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "16px"
+ },
+ "legendLabelColor": "rgba(0, 0, 0, 0.76)",
+ "showTooltip": true,
+ "tooltipTrigger": "axis",
+ "tooltipValueFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "500",
+ "lineHeight": "16px"
+ },
+ "tooltipValueColor": "rgba(0, 0, 0, 0.76)",
+ "tooltipShowDate": true,
+ "tooltipDateFormat": {
+ "format": "MMM dd yyyy HH:mm",
+ "lastUpdateAgo": false,
+ "custom": false,
+ "auto": true,
+ "autoDateFormatSettings": {}
+ },
+ "tooltipDateFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "16px"
+ },
+ "tooltipDateColor": "rgba(0, 0, 0, 0.76)",
+ "tooltipDateInterval": false,
+ "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)",
+ "tooltipBackgroundBlur": 4,
+ "background": {
+ "type": "color",
+ "color": "#fff",
+ "overlay": {
+ "enabled": false,
+ "color": "rgba(255,255,255,0.72)",
+ "blur": 3
+ }
+ },
+ "yAxes": {
+ "default": {
+ "units": "°C",
+ "decimals": 0,
+ "show": true,
+ "label": "",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "600",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.54)",
+ "position": "left",
+ "showTickLabels": true,
+ "tickLabelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "tickLabelColor": "rgba(0, 0, 0, 0.54)",
+ "ticksFormatter": null,
+ "showTicks": true,
+ "ticksColor": "rgba(0, 0, 0, 0.54)",
+ "showLine": true,
+ "lineColor": "rgba(0, 0, 0, 0.54)",
+ "showSplitLines": true,
+ "splitLinesColor": "rgba(0, 0, 0, 0.12)",
+ "id": "default",
+ "order": 0,
+ "min": 0
+ }
+ },
+ "noAggregationBarWidthSettings": {
+ "strategy": "group",
+ "groupWidth": {
+ "relative": true,
+ "relativeWidth": 2,
+ "absoluteWidth": 1000
+ },
+ "barWidth": {
+ "relative": true,
+ "relativeWidth": 2,
+ "absoluteWidth": 1000
+ }
+ },
+ "animation": {
+ "animation": true,
+ "animationThreshold": 2000,
+ "animationDuration": 500,
+ "animationEasing": "cubicOut",
+ "animationDelay": 0,
+ "animationDurationUpdate": 300,
+ "animationEasingUpdate": "cubicOut",
+ "animationDelayUpdate": 0
+ },
+ "padding": "12px"
+ },
+ "title": "Temperature",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": null,
+ "configMode": "basic",
+ "actions": {},
+ "showTitleIcon": false,
+ "titleIcon": "thermostat",
+ "iconColor": "#1F6BDD",
+ "useDashboardTimewindow": false,
+ "displayTimewindow": true,
+ "titleFont": {
+ "size": 16,
+ "sizeUnit": "px",
+ "family": "Roboto",
+ "weight": "500",
+ "style": "normal",
+ "lineHeight": "24px"
+ },
+ "titleColor": "rgba(0, 0, 0, 0.87)",
+ "titleTooltip": "",
+ "widgetStyle": {},
+ "widgetCss": "",
+ "pageSize": 1024,
+ "units": "",
+ "decimals": null,
+ "noDataDisplayMessage": "",
+ "timewindowStyle": {
+ "showIcon": false,
+ "iconSize": "24px",
+ "icon": null,
+ "iconPosition": "left",
+ "font": {
+ "size": 12,
+ "sizeUnit": "px",
+ "family": "Roboto",
+ "weight": "400",
+ "style": "normal",
+ "lineHeight": "16px"
+ },
+ "color": "rgba(0, 0, 0, 0.38)",
+ "displayTypePrefix": true
+ },
+ "margin": "0px",
+ "borderRadius": "0px",
+ "iconSize": "0px"
+ },
+ "row": 0,
+ "col": 0,
+ "id": "eda8a397-0959-690c-405c-11e2c9b2bc7e"
+ },
+ "ac90f089-197f-b767-82c3-2668844265a2": {
+ "typeFullFqn": "system.time_series_chart",
+ "type": "timeseries",
+ "sizeX": 8,
+ "sizeY": 5,
+ "config": {
+ "datasources": [
+ {
+ "type": "entity",
+ "name": "",
+ "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547",
+ "dataKeys": [
+ {
+ "name": "humidity",
+ "type": "timeseries",
+ "label": "Humidity",
+ "color": "#2196F3",
+ "settings": {
+ "yAxisId": "default",
+ "showInLegend": true,
+ "dataHiddenByDefault": false,
+ "type": "line",
+ "lineSettings": {
+ "showLine": true,
+ "step": false,
+ "stepType": "start",
+ "smooth": true,
+ "lineType": "solid",
+ "lineWidth": 2.5,
+ "showPoints": false,
+ "showPointLabel": false,
+ "pointLabelPosition": "top",
+ "pointLabelFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "pointLabelColor": "rgba(0, 0, 0, 0.76)",
+ "pointShape": "circle",
+ "pointSize": 14,
+ "fillAreaSettings": {
+ "type": "gradient",
+ "opacity": 0.4,
+ "gradient": {
+ "start": 60,
+ "end": 10
+ }
+ }
+ },
+ "barSettings": {
+ "showBorder": false,
+ "borderWidth": 2,
+ "borderRadius": 0,
+ "showLabel": false,
+ "labelPosition": "top",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.76)",
+ "backgroundSettings": {
+ "type": "none",
+ "opacity": 0.4,
+ "gradient": {
+ "start": 100,
+ "end": 0
+ }
+ }
+ }
+ },
+ "_hash": 0.4481337125330429,
+ "units": "%",
+ "decimals": 0,
+ "aggregationType": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ],
+ "alarmFilterConfig": {
+ "statusList": [
+ "ACTIVE"
+ ]
+ },
+ "latestDataKeys": [
+ {
+ "name": "humidityAlarmThreshold",
+ "type": "attribute",
+ "label": "humidityAlarmThreshold",
+ "color": "#4caf50",
+ "settings": {
+ "__thresholdKey": true
+ },
+ "_hash": 0.134733085341747
+ }
+ ]
+ }
+ ],
+ "timewindow": {
+ "hideInterval": false,
+ "hideLastInterval": false,
+ "hideQuickInterval": false,
+ "hideAggregation": false,
+ "hideAggInterval": false,
+ "hideTimezone": false,
+ "selectedTab": 0,
+ "realtime": {
+ "realtimeType": 0,
+ "timewindowMs": 3600000,
+ "quickInterval": "CURRENT_DAY",
+ "interval": 30000
+ },
+ "aggregation": {
+ "type": "AVG",
+ "limit": 25000
+ },
+ "timezone": null
+ },
+ "showTitle": true,
+ "backgroundColor": "rgba(0, 0, 0, 0)",
+ "color": "rgba(0, 0, 0, 0.87)",
+ "padding": "0px",
+ "settings": {
+ "showLegend": true,
+ "legendConfig": {
+ "direction": "column",
+ "position": "bottom",
+ "sortDataKeys": false,
+ "showMin": true,
+ "showMax": true,
+ "showAvg": true,
+ "showTotal": false,
+ "showLatest": false
+ },
+ "thresholds": [
+ {
+ "type": "latestKey",
+ "yAxisId": "default",
+ "units": "%",
+ "decimals": 0,
+ "lineColor": "rgb(4, 138, 211)",
+ "lineType": "solid",
+ "lineWidth": 2,
+ "startSymbol": "none",
+ "startSymbolSize": 5,
+ "endSymbol": "arrow",
+ "endSymbolSize": 8,
+ "showLabel": true,
+ "labelPosition": "end",
+ "labelFont": {
+ "size": 14,
+ "family": "Roboto",
+ "weight": "500",
+ "style": "normal"
+ },
+ "labelColor": "rgb(4, 138, 211)",
+ "latestKey": "humidityAlarmThreshold",
+ "latestKeyType": "attribute"
+ }
+ ],
+ "dataZoom": true,
+ "stack": false,
+ "yAxis": {
+ "show": true,
+ "label": "",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "600",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.54)",
+ "position": "left",
+ "showTickLabels": true,
+ "tickLabelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "tickLabelColor": "rgba(0, 0, 0, 0.54)",
+ "showTicks": true,
+ "ticksColor": "rgba(0, 0, 0, 0.54)",
+ "showLine": true,
+ "lineColor": "rgba(0, 0, 0, 0.54)",
+ "showSplitLines": true,
+ "splitLinesColor": "rgba(0, 0, 0, 0.12)"
+ },
+ "xAxis": {
+ "show": true,
+ "label": "",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "600",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.54)",
+ "position": "bottom",
+ "showTickLabels": true,
+ "tickLabelFont": {
+ "family": "Roboto",
+ "size": 10,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "tickLabelColor": "rgba(0, 0, 0, 0.54)",
+ "showTicks": true,
+ "ticksColor": "rgba(0, 0, 0, 0.54)",
+ "showLine": true,
+ "lineColor": "rgba(0, 0, 0, 0.54)",
+ "showSplitLines": true,
+ "splitLinesColor": "rgba(0, 0, 0, 0.12)",
+ "ticksFormat": {}
+ },
+ "legendLabelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "16px"
+ },
+ "legendLabelColor": "rgba(0, 0, 0, 0.76)",
+ "showTooltip": true,
+ "tooltipTrigger": "axis",
+ "tooltipValueFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "500",
+ "lineHeight": "16px"
+ },
+ "tooltipValueColor": "rgba(0, 0, 0, 0.76)",
+ "tooltipShowDate": true,
+ "tooltipDateFormat": {
+ "format": null,
+ "lastUpdateAgo": false,
+ "custom": false,
+ "auto": true,
+ "autoDateFormatSettings": {}
+ },
+ "tooltipDateFont": {
+ "family": "Roboto",
+ "size": 11,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "16px"
+ },
+ "tooltipDateColor": "rgba(0, 0, 0, 0.76)",
+ "tooltipDateInterval": false,
+ "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)",
+ "tooltipBackgroundBlur": 4,
+ "background": {
+ "type": "color",
+ "color": "#fff",
+ "overlay": {
+ "enabled": false,
+ "color": "rgba(255,255,255,0.72)",
+ "blur": 3
+ }
+ },
+ "yAxes": {
+ "default": {
+ "units": "°C",
+ "decimals": 0,
+ "show": true,
+ "label": "",
+ "labelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "600",
+ "lineHeight": "1"
+ },
+ "labelColor": "rgba(0, 0, 0, 0.54)",
+ "position": "left",
+ "showTickLabels": true,
+ "tickLabelFont": {
+ "family": "Roboto",
+ "size": 12,
+ "sizeUnit": "px",
+ "style": "normal",
+ "weight": "400",
+ "lineHeight": "1"
+ },
+ "tickLabelColor": "rgba(0, 0, 0, 0.54)",
+ "ticksFormatter": null,
+ "showTicks": true,
+ "ticksColor": "rgba(0, 0, 0, 0.54)",
+ "showLine": true,
+ "lineColor": "rgba(0, 0, 0, 0.54)",
+ "showSplitLines": true,
+ "splitLinesColor": "rgba(0, 0, 0, 0.12)",
+ "id": "default",
+ "order": 0,
+ "min": 0
+ }
+ },
+ "noAggregationBarWidthSettings": {
+ "strategy": "group",
+ "groupWidth": {
+ "relative": true,
+ "relativeWidth": 2,
+ "absoluteWidth": 1000
+ },
+ "barWidth": {
+ "relative": true,
+ "relativeWidth": 2,
+ "absoluteWidth": 1000
+ }
+ },
+ "animation": {
+ "animation": true,
+ "animationThreshold": 2000,
+ "animationDuration": 500,
+ "animationEasing": "cubicOut",
+ "animationDelay": 0,
+ "animationDurationUpdate": 300,
+ "animationEasingUpdate": "cubicOut",
+ "animationDelayUpdate": 0
+ },
+ "padding": "12px"
+ },
+ "title": "Humidity",
+ "dropShadow": true,
+ "enableFullscreen": true,
+ "titleStyle": null,
+ "configMode": "basic",
+ "actions": {},
+ "showTitleIcon": false,
+ "titleIcon": "thermostat",
+ "iconColor": "#1F6BDD",
+ "useDashboardTimewindow": false,
+ "displayTimewindow": true,
+ "titleFont": {
+ "size": 16,
+ "sizeUnit": "px",
+ "family": "Roboto",
+ "weight": "500",
+ "style": "normal",
+ "lineHeight": "24px"
+ },
+ "titleColor": "rgba(0, 0, 0, 0.87)",
+ "titleTooltip": "",
+ "widgetStyle": {},
+ "widgetCss": "",
+ "pageSize": 1024,
+ "units": "",
+ "decimals": null,
+ "noDataDisplayMessage": "",
+ "timewindowStyle": {
+ "showIcon": false,
+ "iconSize": "24px",
+ "icon": null,
+ "iconPosition": "left",
+ "font": {
+ "size": 12,
+ "sizeUnit": "px",
+ "family": "Roboto",
+ "weight": "400",
+ "style": "normal",
+ "lineHeight": "16px"
+ },
+ "color": "rgba(0, 0, 0, 0.38)",
+ "displayTypePrefix": true
+ },
+ "margin": "0px",
+ "borderRadius": "0px",
+ "iconSize": "0px"
+ },
+ "row": 0,
+ "col": 0,
+ "id": "ac90f089-197f-b767-82c3-2668844265a2"
}
},
"states": {
@@ -1164,19 +1718,25 @@
"sizeX": 11,
"sizeY": 11,
"row": 0,
- "col": 0
+ "col": 0,
+ "mobileOrder": 1,
+ "mobileHeight": 5
},
"7943196b-eedb-d422-f9c3-b32d379ad172": {
"sizeX": 13,
"sizeY": 5,
"row": 0,
- "col": 11
+ "col": 11,
+ "mobileOrder": 2,
+ "mobileHeight": 5
},
"3da9a9a1-0b9a-2e1f-0dcb-0ff34a695abb": {
"sizeX": 13,
"sizeY": 6,
"row": 5,
- "col": 11
+ "col": 11,
+ "mobileOrder": 3,
+ "mobileHeight": 5
}
},
"gridSettings": {
@@ -1226,31 +1786,37 @@
"layouts": {
"main": {
"widgets": {
- "14a19183-f0b2-d6be-0f62-9863f0a51111": {
- "sizeX": 18,
+ "c4631f94-2db3-523b-4d09-2a1a0a75d93f": {
+ "sizeX": 6,
"sizeY": 6,
- "mobileHeight": null,
"row": 0,
- "col": 6
+ "col": 0,
+ "mobileOrder": 3,
+ "mobileHeight": 5
},
- "07f49fd5-a73b-d74c-c220-362c20af81f4": {
- "sizeX": 18,
+ "0a430429-9078-9ae6-2b67-e4a15a2bf8bf": {
+ "sizeX": 6,
"sizeY": 6,
- "mobileHeight": null,
"row": 6,
- "col": 6
+ "col": 0,
+ "mobileOrder": 4,
+ "mobileHeight": 6
},
- "c4631f94-2db3-523b-4d09-2a1a0a75d93f": {
- "sizeX": 6,
+ "eda8a397-0959-690c-405c-11e2c9b2bc7e": {
+ "sizeX": 18,
"sizeY": 6,
"row": 0,
- "col": 0
+ "col": 6,
+ "mobileOrder": 1,
+ "mobileHeight": 6
},
- "0a430429-9078-9ae6-2b67-e4a15a2bf8bf": {
- "sizeX": 6,
+ "ac90f089-197f-b767-82c3-2668844265a2": {
+ "sizeX": 18,
"sizeY": 6,
"row": 6,
- "col": 0
+ "col": 6,
+ "mobileOrder": 2,
+ "mobileHeight": 6
}
},
"gridSettings": {
@@ -1327,6 +1893,5 @@
},
"filters": {}
},
- "externalId": null,
"name": "Thermostats"
}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_bundles/charts.json b/application/src/main/data/json/system/widget_bundles/charts.json
index 07929028ff..979b241e67 100644
--- a/application/src/main/data/json/system/widget_bundles/charts.json
+++ b/application/src/main/data/json/system/widget_bundles/charts.json
@@ -2,12 +2,16 @@
"widgetsBundle": {
"alias": "charts",
"title": "Charts",
- "image": "tb-image:Y2hhcnRzX3N5c3RlbV9idW5kbGVfaW1hZ2UucG5n:IkNoYXJ0cyIgc3lzdGVtIGJ1bmRsZSBpbWFnZQ==;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC91BMVEX////K5vz/wQchlvP0QzZgfYtMr1C20ujl5eUAAADi4uL/ySX9/v7/9+Dg4OA8o/TK2eT+6Ob1Wk7q9f7x8fHw8PHy8/L5+vn09PTT2t/p6ekznvRfs/b+wxHLy8v8/Pz/6KL///3+8/JRsVX6+/vq9uqTw+rn5+diuWVNq/bk5OTY2NjNzc2+vr6lzqfqop3u1Yr/wgzD4/w2oPSs2Pv4+Pnj4+S83/v8xBj/wgr0+v5itfdGqPX/1lv29vZUrvXHx8fH5fx2vvgunPTO5OvQ4+Oryd35xSG23fuRy/qAw/n39/jt7e3U4NK3t7eqqqprufdasfY/pPXL5PHu7u7r6+vf3t3/8srW3sjCwsK6urqxsbGw2vum1fuGxvn//fj/+urtzVf3/P/i8v6g0vvL5vqa0PqMyfns7Ozm5ubl1IL/333p0nLtz1/zyj/7/f/S6v17wfjM5fR4uevS0tJtiJX/3HLq0Wrxy0b1yTj/zDD/xhn9/v/u9/7/+PgpmvT//PPS4dj8w77/663c2qnm03yXzvpwu/dmt/fv7+/m6+2uz+ra4eX93tzV1dX/7LOurq6tra3g15j/4of/2WfvzFH1V0v/0kj0STz3yDH5xyjZ7f2gyerR4t7/9tzb3Nv/9dT61tPY3b/Z3Lrb27KMoavf2J92j5v/5JPrz2P2ZFn/xx++4fvr7/Dg6Ou+ztD8zcquvcT/8MKarbakpKSCmqT5n5iTz5Zmgo/5kIn3cWfpx0z1T0P/zz7U6/3P4+bO3ObO19vF0tq80dm6x82jtLz/7rni1YyJy4zc7/3O6Pz+7uz94d/Y7tm437qx3LPIz7P6sauo1ar/5pv6opuRkZHi1o97xH7aynr4gHdpvGzhymZbtl7zxC36wRC30ui30uTM1Njz69TM6M3F5cbC5MPDz7/p3bt/q52huI64wIf4gXjfzG67umTVvUDk8+Tm5N3a185yss2TwMjCz8T7ta/7s610qaWOr5PSy5H4hn33fnX3eG83NCs7AAAOvklEQVR42tzaSYyLYRzH8Z9SaV6P19a+7+u1VKmUWhJLK7baDpY2StKZCSNmwsFMHJDgYM84WGIZIUgkloMQYt8FsUessUWIiy0ODk4ccDDzvG2fvn3eeVvK+758LxKHJ/30eZ63/+kM/p+ICGVhHA5r9Gj0m7wQv1LlHiRrt8BZSae7kiSS+IWk55WhLXAWRIY4uW/fLaiHTFBsZdO9k5OOgRApJAiCCqm+78LTNZVQwoIQTcgoIlJfL/abPBpEEPYIf6kwiioeEqMpGbToQpLqGknjasWoj6D4BNhYUBVDMpqK+MSw8i9A4kLB1+kvExPE4ZBYOORHEUmiShwMkbmXZ0ZZ51QIUcUg8lPWb4hh2TAYlBDjjoTEREnvOnrs1JlFbre7Izo0b96p95qJQ5ZDlz+sOhCSEAhYwWunGgw0Ckk3YmazZfrzJTsM4hdSYB29pSkYhDVo0q5csCg5CiKLEWTbsN/NYhDW8LvIRjwJB0EiYpDtRobBQ1hTryCbqv5VyKpufKthnCISpIvfYgADCGssu/gpz9+EtHbxtYJhceboeMBtDmF1moBMXo8jIHLWQU5wCAbhGzsruyeqAyDBcj+0lP1uMwhf79lIV1ZjO4RkPwmOHHCbQ/gGZe+8R7Ib4lGgtX6RuxCEb9yY7PsRtBfiVZmjMIRvZbPsTbMVIovcuTKF8I2bz94SGyGZAxE84y4Owjcoc+PDEfsgUhm0TrmLhfD1ngWaX7QNQjKfIDfdxUP4xkKrxmcXJKSkL/r5kiDNh0BLJPZAiJj+94y7NEin5eyk2gFRFdCOuUuA0BY0uSVXO/ONLR1iuCGxRSVA0s0HzZuA1rTInul0+bbN+dr8YUiZBNoJd+mQztASQSt/LiWRtAoighZfVAIkW7Pc0xqsnC7Vo94iSCTENqR0yPDM9yp0Qyqfd7VuR2plNEYWlQJhzdZd900Nd6QcRBBuGM0Cwq/00QAyXhDC+SfrmvvPQOaAts4HliU7Ele5b35KgXSeARqJWg1RY0hHNtw6UBJk5fDBy9lGWw0RQVtVrQ0pJ8zHeFPFMO7qWQkRQOvmWtKNs/CQQgqWlLASwh6+I+n/P9UsR26e+QXIuClMwSJRayFeH2itXFqHD7bmLGaQTmOHzAMtRKBLtBaiRrQr4mIt3do6oFn2F4CMGNxhHpt0FOgSfhsS6G5QIYhA0FiFi8Ysu6lFub7/fFOQUUP0ByqmQleU/C6kXQu+9gUg7K7n12XuXmqJpS16yKgJwwpcCqgxOyBbXQYNWKxZ4m9Onc+FrB04CwD87849MPnZKSXZAVnsMm7otopA+hdX5zXIlGZUEXx37nXLlg9NID6vHZBtrqbbVlGlWdZj2Hx6ueX3dxoUDZ0zgUgpOyAjXaaNPFmFTPKHOz2oogBEqXEgpKEdt1c1Kh6+oIp0d0wgcdWZEJerNQDygikK7UjCaXck3UGgbgXkHrmQJ2Z3ZJ0dkLmFj1YAG3deAB7kQj6YQNZJdkAOFnJ0WYUVh1q0qAP650B6mkASiqUQEbTNhSAVwIWG9S61A3mVdbxCTsFa6ArJlkI8fjS2t4BjLlBHF7wYgJSFPOI/N1gCLIXUKNrA2cXUsSSAdpe0FZ8B79OOHhJy8sjQJVoJYQ/Jy6aQavgvZpY8DtwxeGZBhK0Qf5gbtvg2A2ezS27vDj8dUHrptkD2QJccshLC3rjbZgMKsK8F6yXwufFgPTCZ2oGEZDEkrN32qqYvyeEqdG+fu+hX4BGdfM1OlkCA2OnToX7TR1sD8fkKDSm7ge+6RXduBHnxiPstJA+TUn02JZG0AsIuyWaT0eRxC32HVsAPfSIB+K/GlUqyBfVWQNhLqFpqMprkL3sBeUkq9IUiAOTTHimpbgHxeq8bQbzGbTSAbPe+NYJ4vRIypbzcuMWNJlx10EXKidHDN9a3r6D0jYP4fIYQn3H7jCA+Q4jP50UmIoBW3cVkNNGljSq5qRL0KWVgWXS0EPU3Pcov1kYTvosBsGJh5BUO2gBRVG5LuNGE7xn/9wasoAAbIBBJU7ekGoH0aMK3jzkiyKs2bgOEfQasasWPJs+MDJlRRSss8X88DDsgbEtOmowmfC9B8/iQXyhmE0QJQWsxP5qY9AUAiXqRnyzAJgiiMmhVl/WjySdjARtVEFXAJcjFQnr24HtVCiQoQmv3UpPRhMVGFQIuSUXRkJZ8vUqBoMwHrYrMM/hyAPe50cR0VGEPYxshEGVorR6qfRNfDdz7VMCxvQ58Qgx2QvwiyUgGuBraisaOHzJhXDq7AnypGtgKgVKLrITOvLRA3cWmduPsPRgUEWAzBGoC6SoOL61Gto1n2/PPq5d1VTAqWE5sh6B2HdK1Pgld9x9/O7QzuxU/zh5fAeP85TLsh/xk545xGgaCAIr+Eo1GUzn2apsVkoUlF0Bjl1wASsgZnKOk4AZIKDfIPaDiBtyEDhdZkLPVRPKrd4uv3WKqYaz52/XH6+F4PHzmG+apy0OIWqDMfN9FCNpXlEv2gJMQ6CZKNdbgJ4S4U4rU1uIphGANBWKv+Aoh2abgW1XgLQSmMx9FozR4DKEdO2WxYDX4DIFgUcnJn/QbArXFtHTBgOsQCCKV8o+hs0nBfQjos437RI6GznYDcBEhQNr01k+hZaaP+87sZWAxFfm+OfUm77envmR7lyHb+wyRnuVSeLoy+TXGamC1Wq1WPxoFtAfR4rpyDpE6ugxDHZSIy4EBw1AHfaAYcQDHCCemLMcQEULij3pkUAiNeoR8odq04eERE2ljz2HgkZOBNrHmxkY09wgHBtCmppBZsXMxOzu7mvMBqhuvTc8Y6Q4K9GAEgULNfOI15u8zKU6vISpGtHUdSmhfIQq3G6cxQkGgF1Eaa43S2m3qg6yLNWWsiPGIhLa2ro4OjT1iIh3QzQgDRkEmBDQKm3ta8ztrdnilGDEysjsG2CQT4REdHm1dGsfIbHtNE0Z2RjgwkZ6NW2NyoWY9v0xhmwdUMUijl/Ep4jM7J608YhUnHQd2DwKkB1nh0lhg7BRrhFAJ1dgmHTPgxe8afhlzqHsQIMAaV5Eg3YasDq7RxDhuYD0i3M6fBncPAjhqtmHVaOIci6QKWaO5ZkDSAHkE0N7ZhTQVhnF879NFRcSCPoiOZxREMLKLbdHO2sbcV/uAlXOQm87VdHMwp+kMt5lR4lZCYmBJaFqhIklR0K2UQZDUVRBEEH1c9UVFX1DdtffMebZ5ds5ZlJT0v5j4+Pzf9/zeZ+/zvuzCdZ3f2SY1GuSIBQRZCT2LMVQ1goqAIHmZ7ujig6zx+nTpzkl3HVYQ1KhYW2g8dFKqZLIWGg1V59lnrP8jIA3WkNOoKLMHrrI9D6M23aF8426nU464QJCNkOPELfm9xKsm+n4XyO61Da16pbfuREhNEM62xuILy0iv8+WN1aA5qUeI21inCIhEq2+SQ9ozPZe24FpYK41Om5ynIq1r+UBslU6pRmxcdowQa3ROdaUh2/4FgKCr4hERo1sKuwCjtcrecwQyknX63377HMKF5wZpIKR93CCNGltj3U7rKZQrbhB3YpxZX+M9UVZywiZoBVzvAbSrXz3sOGeZwxnyh5Mf8RA1+z0Vkh0Lb7994vYy3S6ui+cpYs9yLPzKiDP0lQKYvp4NtYv75oYaMW7jMDJ6+RSefb+9YvvzdkLcvuPHu+kMzqfMN0tI0lpQkXqdAek1Po6KXHs9rUpMCFhFJmS2QIIEMuyaC/VWzr1FxV5OY1YDJAw5fAq7QmO3ZkvknkxN0Q8lkeCKFIJ09arRjOtUVV0RkAM92meA1TImHMQ0BC1RczOAJZkJyTUB3HYNGiUSAGLyA8SiCDW25Z02XHvk4N237+MWUD198noNC8iWGx1HMMMj84Al/eOBQJBoCwyZ8D5pSZscdMhKINFutU6OBIC4h4FKMiFukOPVPU3aThnQolT4dfjs5ku5IFuqO64AwIc3ZrqyYQpg0CEEJDgIFtqDgpMUqGImnBVQtEp79UzWneRUbDzKMlYwrIKWUSQIpOPcKsjI8uHdwNgMcj0Ix+k30BHt5eoDdNKlCM7pPLNHKs8WPEUCNHfzg8SAmgc2RWRADbzAx/UyO8rKMRun144cHDAXjGWOA6SCSBiICvdmbdPmahexc94Q/PJGe4Ue/VzT3TOdALCqqVrkFStzVjFCgmpqlAfkMZBuxMgRB3g6hpA+gGhFxyPDuJvGH4UTkFYi5Q4yY01QYBlHSCDIw9OZfxpzngjkGtSVXRc2n8UIuDbNN9I5rTQpM6w5JgNZZIYLZABUE/mRiWEAfwbfPOknAYCaSt7J/bU5acpsrgjA4AwSDJK9eYtteQa9LkQ3gNNNN7WnD6ygM04ULn93MwCZMhUFSapgsjAWfUy3YjdTAmzMK5AqPutY3p0A2SxCpYLsltoLDMrcJpz21Et9LMM6/ABU2MU+0xgJj1m2DW7FMmZTFBi7Z+P4jxYKht2oZJBDZZULDF5CngPS1dvLPiyNkjKxzOSgIMbeWN0JGI4lXcUe0TURswD4Tah0EINTv9AQUNxjQHxSfbFhx1pwVUyFM41awB9kB0HBbp52F3wwgVDpICGNnM0QqjqaBdkpVnIMOzYIQEbMuSF8oMejLI8oOCQMpEJS0T//cVCdWMluMOjqMyDPCS/3sG6M8sTMBFz0gf7nQTb09+8ol2AQfJ8dKXbx1JWtwLpNbOO96r6cBpA9yd5xX0yD5TpL1u8MbccgtfvnP6BrJeqKksulNtzSNO1ClszhV4Fs7rSfAqr7ty4//x5pUDRyGJRir6hLfVLgTI7mOZQIUG60yCAaH6fBS9y36/SCZ8IoqqkUyMbRYoPYeAwBY9XVUmYajeHzbBItOgivwWYtcabRCPkI/YUgvxAyoSUCgv6D/AdZEiB79y4REI9niYAsmYrQWvkPS5SvlSJW/SNZS1F4q/CrZoeApNr15fv5szySDQKmlJSXe0oD8QjKr60VkLR9fW0Nf9bGw+US/qxNnq2SP1KRGgFJF9d5BKTt6z+8jz+rQlJzmCflJ6e3vqMkTdRjAAAAAElFTkSuQmCC",
+ "image": "tb-image:Y2hhcnRzLnN2Zw==:IkNoYXJ0cyIgc3lzdGVtIGJ1bmRsZSBpbWFnZQ==;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF80MTk4XzExMDQ5MykiPgo8cGF0aCBkPSJNMTkuMjQ2MSAyMC45OTY5TDQuOTU0NDggNTQuMzI1NEwwIDYzLjVWNzZIOTZWNjAuOTU2MVY0NS4zMzMzTDg4Ljg5ODcgNjkuMDQ2MUw4NC45NDA3IDU0LjIxNTNMODMuMjIzMyA1OC41NzczTDc1Ljg4MjQgNzQuOTM3NkM3MC45ODg5IDU3LjE0MzMgNjYuOTExMSA1MC41MDM2IDYxLjU4NTEgMzMuMDIwOUw1Ni4wODk3IDU5LjEzMDlINDkuMDIyMUw0Ny42NTcgMTYuNzAyTDQ0LjkwNzYgMEwzMS44MjUgNDEuNjcwOEwyOC43MTQxIDQwLjM2MjdMMTkuMjQ2MSAyMC45OTY5WiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzQxOThfMTEwNDkzKSIvPgo8cGF0aCBkPSJNMCA2My42NDg3TDUuMDAxMDkgNTMuNDg4TDE5LjQyNzEgMjAuMzk4OEwyOC45ODQyIDQwLjYyNTVMMzIuMTI0NCA0MS45MjQzTDQ0Ljg2OTYgMUw0Ny43MzkxIDE2LjE3MDNDNDguMjc3MiAzMi42MjA5IDQ4LjQxODQgNDIuNTQ5NCA0OC45NTY1IDU5SDU2TDYxLjU2NTIgMzIuMTIzM0w3NS45MTMgNzRMODUuMDE4NSA1My40NDEzTDg4Ljk1NjUgNjguMDE3Nkw5NiA0NC43MTk0IiBzdHJva2U9InVybCgjcGFpbnQxX2xpbmVhcl80MTk4XzExMDQ5MykiIHN0cm9rZS13aWR0aD0iMC43NzY1ODIiLz4KPC9nPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDFfNDE5OF8xMTA0OTMpIj4KPHBhdGggZD0iTTAgMEw5NiAxLjYwODcyZS0wNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC4yNTQxNyIvPgo8cGF0aCBkPSJNMCAxOUw5NiAxOSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC4yNTQxNyIvPgo8cGF0aCBkPSJNMCAzOEw5NiAzOCIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC4yNTQxNyIvPgo8cGF0aCBkPSJNMCA1N0w5NiA1NyIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC4yNTQxNyIvPgo8cGF0aCBkPSJNMCA3Nkw5NiA3NiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC4yNTQxNyIvPgo8L2c+CjxyZWN0IHg9IjEwNCIgd2lkdGg9Ijk2IiBoZWlnaHQ9Ijc2IiByeD0iMS4xMzQzMyIgZmlsbD0id2hpdGUiLz4KPG1hc2sgaWQ9InBhdGgtOS1vdXRzaWRlLTFfNDE5OF8xMTA0OTMiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjE1Mi4wMTgiIHk9IjMuMjUiIHdpZHRoPSIzNSIgaGVpZ2h0PSI1MSIgZmlsbD0iYmxhY2siPgo8cmVjdCBmaWxsPSJ3aGl0ZSIgeD0iMTUyLjAxOCIgeT0iMy4yNSIgd2lkdGg9IjM1IiBoZWlnaHQ9IjUxIi8+CjxwYXRoIGQ9Ik0xNTMuMzQxIDcuMjYyNDlDMTUzLjM5IDUuODg2MDYgMTU0LjU0NiA0Ljc5OTU1IDE1NS45MTUgNC45NTA2MUMxNjAuNjM1IDUuNDcxNDMgMTY1LjE5OCA2Ljk5ODQgMTY5LjI5MyA5LjQzOTU4QzE3NC4xMDQgMTIuMzA3MyAxNzguMTEzIDE2LjM0MzggMTgwLjk0NyAyMS4xNzQ0QzE4My43ODEgMjYuMDA1IDE4NS4zNDkgMzEuNDczMiAxODUuNTA1IDM3LjA3MTZDMTg1LjYzOCA0MS44MzczIDE4NC43NDUgNDYuNTY1OSAxODIuODk3IDUwLjkzOThDMTgyLjM2MSA1Mi4yMDg1IDE4MC44NDggNTIuNjg4MiAxNzkuNjIzIDUyLjA1ODZDMTc4LjM5OCA1MS40MjkxIDE3Ny45MjcgNDkuOTI5IDE3OC40NDYgNDguNjUzM0MxNzkuOTE5IDQ1LjAzMjUgMTgwLjYyOSA0MS4xMzY1IDE4MC41MiAzNy4yMTA5QzE4MC4zODcgMzIuNDUyMiAxNzkuMDU0IDI3LjgwNDIgMTc2LjY0NSAyMy42OTgyQzE3NC4yMzYgMTkuNTkyMiAxNzAuODI5IDE2LjE2MTIgMTY2Ljc0IDEzLjcyMzZDMTYzLjM2NiAxMS43MTI4IDE1OS42MTkgMTAuNDMxNyAxNTUuNzQgOS45NTE1MkMxNTQuMzczIDkuNzgyMzQgMTUzLjI5MyA4LjYzODkxIDE1My4zNDEgNy4yNjI0OVoiLz4KPC9tYXNrPgo8cGF0aCBkPSJNMTUzLjM0MSA3LjI2MjQ5QzE1My4zOSA1Ljg4NjA2IDE1NC41NDYgNC43OTk1NSAxNTUuOTE1IDQuOTUwNjFDMTYwLjYzNSA1LjQ3MTQzIDE2NS4xOTggNi45OTg0IDE2OS4yOTMgOS40Mzk1OEMxNzQuMTA0IDEyLjMwNzMgMTc4LjExMyAxNi4zNDM4IDE4MC45NDcgMjEuMTc0NEMxODMuNzgxIDI2LjAwNSAxODUuMzQ5IDMxLjQ3MzIgMTg1LjUwNSAzNy4wNzE2QzE4NS42MzggNDEuODM3MyAxODQuNzQ1IDQ2LjU2NTkgMTgyLjg5NyA1MC45Mzk4QzE4Mi4zNjEgNTIuMjA4NSAxODAuODQ4IDUyLjY4ODIgMTc5LjYyMyA1Mi4wNTg2QzE3OC4zOTggNTEuNDI5MSAxNzcuOTI3IDQ5LjkyOSAxNzguNDQ2IDQ4LjY1MzNDMTc5LjkxOSA0NS4wMzI1IDE4MC42MjkgNDEuMTM2NSAxODAuNTIgMzcuMjEwOUMxODAuMzg3IDMyLjQ1MjIgMTc5LjA1NCAyNy44MDQyIDE3Ni42NDUgMjMuNjk4MkMxNzQuMjM2IDE5LjU5MjIgMTcwLjgyOSAxNi4xNjEyIDE2Ni43NCAxMy43MjM2QzE2My4zNjYgMTEuNzEyOCAxNTkuNjE5IDEwLjQzMTcgMTU1Ljc0IDkuOTUxNTJDMTU0LjM3MyA5Ljc4MjM0IDE1My4yOTMgOC42Mzg5MSAxNTMuMzQxIDcuMjYyNDlaIiBmaWxsPSIjRkY0RDVBIi8+CjxwYXRoIGQ9Ik0xNTMuMzQxIDcuMjYyNDlDMTUzLjM5IDUuODg2MDYgMTU0LjU0NiA0Ljc5OTU1IDE1NS45MTUgNC45NTA2MUMxNjAuNjM1IDUuNDcxNDMgMTY1LjE5OCA2Ljk5ODQgMTY5LjI5MyA5LjQzOTU4QzE3NC4xMDQgMTIuMzA3MyAxNzguMTEzIDE2LjM0MzggMTgwLjk0NyAyMS4xNzQ0QzE4My43ODEgMjYuMDA1IDE4NS4zNDkgMzEuNDczMiAxODUuNTA1IDM3LjA3MTZDMTg1LjYzOCA0MS44MzczIDE4NC43NDUgNDYuNTY1OSAxODIuODk3IDUwLjkzOThDMTgyLjM2MSA1Mi4yMDg1IDE4MC44NDggNTIuNjg4MiAxNzkuNjIzIDUyLjA1ODZDMTc4LjM5OCA1MS40MjkxIDE3Ny45MjcgNDkuOTI5IDE3OC40NDYgNDguNjUzM0MxNzkuOTE5IDQ1LjAzMjUgMTgwLjYyOSA0MS4xMzY1IDE4MC41MiAzNy4yMTA5QzE4MC4zODcgMzIuNDUyMiAxNzkuMDU0IDI3LjgwNDIgMTc2LjY0NSAyMy42OTgyQzE3NC4yMzYgMTkuNTkyMiAxNzAuODI5IDE2LjE2MTIgMTY2Ljc0IDEzLjcyMzZDMTYzLjM2NiAxMS43MTI4IDE1OS42MTkgMTAuNDMxNyAxNTUuNzQgOS45NTE1MkMxNTQuMzczIDkuNzgyMzQgMTUzLjI5MyA4LjYzODkxIDE1My4zNDEgNy4yNjI0OVoiIGZpbGw9IndoaXRlIiBmaWxsLW9wYWNpdHk9IjAuMSIvPgo8cGF0aCBkPSJNMTUzLjM0MSA3LjI2MjQ5QzE1My4zOSA1Ljg4NjA2IDE1NC41NDYgNC43OTk1NSAxNTUuOTE1IDQuOTUwNjFDMTYwLjYzNSA1LjQ3MTQzIDE2NS4xOTggNi45OTg0IDE2OS4yOTMgOS40Mzk1OEMxNzQuMTA0IDEyLjMwNzMgMTc4LjExMyAxNi4zNDM4IDE4MC45NDcgMjEuMTc0NEMxODMuNzgxIDI2LjAwNSAxODUuMzQ5IDMxLjQ3MzIgMTg1LjUwNSAzNy4wNzE2QzE4NS42MzggNDEuODM3MyAxODQuNzQ1IDQ2LjU2NTkgMTgyLjg5NyA1MC45Mzk4QzE4Mi4zNjEgNTIuMjA4NSAxODAuODQ4IDUyLjY4ODIgMTc5LjYyMyA1Mi4wNTg2QzE3OC4zOTggNTEuNDI5MSAxNzcuOTI3IDQ5LjkyOSAxNzguNDQ2IDQ4LjY1MzNDMTc5LjkxOSA0NS4wMzI1IDE4MC42MjkgNDEuMTM2NSAxODAuNTIgMzcuMjEwOUMxODAuMzg3IDMyLjQ1MjIgMTc5LjA1NCAyNy44MDQyIDE3Ni42NDUgMjMuNjk4MkMxNzQuMjM2IDE5LjU5MjIgMTcwLjgyOSAxNi4xNjEyIDE2Ni43NCAxMy43MjM2QzE2My4zNjYgMTEuNzEyOCAxNTkuNjE5IDEwLjQzMTcgMTU1Ljc0IDkuOTUxNTJDMTU0LjM3MyA5Ljc4MjM0IDE1My4yOTMgOC42Mzg5MSAxNTMuMzQxIDcuMjYyNDlaIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjEuOTMzNzEiIG1hc2s9InVybCgjcGF0aC05LW91dHNpZGUtMV80MTk4XzExMDQ5MykiLz4KPHBhdGggZD0iTTE1MS4xOTUgNy4yNjI0OUMxNTEuMTQ3IDUuODg2MDYgMTQ5Ljk5IDQuNzk5NTUgMTQ4LjYyMSA0Ljk1MDYyQzE0My43MjcgNS40OTA2NyAxMzkuMDAzIDcuMTEyMzcgMTM0Ljc5NiA5LjcxMDUzQzEyOS44ODEgMTIuNzQ2NSAxMjUuODQxIDE3LjAxMDkgMTIzLjA3NSAyMi4wODM1QzEyMC4zMDkgMjcuMTU2MSAxMTguOTEzIDMyLjg2MTggMTE5LjAyNCAzOC42MzgzQzExOS4xMzUgNDQuNDE0OCAxMjAuNzQ5IDUwLjA2MjcgMTIzLjcwOCA1NS4wMjU0QzEyNi42NjYgNTkuOTg4MSAxMzAuODY2IDY0LjA5NDMgMTM1Ljg5NSA2Ni45MzkzQzE0MC45MjQgNjkuNzg0MyAxNDYuNjA3IDcxLjI3IDE1Mi4zODQgNzEuMjQ5OEMxNTguMTYyIDcxLjIyOTYgMTYzLjgzNCA2OS43MDQ0IDE2OC44NDMgNjYuODI0M0MxNzMuMTI5IDY0LjM1OTYgMTc2LjgwMiA2MC45NzU0IDE3OS42MDUgNTYuOTI3M0MxODAuMzg5IDU1Ljc5NDkgMTc5Ljk4NCA1NC4yNjA3IDE3OC43OTYgNTMuNTYzN0MxNzcuNjA4IDUyLjg2NjggMTc2LjA4OSA1My4yNzI0IDE3NS4yOSA1NC4zOTQzQzE3Mi45MzYgNTcuNjk5OSAxNjkuODkxIDYwLjQ2ODEgMTY2LjM1NyA2Mi41MDA3QzE2Mi4wOTkgNjQuOTQ4NyAxNTcuMjc4IDY2LjI0NTIgMTUyLjM2NyA2Ni4yNjIzQzE0Ny40NTYgNjYuMjc5NSAxNDIuNjI1IDY1LjAxNjcgMTM4LjM1MSA2Mi41OTg0QzEzNC4wNzcgNjAuMTgwMiAxMzAuNTA2IDU2LjY4OTkgMTI3Ljk5MiA1Mi40NzE2QzEyNS40NzcgNDguMjUzMyAxMjQuMTA1IDQzLjQ1MjYgMTI0LjAxMSAzOC41NDI2QzEyMy45MTYgMzMuNjMyNSAxMjUuMTAzIDI4Ljc4MjcgMTI3LjQ1NCAyNC40NzFDMTI5LjgwNSAyMC4xNTkzIDEzMy4yMzkgMTYuNTM0NSAxMzcuNDE3IDEzLjk1MzlDMTQwLjg4NiAxMS44MTEzIDE0NC43NjkgMTAuNDUgMTQ4Ljc5NiA5Ljk1MTUxQzE1MC4xNjMgOS43ODIzNCAxNTEuMjQzIDguNjM4OTEgMTUxLjE5NSA3LjI2MjQ5WiIgZmlsbD0iIzRDQUY1MCIvPgo8cGF0aCBkPSJNMTQ3LjEwNiAyOS4yMzQ0VjMzLjVIMTQ2LjU0OVYyOS4yMzQ0SDE0Ny4xMDZaTTE0OC40NzcgMjkuMjM0NFYyOS42OTczSDE0NS4xODFWMjkuMjM0NEgxNDguNDc3Wk0xNDguNTkxIDMxLjk1MDJWMzEuODgyOEMxNDguNTkxIDMxLjY1NDMgMTQ4LjYyNSAzMS40NDI0IDE0OC42OTEgMzEuMjQ3MUMxNDguNzU3IDMxLjA0OTggMTQ4Ljg1MyAzMC44Nzg5IDE0OC45NzggMzAuNzM0NEMxNDkuMTAzIDMwLjU4NzkgMTQ5LjI1NCAzMC40NzQ2IDE0OS40MzIgMzAuMzk0NUMxNDkuNjEgMzAuMzEyNSAxNDkuODA5IDMwLjI3MTUgMTUwLjAzIDMwLjI3MTVDMTUwLjI1MiAzMC4yNzE1IDE1MC40NTMgMzAuMzEyNSAxNTAuNjMgMzAuMzk0NUMxNTAuODEgMzAuNDc0NiAxNTAuOTYyIDMwLjU4NzkgMTUxLjA4NyAzMC43MzQ0QzE1MS4yMTQgMzAuODc4OSAxNTEuMzExIDMxLjA0OTggMTUxLjM3NyAzMS4yNDcxQzE1MS40NDQgMzEuNDQyNCAxNTEuNDc3IDMxLjY1NDMgMTUxLjQ3NyAzMS44ODI4VjMxLjk1MDJDMTUxLjQ3NyAzMi4xNzg3IDE1MS40NDQgMzIuMzkwNiAxNTEuMzc3IDMyLjU4NTlDMTUxLjMxMSAzMi43ODEyIDE1MS4yMTQgMzIuOTUyMSAxNTEuMDg3IDMzLjA5ODZDMTUwLjk2MiAzMy4yNDMyIDE1MC44MTEgMzMuMzU2NCAxNTAuNjMzIDMzLjQzODVDMTUwLjQ1OCAzMy41MTg2IDE1MC4yNTggMzMuNTU4NiAxNTAuMDM2IDMzLjU1ODZDMTQ5LjgxMyAzMy41NTg2IDE0OS42MTMgMzMuNTE4NiAxNDkuNDM1IDMzLjQzODVDMTQ5LjI1NyAzMy4zNTY0IDE0OS4xMDUgMzMuMjQzMiAxNDguOTc4IDMzLjA5ODZDMTQ4Ljg1MyAzMi45NTIxIDE0OC43NTcgMzIuNzgxMiAxNDguNjkxIDMyLjU4NTlDMTQ4LjYyNSAzMi4zOTA2IDE0OC41OTEgMzIuMTc4NyAxNDguNTkxIDMxLjk1MDJaTTE0OS4xMzMgMzEuODgyOFYzMS45NTAyQzE0OS4xMzMgMzIuMTA4NCAxNDkuMTUyIDMyLjI1NzggMTQ5LjE4OSAzMi4zOTg0QzE0OS4yMjYgMzIuNTM3MSAxNDkuMjgyIDMyLjY2MDIgMTQ5LjM1NiAzMi43Njc2QzE0OS40MzIgMzIuODc1IDE0OS41MjcgMzIuOTYgMTQ5LjY0IDMzLjAyMjVDMTQ5Ljc1MyAzMy4wODMgMTQ5Ljg4NSAzMy4xMTMzIDE1MC4wMzYgMzMuMTEzM0MxNTAuMTg0IDMzLjExMzMgMTUwLjMxNCAzMy4wODMgMTUwLjQyNSAzMy4wMjI1QzE1MC41MzkgMzIuOTYgMTUwLjYzMiAzMi44NzUgMTUwLjcwNyAzMi43Njc2QzE1MC43ODEgMzIuNjYwMiAxNTAuODM2IDMyLjUzNzEgMTUwLjg3NCAzMi4zOTg0QzE1MC45MTMgMzIuMjU3OCAxNTAuOTMyIDMyLjEwODQgMTUwLjkzMiAzMS45NTAyVjMxLjg4MjhDMTUwLjkzMiAzMS43MjY2IDE1MC45MTMgMzEuNTc5MSAxNTAuODc0IDMxLjQ0MDRDMTUwLjgzNiAzMS4yOTk4IDE1MC43OCAzMS4xNzU4IDE1MC43MDQgMzEuMDY4NEMxNTAuNjI5IDMwLjk1OSAxNTAuNTM2IDMwLjg3MyAxNTAuNDIyIDMwLjgxMDVDMTUwLjMxMSAzMC43NDggMTUwLjE4IDMwLjcxNjggMTUwLjAzIDMwLjcxNjhDMTQ5Ljg4MSAzMC43MTY4IDE0OS43NSAzMC43NDggMTQ5LjYzNyAzMC44MTA1QzE0OS41MjYgMzAuODczIDE0OS40MzIgMzAuOTU5IDE0OS4zNTYgMzEuMDY4NEMxNDkuMjgyIDMxLjE3NTggMTQ5LjIyNiAzMS4yOTk4IDE0OS4xODkgMzEuNDQwNEMxNDkuMTUyIDMxLjU3OTEgMTQ5LjEzMyAzMS43MjY2IDE0OS4xMzMgMzEuODgyOFpNMTUzLjQ4NCAzMC4zMzAxVjMwLjc0NjFIMTUxLjc3VjMwLjMzMDFIMTUzLjQ4NFpNMTUyLjM1IDI5LjU1OTZIMTUyLjg5MlYzMi43MTQ4QzE1Mi44OTIgMzIuODIyMyAxNTIuOTA5IDMyLjkwMzMgMTUyLjk0MiAzMi45NThDMTUyLjk3NSAzMy4wMTI3IDE1My4wMTggMzMuMDQ4OCAxNTMuMDcxIDMzLjA2NjRDMTUzLjEyNCAzMy4wODQgMTUzLjE4IDMzLjA5MjggMTUzLjI0MSAzMy4wOTI4QzE1My4yODYgMzMuMDkyOCAxNTMuMzMzIDMzLjA4ODkgMTUzLjM4MSAzMy4wODExQzE1My40MzIgMzMuMDcxMyAxNTMuNDcgMzMuMDYzNSAxNTMuNDk2IDMzLjA1NzZMMTUzLjQ5OSAzMy41QzE1My40NTYgMzMuNTEzNyAxNTMuMzk5IDMzLjUyNjQgMTUzLjMyOSAzMy41MzgxQzE1My4yNiAzMy41NTE4IDE1My4xNzcgMzMuNTU4NiAxNTMuMDggMzMuNTU4NkMxNTIuOTQ3IDMzLjU1ODYgMTUyLjgyNSAzMy41MzIyIDE1Mi43MTMgMzMuNDc5NUMxNTIuNjAyIDMzLjQyNjggMTUyLjUxMyAzMy4zMzg5IDE1Mi40NDcgMzMuMjE1OEMxNTIuMzgyIDMzLjA5MDggMTUyLjM1IDMyLjkyMjkgMTUyLjM1IDMyLjcxMTlWMjkuNTU5NlpNMTU1Ljk4OSAzMi45NThWMzEuMzI2MkMxNTUuOTg5IDMxLjIwMTIgMTU1Ljk2MyAzMS4wOTI4IDE1NS45MTMgMzEuMDAxQzE1NS44NjQgMzAuOTA3MiAxNTUuNzkgMzAuODM1IDE1NS42OSAzMC43ODQyQzE1NS41OSAzMC43MzM0IDE1NS40NjcgMzAuNzA4IDE1NS4zMjEgMzAuNzA4QzE1NS4xODQgMzAuNzA4IDE1NS4wNjQgMzAuNzMxNCAxNTQuOTYgMzAuNzc4M0MxNTQuODU5IDMwLjgyNTIgMTU0Ljc3OSAzMC44ODY3IDE1NC43MiAzMC45NjI5QzE1NC42NjQgMzEuMDM5MSAxNTQuNjM1IDMxLjEyMTEgMTU0LjYzNSAzMS4yMDlIMTU0LjA5M0MxNTQuMDkzIDMxLjA5NTcgMTU0LjEyMyAzMC45ODM0IDE1NC4xODEgMzAuODcyMUMxNTQuMjQgMzAuNzYwNyAxNTQuMzI0IDMwLjY2MDIgMTU0LjQzMyAzMC41NzAzQzE1NC41NDQgMzAuNDc4NSAxNTQuNjc3IDMwLjQwNjIgMTU0LjgzMiAzMC4zNTM1QzE1NC45ODggMzAuMjk4OCAxNTUuMTYyIDMwLjI3MTUgMTU1LjM1MyAzMC4yNzE1QzE1NS41ODMgMzAuMjcxNSAxNTUuNzg3IDMwLjMxMDUgMTU1Ljk2MiAzMC4zODg3QzE1Ni4xNCAzMC40NjY4IDE1Ni4yNzkgMzAuNTg1IDE1Ni4zNzggMzAuNzQzMkMxNTYuNDggMzAuODk5NCAxNTYuNTMxIDMxLjA5NTcgMTU2LjUzMSAzMS4zMzJWMzIuODA4NkMxNTYuNTMxIDMyLjkxNDEgMTU2LjU0IDMzLjAyNjQgMTU2LjU1NyAzMy4xNDU1QzE1Ni41NzcgMzMuMjY0NiAxNTYuNjA1IDMzLjM2NzIgMTU2LjY0MiAzMy40NTMxVjMzLjVIMTU2LjA3N0MxNTYuMDQ5IDMzLjQzNzUgMTU2LjAyOCAzMy4zNTQ1IDE1Ni4wMTIgMzMuMjUxQzE1NS45OTcgMzMuMTQ1NSAxNTUuOTg5IDMzLjA0NzkgMTU1Ljk4OSAzMi45NThaTTE1Ni4wODMgMzEuNTc4MUwxNTYuMDg4IDMxLjk1OUgxNTUuNTQxQzE1NS4zODYgMzEuOTU5IDE1NS4yNDkgMzEuOTcxNyAxNTUuMTI3IDMxLjk5NzFDMTU1LjAwNiAzMi4wMjA1IDE1NC45MDUgMzIuMDU2NiAxNTQuODIzIDMyLjEwNTVDMTU0Ljc0MSAzMi4xNTQzIDE1NC42NzggMzIuMjE1OCAxNTQuNjM1IDMyLjI5QzE1NC41OTIgMzIuMzYyMyAxNTQuNTcxIDMyLjQ0NzMgMTU0LjU3MSAzMi41NDQ5QzE1NC41NzEgMzIuNjQ0NSAxNTQuNTkzIDMyLjczNTQgMTU0LjYzOCAzMi44MTc0QzE1NC42ODMgMzIuODk5NCAxNTQuNzUgMzIuOTY0OCAxNTQuODQgMzMuMDEzN0MxNTQuOTMyIDMzLjA2MDUgMTU1LjA0NCAzMy4wODQgMTU1LjE3NyAzMy4wODRDMTU1LjM0MyAzMy4wODQgMTU1LjQ5IDMzLjA0ODggMTU1LjYxNyAzMi45Nzg1QzE1NS43NDQgMzIuOTA4MiAxNTUuODQ0IDMyLjgyMjMgMTU1LjkxOCAzMi43MjA3QzE1NS45OTUgMzIuNjE5MSAxNTYuMDM2IDMyLjUyMDUgMTU2LjA0MiAzMi40MjQ4TDE1Ni4yNzMgMzIuNjg1NUMxNTYuMjU5IDMyLjc2NzYgMTU2LjIyMiAzMi44NTg0IDE1Ni4xNjIgMzIuOTU4QzE1Ni4xMDEgMzMuMDU3NiAxNTYuMDIgMzMuMTUzMyAxNTUuOTE4IDMzLjI0NTFDMTU1LjgxOSAzMy4zMzUgMTU1LjcgMzMuNDEwMiAxNTUuNTYxIDMzLjQ3MDdDMTU1LjQyNCAzMy41MjkzIDE1NS4yNyAzMy41NTg2IDE1NS4wOTggMzMuNTU4NkMxNTQuODgzIDMzLjU1ODYgMTU0LjY5NSAzMy41MTY2IDE1NC41MzMgMzMuNDMyNkMxNTQuMzczIDMzLjM0ODYgMTU0LjI0OCAzMy4yMzYzIDE1NC4xNTggMzMuMDk1N0MxNTQuMDcgMzIuOTUzMSAxNTQuMDI2IDMyLjc5MzkgMTU0LjAyNiAzMi42MTgyQzE1NC4wMjYgMzIuNDQ4MiAxNTQuMDU5IDMyLjI5ODggMTU0LjEyNSAzMi4xNjk5QzE1NC4xOTIgMzIuMDM5MSAxNTQuMjg4IDMxLjkzMDcgMTU0LjQxMyAzMS44NDQ3QzE1NC41MzggMzEuNzU2OCAxNTQuNjg4IDMxLjY5MDQgMTU0Ljg2NCAzMS42NDU1QzE1NS4wNCAzMS42MDA2IDE1NS4yMzYgMzEuNTc4MSAxNTUuNDUzIDMxLjU3ODFIMTU2LjA4M1pNMTU3Ljk3MiAyOVYzMy41SDE1Ny40MjdWMjlIMTU3Ljk3MloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuMzgiLz4KPHBhdGggZD0iTTE0NS43MzIgMzYuOTM5NVY0NS41SDE0NC4zMlYzOC42MTUyTDE0Mi4yMjggMzkuMzI0MlYzOC4xNTgyTDE0NS41NjIgMzYuOTM5NUgxNDUuNzMyWk0xNTQuMjc1IDQzLjE3MzhDMTU0LjI3NSA0My43MDUxIDE1NC4xNTIgNDQuMTUyMyAxNTMuOTA2IDQ0LjUxNTZDMTUzLjY2IDQ0Ljg3ODkgMTUzLjMyNCA0NS4xNTQzIDE1Mi44OTggNDUuMzQxOEMxNTIuNDc2IDQ1LjUyNTQgMTUyIDQ1LjYxNzIgMTUxLjQ2OCA0NS42MTcyQzE1MC45MzcgNDUuNjE3MiAxNTAuNDU4IDQ1LjUyNTQgMTUwLjAzMyA0NS4zNDE4QzE0OS42MDcgNDUuMTU0MyAxNDkuMjcxIDQ0Ljg3ODkgMTQ5LjAyNSA0NC41MTU2QzE0OC43NzkgNDQuMTUyMyAxNDguNjU2IDQzLjcwNTEgMTQ4LjY1NiA0My4xNzM4QzE0OC42NTYgNDIuODIyMyAxNDguNzI0IDQyLjUwMzkgMTQ4Ljg2MSA0Mi4yMTg4QzE0OC45OTggNDEuOTI5NyAxNDkuMTkxIDQxLjY4MTYgMTQ5LjQ0MSA0MS40NzQ2QzE0OS42OTUgNDEuMjYzNyAxNDkuOTkyIDQxLjEwMTYgMTUwLjMzMiA0MC45ODgzQzE1MC42NzUgNDAuODc1IDE1MS4wNSA0MC44MTg0IDE1MS40NTcgNDAuODE4NEMxNTEuOTk2IDQwLjgxODQgMTUyLjQ3OCA0MC45MTggMTUyLjkwNCA0MS4xMTcyQzE1My4zMyA0MS4zMTY0IDE1My42NjQgNDEuNTkxOCAxNTMuOTA2IDQxLjk0MzRDMTU0LjE1MiA0Mi4yOTQ5IDE1NC4yNzUgNDIuNzA1MSAxNTQuMjc1IDQzLjE3MzhaTTE1Mi44NTcgNDMuMTAzNUMxNTIuODU3IDQyLjgxODQgMTUyLjc5OCA0Mi41Njg0IDE1Mi42ODEgNDIuMzUzNUMxNTIuNTY0IDQyLjEzODcgMTUyLjQgNDEuOTcyNyAxNTIuMTg5IDQxLjg1NTVDMTUxLjk3OCA0MS43MzgzIDE1MS43MzQgNDEuNjc5NyAxNTEuNDU3IDQxLjY3OTdDMTUxLjE3NSA0MS42Nzk3IDE1MC45MzEgNDEuNzM4MyAxNTAuNzI0IDQxLjg1NTVDMTUwLjUxNyA0MS45NzI3IDE1MC4zNTUgNDIuMTM4NyAxNTAuMjM4IDQyLjM1MzVDMTUwLjEyNSA0Mi41Njg0IDE1MC4wNjggNDIuODE4NCAxNTAuMDY4IDQzLjEwMzVDMTUwLjA2OCA0My4zOTI2IDE1MC4xMjUgNDMuNjQyNiAxNTAuMjM4IDQzLjg1MzVDMTUwLjM1MSA0NC4wNjA1IDE1MC41MTMgNDQuMjE4OCAxNTAuNzI0IDQ0LjMyODFDMTUwLjkzNSA0NC40Mzc1IDE1MS4xODMgNDQuNDkyMiAxNTEuNDY4IDQ0LjQ5MjJDMTUxLjc1MyA0NC40OTIyIDE1MiA0NC40Mzc1IDE1Mi4yMDcgNDQuMzI4MUMxNTIuNDE0IDQ0LjIxODggMTUyLjU3NCA0NC4wNjA1IDE1Mi42ODcgNDMuODUzNUMxNTIuOCA0My42NDI2IDE1Mi44NTcgNDMuMzkyNiAxNTIuODU3IDQzLjEwMzVaTTE1NC4wODIgMzkuMjEyOUMxNTQuMDgyIDM5LjYzODcgMTUzLjk2OCA0MC4wMTc2IDE1My43NDIgNDAuMzQ5NkMxNTMuNTE5IDQwLjY4MTYgMTUzLjIxIDQwLjk0MzQgMTUyLjgxNiA0MS4xMzQ4QzE1Mi40MjEgNDEuMzIyMyAxNTEuOTcyIDQxLjQxNiAxNTEuNDY4IDQxLjQxNkMxNTAuOTYgNDEuNDE2IDE1MC41MDcgNDEuMzIyMyAxNTAuMTA5IDQxLjEzNDhDMTQ5LjcxNCA0MC45NDM0IDE0OS40MDQgNDAuNjgxNiAxNDkuMTc3IDQwLjM0OTZDMTQ4Ljk1NSA0MC4wMTc2IDE0OC44NDMgMzkuNjM4NyAxNDguODQzIDM5LjIxMjlDMTQ4Ljg0MyAzOC43MDUxIDE0OC45NTUgMzguMjc3MyAxNDkuMTc3IDM3LjkyOTdDMTQ5LjQwNCAzNy41NzgxIDE0OS43MTQgMzcuMzEwNSAxNTAuMTA5IDM3LjEyN0MxNTAuNTAzIDM2Ljk0MzQgMTUwLjk1NSAzNi44NTE2IDE1MS40NjIgMzYuODUxNkMxNTEuOTcgMzYuODUxNiAxNTIuNDIxIDM2Ljk0MzQgMTUyLjgxNiAzNy4xMjdDMTUzLjIxIDM3LjMxMDUgMTUzLjUxOSAzNy41NzgxIDE1My43NDIgMzcuOTI5N0MxNTMuOTY4IDM4LjI3NzMgMTU0LjA4MiAzOC43MDUxIDE1NC4wODIgMzkuMjEyOVpNMTUyLjY2OSAzOS4yNTk4QzE1Mi42NjkgMzkuMDA1OSAxNTIuNjE5IDM4Ljc4MzIgMTUyLjUxNyAzOC41OTE4QzE1Mi40MTkgMzguMzk2NSAxNTIuMjgxIDM4LjI0NDEgMTUyLjEwMSAzOC4xMzQ4QzE1MS45MjEgMzguMDI1NCAxNTEuNzA4IDM3Ljk3MDcgMTUxLjQ2MiAzNy45NzA3QzE1MS4yMTYgMzcuOTcwNyAxNTEuMDAzIDM4LjAyMzQgMTUwLjgyNCAzOC4xMjg5QzE1MC42NDQgMzguMjM0NCAxNTAuNTA1IDM4LjM4MjggMTUwLjQwOCAzOC41NzQyQzE1MC4zMSAzOC43NjU2IDE1MC4yNjEgMzguOTk0MSAxNTAuMjYxIDM5LjI1OThDMTUwLjI2MSAzOS41MjE1IDE1MC4zMSAzOS43NSAxNTAuNDA4IDM5Ljk0NTNDMTUwLjUwNSA0MC4xMzY3IDE1MC42NDQgNDAuMjg3MSAxNTAuODI0IDQwLjM5NjVDMTUxLjAwNyA0MC41MDU5IDE1MS4yMjIgNDAuNTYwNSAxNTEuNDY4IDQwLjU2MDVDMTUxLjcxNCA0MC41NjA1IDE1MS45MjcgNDAuNTA1OSAxNTIuMTA3IDQwLjM5NjVDMTUyLjI4NyA0MC4yODcxIDE1Mi40MjUgNDAuMTM2NyAxNTIuNTIzIDM5Ljk0NTNDMTUyLjYyMSAzOS43NSAxNTIuNjY5IDM5LjUyMTUgMTUyLjY2OSAzOS4yNTk4Wk0xNTcuMTc1IDQwLjU5NTdIMTU4LjAxOUMxNTguMzQ3IDQwLjU5NTcgMTU4LjYxOSA0MC41MzkxIDE1OC44MzMgNDAuNDI1OEMxNTkuMDUyIDQwLjMxMjUgMTU5LjIxNCA0MC4xNTYyIDE1OS4zMiAzOS45NTdDMTU5LjQyNSAzOS43NTc4IDE1OS40NzggMzkuNTI5MyAxNTkuNDc4IDM5LjI3MTVDMTU5LjQ3OCAzOS4wMDIgMTU5LjQyOSAzOC43NzE1IDE1OS4zMzIgMzguNTgwMUMxNTkuMjM4IDM4LjM4NDggMTU5LjA5MyAzOC4yMzQ0IDE1OC44OTggMzguMTI4OUMxNTguNzA3IDM4LjAyMzQgMTU4LjQ2MiAzNy45NzA3IDE1OC4xNjYgMzcuOTcwN0MxNTcuOTE2IDM3Ljk3MDcgMTU3LjY4OSAzOC4wMjE1IDE1Ny40ODYgMzguMTIzQzE1Ny4yODcgMzguMjIwNyAxNTcuMTI4IDM4LjM2MTMgMTU3LjAxMSAzOC41NDQ5QzE1Ni44OTQgMzguNzI0NiAxNTYuODM1IDM4LjkzOTUgMTU2LjgzNSAzOS4xODk1SDE1NS40MTdDMTU1LjQxNyAzOC43MzYzIDE1NS41MzcgMzguMzM0IDE1NS43NzUgMzcuOTgyNEMxNTYuMDEzIDM3LjYzMDkgMTU2LjMzNyAzNy4zNTU1IDE1Ni43NDggMzcuMTU2MkMxNTcuMTYyIDM2Ljk1MzEgMTU3LjYyNiAzNi44NTE2IDE1OC4xNDIgMzYuODUxNkMxNTguNjkzIDM2Ljg1MTYgMTU5LjE3MyAzNi45NDM0IDE1OS41ODMgMzcuMTI3QzE1OS45OTggMzcuMzA2NiAxNjAuMzIgMzcuNTc2MiAxNjAuNTUgMzcuOTM1NUMxNjAuNzgxIDM4LjI5NDkgMTYwLjg5NiAzOC43NDAyIDE2MC44OTYgMzkuMjcxNUMxNjAuODk2IDM5LjUxMzcgMTYwLjgzOSAzOS43NTk4IDE2MC43MjYgNDAuMDA5OEMxNjAuNjEzIDQwLjI1OTggMTYwLjQ0NSA0MC40ODgzIDE2MC4yMjIgNDAuNjk1M0MxNjAgNDAuODk4NCAxNTkuNzIyIDQxLjA2NDUgMTU5LjM5IDQxLjE5MzRDMTU5LjA1OCA0MS4zMTg0IDE1OC42NzMgNDEuMzgwOSAxNTguMjM2IDQxLjM4MDlIMTU3LjE3NVY0MC41OTU3Wk0xNTcuMTc1IDQxLjY5NzNWNDAuOTIzOEgxNTguMjM2QzE1OC43MzYgNDAuOTIzOCAxNTkuMTYyIDQwLjk4MjQgMTU5LjUxMyA0MS4wOTk2QzE1OS44NjkgNDEuMjE2OCAxNjAuMTU4IDQxLjM3ODkgMTYwLjM4IDQxLjU4NTlDMTYwLjYwMyA0MS43ODkxIDE2MC43NjUgNDIuMDIxNSAxNjAuODY3IDQyLjI4MzJDMTYwLjk3MiA0Mi41NDQ5IDE2MS4wMjUgNDIuODIyMyAxNjEuMDI1IDQzLjExNTJDMTYxLjAyNSA0My41MTM3IDE2MC45NTMgNDMuODY5MSAxNjAuODA4IDQ0LjE4MTZDMTYwLjY2NyA0NC40OTAyIDE2MC40NjYgNDQuNzUyIDE2MC4yMDUgNDQuOTY2OEMxNTkuOTQzIDQ1LjE4MTYgMTU5LjYzNiA0NS4zNDM4IDE1OS4yODUgNDUuNDUzMUMxNTguOTM3IDQ1LjU2MjUgMTU4LjU1OCA0NS42MTcyIDE1OC4xNDggNDUuNjE3MkMxNTcuNzgxIDQ1LjYxNzIgMTU3LjQyOSA0NS41NjY0IDE1Ny4wOTMgNDUuNDY0OEMxNTYuNzU3IDQ1LjM2MzMgMTU2LjQ1NyA0NS4yMTI5IDE1Ni4xOTEgNDUuMDEzN0MxNTUuOTI1IDQ0LjgxMDUgMTU1LjcxNCA0NC41NTg2IDE1NS41NTggNDQuMjU3OEMxNTUuNDA2IDQzLjk1MzEgMTU1LjMzIDQzLjYwMTYgMTU1LjMzIDQzLjIwMzFIMTU2Ljc0MkMxNTYuNzQyIDQzLjQ1NyAxNTYuOCA0My42ODE2IDE1Ni45MTcgNDMuODc3QzE1Ny4wMzkgNDQuMDY4NCAxNTcuMjA3IDQ0LjIxODggMTU3LjQyMSA0NC4zMjgxQzE1Ny42NCA0NC40Mzc1IDE1Ny44OSA0NC40OTIyIDE1OC4xNzEgNDQuNDkyMkMxNTguNDY4IDQ0LjQ5MjIgMTU4LjcyNCA0NC40Mzk1IDE1OC45MzkgNDQuMzM0QzE1OS4xNTQgNDQuMjI4NSAxNTkuMzE4IDQ0LjA3MjMgMTU5LjQzMSA0My44NjUyQzE1OS41NDggNDMuNjU4MiAxNTkuNjA3IDQzLjQwODIgMTU5LjYwNyA0My4xMTUyQzE1OS42MDcgNDIuNzgzMiAxNTkuNTQyIDQyLjUxMzcgMTU5LjQxNCA0Mi4zMDY2QzE1OS4yODUgNDIuMDk5NiAxNTkuMTAxIDQxLjk0NzMgMTU4Ljg2MyA0MS44NDk2QzE1OC42MjUgNDEuNzQ4IDE1OC4zNDMgNDEuNjk3MyAxNTguMDE5IDQxLjY5NzNIMTU3LjE3NVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAyXzQxOThfMTEwNDkzKSI+CjxwYXRoIGQ9Ik0yIDg4TDE5NyA4OCIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC41Ii8+CjxwYXRoIGQ9Ik0yIDEwNkwxOTcgMTA2IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIgc3Ryb2tlLXdpZHRoPSIwLjUiLz4KPHBhdGggZD0iTTIgMTI0TDE5NyAxMjQiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIiBzdHJva2Utd2lkdGg9IjAuNSIvPgo8cGF0aCBkPSJNMiAxNDJMMTk3IDE0MiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC41Ii8+CjxwYXRoIGQ9Ik0yIDE2MEwxOTcgMTYwIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMTAzLjY5NSA5MUMxMDMuNjk1IDg5Ljg5NTQgMTA0LjU5IDg5IDEwNS42OTUgODlIMTE3LjY5NUMxMTguNzk5IDg5IDExOS42OTUgODkuODk1NCAxMTkuNjk1IDkxVjE2MEgxMDMuNjk1VjkxWiIgZmlsbD0iIzQxOTZGNiIvPgo8cGF0aCBkPSJNMTM2IDEyNEMxMzYgMTIyLjg5NSAxMzYuODk1IDEyMiAxMzggMTIySDE1MEMxNTEuMTA1IDEyMiAxNTIgMTIyLjg5NSAxNTIgMTI0VjE2MEgxMzZWMTI0WiIgZmlsbD0iIzQxOTZGNiIvPgo8cGF0aCBkPSJNNy42OTQ4MiAxNDNDNy42OTQ4MiAxNDEuODk1IDguNTkwMjUgMTQxIDkuNjk0ODIgMTQxSDIxLjY5NDhDMjIuNzk5NCAxNDEgMjMuNjk0OCAxNDEuODk1IDIzLjY5NDggMTQzVjE2MEg3LjY5NDgyVjE0M1oiIGZpbGw9IiM0MTk2RjYiLz4KPHBhdGggZD0iTTM5LjY5NDggMTA3QzM5LjY5NDggMTA1Ljg5NSA0MC41OTAzIDEwNSA0MS42OTQ4IDEwNUg1My42OTQ4QzU0Ljc5OTQgMTA1IDU1LjY5NDggMTA1Ljg5NSA1NS42OTQ4IDEwN1YxNjBIMzkuNjk0OFYxMDdaIiBmaWxsPSIjNDE5NkY2Ii8+CjxwYXRoIGQ9Ik03MiAxMzJDNzIgMTMwLjg5NSA3Mi44OTU0IDEzMCA3NCAxMzBIODZDODcuMTA0NiAxMzAgODggMTMwLjg5NSA4OCAxMzJWMTYwSDcyVjEzMloiIGZpbGw9IiM0MTk2RjYiLz4KPHBhdGggZD0iTTE2Ny42OTUgMTUwQzE2Ny42OTUgMTQ4Ljg5NSAxNjguNTkgMTQ4IDE2OS42OTUgMTQ4SDE4MS42OTVDMTgyLjc5OSAxNDggMTgzLjY5NSAxNDguODk1IDE4My42OTUgMTUwVjE2MEgxNjcuNjk1VjE1MFoiIGZpbGw9IiM0MTk2RjYiLz4KPHBhdGggZD0iTTQgMTA5TDE4LjUyMTEgMTAzLjAzMUMxOC41OTMzIDEwMy4wMDEgMTguNjY4NyAxMDIuOTggMTguNzQ1NyAxMDIuOTY4TDU5LjU3NDcgOTYuNTM2NUM1OS42NjE1IDk2LjUyMjggNTkuNzQ5OCA5Ni41MjA3IDU5LjgzNzMgOTYuNTMwMUw5OS4wOTMzIDEwMC43NTVMMTM5LjA0NCA5OS43MTMxTDE4MS4xNDIgOTUuODQwMkMxODEuMjEgOTUuODMzOSAxODEuMjc4IDk1LjgyMDYgMTgxLjM0NCA5NS44MDA1TDE5NyA5MSIgc3Ryb2tlPSIjRkZDMTA3IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8Y2lyY2xlIGN4PSI1OSIgY3k9Ijk3IiByPSIxLjUiIGZpbGw9IndoaXRlIiBzdHJva2U9IiNGRkMxMDciLz4KPGNpcmNsZSBjeD0iMTguNjk0OCIgY3k9IjEwMy4xNjEiIHI9IjEuNSIgZmlsbD0id2hpdGUiIHN0cm9rZT0iI0ZGQzEwNyIvPgo8Y2lyY2xlIGN4PSIzLjY5NDgyIiBjeT0iMTA5LjE2MSIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjRkZDMTA3Ii8+CjxjaXJjbGUgY3g9IjEwMCIgY3k9IjEwMSIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjRkZDMTA3Ii8+CjxjaXJjbGUgY3g9IjE0MyIgY3k9Ijk5IiByPSIxLjUiIGZpbGw9IndoaXRlIiBzdHJva2U9IiNGRkMxMDciLz4KPGNpcmNsZSBjeD0iMTgwLjY5NSIgY3k9Ijk2LjE2MTEiIHI9IjEuNSIgZmlsbD0id2hpdGUiIHN0cm9rZT0iI0ZGQzEwNyIvPgo8Y2lyY2xlIGN4PSIxOTYiIGN5PSI5MSIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjRkZDMTA3Ii8+CjxwYXRoIGQ9Ik00IDEzMkwxOC42MzkzIDExOS44MTRMNjAuMzA1IDEyNy42TDEwMC4yODIgMTE2TDE0MC4yNTggMTE2TDE3Ny45ODIgMTIyLjE4NEwxOTYgMTI3LjYiIHN0cm9rZT0iIzRDQUY1MCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPGNpcmNsZSBjeD0iNTkiIGN5PSIxMjgiIHI9IjEuNSIgZmlsbD0id2hpdGUiIHN0cm9rZT0iIzRDQUY1MCIvPgo8Y2lyY2xlIGN4PSIxOSIgY3k9IjEyMCIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjNENBRjUwIi8+CjxjaXJjbGUgY3g9IjEwMCIgY3k9IjExNiIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjNENBRjUwIi8+CjxjaXJjbGUgY3g9IjE3OC42OTUiIGN5PSIxMjIiIHI9IjEuNSIgZmlsbD0id2hpdGUiIHN0cm9rZT0iIzRDQUY1MCIvPgo8Y2lyY2xlIGN4PSIxOTYiIGN5PSIxMjgiIHI9IjEuNSIgZmlsbD0id2hpdGUiIHN0cm9rZT0iIzRDQUY1MCIvPgo8Y2lyY2xlIGN4PSIzLjY5NDgyIiBjeT0iMTMxLjE2MSIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjNENBRjUwIi8+CjxjaXJjbGUgY3g9IjE0MyIgY3k9IjExNiIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjNENBRjUwIi8+CjwvZz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl80MTk4XzExMDQ5MyIgeDE9IjQ1LjY5ODYiIHkxPSI4NC43NDkzIiB4Mj0iNDUuNjk4NiIgeTI9Ii0xOS45Mzg4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIG9mZnNldD0iMC4xNzMzODkiIHN0b3AtY29sb3I9IiM2REM3NzAiLz4KPHN0b3Agb2Zmc2V0PSIwLjI2Mjk3OSIgc3RvcC1jb2xvcj0iIzZEQzc3MCIvPgo8c3RvcCBvZmZzZXQ9IjAuMjY3OTY5IiBzdG9wLWNvbG9yPSIjRkZDMTA3Ii8+CjxzdG9wIG9mZnNldD0iMC40NDYxNzgiIHN0b3AtY29sb3I9IiNGRkMxMDciLz4KPHN0b3Agb2Zmc2V0PSIwLjQ0ODMxNiIgc3RvcC1jb2xvcj0iI0ZEOEYzQyIvPgo8c3RvcCBvZmZzZXQ9IjAuNjI0Mzg3IiBzdG9wLWNvbG9yPSIjRkQ4RjNDIi8+CjxzdG9wIG9mZnNldD0iMC42MzA4MDMiIHN0b3AtY29sb3I9IiNGMjczN0MiLz4KPC9saW5lYXJHcmFkaWVudD4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDFfbGluZWFyXzQxOThfMTEwNDkzIiB4MT0iNDUuMjc5NyIgeTE9Ijc0LjY2MzUiIHgyPSI0NS4yMjYxIiB5Mj0iLTIwLjI0MzEiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agb2Zmc2V0PSIwLjE4MjI1NCIgc3RvcC1jb2xvcj0iIzNGQTcxQSIvPgo8c3RvcCBvZmZzZXQ9IjAuMTkwMTE4IiBzdG9wLWNvbG9yPSIjRkZBNjAwIi8+CjxzdG9wIG9mZnNldD0iMC4zODI3NjEiIHN0b3AtY29sb3I9IiNGRkE2MDAiLz4KPHN0b3Agb2Zmc2V0PSIwLjM4ODI2OCIgc3RvcC1jb2xvcj0iI0Y1NkUwOCIvPgo8c3RvcCBvZmZzZXQ9IjAuNTgxNjk1IiBzdG9wLWNvbG9yPSIjRjU2RTA4Ii8+CjxzdG9wIG9mZnNldD0iMC41ODc5ODUiIHN0b3AtY29sb3I9IiNGRjRENUEiLz4KPC9saW5lYXJHcmFkaWVudD4KPGNsaXBQYXRoIGlkPSJjbGlwMF80MTk4XzExMDQ5MyI+CjxyZWN0IHdpZHRoPSI5NiIgaGVpZ2h0PSI3NiIgZmlsbD0id2hpdGUiLz4KPC9jbGlwUGF0aD4KPGNsaXBQYXRoIGlkPSJjbGlwMV80MTk4XzExMDQ5MyI+CjxyZWN0IHdpZHRoPSI5NiIgaGVpZ2h0PSI3NiIgZmlsbD0id2hpdGUiLz4KPC9jbGlwUGF0aD4KPGNsaXBQYXRoIGlkPSJjbGlwMl80MTk4XzExMDQ5MyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iNzYiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDg0KSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo=",
"description": "Display time series data using customizable line and bar charts. Use various pie charts to display the latest values.",
"order": 1000,
"name": "Charts"
},
"widgetTypeFqns": [
+ "time_series_chart",
+ "line_chart",
+ "bar_chart",
+ "point_chart",
"charts.basic_timeseries",
"charts.state_chart",
"range_chart",
diff --git a/application/src/main/data/json/system/widget_types/bar_chart.json b/application/src/main/data/json/system/widget_types/bar_chart.json
new file mode 100644
index 0000000000..264c314686
--- /dev/null
+++ b/application/src/main/data/json/system/widget_types/bar_chart.json
@@ -0,0 +1,32 @@
+{
+ "fqn": "bar_chart",
+ "name": "Bar chart",
+ "deprecated": false,
+ "image": "tb-image:Y2hhcnRfKDIpLnN2Zw==:IkJhciBjaGFydCIgc3lzdGVtIHdpZGdldCBpbWFnZQ==;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF80MTgzXzkwODc3KSI+CjxwYXRoIGQ9Ik0yLjg0NzY2IDEuMjgxMjVWN0gyLjEyNVYyLjE4MzU5TDAuNjY3OTY5IDIuNzE0ODRWMi4wNjI1TDIuNzM0MzggMS4yODEyNUgyLjg0NzY2Wk04LjY3NTE3IDMuNzAzMTJWNC41NzAzMUM4LjY3NTE3IDUuMDM2NDYgOC42MzM1MSA1LjQyOTY5IDguNTUwMTcgNS43NUM4LjQ2Njg0IDYuMDcwMzEgOC4zNDcwNSA2LjMyODEyIDguMTkwOCA2LjUyMzQ0QzguMDM0NTUgNi43MTg3NSA3Ljg0NTc1IDYuODYwNjggNy42MjQzOSA2Ljk0OTIyQzcuNDA1NjQgNy4wMzUxNiA3LjE1ODI1IDcuMDc4MTIgNi44ODIyIDcuMDc4MTJDNi42NjM0NSA3LjA3ODEyIDYuNDYxNjMgNy4wNTA3OCA2LjI3NjczIDYuOTk2MDlDNi4wOTE4NCA2Ljk0MTQxIDUuOTI1MTcgNi44NTQxNyA1Ljc3NjczIDYuNzM0MzhDNS42MzA5IDYuNjExOTggNS41MDU5IDYuNDUzMTIgNS40MDE3MyA2LjI1NzgxQzUuMjk3NTcgNi4wNjI1IDUuMjE4MTQgNS44MjU1MiA1LjE2MzQ1IDUuNTQ2ODhDNS4xMDg3NyA1LjI2ODIzIDUuMDgxNDIgNC45NDI3MSA1LjA4MTQyIDQuNTcwMzFWMy43MDMxMkM1LjA4MTQyIDMuMjM2OTggNS4xMjMwOSAyLjg0NjM1IDUuMjA2NDIgMi41MzEyNUM1LjI5MjM2IDIuMjE2MTUgNS40MTM0NSAxLjk2MzU0IDUuNTY5NyAxLjc3MzQ0QzUuNzI1OTUgMS41ODA3MyA1LjkxMzQ1IDEuNDQyNzEgNi4xMzIyIDEuMzU5MzhDNi4zNTM1NiAxLjI3NjA0IDYuNjAwOTUgMS4yMzQzOCA2Ljg3NDM5IDEuMjM0MzhDNy4wOTU3NSAxLjIzNDM4IDcuMjk4ODcgMS4yNjE3MiA3LjQ4Mzc3IDEuMzE2NDFDNy42NzEyNyAxLjM2ODQ5IDcuODM3OTMgMS40NTMxMiA3Ljk4Mzc3IDEuNTcwMzFDOC4xMjk2IDEuNjg0OSA4LjI1MzMgMS44Mzg1NCA4LjM1NDg2IDIuMDMxMjVDOC40NTkwMyAyLjIyMTM1IDguNTM4NDUgMi40NTQ0MyA4LjU5MzE0IDIuNzMwNDdDOC42NDc4MyAzLjAwNjUxIDguNjc1MTcgMy4zMzA3MyA4LjY3NTE3IDMuNzAzMTJaTTcuOTQ4NjEgNC42ODc1VjMuNTgyMDNDNy45NDg2MSAzLjMyNjgyIDcuOTMyOTggMy4xMDI4NiA3LjkwMTczIDIuOTEwMTZDNy44NzMwOSAyLjcxNDg0IDcuODMwMTIgMi41NDgxOCA3Ljc3MjgzIDIuNDEwMTZDNy43MTU1NCAyLjI3MjE0IDcuNjQyNjIgMi4xNjAxNiA3LjU1NDA4IDIuMDc0MjJDNy40NjgxNCAxLjk4ODI4IDcuMzY3ODggMS45MjU3OCA3LjI1MzMgMS44ODY3MkM3LjE0MTMyIDEuODQ1MDUgNy4wMTUwMiAxLjgyNDIyIDYuODc0MzkgMS44MjQyMkM2LjcwMjUyIDEuODI0MjIgNi41NTAxNyAxLjg1Njc3IDYuNDE3MzYgMS45MjE4OEM2LjI4NDU1IDEuOTg0MzggNi4xNzI1NyAyLjA4NDY0IDYuMDgxNDIgMi4yMjI2NkM1Ljk5Mjg4IDIuMzYwNjggNS45MjUxNyAyLjU0MTY3IDUuODc4MyAyLjc2NTYyQzUuODMxNDIgMi45ODk1OCA1LjgwNzk4IDMuMjYxNzIgNS44MDc5OCAzLjU4MjAzVjQuNjg3NUM1LjgwNzk4IDQuOTQyNzEgNS44MjIzMSA1LjE2Nzk3IDUuODUwOTUgNS4zNjMyOEM1Ljg4MjIgNS41NTg1OSA1LjkyNzc4IDUuNzI3ODYgNS45ODc2NyA1Ljg3MTA5QzYuMDQ3NTcgNi4wMTE3MiA2LjEyMDQ4IDYuMTI3NiA2LjIwNjQyIDYuMjE4NzVDNi4yOTIzNiA2LjMwOTkgNi4zOTEzMiA2LjM3NzYgNi41MDMzIDYuNDIxODhDNi42MTc4OCA2LjQ2MzU0IDYuNzQ0MTggNi40ODQzOCA2Ljg4MjIgNi40ODQzOEM3LjA1OTI5IDYuNDg0MzggNy4yMTQyMyA2LjQ1MDUyIDcuMzQ3MDUgNi4zODI4MUM3LjQ3OTg2IDYuMzE1MSA3LjU5MDU0IDYuMjA5NjQgNy42NzkwOCA2LjA2NjQxQzcuNzcwMjIgNS45MjA1NyA3LjgzNzkzIDUuNzM0MzggNy44ODIyIDUuNTA3ODFDNy45MjY0NyA1LjI3ODY1IDcuOTQ4NjEgNS4wMDUyMSA3Ljk0ODYxIDQuNjg3NVpNMTMuMzA3NCAzLjcwMzEyVjQuNTcwMzFDMTMuMzA3NCA1LjAzNjQ2IDEzLjI2NTcgNS40Mjk2OSAxMy4xODI0IDUuNzVDMTMuMDk5IDYuMDcwMzEgMTIuOTc5MyA2LjMyODEyIDEyLjgyMyA2LjUyMzQ0QzEyLjY2NjggNi43MTg3NSAxMi40Nzc5IDYuODYwNjggMTIuMjU2NiA2Ljk0OTIyQzEyLjAzNzggNy4wMzUxNiAxMS43OTA0IDcuMDc4MTIgMTEuNTE0NCA3LjA3ODEyQzExLjI5NTcgNy4wNzgxMiAxMS4wOTM4IDcuMDUwNzggMTAuOTA4OSA2Ljk5NjA5QzEwLjcyNCA2Ljk0MTQxIDEwLjU1NzQgNi44NTQxNyAxMC40MDg5IDYuNzM0MzhDMTAuMjYzMSA2LjYxMTk4IDEwLjEzODEgNi40NTMxMiAxMC4wMzM5IDYuMjU3ODFDOS45Mjk3NyA2LjA2MjUgOS44NTAzNCA1LjgyNTUyIDkuNzk1NjYgNS41NDY4OEM5Ljc0MDk3IDUuMjY4MjMgOS43MTM2MyA0Ljk0MjcxIDkuNzEzNjMgNC41NzAzMVYzLjcwMzEyQzkuNzEzNjMgMy4yMzY5OCA5Ljc1NTI5IDIuODQ2MzUgOS44Mzg2MyAyLjUzMTI1QzkuOTI0NTYgMi4yMTYxNSAxMC4wNDU3IDEuOTYzNTQgMTAuMjAxOSAxLjc3MzQ0QzEwLjM1ODIgMS41ODA3MyAxMC41NDU3IDEuNDQyNzEgMTAuNzY0NCAxLjM1OTM4QzEwLjk4NTggMS4yNzYwNCAxMS4yMzMyIDEuMjM0MzggMTEuNTA2NiAxLjIzNDM4QzExLjcyNzkgMS4yMzQzOCAxMS45MzExIDEuMjYxNzIgMTIuMTE2IDEuMzE2NDFDMTIuMzAzNSAxLjM2ODQ5IDEyLjQ3MDEgMS40NTMxMiAxMi42MTYgMS41NzAzMUMxMi43NjE4IDEuNjg0OSAxMi44ODU1IDEuODM4NTQgMTIuOTg3MSAyLjAzMTI1QzEzLjA5MTIgMi4yMjEzNSAxMy4xNzA3IDIuNDU0NDMgMTMuMjI1MyAyLjczMDQ3QzEzLjI4IDMuMDA2NTEgMTMuMzA3NCAzLjMzMDczIDEzLjMwNzQgMy43MDMxMlpNMTIuNTgwOCA0LjY4NzVWMy41ODIwM0MxMi41ODA4IDMuMzI2ODIgMTIuNTY1MiAzLjEwMjg2IDEyLjUzMzkgMi45MTAxNkMxMi41MDUzIDIuNzE0ODQgMTIuNDYyMyAyLjU0ODE4IDEyLjQwNSAyLjQxMDE2QzEyLjM0NzcgMi4yNzIxNCAxMi4yNzQ4IDIuMTYwMTYgMTIuMTg2MyAyLjA3NDIyQzEyLjEwMDMgMS45ODgyOCAxMi4wMDAxIDEuOTI1NzggMTEuODg1NSAxLjg4NjcyQzExLjc3MzUgMS44NDUwNSAxMS42NDcyIDEuODI0MjIgMTEuNTA2NiAxLjgyNDIyQzExLjMzNDcgMS44MjQyMiAxMS4xODI0IDEuODU2NzcgMTEuMDQ5NiAxLjkyMTg4QzEwLjkxNjggMS45ODQzOCAxMC44MDQ4IDIuMDg0NjQgMTAuNzEzNiAyLjIyMjY2QzEwLjYyNTEgMi4zNjA2OCAxMC41NTc0IDIuNTQxNjcgMTAuNTEwNSAyLjc2NTYyQzEwLjQ2MzYgMi45ODk1OCAxMC40NDAyIDMuMjYxNzIgMTAuNDQwMiAzLjU4MjAzVjQuNjg3NUMxMC40NDAyIDQuOTQyNzEgMTAuNDU0NSA1LjE2Nzk3IDEwLjQ4MzIgNS4zNjMyOEMxMC41MTQ0IDUuNTU4NTkgMTAuNTYgNS43Mjc4NiAxMC42MTk5IDUuODcxMDlDMTAuNjc5OCA2LjAxMTcyIDEwLjc1MjcgNi4xMjc2IDEwLjgzODYgNi4yMTg3NUMxMC45MjQ2IDYuMzA5OSAxMS4wMjM1IDYuMzc3NiAxMS4xMzU1IDYuNDIxODhDMTEuMjUwMSA2LjQ2MzU0IDExLjM3NjQgNi40ODQzOCAxMS41MTQ0IDYuNDg0MzhDMTEuNjkxNSA2LjQ4NDM4IDExLjg0NjQgNi40NTA1MiAxMS45NzkzIDYuMzgyODFDMTIuMTEyMSA2LjMxNTEgMTIuMjIyNyA2LjIwOTY0IDEyLjMxMTMgNi4wNjY0MUMxMi40MDI0IDUuOTIwNTcgMTIuNDcwMSA1LjczNDM4IDEyLjUxNDQgNS41MDc4MUMxMi41NTg3IDUuMjc4NjUgMTIuNTgwOCA1LjAwNTIxIDEyLjU4MDggNC42ODc1Wk0xNC4zMDY4IDIuNzA3MDNWMi40MDYyNUMxNC4zMDY4IDIuMTkwMSAxNC4zNTM2IDEuOTkzNDkgMTQuNDQ3NCAxLjgxNjQxQzE0LjU0MTEgMS42MzkzMiAxNC42NzUzIDEuNDk3NCAxNC44NDk3IDEuMzkwNjJDMTUuMDI0MiAxLjI4Mzg1IDE1LjIzMTIgMS4yMzA0NyAxNS40NzA4IDEuMjMwNDdDMTUuNzE1NiAxLjIzMDQ3IDE1LjkyNCAxLjI4Mzg1IDE2LjA5NTggMS4zOTA2MkMxNi4yNzAzIDEuNDk3NCAxNi40MDQ0IDEuNjM5MzIgMTYuNDk4MiAxLjgxNjQxQzE2LjU5MTkgMS45OTM0OSAxNi42Mzg4IDIuMTkwMSAxNi42Mzg4IDIuNDA2MjVWMi43MDcwM0MxNi42Mzg4IDIuOTE3OTcgMTYuNTkxOSAzLjExMTk4IDE2LjQ5ODIgMy4yODkwNkMxNi40MDcgMy40NjYxNSAxNi4yNzQyIDMuNjA4MDcgMTYuMDk5NyAzLjcxNDg0QzE1LjkyNzkgMy44MjE2MSAxNS43MjA4IDMuODc1IDE1LjQ3ODYgMy44NzVDMTUuMjM2NSAzLjg3NSAxNS4wMjY4IDMuODIxNjEgMTQuODQ5NyAzLjcxNDg0QzE0LjY3NTMgMy42MDgwNyAxNC41NDExIDMuNDY2MTUgMTQuNDQ3NCAzLjI4OTA2QzE0LjM1MzYgMy4xMTE5OCAxNC4zMDY4IDIuOTE3OTcgMTQuMzA2OCAyLjcwNzAzWk0xNC44NDk3IDIuNDA2MjVWMi43MDcwM0MxNC44NDk3IDIuODI2ODIgMTQuODcxOSAyLjk0MDEgMTQuOTE2MSAzLjA0Njg4QzE0Ljk2MyAzLjE1MzY1IDE1LjAzMzMgMy4yNDA4OSAxNS4xMjcxIDMuMzA4NTlDMTUuMjIwOCAzLjM3MzcgMTUuMzM4IDMuNDA2MjUgMTUuNDc4NiAzLjQwNjI1QzE1LjYxOTMgMy40MDYyNSAxNS43MzUyIDMuMzczNyAxNS44MjYzIDMuMzA4NTlDMTUuOTE3NCAzLjI0MDg5IDE1Ljk4NTIgMy4xNTM2NSAxNi4wMjk0IDMuMDQ2ODhDMTYuMDczNyAyLjk0MDEgMTYuMDk1OCAyLjgyNjgyIDE2LjA5NTggMi43MDcwM1YyLjQwNjI1QzE2LjA5NTggMi4yODM4NSAxNi4wNzI0IDIuMTY5MjcgMTYuMDI1NSAyLjA2MjVDMTUuOTgxMiAxLjk1MzEyIDE1LjkxMjIgMS44NjU4OSAxNS44MTg1IDEuODAwNzhDMTUuNzI3MyAxLjczMzA3IDE1LjYxMTUgMS42OTkyMiAxNS40NzA4IDEuNjk5MjJDMTUuMzMyOCAxLjY5OTIyIDE1LjIxNjkgMS43MzMwNyAxNS4xMjMyIDEuODAwNzhDMTUuMDMyIDEuODY1ODkgMTQuOTYzIDEuOTUzMTIgMTQuOTE2MSAyLjA2MjVDMTQuODcxOSAyLjE2OTI3IDE0Ljg0OTcgMi4yODM4NSAxNC44NDk3IDIuNDA2MjVaTTE3LjA3NjMgNS45MTAxNlY1LjYwNTQ3QzE3LjA3NjMgNS4zOTE5MyAxNy4xMjMyIDUuMTk2NjEgMTcuMjE2OSA1LjAxOTUzQzE3LjMxMDcgNC44NDI0NSAxNy40NDQ4IDQuNzAwNTIgMTcuNjE5MyA0LjU5Mzc1QzE3Ljc5MzcgNC40ODY5OCAxOC4wMDA4IDQuNDMzNTkgMTguMjQwNCA0LjQzMzU5QzE4LjQ4NTIgNC40MzM1OSAxOC42OTM1IDQuNDg2OTggMTguODY1NCA0LjU5Mzc1QzE5LjAzOTggNC43MDA1MiAxOS4xNzQgNC44NDI0NSAxOS4yNjc3IDUuMDE5NTNDMTkuMzYxNSA1LjE5NjYxIDE5LjQwODMgNS4zOTE5MyAxOS40MDgzIDUuNjA1NDdWNS45MTAxNkMxOS40MDgzIDYuMTIzNyAxOS4zNjE1IDYuMzE5MDEgMTkuMjY3NyA2LjQ5NjA5QzE5LjE3NjYgNi42NzMxOCAxOS4wNDM3IDYuODE1MSAxOC44NjkzIDYuOTIxODhDMTguNjk3NCA3LjAyODY1IDE4LjQ5MDQgNy4wODIwMyAxOC4yNDgyIDcuMDgyMDNDMTguMDA2IDcuMDgyMDMgMTcuNzk3NyA3LjAyODY1IDE3LjYyMzIgNi45MjE4OEMxNy40NDg3IDYuODE1MSAxNy4zMTMzIDYuNjczMTggMTcuMjE2OSA2LjQ5NjA5QzE3LjEyMzIgNi4zMTkwMSAxNy4wNzYzIDYuMTIzNyAxNy4wNzYzIDUuOTEwMTZaTTE3LjYxOTMgNS42MDU0N1Y1LjkxMDE2QzE3LjYxOTMgNi4wMjk5NSAxNy42NDE0IDYuMTQ0NTMgMTcuNjg1NyA2LjI1MzkxQzE3LjczMjUgNi4zNjA2OCAxNy44MDI5IDYuNDQ3OTIgMTcuODk2NiA2LjUxNTYyQzE3Ljk5MDQgNi41ODA3MyAxOC4xMDc1IDYuNjEzMjggMTguMjQ4MiA2LjYxMzI4QzE4LjM4ODggNi42MTMyOCAxOC41MDQ3IDYuNTgwNzMgMTguNTk1OCA2LjUxNTYyQzE4LjY4OTYgNi40NDc5MiAxOC43NTg2IDYuMzYwNjggMTguODAyOSA2LjI1MzkxQzE4Ljg0NzEgNi4xNDcxNCAxOC44NjkzIDYuMDMyNTUgMTguODY5MyA1LjkxMDE2VjUuNjA1NDdDMTguODY5MyA1LjQ4MzA3IDE4Ljg0NTggNS4zNjg0OSAxOC43OTkgNS4yNjE3MkMxOC43NTQ3IDUuMTU0OTUgMTguNjg1NyA1LjA2OTAxIDE4LjU5MTkgNS4wMDM5MUMxOC41MDA4IDQuOTM2MiAxOC4zODM2IDQuOTAyMzQgMTguMjQwNCA0LjkwMjM0QzE4LjEwMjMgNC45MDIzNCAxNy45ODY1IDQuOTM2MiAxNy44OTI3IDUuMDAzOTFDMTcuODAxNiA1LjA2OTAxIDE3LjczMjUgNS4xNTQ5NSAxNy42ODU3IDUuMjYxNzJDMTcuNjQxNCA1LjM2ODQ5IDE3LjYxOTMgNS40ODMwNyAxNy42MTkzIDUuNjA1NDdaTTE4LjQyIDIuMTIxMDlMMTUuNjQyNyA2LjU2NjQxTDE1LjIzNjUgNi4zMDg1OUwxOC4wMTM4IDEuODYzMjhMMTguNDIgMi4xMjEwOVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTguMDU4NTkgMzMuNjY3N0M4LjA1ODU5IDM0LjAxNDEgNy45Nzc4NiAzNC4zMDgzIDcuODE2NDEgMzQuNTUwNUM3LjY1NzU1IDM0Ljc5MDEgNy40NDE0MSAzNC45NzI0IDcuMTY3OTcgMzUuMDk3NEM2Ljg5NzE0IDM1LjIyMjQgNi41OTExNSAzNS4yODQ5IDYuMjUgMzUuMjg0OUM1LjkwODg1IDM1LjI4NDkgNS42MDE1NiAzNS4yMjI0IDUuMzI4MTIgMzUuMDk3NEM1LjA1NDY5IDM0Ljk3MjQgNC44Mzg1NCAzNC43OTAxIDQuNjc5NjkgMzQuNTUwNUM0LjUyMDgzIDM0LjMwODMgNC40NDE0MSAzNC4wMTQxIDQuNDQxNDEgMzMuNjY3N0M0LjQ0MTQxIDMzLjQ0MTIgNC40ODQzOCAzMy4yMzQxIDQuNTcwMzEgMzMuMDQ2NkM0LjY1ODg1IDMyLjg1NjUgNC43ODI1NSAzMi42OTEyIDQuOTQxNDEgMzIuNTUwNUM1LjEwMjg2IDMyLjQwOTkgNS4yOTI5NyAzMi4zMDE4IDUuNTExNzIgMzIuMjI2M0M1LjczMzA3IDMyLjE0ODIgNS45NzY1NiAzMi4xMDkxIDYuMjQyMTkgMzIuMTA5MUM2LjU5MTE1IDMyLjEwOTEgNi45MDIzNCAzMi4xNzY4IDcuMTc1NzggMzIuMzEyM0M3LjQ0OTIyIDMyLjQ0NTEgNy42NjQwNiAzMi42Mjg3IDcuODIwMzEgMzIuODYzQzcuOTc5MTcgMzMuMDk3NCA4LjA1ODU5IDMzLjM2NTYgOC4wNTg1OSAzMy42Njc3Wk03LjMzMjAzIDMzLjY1MjFDNy4zMzIwMyAzMy40NDEyIDcuMjg2NDYgMzMuMjU1IDcuMTk1MzEgMzMuMDkzNUM3LjEwNDE3IDMyLjkyOTQgNi45NzY1NiAzMi44MDE4IDYuODEyNSAzMi43MTA3QzYuNjQ4NDQgMzIuNjE5NSA2LjQ1ODMzIDMyLjU3NCA2LjI0MjE5IDMyLjU3NEM2LjAyMDgzIDMyLjU3NCA1LjgyOTQzIDMyLjYxOTUgNS42Njc5NyAzMi43MTA3QzUuNTA5MTEgMzIuODAxOCA1LjM4NTQyIDMyLjkyOTQgNS4yOTY4OCAzMy4wOTM1QzUuMjA4MzMgMzMuMjU1IDUuMTY0MDYgMzMuNDQxMiA1LjE2NDA2IDMzLjY1MjFDNS4xNjQwNiAzMy44NzA4IDUuMjA3MDMgMzQuMDU4MyA1LjI5Mjk3IDM0LjIxNDZDNS4zODE1MSAzNC4zNjgyIDUuNTA2NTEgMzQuNDg2NyA1LjY2Nzk3IDM0LjU3MDFDNS44MzIwMyAzNC42NTA4IDYuMDI2MDQgMzQuNjkxMiA2LjI1IDM0LjY5MTJDNi40NzM5NiAzNC42OTEyIDYuNjY2NjcgMzQuNjUwOCA2LjgyODEyIDM0LjU3MDFDNi45ODk1OCAzNC40ODY3IDcuMTEzMjggMzQuMzY4MiA3LjE5OTIyIDM0LjIxNDZDNy4yODc3NiAzNC4wNTgzIDcuMzMyMDMgMzMuODcwOCA3LjMzMjAzIDMzLjY1MjFaTTcuOTI1NzggMzAuOTk5OEM3LjkyNTc4IDMxLjI3NTggNy44NTI4NiAzMS41MjQ1IDcuNzA3MDMgMzEuNzQ1OEM3LjU2MTIgMzEuOTY3MiA3LjM2MTk4IDMyLjE0MTcgNy4xMDkzOCAzMi4yNjkzQzYuODU2NzcgMzIuMzk2OSA2LjU3MDMxIDMyLjQ2MDcgNi4yNSAzMi40NjA3QzUuOTI0NDggMzIuNDYwNyA1LjYzNDExIDMyLjM5NjkgNS4zNzg5MSAzMi4yNjkzQzUuMTI2MyAzMi4xNDE3IDQuOTI4MzkgMzEuOTY3MiA0Ljc4NTE2IDMxLjc0NThDNC42NDE5MyAzMS41MjQ1IDQuNTcwMzEgMzEuMjc1OCA0LjU3MDMxIDMwLjk5OThDNC41NzAzMSAzMC42NjkgNC42NDE5MyAzMC4zODc4IDQuNzg1MTYgMzAuMTU2QzQuOTMwOTkgMjkuOTI0MiA1LjEzMDIxIDI5Ljc0NzIgNS4zODI4MSAyOS42MjQ4QzUuNjM1NDIgMjkuNTAyNCA1LjkyMzE4IDI5LjQ0MTIgNi4yNDYwOSAyOS40NDEyQzYuNTcxNjEgMjkuNDQxMiA2Ljg2MDY4IDI5LjUwMjQgNy4xMTMyOCAyOS42MjQ4QzcuMzY1ODkgMjkuNzQ3MiA3LjU2MzggMjkuOTI0MiA3LjcwNzAzIDMwLjE1NkM3Ljg1Mjg2IDMwLjM4NzggNy45MjU3OCAzMC42NjkgNy45MjU3OCAzMC45OTk4Wk03LjIwMzEyIDMxLjAxMTVDNy4yMDMxMiAzMC44MjE0IDcuMTYyNzYgMzAuNjUzNCA3LjA4MjAzIDMwLjUwNzZDNy4wMDEzIDMwLjM2MTcgNi44ODkzMiAzMC4yNDcyIDYuNzQ2MDkgMzAuMTYzOEM2LjYwMjg2IDMwLjA3NzkgNi40MzYyIDMwLjAzNDkgNi4yNDYwOSAzMC4wMzQ5QzYuMDU1OTkgMzAuMDM0OSA1Ljg4OTMyIDMwLjA3NTMgNS43NDYwOSAzMC4xNTZDNS42MDU0NyAzMC4yMzQxIDUuNDk0NzkgMzAuMzQ2MSA1LjQxNDA2IDMwLjQ5MTlDNS4zMzU5NCAzMC42Mzc4IDUuMjk2ODggMzAuODExIDUuMjk2ODggMzEuMDExNUM1LjI5Njg4IDMxLjIwNjggNS4zMzU5NCAzMS4zNzc0IDUuNDE0MDYgMzEuNTIzMkM1LjQ5NDc5IDMxLjY2OSA1LjYwNjc3IDMxLjc4MjMgNS43NSAzMS44NjNDNS44OTMyMyAzMS45NDM4IDYuMDU5OSAzMS45ODQxIDYuMjUgMzEuOTg0MUM2LjQ0MDEgMzEuOTg0MSA2LjYwNTQ3IDMxLjk0MzggNi43NDYwOSAzMS44NjNDNi44ODkzMiAzMS43ODIzIDcuMDAxMyAzMS42NjkgNy4wODIwMyAzMS41MjMyQzcuMTYyNzYgMzEuMzc3NCA3LjIwMzEyIDMxLjIwNjggNy4yMDMxMiAzMS4wMTE1Wk0xMi42NzUyIDMxLjkwOTlWMzIuNzc3MUMxMi42NzUyIDMzLjI0MzIgMTIuNjMzNSAzMy42MzY1IDEyLjU1MDIgMzMuOTU2OEMxMi40NjY4IDM0LjI3NzEgMTIuMzQ3IDM0LjUzNDkgMTIuMTkwOCAzNC43MzAyQzEyLjAzNDUgMzQuOTI1NSAxMS44NDU3IDM1LjA2NzUgMTEuNjI0NCAzNS4xNTZDMTEuNDA1NiAzNS4yNDE5IDExLjE1ODIgMzUuMjg0OSAxMC44ODIyIDM1LjI4NDlDMTAuNjYzNSAzNS4yODQ5IDEwLjQ2MTYgMzUuMjU3NiAxMC4yNzY3IDM1LjIwMjlDMTAuMDkxOCAzNS4xNDgyIDkuOTI1MTcgMzUuMDYxIDkuNzc2NzMgMzQuOTQxMkM5LjYzMDkgMzQuODE4OCA5LjUwNTkgMzQuNjU5OSA5LjQwMTczIDM0LjQ2NDZDOS4yOTc1NyAzNC4yNjkzIDkuMjE4MTQgMzQuMDMyMyA5LjE2MzQ1IDMzLjc1MzdDOS4xMDg3NyAzMy40NzUgOS4wODE0MiAzMy4xNDk1IDkuMDgxNDIgMzIuNzc3MVYzMS45MDk5QzkuMDgxNDIgMzEuNDQzOCA5LjEyMzA5IDMxLjA1MzEgOS4yMDY0MiAzMC43MzhDOS4yOTIzNiAzMC40MjI5IDkuNDEzNDUgMzAuMTcwMyA5LjU2OTcgMjkuOTgwMkM5LjcyNTk1IDI5Ljc4NzUgOS45MTM0NSAyOS42NDk1IDEwLjEzMjIgMjkuNTY2MkMxMC4zNTM2IDI5LjQ4MjggMTAuNjAxIDI5LjQ0MTIgMTAuODc0NCAyOS40NDEyQzExLjA5NTcgMjkuNDQxMiAxMS4yOTg5IDI5LjQ2ODUgMTEuNDgzOCAyOS41MjMyQzExLjY3MTMgMjkuNTc1MyAxMS44Mzc5IDI5LjY1OTkgMTEuOTgzOCAyOS43NzcxQzEyLjEyOTYgMjkuODkxNyAxMi4yNTMzIDMwLjA0NTMgMTIuMzU0OSAzMC4yMzhDMTIuNDU5IDMwLjQyODEgMTIuNTM4NSAzMC42NjEyIDEyLjU5MzEgMzAuOTM3M0MxMi42NDc4IDMxLjIxMzMgMTIuNjc1MiAzMS41Mzc1IDEyLjY3NTIgMzEuOTA5OVpNMTEuOTQ4NiAzMi44OTQzVjMxLjc4ODhDMTEuOTQ4NiAzMS41MzM2IDExLjkzMyAzMS4zMDk3IDExLjkwMTcgMzEuMTE2OUMxMS44NzMxIDMwLjkyMTYgMTEuODMwMSAzMC43NTUgMTEuNzcyOCAzMC42MTY5QzExLjcxNTUgMzAuNDc4OSAxMS42NDI2IDMwLjM2NjkgMTEuNTU0MSAzMC4yODFDMTEuNDY4MSAzMC4xOTUxIDExLjM2NzkgMzAuMTMyNiAxMS4yNTMzIDMwLjA5MzVDMTEuMTQxMyAzMC4wNTE4IDExLjAxNSAzMC4wMzEgMTAuODc0NCAzMC4wMzFDMTAuNzAyNSAzMC4wMzEgMTAuNTUwMiAzMC4wNjM2IDEwLjQxNzQgMzAuMTI4N0MxMC4yODQ1IDMwLjE5MTIgMTAuMTcyNiAzMC4yOTE0IDEwLjA4MTQgMzAuNDI5NEM5Ljk5Mjg4IDMwLjU2NzUgOS45MjUxNyAzMC43NDg1IDkuODc4MyAzMC45NzI0QzkuODMxNDIgMzEuMTk2NCA5LjgwNzk4IDMxLjQ2ODUgOS44MDc5OCAzMS43ODg4VjMyLjg5NDNDOS44MDc5OCAzMy4xNDk1IDkuODIyMzEgMzMuMzc0OCA5Ljg1MDk1IDMzLjU3MDFDOS44ODIyIDMzLjc2NTQgOS45Mjc3OCAzMy45MzQ3IDkuOTg3NjcgMzQuMDc3OUMxMC4wNDc2IDM0LjIxODUgMTAuMTIwNSAzNC4zMzQ0IDEwLjIwNjQgMzQuNDI1NUMxMC4yOTI0IDM0LjUxNjcgMTAuMzkxMyAzNC41ODQ0IDEwLjUwMzMgMzQuNjI4N0MxMC42MTc5IDM0LjY3MDMgMTAuNzQ0MiAzNC42OTEyIDEwLjg4MjIgMzQuNjkxMkMxMS4wNTkzIDM0LjY5MTIgMTEuMjE0MiAzNC42NTczIDExLjM0NyAzNC41ODk2QzExLjQ3OTkgMzQuNTIxOSAxMS41OTA1IDM0LjQxNjQgMTEuNjc5MSAzNC4yNzMyQzExLjc3MDIgMzQuMTI3NCAxMS44Mzc5IDMzLjk0MTIgMTEuODgyMiAzMy43MTQ2QzExLjkyNjUgMzMuNDg1NCAxMS45NDg2IDMzLjIxMiAxMS45NDg2IDMyLjg5NDNaTTEzLjY3NDYgMzAuOTEzOFYzMC42MTNDMTMuNjc0NiAzMC4zOTY5IDEzLjcyMTQgMzAuMjAwMyAxMy44MTUyIDMwLjAyMzJDMTMuOTA4OSAyOS44NDYxIDE0LjA0MzEgMjkuNzA0MiAxNC4yMTc1IDI5LjU5NzRDMTQuMzkyIDI5LjQ5MDYgMTQuNTk5IDI5LjQzNzMgMTQuODM4NiAyOS40MzczQzE1LjA4MzQgMjkuNDM3MyAxNS4yOTE4IDI5LjQ5MDYgMTUuNDYzNiAyOS41OTc0QzE1LjYzODEgMjkuNzA0MiAxNS43NzIyIDI5Ljg0NjEgMTUuODY2IDMwLjAyMzJDMTUuOTU5NyAzMC4yMDAzIDE2LjAwNjYgMzAuMzk2OSAxNi4wMDY2IDMwLjYxM1YzMC45MTM4QzE2LjAwNjYgMzEuMTI0OCAxNS45NTk3IDMxLjMxODggMTUuODY2IDMxLjQ5NThDMTUuNzc0OCAzMS42NzI5IDE1LjY0MiAzMS44MTQ5IDE1LjQ2NzUgMzEuOTIxNkMxNS4yOTU3IDMyLjAyODQgMTUuMDg4NiAzMi4wODE4IDE0Ljg0NjQgMzIuMDgxOEMxNC42MDQzIDMyLjA4MTggMTQuMzk0NiAzMi4wMjg0IDE0LjIxNzUgMzEuOTIxNkMxNC4wNDMxIDMxLjgxNDkgMTMuOTA4OSAzMS42NzI5IDEzLjgxNTIgMzEuNDk1OEMxMy43MjE0IDMxLjMxODggMTMuNjc0NiAzMS4xMjQ4IDEzLjY3NDYgMzAuOTEzOFpNMTQuMjE3NSAzMC42MTNWMzAuOTEzOEMxNC4yMTc1IDMxLjAzMzYgMTQuMjM5NyAzMS4xNDY5IDE0LjI4MzkgMzEuMjUzN0MxNC4zMzA4IDMxLjM2MDQgMTQuNDAxMSAzMS40NDc3IDE0LjQ5NDkgMzEuNTE1NEMxNC41ODg2IDMxLjU4MDUgMTQuNzA1OCAzMS42MTMgMTQuODQ2NCAzMS42MTNDMTQuOTg3MSAzMS42MTMgMTUuMTAyOSAzMS41ODA1IDE1LjE5NDEgMzEuNTE1NEMxNS4yODUyIDMxLjQ0NzcgMTUuMzUyOSAzMS4zNjA0IDE1LjM5NzIgMzEuMjUzN0MxNS40NDE1IDMxLjE0NjkgMTUuNDYzNiAzMS4wMzM2IDE1LjQ2MzYgMzAuOTEzOFYzMC42MTNDMTUuNDYzNiAzMC40OTA2IDE1LjQ0MDIgMzAuMzc2MSAxNS4zOTMzIDMwLjI2OTNDMTUuMzQ5IDMwLjE1OTkgMTUuMjggMzAuMDcyNyAxNS4xODYzIDMwLjAwNzZDMTUuMDk1MSAyOS45Mzk5IDE0Ljk3OTMgMjkuOTA2IDE0LjgzODYgMjkuOTA2QzE0LjcwMDYgMjkuOTA2IDE0LjU4NDcgMjkuOTM5OSAxNC40OTEgMzAuMDA3NkMxNC4zOTk4IDMwLjA3MjcgMTQuMzMwOCAzMC4xNTk5IDE0LjI4MzkgMzAuMjY5M0MxNC4yMzk3IDMwLjM3NjEgMTQuMjE3NSAzMC40OTA2IDE0LjIxNzUgMzAuNjEzWk0xNi40NDQxIDM0LjExNjlWMzMuODEyM0MxNi40NDQxIDMzLjU5ODcgMTYuNDkxIDMzLjQwMzQgMTYuNTg0NyAzMy4yMjYzQzE2LjY3ODUgMzMuMDQ5MiAxNi44MTI2IDMyLjkwNzMgMTYuOTg3MSAzMi44MDA1QzE3LjE2MTUgMzIuNjkzOCAxNy4zNjg2IDMyLjY0MDQgMTcuNjA4MiAzMi42NDA0QzE3Ljg1MjkgMzIuNjQwNCAxOC4wNjEzIDMyLjY5MzggMTguMjMzMiAzMi44MDA1QzE4LjQwNzYgMzIuOTA3MyAxOC41NDE4IDMzLjA0OTIgMTguNjM1NSAzMy4yMjYzQzE4LjcyOTMgMzMuNDAzNCAxOC43NzYxIDMzLjU5ODcgMTguNzc2MSAzMy44MTIzVjM0LjExNjlDMTguNzc2MSAzNC4zMzA1IDE4LjcyOTMgMzQuNTI1OCAxOC42MzU1IDM0LjcwMjlDMTguNTQ0NCAzNC44OCAxOC40MTE1IDM1LjAyMTkgMTguMjM3MSAzNS4xMjg3QzE4LjA2NTIgMzUuMjM1NCAxNy44NTgyIDM1LjI4ODggMTcuNjE2IDM1LjI4ODhDMTcuMzczOCAzNS4yODg4IDE3LjE2NTQgMzUuMjM1NCAxNi45OTEgMzUuMTI4N0MxNi44MTY1IDM1LjAyMTkgMTYuNjgxMSAzNC44OCAxNi41ODQ3IDM0LjcwMjlDMTYuNDkxIDM0LjUyNTggMTYuNDQ0MSAzNC4zMzA1IDE2LjQ0NDEgMzQuMTE2OVpNMTYuOTg3MSAzMy44MTIzVjM0LjExNjlDMTYuOTg3MSAzNC4yMzY3IDE3LjAwOTIgMzQuMzUxMyAxNy4wNTM1IDM0LjQ2MDdDMTcuMTAwMyAzNC41Njc1IDE3LjE3MDcgMzQuNjU0NyAxNy4yNjQ0IDM0LjcyMjRDMTcuMzU4MiAzNC43ODc1IDE3LjQ3NTMgMzQuODIwMSAxNy42MTYgMzQuODIwMUMxNy43NTY2IDM0LjgyMDEgMTcuODcyNSAzNC43ODc1IDE3Ljk2MzYgMzQuNzIyNEMxOC4wNTc0IDM0LjY1NDcgMTguMTI2NCAzNC41Njc1IDE4LjE3MDcgMzQuNDYwN0MxOC4yMTQ5IDM0LjM1MzkgMTguMjM3MSAzNC4yMzkzIDE4LjIzNzEgMzQuMTE2OVYzMy44MTIzQzE4LjIzNzEgMzMuNjg5OSAxOC4yMTM2IDMzLjU3NTMgMTguMTY2OCAzMy40Njg1QzE4LjEyMjUgMzMuMzYxNyAxOC4wNTM1IDMzLjI3NTggMTcuOTU5NyAzMy4yMTA3QzE3Ljg2ODYgMzMuMTQzIDE3Ljc1MTQgMzMuMTA5MSAxNy42MDgyIDMzLjEwOTFDMTcuNDcwMSAzMy4xMDkxIDE3LjM1NDMgMzMuMTQzIDE3LjI2MDUgMzMuMjEwN0MxNy4xNjk0IDMzLjI3NTggMTcuMTAwMyAzMy4zNjE3IDE3LjA1MzUgMzMuNDY4NUMxNy4wMDkyIDMzLjU3NTMgMTYuOTg3MSAzMy42ODk5IDE2Ljk4NzEgMzMuODEyM1pNMTcuNzg3OCAzMC4zMjc5TDE1LjAxMDUgMzQuNzczMkwxNC42MDQzIDM0LjUxNTRMMTcuMzgxNiAzMC4wNzAxTDE3Ljc4NzggMzAuMzI3OVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTcuMjQ2MDkgNTcuNzE4M0g3LjMwODU5VjU4LjMzMTVINy4yNDYwOUM2Ljg2MzI4IDU4LjMzMTUgNi41NDI5NyA1OC4zOTQgNi4yODUxNiA1OC41MTlDNi4wMjczNCA1OC42NDE0IDUuODIyOTIgNTguODA2OCA1LjY3MTg4IDU5LjAxNTFDNS41MjA4MyA1OS4yMjA5IDUuNDExNDYgNTkuNDUyNiA1LjM0Mzc1IDU5LjcxMDRDNS4yNzg2NSA1OS45NjgzIDUuMjQ2MDkgNjAuMjMgNS4yNDYwOSA2MC40OTU2VjYxLjMzMTVDNS4yNDYwOSA2MS41ODQxIDUuMjc2MDQgNjEuODA4MSA1LjMzNTk0IDYyLjAwMzRDNS4zOTU4MyA2Mi4xOTYxIDUuNDc3ODYgNjIuMzU4OSA1LjU4MjAzIDYyLjQ5MTdDNS42ODYyIDYyLjYyNDUgNS44MDMzOSA2Mi43MjQ4IDUuOTMzNTkgNjIuNzkyNUM2LjA2NjQxIDYyLjg2MDIgNi4yMDQ0MyA2Mi44OTQgNi4zNDc2NiA2Mi44OTRDNi41MTQzMiA2Mi44OTQgNi42NjI3NiA2Mi44NjI4IDYuNzkyOTcgNjIuODAwM0M2LjkyMzE4IDYyLjczNTIgNy4wMzI1NSA2Mi42NDUzIDcuMTIxMDkgNjIuNTMwOEM3LjIxMjI0IDYyLjQxMzYgNy4yODEyNSA2Mi4yNzU2IDcuMzI4MTIgNjIuMTE2N0M3LjM3NSA2MS45NTc4IDcuMzk4NDQgNjEuNzgzNCA3LjM5ODQ0IDYxLjU5MzNDNy4zOTg0NCA2MS40MjQgNy4zNzc2IDYxLjI2MTIgNy4zMzU5NCA2MS4xMDVDNy4yOTQyNyA2MC45NDYxIDcuMjMwNDcgNjAuODA1NSA3LjE0NDUzIDYwLjY4MzFDNy4wNTg1OSA2MC41NTgxIDYuOTUwNTIgNjAuNDYwNCA2LjgyMDMxIDYwLjM5MDFDNi42OTI3MSA2MC4zMTcyIDYuNTQwMzYgNjAuMjgwOCA2LjM2MzI4IDYwLjI4MDhDNi4xNjI3NiA2MC4yODA4IDUuOTc1MjYgNjAuMzMwMiA1LjgwMDc4IDYwLjQyOTJDNS42Mjg5MSA2MC41MjU2IDUuNDg2OTggNjAuNjUzMiA1LjM3NSA2MC44MTJDNS4yNjU2MiA2MC45NjgzIDUuMjAzMTIgNjEuMTM4OCA1LjE4NzUgNjEuMzIzN0w0LjgwNDY5IDYxLjMxOThDNC44NDExNSA2MS4wMjgyIDQuOTA4ODUgNjAuNzc5NSA1LjAwNzgxIDYwLjU3MzdDNS4xMDkzOCA2MC4zNjU0IDUuMjM0MzggNjAuMTk2MSA1LjM4MjgxIDYwLjA2NTlDNS41MzM4NSA1OS45MzMxIDUuNzAxODIgNTkuODM2OCA1Ljg4NjcyIDU5Ljc3NjlDNi4wNzQyMiA1OS43MTQ0IDYuMjcyMTQgNTkuNjgzMSA2LjQ4MDQ3IDU5LjY4MzFDNi43NjQzMiA1OS42ODMxIDcuMDA5MTEgNTkuNzM2NSA3LjIxNDg0IDU5Ljg0MzNDNy40MjA1NyA1OS45NSA3LjU4OTg0IDYwLjA5MzMgNy43MjI2NiA2MC4yNzI5QzcuODU1NDcgNjAuNDUgNy45NTMxMiA2MC42NTA2IDguMDE1NjIgNjAuODc0NUM4LjA4MDczIDYxLjA5NTkgOC4xMTMyOCA2MS4zMjM3IDguMTEzMjggNjEuNTU4MUM4LjExMzI4IDYxLjgyNjMgOC4wNzU1MiA2Mi4wNzc2IDggNjIuMzEyQzcuOTI0NDggNjIuNTQ2NCA3LjgxMTIgNjIuNzUyMSA3LjY2MDE2IDYyLjkyOTJDNy41MTE3MiA2My4xMDYzIDcuMzI4MTIgNjMuMjQ0MyA3LjEwOTM4IDYzLjM0MzNDNi44OTA2MiA2My40NDIyIDYuNjM2NzIgNjMuNDkxNyA2LjM0NzY2IDYzLjQ5MTdDNi4wNDAzNiA2My40OTE3IDUuNzcyMTQgNjMuNDI5MiA1LjU0Mjk3IDYzLjMwNDJDNS4zMTM4IDYzLjE3NjYgNS4xMjM3IDYzLjAwNzMgNC45NzI2NiA2Mi43OTY0QzQuODIxNjEgNjIuNTg1NCA0LjcwODMzIDYyLjM1MTEgNC42MzI4MSA2Mi4wOTMzQzQuNTU3MjkgNjEuODM1NCA0LjUxOTUzIDYxLjU3MzcgNC41MTk1MyA2MS4zMDgxVjYwLjk2ODNDNC41MTk1MyA2MC41NjcyIDQuNTU5OSA2MC4xNzQgNC42NDA2MiA1OS43ODg2QzQuNzIxMzUgNTkuNDAzMiA0Ljg2MDY4IDU5LjA1NDIgNS4wNTg1OSA1OC43NDE3QzUuMjU5MTEgNTguNDI5MiA1LjUzNjQ2IDU4LjE4MDUgNS44OTA2MiA1Ny45OTU2QzYuMjQ0NzkgNTcuODEwNyA2LjY5NjYxIDU3LjcxODMgNy4yNDYwOSA1Ny43MTgzWk0xMi42NzUyIDYwLjExNjdWNjAuOTgzOUMxMi42NzUyIDYxLjQ1IDEyLjYzMzUgNjEuODQzMyAxMi41NTAyIDYyLjE2MzZDMTIuNDY2OCA2Mi40ODM5IDEyLjM0NyA2Mi43NDE3IDEyLjE5MDggNjIuOTM3QzEyLjAzNDUgNjMuMTMyMyAxMS44NDU3IDYzLjI3NDMgMTEuNjI0NCA2My4zNjI4QzExLjQwNTYgNjMuNDQ4NyAxMS4xNTgyIDYzLjQ5MTcgMTAuODgyMiA2My40OTE3QzEwLjY2MzUgNjMuNDkxNyAxMC40NjE2IDYzLjQ2NDQgMTAuMjc2NyA2My40MDk3QzEwLjA5MTggNjMuMzU1IDkuOTI1MTcgNjMuMjY3NyA5Ljc3NjczIDYzLjE0NzlDOS42MzA5IDYzLjAyNTYgOS41MDU5IDYyLjg2NjcgOS40MDE3MyA2Mi42NzE0QzkuMjk3NTcgNjIuNDc2MSA5LjIxODE0IDYyLjIzOTEgOS4xNjM0NSA2MS45NjA0QzkuMTA4NzcgNjEuNjgxOCA5LjA4MTQyIDYxLjM1NjMgOS4wODE0MiA2MC45ODM5VjYwLjExNjdDOS4wODE0MiA1OS42NTA2IDkuMTIzMDkgNTkuMjU5OSA5LjIwNjQyIDU4Ljk0NDhDOS4yOTIzNiA1OC42Mjk3IDkuNDEzNDUgNTguMzc3MSA5LjU2OTcgNTguMTg3QzkuNzI1OTUgNTcuOTk0MyA5LjkxMzQ1IDU3Ljg1NjMgMTAuMTMyMiA1Ny43NzI5QzEwLjM1MzYgNTcuNjg5NiAxMC42MDEgNTcuNjQ3OSAxMC44NzQ0IDU3LjY0NzlDMTEuMDk1NyA1Ny42NDc5IDExLjI5ODkgNTcuNjc1MyAxMS40ODM4IDU3LjczQzExLjY3MTMgNTcuNzgyMSAxMS44Mzc5IDU3Ljg2NjcgMTEuOTgzOCA1Ny45ODM5QzEyLjEyOTYgNTguMDk4NSAxMi4yNTMzIDU4LjI1MjEgMTIuMzU0OSA1OC40NDQ4QzEyLjQ1OSA1OC42MzQ5IDEyLjUzODUgNTguODY4IDEyLjU5MzEgNTkuMTQ0QzEyLjY0NzggNTkuNDIwMSAxMi42NzUyIDU5Ljc0NDMgMTIuNjc1MiA2MC4xMTY3Wk0xMS45NDg2IDYxLjEwMTFWNTkuOTk1NkMxMS45NDg2IDU5Ljc0MDQgMTEuOTMzIDU5LjUxNjQgMTEuOTAxNyA1OS4zMjM3QzExLjg3MzEgNTkuMTI4NCAxMS44MzAxIDU4Ljk2MTggMTEuNzcyOCA1OC44MjM3QzExLjcxNTUgNTguNjg1NyAxMS42NDI2IDU4LjU3MzcgMTEuNTU0MSA1OC40ODc4QzExLjQ2ODEgNTguNDAxOSAxMS4zNjc5IDU4LjMzOTQgMTEuMjUzMyA1OC4zMDAzQzExLjE0MTMgNTguMjU4NiAxMS4wMTUgNTguMjM3OCAxMC44NzQ0IDU4LjIzNzhDMTAuNzAyNSA1OC4yMzc4IDEwLjU1MDIgNTguMjcwMyAxMC40MTc0IDU4LjMzNTRDMTAuMjg0NSA1OC4zOTc5IDEwLjE3MjYgNTguNDk4MiAxMC4wODE0IDU4LjYzNjJDOS45OTI4OCA1OC43NzQzIDkuOTI1MTcgNTguOTU1MiA5Ljg3ODMgNTkuMTc5MkM5LjgzMTQyIDU5LjQwMzIgOS44MDc5OCA1OS42NzUzIDkuODA3OTggNTkuOTk1NlY2MS4xMDExQzkuODA3OTggNjEuMzU2MyA5LjgyMjMxIDYxLjU4MTUgOS44NTA5NSA2MS43NzY5QzkuODgyMiA2MS45NzIyIDkuOTI3NzggNjIuMTQxNCA5Ljk4NzY3IDYyLjI4NDdDMTAuMDQ3NiA2Mi40MjUzIDEwLjEyMDUgNjIuNTQxMiAxMC4yMDY0IDYyLjYzMjNDMTAuMjkyNCA2Mi43MjM1IDEwLjM5MTMgNjIuNzkxMiAxMC41MDMzIDYyLjgzNTRDMTAuNjE3OSA2Mi44NzcxIDEwLjc0NDIgNjIuODk3OSAxMC44ODIyIDYyLjg5NzlDMTEuMDU5MyA2Mi44OTc5IDExLjIxNDIgNjIuODY0MSAxMS4zNDcgNjIuNzk2NEMxMS40Nzk5IDYyLjcyODcgMTEuNTkwNSA2Mi42MjMyIDExLjY3OTEgNjIuNDhDMTEuNzcwMiA2Mi4zMzQxIDExLjgzNzkgNjIuMTQ3OSAxMS44ODIyIDYxLjkyMTRDMTEuOTI2NSA2MS42OTIyIDExLjk0ODYgNjEuNDE4OCAxMS45NDg2IDYxLjEwMTFaTTEzLjY3NDYgNTkuMTIwNlY1OC44MTk4QzEzLjY3NDYgNTguNjAzNyAxMy43MjE0IDU4LjQwNzEgMTMuODE1MiA1OC4yM0MxMy45MDg5IDU4LjA1MjkgMTQuMDQzMSA1Ny45MTEgMTQuMjE3NSA1Ny44MDQyQzE0LjM5MiA1Ny42OTc0IDE0LjU5OSA1Ny42NDQgMTQuODM4NiA1Ny42NDRDMTUuMDgzNCA1Ny42NDQgMTUuMjkxOCA1Ny42OTc0IDE1LjQ2MzYgNTcuODA0MkMxNS42MzgxIDU3LjkxMSAxNS43NzIyIDU4LjA1MjkgMTUuODY2IDU4LjIzQzE1Ljk1OTcgNTguNDA3MSAxNi4wMDY2IDU4LjYwMzcgMTYuMDA2NiA1OC44MTk4VjU5LjEyMDZDMTYuMDA2NiA1OS4zMzE1IDE1Ljk1OTcgNTkuNTI1NiAxNS44NjYgNTkuNzAyNkMxNS43NzQ4IDU5Ljg3OTcgMTUuNjQyIDYwLjAyMTYgMTUuNDY3NSA2MC4xMjg0QzE1LjI5NTcgNjAuMjM1MiAxNS4wODg2IDYwLjI4ODYgMTQuODQ2NCA2MC4yODg2QzE0LjYwNDMgNjAuMjg4NiAxNC4zOTQ2IDYwLjIzNTIgMTQuMjE3NSA2MC4xMjg0QzE0LjA0MzEgNjAuMDIxNiAxMy45MDg5IDU5Ljg3OTcgMTMuODE1MiA1OS43MDI2QzEzLjcyMTQgNTkuNTI1NiAxMy42NzQ2IDU5LjMzMTUgMTMuNjc0NiA1OS4xMjA2Wk0xNC4yMTc1IDU4LjgxOThWNTkuMTIwNkMxNC4yMTc1IDU5LjI0MDQgMTQuMjM5NyA1OS4zNTM3IDE0LjI4MzkgNTkuNDYwNEMxNC4zMzA4IDU5LjU2NzIgMTQuNDAxMSA1OS42NTQ1IDE0LjQ5NDkgNTkuNzIyMkMxNC41ODg2IDU5Ljc4NzMgMTQuNzA1OCA1OS44MTk4IDE0Ljg0NjQgNTkuODE5OEMxNC45ODcxIDU5LjgxOTggMTUuMTAyOSA1OS43ODczIDE1LjE5NDEgNTkuNzIyMkMxNS4yODUyIDU5LjY1NDUgMTUuMzUyOSA1OS41NjcyIDE1LjM5NzIgNTkuNDYwNEMxNS40NDE1IDU5LjM1MzcgMTUuNDYzNiA1OS4yNDA0IDE1LjQ2MzYgNTkuMTIwNlY1OC44MTk4QzE1LjQ2MzYgNTguNjk3NCAxNS40NDAyIDU4LjU4MjggMTUuMzkzMyA1OC40NzYxQzE1LjM0OSA1OC4zNjY3IDE1LjI4IDU4LjI3OTUgMTUuMTg2MyA1OC4yMTQ0QzE1LjA5NTEgNTguMTQ2NiAxNC45NzkzIDU4LjExMjggMTQuODM4NiA1OC4xMTI4QzE0LjcwMDYgNTguMTEyOCAxNC41ODQ3IDU4LjE0NjYgMTQuNDkxIDU4LjIxNDRDMTQuMzk5OCA1OC4yNzk1IDE0LjMzMDggNTguMzY2NyAxNC4yODM5IDU4LjQ3NjFDMTQuMjM5NyA1OC41ODI4IDE0LjIxNzUgNTguNjk3NCAxNC4yMTc1IDU4LjgxOThaTTE2LjQ0NDEgNjIuMzIzN1Y2Mi4wMTlDMTYuNDQ0MSA2MS44MDU1IDE2LjQ5MSA2MS42MTAyIDE2LjU4NDcgNjEuNDMzMUMxNi42Nzg1IDYxLjI1NiAxNi44MTI2IDYxLjExNDEgMTYuOTg3MSA2MS4wMDczQzE3LjE2MTUgNjAuOTAwNiAxNy4zNjg2IDYwLjg0NzIgMTcuNjA4MiA2MC44NDcyQzE3Ljg1MjkgNjAuODQ3MiAxOC4wNjEzIDYwLjkwMDYgMTguMjMzMiA2MS4wMDczQzE4LjQwNzYgNjEuMTE0MSAxOC41NDE4IDYxLjI1NiAxOC42MzU1IDYxLjQzMzFDMTguNzI5MyA2MS42MTAyIDE4Ljc3NjEgNjEuODA1NSAxOC43NzYxIDYyLjAxOVY2Mi4zMjM3QzE4Ljc3NjEgNjIuNTM3MyAxOC43MjkzIDYyLjczMjYgMTguNjM1NSA2Mi45MDk3QzE4LjU0NDQgNjMuMDg2OCAxOC40MTE1IDYzLjIyODcgMTguMjM3MSA2My4zMzU0QzE4LjA2NTIgNjMuNDQyMiAxNy44NTgyIDYzLjQ5NTYgMTcuNjE2IDYzLjQ5NTZDMTcuMzczOCA2My40OTU2IDE3LjE2NTQgNjMuNDQyMiAxNi45OTEgNjMuMzM1NEMxNi44MTY1IDYzLjIyODcgMTYuNjgxMSA2My4wODY4IDE2LjU4NDcgNjIuOTA5N0MxNi40OTEgNjIuNzMyNiAxNi40NDQxIDYyLjUzNzMgMTYuNDQ0MSA2Mi4zMjM3Wk0xNi45ODcxIDYyLjAxOVY2Mi4zMjM3QzE2Ljk4NzEgNjIuNDQzNSAxNy4wMDkyIDYyLjU1ODEgMTcuMDUzNSA2Mi42Njc1QzE3LjEwMDMgNjIuNzc0MyAxNy4xNzA3IDYyLjg2MTUgMTcuMjY0NCA2Mi45MjkyQzE3LjM1ODIgNjIuOTk0MyAxNy40NzUzIDYzLjAyNjkgMTcuNjE2IDYzLjAyNjlDMTcuNzU2NiA2My4wMjY5IDE3Ljg3MjUgNjIuOTk0MyAxNy45NjM2IDYyLjkyOTJDMTguMDU3NCA2Mi44NjE1IDE4LjEyNjQgNjIuNzc0MyAxOC4xNzA3IDYyLjY2NzVDMTguMjE0OSA2Mi41NjA3IDE4LjIzNzEgNjIuNDQ2MSAxOC4yMzcxIDYyLjMyMzdWNjIuMDE5QzE4LjIzNzEgNjEuODk2NiAxOC4yMTM2IDYxLjc4MjEgMTguMTY2OCA2MS42NzUzQzE4LjEyMjUgNjEuNTY4NSAxOC4wNTM1IDYxLjQ4MjYgMTcuOTU5NyA2MS40MTc1QzE3Ljg2ODYgNjEuMzQ5OCAxNy43NTE0IDYxLjMxNTkgMTcuNjA4MiA2MS4zMTU5QzE3LjQ3MDEgNjEuMzE1OSAxNy4zNTQzIDYxLjM0OTggMTcuMjYwNSA2MS40MTc1QzE3LjE2OTQgNjEuNDgyNiAxNy4xMDAzIDYxLjU2ODUgMTcuMDUzNSA2MS42NzUzQzE3LjAwOTIgNjEuNzgyMSAxNi45ODcxIDYxLjg5NjYgMTYuOTg3MSA2Mi4wMTlaTTE3Ljc4NzggNTguNTM0N0wxNS4wMTA1IDYyLjk4TDE0LjYwNDMgNjIuNzIyMkwxNy4zODE2IDU4LjI3NjlMMTcuNzg3OCA1OC41MzQ3WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNOC4zMTY0MSA4OS43MDYzVjkwLjNINC4yMDcwM1Y4OS44NzQzTDYuNzUzOTEgODUuOTMyOUg3LjM0Mzc1TDYuNzEwOTQgODcuMDczNUw1LjAyNzM0IDg5LjcwNjNIOC4zMTY0MVpNNy41MjM0NCA4NS45MzI5VjkxLjYyMDRINi44MDA3OFY4NS45MzI5SDcuNTIzNDRaTTEyLjY3NTIgODguMzIzNVY4OS4xOTA3QzEyLjY3NTIgODkuNjU2OCAxMi42MzM1IDkwLjA1IDEyLjU1MDIgOTAuMzcwNEMxMi40NjY4IDkwLjY5MDcgMTIuMzQ3IDkwLjk0ODUgMTIuMTkwOCA5MS4xNDM4QzEyLjAzNDUgOTEuMzM5MSAxMS44NDU3IDkxLjQ4MSAxMS42MjQ0IDkxLjU2OTZDMTEuNDA1NiA5MS42NTU1IDExLjE1ODIgOTEuNjk4NSAxMC44ODIyIDkxLjY5ODVDMTAuNjYzNSA5MS42OTg1IDEwLjQ2MTYgOTEuNjcxMSAxMC4yNzY3IDkxLjYxNjVDMTAuMDkxOCA5MS41NjE4IDkuOTI1MTcgOTEuNDc0NSA5Ljc3NjczIDkxLjM1NDdDOS42MzA5IDkxLjIzMjMgOS41MDU5IDkxLjA3MzUgOS40MDE3MyA5MC44NzgyQzkuMjk3NTcgOTAuNjgyOSA5LjIxODE0IDkwLjQ0NTkgOS4xNjM0NSA5MC4xNjcyQzkuMTA4NzcgODkuODg4NiA5LjA4MTQyIDg5LjU2MzEgOS4wODE0MiA4OS4xOTA3Vjg4LjMyMzVDOS4wODE0MiA4Ny44NTczIDkuMTIzMDkgODcuNDY2NyA5LjIwNjQyIDg3LjE1MTZDOS4yOTIzNiA4Ni44MzY1IDkuNDEzNDUgODYuNTgzOSA5LjU2OTcgODYuMzkzOEM5LjcyNTk1IDg2LjIwMTEgOS45MTM0NSA4Ni4wNjMxIDEwLjEzMjIgODUuOTc5N0MxMC4zNTM2IDg1Ljg5NjQgMTAuNjAxIDg1Ljg1NDcgMTAuODc0NCA4NS44NTQ3QzExLjA5NTcgODUuODU0NyAxMS4yOTg5IDg1Ljg4MjEgMTEuNDgzOCA4NS45MzY4QzExLjY3MTMgODUuOTg4OSAxMS44Mzc5IDg2LjA3MzUgMTEuOTgzOCA4Ni4xOTA3QzEyLjEyOTYgODYuMzA1MyAxMi4yNTMzIDg2LjQ1ODkgMTIuMzU0OSA4Ni42NTE2QzEyLjQ1OSA4Ni44NDE3IDEyLjUzODUgODcuMDc0OCAxMi41OTMxIDg3LjM1MDhDMTIuNjQ3OCA4Ny42MjY5IDEyLjY3NTIgODcuOTUxMSAxMi42NzUyIDg4LjMyMzVaTTExLjk0ODYgODkuMzA3OVY4OC4yMDI0QzExLjk0ODYgODcuOTQ3MiAxMS45MzMgODcuNzIzMiAxMS45MDE3IDg3LjUzMDVDMTEuODczMSA4Ny4zMzUyIDExLjgzMDEgODcuMTY4NSAxMS43NzI4IDg3LjAzMDVDMTEuNzE1NSA4Ni44OTI1IDExLjY0MjYgODYuNzgwNSAxMS41NTQxIDg2LjY5NDZDMTEuNDY4MSA4Ni42MDg2IDExLjM2NzkgODYuNTQ2MSAxMS4yNTMzIDg2LjUwNzFDMTEuMTQxMyA4Ni40NjU0IDExLjAxNSA4Ni40NDQ2IDEwLjg3NDQgODYuNDQ0NkMxMC43MDI1IDg2LjQ0NDYgMTAuNTUwMiA4Ni40NzcxIDEwLjQxNzQgODYuNTQyMkMxMC4yODQ1IDg2LjYwNDcgMTAuMTcyNiA4Ni43MDUgMTAuMDgxNCA4Ni44NDNDOS45OTI4OCA4Ni45ODEgOS45MjUxNyA4Ny4xNjIgOS44NzgzIDg3LjM4NkM5LjgzMTQyIDg3LjYwOTkgOS44MDc5OCA4Ny44ODIxIDkuODA3OTggODguMjAyNFY4OS4zMDc5QzkuODA3OTggODkuNTYzMSA5LjgyMjMxIDg5Ljc4ODMgOS44NTA5NSA4OS45ODM2QzkuODgyMiA5MC4xNzkgOS45Mjc3OCA5MC4zNDgyIDkuOTg3NjcgOTAuNDkxNUMxMC4wNDc2IDkwLjYzMjEgMTAuMTIwNSA5MC43NDggMTAuMjA2NCA5MC44MzkxQzEwLjI5MjQgOTAuOTMwMyAxMC4zOTEzIDkwLjk5OCAxMC41MDMzIDkxLjA0MjJDMTAuNjE3OSA5MS4wODM5IDEwLjc0NDIgOTEuMTA0NyAxMC44ODIyIDkxLjEwNDdDMTEuMDU5MyA5MS4xMDQ3IDExLjIxNDIgOTEuMDcwOSAxMS4zNDcgOTEuMDAzMkMxMS40Nzk5IDkwLjkzNTUgMTEuNTkwNSA5MC44MyAxMS42NzkxIDkwLjY4NjhDMTEuNzcwMiA5MC41NDA5IDExLjgzNzkgOTAuMzU0NyAxMS44ODIyIDkwLjEyODJDMTEuOTI2NSA4OS44OTkgMTEuOTQ4NiA4OS42MjU2IDExLjk0ODYgODkuMzA3OVpNMTMuNjc0NiA4Ny4zMjc0Vjg3LjAyNjZDMTMuNjc0NiA4Ni44MTA1IDEzLjcyMTQgODYuNjEzOSAxMy44MTUyIDg2LjQzNjhDMTMuOTA4OSA4Ni4yNTk3IDE0LjA0MzEgODYuMTE3OCAxNC4yMTc1IDg2LjAxMUMxNC4zOTIgODUuOTA0MiAxNC41OTkgODUuODUwOCAxNC44Mzg2IDg1Ljg1MDhDMTUuMDgzNCA4NS44NTA4IDE1LjI5MTggODUuOTA0MiAxNS40NjM2IDg2LjAxMUMxNS42MzgxIDg2LjExNzggMTUuNzcyMiA4Ni4yNTk3IDE1Ljg2NiA4Ni40MzY4QzE1Ljk1OTcgODYuNjEzOSAxNi4wMDY2IDg2LjgxMDUgMTYuMDA2NiA4Ny4wMjY2Vjg3LjMyNzRDMTYuMDA2NiA4Ny41MzgzIDE1Ljk1OTcgODcuNzMyMyAxNS44NjYgODcuOTA5NEMxNS43NzQ4IDg4LjA4NjUgMTUuNjQyIDg4LjIyODQgMTUuNDY3NSA4OC4zMzUyQzE1LjI5NTcgODguNDQyIDE1LjA4ODYgODguNDk1NCAxNC44NDY0IDg4LjQ5NTRDMTQuNjA0MyA4OC40OTU0IDE0LjM5NDYgODguNDQyIDE0LjIxNzUgODguMzM1MkMxNC4wNDMxIDg4LjIyODQgMTMuOTA4OSA4OC4wODY1IDEzLjgxNTIgODcuOTA5NEMxMy43MjE0IDg3LjczMjMgMTMuNjc0NiA4Ny41MzgzIDEzLjY3NDYgODcuMzI3NFpNMTQuMjE3NSA4Ny4wMjY2Vjg3LjMyNzRDMTQuMjE3NSA4Ny40NDcyIDE0LjIzOTcgODcuNTYwNSAxNC4yODM5IDg3LjY2NzJDMTQuMzMwOCA4Ny43NzQgMTQuNDAxMSA4Ny44NjEyIDE0LjQ5NDkgODcuOTI5QzE0LjU4ODYgODcuOTk0MSAxNC43MDU4IDg4LjAyNjYgMTQuODQ2NCA4OC4wMjY2QzE0Ljk4NzEgODguMDI2NiAxNS4xMDI5IDg3Ljk5NDEgMTUuMTk0MSA4Ny45MjlDMTUuMjg1MiA4Ny44NjEyIDE1LjM1MjkgODcuNzc0IDE1LjM5NzIgODcuNjY3MkMxNS40NDE1IDg3LjU2MDUgMTUuNDYzNiA4Ny40NDcyIDE1LjQ2MzYgODcuMzI3NFY4Ny4wMjY2QzE1LjQ2MzYgODYuOTA0MiAxNS40NDAyIDg2Ljc4OTYgMTUuMzkzMyA4Ni42ODI5QzE1LjM0OSA4Ni41NzM1IDE1LjI4IDg2LjQ4NjIgMTUuMTg2MyA4Ni40MjExQzE1LjA5NTEgODYuMzUzNCAxNC45NzkzIDg2LjMxOTYgMTQuODM4NiA4Ni4zMTk2QzE0LjcwMDYgODYuMzE5NiAxNC41ODQ3IDg2LjM1MzQgMTQuNDkxIDg2LjQyMTFDMTQuMzk5OCA4Ni40ODYyIDE0LjMzMDggODYuNTczNSAxNC4yODM5IDg2LjY4MjlDMTQuMjM5NyA4Ni43ODk2IDE0LjIxNzUgODYuOTA0MiAxNC4yMTc1IDg3LjAyNjZaTTE2LjQ0NDEgOTAuNTMwNVY5MC4yMjU4QzE2LjQ0NDEgOTAuMDEyMyAxNi40OTEgODkuODE3IDE2LjU4NDcgODkuNjM5OUMxNi42Nzg1IDg5LjQ2MjggMTYuODEyNiA4OS4zMjA5IDE2Ljk4NzEgODkuMjE0MUMxNy4xNjE1IDg5LjEwNzMgMTcuMzY4NiA4OS4wNTQgMTcuNjA4MiA4OS4wNTRDMTcuODUyOSA4OS4wNTQgMTguMDYxMyA4OS4xMDczIDE4LjIzMzIgODkuMjE0MUMxOC40MDc2IDg5LjMyMDkgMTguNTQxOCA4OS40NjI4IDE4LjYzNTUgODkuNjM5OUMxOC43MjkzIDg5LjgxNyAxOC43NzYxIDkwLjAxMjMgMTguNzc2MSA5MC4yMjU4VjkwLjUzMDVDMTguNzc2MSA5MC43NDQxIDE4LjcyOTMgOTAuOTM5NCAxOC42MzU1IDkxLjExNjVDMTguNTQ0NCA5MS4yOTM1IDE4LjQxMTUgOTEuNDM1NSAxOC4yMzcxIDkxLjU0MjJDMTguMDY1MiA5MS42NDkgMTcuODU4MiA5MS43MDI0IDE3LjYxNiA5MS43MDI0QzE3LjM3MzggOTEuNzAyNCAxNy4xNjU0IDkxLjY0OSAxNi45OTEgOTEuNTQyMkMxNi44MTY1IDkxLjQzNTUgMTYuNjgxMSA5MS4yOTM1IDE2LjU4NDcgOTEuMTE2NUMxNi40OTEgOTAuOTM5NCAxNi40NDQxIDkwLjc0NDEgMTYuNDQ0MSA5MC41MzA1Wk0xNi45ODcxIDkwLjIyNThWOTAuNTMwNUMxNi45ODcxIDkwLjY1MDMgMTcuMDA5MiA5MC43NjQ5IDE3LjA1MzUgOTAuODc0M0MxNy4xMDAzIDkwLjk4MSAxNy4xNzA3IDkxLjA2ODMgMTcuMjY0NCA5MS4xMzZDMTcuMzU4MiA5MS4yMDExIDE3LjQ3NTMgOTEuMjMzNiAxNy42MTYgOTEuMjMzNkMxNy43NTY2IDkxLjIzMzYgMTcuODcyNSA5MS4yMDExIDE3Ljk2MzYgOTEuMTM2QzE4LjA1NzQgOTEuMDY4MyAxOC4xMjY0IDkwLjk4MSAxOC4xNzA3IDkwLjg3NDNDMTguMjE0OSA5MC43Njc1IDE4LjIzNzEgOTAuNjUyOSAxOC4yMzcxIDkwLjUzMDVWOTAuMjI1OEMxOC4yMzcxIDkwLjEwMzQgMTguMjEzNiA4OS45ODg5IDE4LjE2NjggODkuODgyMUMxOC4xMjI1IDg5Ljc3NTMgMTguMDUzNSA4OS42ODk0IDE3Ljk1OTcgODkuNjI0M0MxNy44Njg2IDg5LjU1NjYgMTcuNzUxNCA4OS41MjI3IDE3LjYwODIgODkuNTIyN0MxNy40NzAxIDg5LjUyMjcgMTcuMzU0MyA4OS41NTY2IDE3LjI2MDUgODkuNjI0M0MxNy4xNjk0IDg5LjY4OTQgMTcuMTAwMyA4OS43NzUzIDE3LjA1MzUgODkuODgyMUMxNy4wMDkyIDg5Ljk4ODkgMTYuOTg3MSA5MC4xMDM0IDE2Ljk4NzEgOTAuMjI1OFpNMTcuNzg3OCA4Ni43NDE1TDE1LjAxMDUgOTEuMTg2OEwxNC42MDQzIDkwLjkyOUwxNy4zODE2IDg2LjQ4MzZMMTcuNzg3OCA4Ni43NDE1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNOC4xOTkyMiAxMTkuMjMzVjExOS44MjdINC40NzY1NlYxMTkuMzA4TDYuMzM5ODQgMTE3LjIzM0M2LjU2OTAxIDExNi45NzggNi43NDYwOSAxMTYuNzYyIDYuODcxMDkgMTE2LjU4NUM2Ljk5ODcgMTE2LjQwNSA3LjA4NzI0IDExNi4yNDUgNy4xMzY3MiAxMTYuMTA0QzcuMTg4OCAxMTUuOTYxIDcuMjE0ODQgMTE1LjgxNSA3LjIxNDg0IDExNS42NjdDNy4yMTQ4NCAxMTUuNDc5IDcuMTc1NzggMTE1LjMxIDcuMDk3NjYgMTE1LjE1OUM3LjAyMjE0IDExNS4wMDYgNi45MTAxNiAxMTQuODgzIDYuNzYxNzIgMTE0Ljc5MkM2LjYxMzI4IDExNC43MDEgNi40MzM1OSAxMTQuNjU1IDYuMjIyNjYgMTE0LjY1NUM1Ljk3MDA1IDExNC42NTUgNS43NTkxMSAxMTQuNzA1IDUuNTg5ODQgMTE0LjgwNEM1LjQyMzE4IDExNC45IDUuMjk4MTggMTE1LjAzNSA1LjIxNDg0IDExNS4yMUM1LjEzMTUxIDExNS4zODQgNS4wODk4NCAxMTUuNTg1IDUuMDg5ODQgMTE1LjgxMkg0LjM2NzE5QzQuMzY3MTkgMTE1LjQ5MSA0LjQzNzUgMTE1LjE5OCA0LjU3ODEyIDExNC45MzNDNC43MTg3NSAxMTQuNjY3IDQuOTI3MDggMTE0LjQ1NiA1LjIwMzEyIDExNC4zQzUuNDc5MTcgMTE0LjE0MSA1LjgxOTAxIDExNC4wNjIgNi4yMjI2NiAxMTQuMDYyQzYuNTgyMDMgMTE0LjA2MiA2Ljg4OTMyIDExNC4xMjUgNy4xNDQ1MyAxMTQuMjUzQzcuMzk5NzQgMTE0LjM3OCA3LjU5NTA1IDExNC41NTUgNy43MzA0NyAxMTQuNzg0QzcuODY4NDkgMTE1LjAxMSA3LjkzNzUgMTE1LjI3NiA3LjkzNzUgMTE1LjU4MUM3LjkzNzUgMTE1Ljc0OCA3LjkwODg1IDExNS45MTcgNy44NTE1NiAxMTYuMDg5QzcuNzk2ODggMTE2LjI1OCA3LjcyMDA1IDExNi40MjcgNy42MjEwOSAxMTYuNTk3QzcuNTI0NzQgMTE2Ljc2NiA3LjQxMTQ2IDExNi45MzMgNy4yODEyNSAxMTcuMDk3QzcuMTUzNjUgMTE3LjI2MSA3LjAxNjkzIDExNy40MjIgNi44NzEwOSAxMTcuNTgxTDUuMzQ3NjYgMTE5LjIzM0g4LjE5OTIyWk0xMi42NzUyIDExNi41M1YxMTcuMzk3QzEyLjY3NTIgMTE3Ljg2NCAxMi42MzM1IDExOC4yNTcgMTIuNTUwMiAxMTguNTc3QzEyLjQ2NjggMTE4Ljg5NyAxMi4zNDcgMTE5LjE1NSAxMi4xOTA4IDExOS4zNTFDMTIuMDM0NSAxMTkuNTQ2IDExLjg0NTcgMTE5LjY4OCAxMS42MjQ0IDExOS43NzZDMTEuNDA1NiAxMTkuODYyIDExLjE1ODIgMTE5LjkwNSAxMC44ODIyIDExOS45MDVDMTAuNjYzNSAxMTkuOTA1IDEwLjQ2MTYgMTE5Ljg3OCAxMC4yNzY3IDExOS44MjNDMTAuMDkxOCAxMTkuNzY5IDkuOTI1MTcgMTE5LjY4MSA5Ljc3NjczIDExOS41NjJDOS42MzA5IDExOS40MzkgOS41MDU5IDExOS4yOCA5LjQwMTczIDExOS4wODVDOS4yOTc1NyAxMTguODkgOS4yMTgxNCAxMTguNjUzIDkuMTYzNDUgMTE4LjM3NEM5LjEwODc3IDExOC4wOTUgOS4wODE0MiAxMTcuNzcgOS4wODE0MiAxMTcuMzk3VjExNi41M0M5LjA4MTQyIDExNi4wNjQgOS4xMjMwOSAxMTUuNjc0IDkuMjA2NDIgMTE1LjM1OEM5LjI5MjM2IDExNS4wNDMgOS40MTM0NSAxMTQuNzkxIDkuNTY5NyAxMTQuNjAxQzkuNzI1OTUgMTE0LjQwOCA5LjkxMzQ1IDExNC4yNyAxMC4xMzIyIDExNC4xODdDMTAuMzUzNiAxMTQuMTAzIDEwLjYwMSAxMTQuMDYyIDEwLjg3NDQgMTE0LjA2MkMxMS4wOTU3IDExNC4wNjIgMTEuMjk4OSAxMTQuMDg5IDExLjQ4MzggMTE0LjE0NEMxMS42NzEzIDExNC4xOTYgMTEuODM3OSAxMTQuMjggMTEuOTgzOCAxMTQuMzk3QzEyLjEyOTYgMTE0LjUxMiAxMi4yNTMzIDExNC42NjYgMTIuMzU0OSAxMTQuODU4QzEyLjQ1OSAxMTUuMDQ5IDEyLjUzODUgMTE1LjI4MiAxMi41OTMxIDExNS41NThDMTIuNjQ3OCAxMTUuODM0IDEyLjY3NTIgMTE2LjE1OCAxMi42NzUyIDExNi41M1pNMTEuOTQ4NiAxMTcuNTE1VjExNi40MDlDMTEuOTQ4NiAxMTYuMTU0IDExLjkzMyAxMTUuOTMgMTEuOTAxNyAxMTUuNzM3QzExLjg3MzEgMTE1LjU0MiAxMS44MzAxIDExNS4zNzUgMTEuNzcyOCAxMTUuMjM3QzExLjcxNTUgMTE1LjA5OSAxMS42NDI2IDExNC45ODcgMTEuNTU0MSAxMTQuOTAxQzExLjQ2ODEgMTE0LjgxNSAxMS4zNjc5IDExNC43NTMgMTEuMjUzMyAxMTQuNzE0QzExLjE0MTMgMTE0LjY3MiAxMS4wMTUgMTE0LjY1MSAxMC44NzQ0IDExNC42NTFDMTAuNzAyNSAxMTQuNjUxIDEwLjU1MDIgMTE0LjY4NCAxMC40MTc0IDExNC43NDlDMTAuMjg0NSAxMTQuODEyIDEwLjE3MjYgMTE0LjkxMiAxMC4wODE0IDExNS4wNUM5Ljk5Mjg4IDExNS4xODggOS45MjUxNyAxMTUuMzY5IDkuODc4MyAxMTUuNTkzQzkuODMxNDIgMTE1LjgxNyA5LjgwNzk4IDExNi4wODkgOS44MDc5OCAxMTYuNDA5VjExNy41MTVDOS44MDc5OCAxMTcuNzcgOS44MjIzMSAxMTcuOTk1IDkuODUwOTUgMTE4LjE5QzkuODgyMiAxMTguMzg2IDkuOTI3NzggMTE4LjU1NSA5Ljk4NzY3IDExOC42OThDMTAuMDQ3NiAxMTguODM5IDEwLjEyMDUgMTE4Ljk1NSAxMC4yMDY0IDExOS4wNDZDMTAuMjkyNCAxMTkuMTM3IDEwLjM5MTMgMTE5LjIwNSAxMC41MDMzIDExOS4yNDlDMTAuNjE3OSAxMTkuMjkxIDEwLjc0NDIgMTE5LjMxMiAxMC44ODIyIDExOS4zMTJDMTEuMDU5MyAxMTkuMzEyIDExLjIxNDIgMTE5LjI3OCAxMS4zNDcgMTE5LjIxQzExLjQ3OTkgMTE5LjE0MiAxMS41OTA1IDExOS4wMzcgMTEuNjc5MSAxMTguODk0QzExLjc3MDIgMTE4Ljc0OCAxMS44Mzc5IDExOC41NjIgMTEuODgyMiAxMTguMzM1QzExLjkyNjUgMTE4LjEwNiAxMS45NDg2IDExNy44MzIgMTEuOTQ4NiAxMTcuNTE1Wk0xMy42NzQ2IDExNS41MzRWMTE1LjIzM0MxMy42NzQ2IDExNS4wMTcgMTMuNzIxNCAxMTQuODIxIDEzLjgxNTIgMTE0LjY0NEMxMy45MDg5IDExNC40NjYgMTQuMDQzMSAxMTQuMzI1IDE0LjIxNzUgMTE0LjIxOEMxNC4zOTIgMTE0LjExMSAxNC41OTkgMTE0LjA1OCAxNC44Mzg2IDExNC4wNThDMTUuMDgzNCAxMTQuMDU4IDE1LjI5MTggMTE0LjExMSAxNS40NjM2IDExNC4yMThDMTUuNjM4MSAxMTQuMzI1IDE1Ljc3MjIgMTE0LjQ2NiAxNS44NjYgMTE0LjY0NEMxNS45NTk3IDExNC44MjEgMTYuMDA2NiAxMTUuMDE3IDE2LjAwNjYgMTE1LjIzM1YxMTUuNTM0QzE2LjAwNjYgMTE1Ljc0NSAxNS45NTk3IDExNS45MzkgMTUuODY2IDExNi4xMTZDMTUuNzc0OCAxMTYuMjkzIDE1LjY0MiAxMTYuNDM1IDE1LjQ2NzUgMTE2LjU0MkMxNS4yOTU3IDExNi42NDkgMTUuMDg4NiAxMTYuNzAyIDE0Ljg0NjQgMTE2LjcwMkMxNC42MDQzIDExNi43MDIgMTQuMzk0NiAxMTYuNjQ5IDE0LjIxNzUgMTE2LjU0MkMxNC4wNDMxIDExNi40MzUgMTMuOTA4OSAxMTYuMjkzIDEzLjgxNTIgMTE2LjExNkMxMy43MjE0IDExNS45MzkgMTMuNjc0NiAxMTUuNzQ1IDEzLjY3NDYgMTE1LjUzNFpNMTQuMjE3NSAxMTUuMjMzVjExNS41MzRDMTQuMjE3NSAxMTUuNjU0IDE0LjIzOTcgMTE1Ljc2NyAxNC4yODM5IDExNS44NzRDMTQuMzMwOCAxMTUuOTgxIDE0LjQwMTEgMTE2LjA2OCAxNC40OTQ5IDExNi4xMzZDMTQuNTg4NiAxMTYuMjAxIDE0LjcwNTggMTE2LjIzMyAxNC44NDY0IDExNi4yMzNDMTQuOTg3MSAxMTYuMjMzIDE1LjEwMjkgMTE2LjIwMSAxNS4xOTQxIDExNi4xMzZDMTUuMjg1MiAxMTYuMDY4IDE1LjM1MjkgMTE1Ljk4MSAxNS4zOTcyIDExNS44NzRDMTUuNDQxNSAxMTUuNzY3IDE1LjQ2MzYgMTE1LjY1NCAxNS40NjM2IDExNS41MzRWMTE1LjIzM0MxNS40NjM2IDExNS4xMTEgMTUuNDQwMiAxMTQuOTk2IDE1LjM5MzMgMTE0Ljg5QzE1LjM0OSAxMTQuNzggMTUuMjggMTE0LjY5MyAxNS4xODYzIDExNC42MjhDMTUuMDk1MSAxMTQuNTYgMTQuOTc5MyAxMTQuNTI2IDE0LjgzODYgMTE0LjUyNkMxNC43MDA2IDExNC41MjYgMTQuNTg0NyAxMTQuNTYgMTQuNDkxIDExNC42MjhDMTQuMzk5OCAxMTQuNjkzIDE0LjMzMDggMTE0Ljc4IDE0LjI4MzkgMTE0Ljg5QzE0LjIzOTcgMTE0Ljk5NiAxNC4yMTc1IDExNS4xMTEgMTQuMjE3NSAxMTUuMjMzWk0xNi40NDQxIDExOC43MzdWMTE4LjQzM0MxNi40NDQxIDExOC4yMTkgMTYuNDkxIDExOC4wMjQgMTYuNTg0NyAxMTcuODQ3QzE2LjY3ODUgMTE3LjY3IDE2LjgxMjYgMTE3LjUyOCAxNi45ODcxIDExNy40MjFDMTcuMTYxNSAxMTcuMzE0IDE3LjM2ODYgMTE3LjI2MSAxNy42MDgyIDExNy4yNjFDMTcuODUyOSAxMTcuMjYxIDE4LjA2MTMgMTE3LjMxNCAxOC4yMzMyIDExNy40MjFDMTguNDA3NiAxMTcuNTI4IDE4LjU0MTggMTE3LjY3IDE4LjYzNTUgMTE3Ljg0N0MxOC43MjkzIDExOC4wMjQgMTguNzc2MSAxMTguMjE5IDE4Ljc3NjEgMTE4LjQzM1YxMTguNzM3QzE4Ljc3NjEgMTE4Ljk1MSAxOC43MjkzIDExOS4xNDYgMTguNjM1NSAxMTkuMzIzQzE4LjU0NDQgMTE5LjUgMTguNDExNSAxMTkuNjQyIDE4LjIzNzEgMTE5Ljc0OUMxOC4wNjUyIDExOS44NTYgMTcuODU4MiAxMTkuOTA5IDE3LjYxNiAxMTkuOTA5QzE3LjM3MzggMTE5LjkwOSAxNy4xNjU0IDExOS44NTYgMTYuOTkxIDExOS43NDlDMTYuODE2NSAxMTkuNjQyIDE2LjY4MTEgMTE5LjUgMTYuNTg0NyAxMTkuMzIzQzE2LjQ5MSAxMTkuMTQ2IDE2LjQ0NDEgMTE4Ljk1MSAxNi40NDQxIDExOC43MzdaTTE2Ljk4NzEgMTE4LjQzM1YxMTguNzM3QzE2Ljk4NzEgMTE4Ljg1NyAxNy4wMDkyIDExOC45NzIgMTcuMDUzNSAxMTkuMDgxQzE3LjEwMDMgMTE5LjE4OCAxNy4xNzA3IDExOS4yNzUgMTcuMjY0NCAxMTkuMzQzQzE3LjM1ODIgMTE5LjQwOCAxNy40NzUzIDExOS40NCAxNy42MTYgMTE5LjQ0QzE3Ljc1NjYgMTE5LjQ0IDE3Ljg3MjUgMTE5LjQwOCAxNy45NjM2IDExOS4zNDNDMTguMDU3NCAxMTkuMjc1IDE4LjEyNjQgMTE5LjE4OCAxOC4xNzA3IDExOS4wODFDMTguMjE0OSAxMTguOTc0IDE4LjIzNzEgMTE4Ljg2IDE4LjIzNzEgMTE4LjczN1YxMTguNDMzQzE4LjIzNzEgMTE4LjMxIDE4LjIxMzYgMTE4LjE5NiAxOC4xNjY4IDExOC4wODlDMTguMTIyNSAxMTcuOTgyIDE4LjA1MzUgMTE3Ljg5NiAxNy45NTk3IDExNy44MzFDMTcuODY4NiAxMTcuNzYzIDE3Ljc1MTQgMTE3LjcyOSAxNy42MDgyIDExNy43MjlDMTcuNDcwMSAxMTcuNzI5IDE3LjM1NDMgMTE3Ljc2MyAxNy4yNjA1IDExNy44MzFDMTcuMTY5NCAxMTcuODk2IDE3LjEwMDMgMTE3Ljk4MiAxNy4wNTM1IDExOC4wODlDMTcuMDA5MiAxMTguMTk2IDE2Ljk4NzEgMTE4LjMxIDE2Ljk4NzEgMTE4LjQzM1pNMTcuNzg3OCAxMTQuOTQ4TDE1LjAxMDUgMTE5LjM5NEwxNC42MDQzIDExOS4xMzZMMTcuMzgxNiAxMTQuNjlMMTcuNzg3OCAxMTQuOTQ4WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNMTMuMDQzIDE0NC43MzdWMTQ1LjYwNEMxMy4wNDMgMTQ2LjA3IDEzLjAwMTMgMTQ2LjQ2NCAxMi45MTggMTQ2Ljc4NEMxMi44MzQ2IDE0Ny4xMDQgMTIuNzE0OCAxNDcuMzYyIDEyLjU1ODYgMTQ3LjU1N0MxMi40MDIzIDE0Ny43NTMgMTIuMjEzNSAxNDcuODk1IDExLjk5MjIgMTQ3Ljk4M0MxMS43NzM0IDE0OC4wNjkgMTEuNTI2IDE0OC4xMTIgMTEuMjUgMTQ4LjExMkMxMS4wMzEyIDE0OC4xMTIgMTAuODI5NCAxNDguMDg1IDEwLjY0NDUgMTQ4LjAzQzEwLjQ1OTYgMTQ3Ljk3NSAxMC4yOTMgMTQ3Ljg4OCAxMC4xNDQ1IDE0Ny43NjhDOS45OTg3IDE0Ny42NDYgOS44NzM3IDE0Ny40ODcgOS43Njk1MyAxNDcuMjkyQzkuNjY1MzYgMTQ3LjA5NiA5LjU4NTk0IDE0Ni44NTkgOS41MzEyNSAxNDYuNTgxQzkuNDc2NTYgMTQ2LjMwMiA5LjQ0OTIyIDE0NS45NzcgOS40NDkyMiAxNDUuNjA0VjE0NC43MzdDOS40NDkyMiAxNDQuMjcxIDkuNDkwODkgMTQzLjg4IDkuNTc0MjIgMTQzLjU2NUM5LjY2MDE2IDE0My4yNSA5Ljc4MTI1IDE0Mi45OTcgOS45Mzc1IDE0Mi44MDdDMTAuMDkzOCAxNDIuNjE1IDEwLjI4MTIgMTQyLjQ3NyAxMC41IDE0Mi4zOTNDMTAuNzIxNCAxNDIuMzEgMTAuOTY4OCAxNDIuMjY4IDExLjI0MjIgMTQyLjI2OEMxMS40NjM1IDE0Mi4yNjggMTEuNjY2NyAxNDIuMjk2IDExLjg1MTYgMTQyLjM1QzEyLjAzOTEgMTQyLjQwMiAxMi4yMDU3IDE0Mi40ODcgMTIuMzUxNiAxNDIuNjA0QzEyLjQ5NzQgMTQyLjcxOSAxMi42MjExIDE0Mi44NzIgMTIuNzIyNyAxNDMuMDY1QzEyLjgyNjggMTQzLjI1NSAxMi45MDYyIDE0My40ODggMTIuOTYwOSAxNDMuNzY0QzEzLjAxNTYgMTQ0LjA0IDEzLjA0MyAxNDQuMzY1IDEzLjA0MyAxNDQuNzM3Wk0xMi4zMTY0IDE0NS43MjFWMTQ0LjYxNkMxMi4zMTY0IDE0NC4zNjEgMTIuMzAwOCAxNDQuMTM3IDEyLjI2OTUgMTQzLjk0NEMxMi4yNDA5IDE0My43NDkgMTIuMTk3OSAxNDMuNTgyIDEyLjE0MDYgMTQzLjQ0NEMxMi4wODMzIDE0My4zMDYgMTIuMDEwNCAxNDMuMTk0IDExLjkyMTkgMTQzLjEwOEMxMS44MzU5IDE0My4wMjIgMTEuNzM1NyAxNDIuOTYgMTEuNjIxMSAxNDIuOTIxQzExLjUwOTEgMTQyLjg3OSAxMS4zODI4IDE0Mi44NTggMTEuMjQyMiAxNDIuODU4QzExLjA3MDMgMTQyLjg1OCAxMC45MTggMTQyLjg5MSAxMC43ODUyIDE0Mi45NTZDMTAuNjUyMyAxNDMuMDE4IDEwLjU0MDQgMTQzLjExOSAxMC40NDkyIDE0My4yNTdDMTAuMzYwNyAxNDMuMzk1IDEwLjI5MyAxNDMuNTc2IDEwLjI0NjEgMTQzLjhDMTAuMTk5MiAxNDQuMDI0IDEwLjE3NTggMTQ0LjI5NiAxMC4xNzU4IDE0NC42MTZWMTQ1LjcyMUMxMC4xNzU4IDE0NS45NzcgMTAuMTkwMSAxNDYuMjAyIDEwLjIxODggMTQ2LjM5N0MxMC4yNSAxNDYuNTkzIDEwLjI5NTYgMTQ2Ljc2MiAxMC4zNTU1IDE0Ni45MDVDMTAuNDE1NCAxNDcuMDQ2IDEwLjQ4ODMgMTQ3LjE2MiAxMC41NzQyIDE0Ny4yNTNDMTAuNjYwMiAxNDcuMzQ0IDEwLjc1OTEgMTQ3LjQxMiAxMC44NzExIDE0Ny40NTZDMTAuOTg1NyAxNDcuNDk3IDExLjExMiAxNDcuNTE4IDExLjI1IDE0Ny41MThDMTEuNDI3MSAxNDcuNTE4IDExLjU4MiAxNDcuNDg0IDExLjcxNDggMTQ3LjQxN0MxMS44NDc3IDE0Ny4zNDkgMTEuOTU4MyAxNDcuMjQ0IDEyLjA0NjkgMTQ3LjFDMTIuMTM4IDE0Ni45NTUgMTIuMjA1NyAxNDYuNzY4IDEyLjI1IDE0Ni41NDJDMTIuMjk0MyAxNDYuMzEzIDEyLjMxNjQgMTQ2LjAzOSAxMi4zMTY0IDE0NS43MjFaTTE0LjA0MjQgMTQzLjc0MVYxNDMuNDRDMTQuMDQyNCAxNDMuMjI0IDE0LjA4OTIgMTQzLjAyNyAxNC4xODMgMTQyLjg1QzE0LjI3NjcgMTQyLjY3MyAxNC40MTA4IDE0Mi41MzEgMTQuNTg1MyAxNDIuNDI1QzE0Ljc1OTggMTQyLjMxOCAxNC45NjY4IDE0Mi4yNjQgMTUuMjA2NCAxNDIuMjY0QzE1LjQ1MTIgMTQyLjI2NCAxNS42NTk1IDE0Mi4zMTggMTUuODMxNCAxNDIuNDI1QzE2LjAwNTkgMTQyLjUzMSAxNi4xNCAxNDIuNjczIDE2LjIzMzggMTQyLjg1QzE2LjMyNzUgMTQzLjAyNyAxNi4zNzQ0IDE0My4yMjQgMTYuMzc0NCAxNDMuNDRWMTQzLjc0MUMxNi4zNzQ0IDE0My45NTIgMTYuMzI3NSAxNDQuMTQ2IDE2LjIzMzggMTQ0LjMyM0MxNi4xNDI2IDE0NC41IDE2LjAwOTggMTQ0LjY0MiAxNS44MzUzIDE0NC43NDlDMTUuNjYzNSAxNDQuODU2IDE1LjQ1NjQgMTQ0LjkwOSAxNS4yMTQyIDE0NC45MDlDMTQuOTcyIDE0NC45MDkgMTQuNzYyNCAxNDQuODU2IDE0LjU4NTMgMTQ0Ljc0OUMxNC40MTA4IDE0NC42NDIgMTQuMjc2NyAxNDQuNSAxNC4xODMgMTQ0LjMyM0MxNC4wODkyIDE0NC4xNDYgMTQuMDQyNCAxNDMuOTUyIDE0LjA0MjQgMTQzLjc0MVpNMTQuNTg1MyAxNDMuNDRWMTQzLjc0MUMxNC41ODUzIDE0My44NjEgMTQuNjA3NSAxNDMuOTc0IDE0LjY1MTcgMTQ0LjA4MUMxNC42OTg2IDE0NC4xODggMTQuNzY4OSAxNDQuMjc1IDE0Ljg2MjcgMTQ0LjM0M0MxNC45NTY0IDE0NC40MDggMTUuMDczNiAxNDQuNDQgMTUuMjE0MiAxNDQuNDRDMTUuMzU0OSAxNDQuNDQgMTUuNDcwNyAxNDQuNDA4IDE1LjU2MTkgMTQ0LjM0M0MxNS42NTMgMTQ0LjI3NSAxNS43MjA3IDE0NC4xODggMTUuNzY1IDE0NC4wODFDMTUuODA5MyAxNDMuOTc0IDE1LjgzMTQgMTQzLjg2MSAxNS44MzE0IDE0My43NDFWMTQzLjQ0QzE1LjgzMTQgMTQzLjMxOCAxNS44MDggMTQzLjIwMyAxNS43NjExIDE0My4wOTZDMTUuNzE2OCAxNDIuOTg3IDE1LjY0NzggMTQyLjkgMTUuNTU0MSAxNDIuODM1QzE1LjQ2MjkgMTQyLjc2NyAxNS4zNDcgMTQyLjczMyAxNS4yMDY0IDE0Mi43MzNDMTUuMDY4NCAxNDIuNzMzIDE0Ljk1MjUgMTQyLjc2NyAxNC44NTg4IDE0Mi44MzVDMTQuNzY3NiAxNDIuOSAxNC42OTg2IDE0Mi45ODcgMTQuNjUxNyAxNDMuMDk2QzE0LjYwNzUgMTQzLjIwMyAxNC41ODUzIDE0My4zMTggMTQuNTg1MyAxNDMuNDRaTTE2LjgxMTkgMTQ2Ljk0NFYxNDYuNjM5QzE2LjgxMTkgMTQ2LjQyNiAxNi44NTg4IDE0Ni4yMzEgMTYuOTUyNSAxNDYuMDUzQzE3LjA0NjMgMTQ1Ljg3NiAxNy4xODA0IDE0NS43MzQgMTcuMzU0OSAxNDUuNjI4QzE3LjUyOTMgMTQ1LjUyMSAxNy43MzY0IDE0NS40NjggMTcuOTc2IDE0NS40NjhDMTguMjIwNyAxNDUuNDY4IDE4LjQyOTEgMTQ1LjUyMSAxOC42MDEgMTQ1LjYyOEMxOC43NzU0IDE0NS43MzQgMTguOTA5NSAxNDUuODc2IDE5LjAwMzMgMTQ2LjA1M0MxOS4wOTcgMTQ2LjIzMSAxOS4xNDM5IDE0Ni40MjYgMTkuMTQzOSAxNDYuNjM5VjE0Ni45NDRDMTkuMTQzOSAxNDcuMTU4IDE5LjA5NyAxNDcuMzUzIDE5LjAwMzMgMTQ3LjUzQzE4LjkxMjIgMTQ3LjcwNyAxOC43NzkzIDE0Ny44NDkgMTguNjA0OSAxNDcuOTU2QzE4LjQzMyAxNDguMDYzIDE4LjIyNiAxNDguMTE2IDE3Ljk4MzggMTQ4LjExNkMxNy43NDE2IDE0OC4xMTYgMTcuNTMzMiAxNDguMDYzIDE3LjM1ODggMTQ3Ljk1NkMxNy4xODQzIDE0Ny44NDkgMTcuMDQ4OSAxNDcuNzA3IDE2Ljk1MjUgMTQ3LjUzQzE2Ljg1ODggMTQ3LjM1MyAxNi44MTE5IDE0Ny4xNTggMTYuODExOSAxNDYuOTQ0Wk0xNy4zNTQ5IDE0Ni42MzlWMTQ2Ljk0NEMxNy4zNTQ5IDE0Ny4wNjQgMTcuMzc3IDE0Ny4xNzggMTcuNDIxMyAxNDcuMjg4QzE3LjQ2ODEgMTQ3LjM5NSAxNy41Mzg1IDE0Ny40ODIgMTcuNjMyMiAxNDcuNTVDMTcuNzI2IDE0Ny42MTUgMTcuODQzMSAxNDcuNjQ3IDE3Ljk4MzggMTQ3LjY0N0MxOC4xMjQ0IDE0Ny42NDcgMTguMjQwMyAxNDcuNjE1IDE4LjMzMTQgMTQ3LjU1QzE4LjQyNTIgMTQ3LjQ4MiAxOC40OTQyIDE0Ny4zOTUgMTguNTM4NSAxNDcuMjg4QzE4LjU4MjcgMTQ3LjE4MSAxOC42MDQ5IDE0Ny4wNjYgMTguNjA0OSAxNDYuOTQ0VjE0Ni42MzlDMTguNjA0OSAxNDYuNTE3IDE4LjU4MTQgMTQ2LjQwMiAxOC41MzQ1IDE0Ni4yOTZDMTguNDkwMyAxNDYuMTg5IDE4LjQyMTMgMTQ2LjEwMyAxOC4zMjc1IDE0Ni4wMzhDMTguMjM2NCAxNDUuOTcgMTguMTE5MiAxNDUuOTM2IDE3Ljk3NiAxNDUuOTM2QzE3LjgzNzkgMTQ1LjkzNiAxNy43MjIgMTQ1Ljk3IDE3LjYyODMgMTQ2LjAzOEMxNy41MzcyIDE0Ni4xMDMgMTcuNDY4MSAxNDYuMTg5IDE3LjQyMTMgMTQ2LjI5NkMxNy4zNzcgMTQ2LjQwMiAxNy4zNTQ5IDE0Ni41MTcgMTcuMzU0OSAxNDYuNjM5Wk0xOC4xNTU2IDE0My4xNTVMMTUuMzc4MyAxNDcuNkwxNC45NzIgMTQ3LjM0M0wxNy43NDk0IDE0Mi44OTdMMTguMTU1NiAxNDMuMTU1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNMjUgNC4xNjExM0wyMDAgNC4xNjExNiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTI1IDMzLjE2MTFMMjAwIDMzLjE2MTIiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIi8+CjxwYXRoIGQ9Ik0yNSA2MS4xNjExTDIwMCA2MS4xNjEyIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMjUgODkuMTYxMUwyMDAgODkuMTYxMiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTI1IDExOC4xNjFMMjAwIDExOC4xNjEiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIi8+CjxwYXRoIGQ9Ik0zNiA2MkMzNiA2MC44OTU0IDM2Ljg5NTQgNjAgMzggNjBINTJDNTMuMTA0NiA2MCA1NCA2MC44OTU0IDU0IDYyVjE0NkgzNlY2MloiIGZpbGw9IiNGRkMxMDciLz4KPHBhdGggZD0iTTU4IDkyQzU4IDkwLjg5NTQgNTguODk1NCA5MCA2MCA5MEg3NEM3NS4xMDQ2IDkwIDc2IDkwLjg5NTQgNzYgOTJWMTQ2SDU4VjkyWiIgZmlsbD0iIzRDQUY1MCIvPgo8cGF0aCBkPSJNODAgNzlDODAgNzcuODk1NCA4MC44OTU0IDc3IDgyIDc3SDk2Qzk3LjEwNDYgNzcgOTggNzcuODk1NCA5OCA3OVYxNDZIODBWNzlaIiBmaWxsPSIjMjE5NkYzIi8+CjxwYXRoIGQ9Ik0xMjUgNzhDMTI1IDc2Ljg5NTQgMTI1Ljg5NSA3NiAxMjcgNzZIMTQxQzE0Mi4xMDUgNzYgMTQzIDc2Ljg5NTQgMTQzIDc4VjE0NkgxMjVWNzhaIiBmaWxsPSIjRkZDMTA3Ii8+CjxwYXRoIGQ9Ik0xNDcgNTFDMTQ3IDQ5Ljg5NTQgMTQ3Ljg5NSA0OSAxNDkgNDlIMTYzQzE2NC4xMDUgNDkgMTY1IDQ5Ljg5NTQgMTY1IDUxVjE0NkgxNDdWNTFaIiBmaWxsPSIjNENBRjUwIi8+CjxwYXRoIGQ9Ik0xNjkgMzZDMTY5IDM0Ljg5NTQgMTY5Ljg5NSAzNCAxNzEgMzRIMTg1QzE4Ni4xMDUgMzQgMTg3IDM0Ljg5NTQgMTg3IDM2VjE0NkgxNjlWMzZaIiBmaWxsPSIjMjE5NkYzIi8+CjxsaW5lIHgxPSIyMy4yIiB5MT0iMTQ1Ljk2MSIgeDI9IjIwMi44IiB5Mj0iMTQ1Ljk2MSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNyIgc3Ryb2tlLXdpZHRoPSIwLjQiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPGxpbmUgeDE9IjY3IiB5MT0iMTQ4LjA3MiIgeDI9IjY3IiB5Mj0iMTQ3LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNNTkuMDU5MSAxNTIuMDI1VjE1Mi44OTNDNTkuMDU5MSAxNTMuMzU5IDU5LjAxNzQgMTUzLjc1MiA1OC45MzQxIDE1NC4wNzJDNTguODUwNyAxNTQuMzkzIDU4LjczMDkgMTU0LjY1IDU4LjU3NDcgMTU0Ljg0NkM1OC40MTg0IDE1NS4wNDEgNTguMjI5NiAxNTUuMTgzIDU4LjAwODMgMTU1LjI3MUM1Ny43ODk1IDE1NS4zNTcgNTcuNTQyMSAxNTUuNCA1Ny4yNjYxIDE1NS40QzU3LjA0NzMgMTU1LjQgNTYuODQ1NSAxNTUuMzczIDU2LjY2MDYgMTU1LjMxOEM1Ni40NzU3IDE1NS4yNjQgNTYuMzA5MSAxNTUuMTc2IDU2LjE2MDYgMTU1LjA1N0M1Ni4wMTQ4IDE1NC45MzQgNTUuODg5OCAxNTQuNzc1IDU1Ljc4NTYgMTU0LjU4QzU1LjY4MTUgMTU0LjM4NSA1NS42MDIgMTU0LjE0OCA1NS41NDczIDE1My44NjlDNTUuNDkyNyAxNTMuNTkgNTUuNDY1MyAxNTMuMjY1IDU1LjQ2NTMgMTUyLjg5M1YxNTIuMDI1QzU1LjQ2NTMgMTUxLjU1OSA1NS41MDcgMTUxLjE2OSA1NS41OTAzIDE1MC44NTRDNTUuNjc2MiAxNTAuNTM4IDU1Ljc5NzMgMTUwLjI4NiA1NS45NTM2IDE1MC4wOTZDNTYuMTA5OCAxNDkuOTAzIDU2LjI5NzMgMTQ5Ljc2NSA1Ni41MTYxIDE0OS42ODJDNTYuNzM3NCAxNDkuNTk4IDU2Ljk4NDggMTQ5LjU1NyA1Ny4yNTgzIDE0OS41NTdDNTcuNDc5NiAxNDkuNTU3IDU3LjY4MjggMTQ5LjU4NCA1Ny44Njc3IDE0OS42MzlDNTguMDU1MiAxNDkuNjkxIDU4LjIyMTggMTQ5Ljc3NSA1OC4zNjc3IDE0OS44OTNDNTguNTEzNSAxNTAuMDA3IDU4LjYzNzIgMTUwLjE2MSA1OC43Mzg3IDE1MC4zNTRDNTguODQyOSAxNTAuNTQ0IDU4LjkyMjMgMTUwLjc3NyA1OC45NzcgMTUxLjA1M0M1OS4wMzE3IDE1MS4zMjkgNTkuMDU5MSAxNTEuNjUzIDU5LjA1OTEgMTUyLjAyNVpNNTguMzMyNSAxNTMuMDFWMTUxLjkwNEM1OC4zMzI1IDE1MS42NDkgNTguMzE2OSAxNTEuNDI1IDU4LjI4NTYgMTUxLjIzMkM1OC4yNTcgMTUxLjAzNyA1OC4yMTQgMTUwLjg3IDU4LjE1NjcgMTUwLjczMkM1OC4wOTk0IDE1MC41OTQgNTguMDI2NSAxNTAuNDgyIDU3LjkzOCAxNTAuMzk2QzU3Ljg1MiAxNTAuMzExIDU3Ljc1MTggMTUwLjI0OCA1Ny42MzcyIDE1MC4yMDlDNTcuNTI1MiAxNTAuMTY3IDU3LjM5ODkgMTUwLjE0NiA1Ny4yNTgzIDE1MC4xNDZDNTcuMDg2NCAxNTAuMTQ2IDU2LjkzNDEgMTUwLjE3OSA1Ni44MDEyIDE1MC4yNDRDNTYuNjY4NCAxNTAuMzA3IDU2LjU1NjUgMTUwLjQwNyA1Ni40NjUzIDE1MC41NDVDNTYuMzc2OCAxNTAuNjgzIDU2LjMwOTEgMTUwLjg2NCA1Ni4yNjIyIDE1MS4wODhDNTYuMjE1MyAxNTEuMzEyIDU2LjE5MTkgMTUxLjU4NCA1Ni4xOTE5IDE1MS45MDRWMTUzLjAxQzU2LjE5MTkgMTUzLjI2NSA1Ni4yMDYyIDE1My40OSA1Ni4yMzQ4IDE1My42ODZDNTYuMjY2MSAxNTMuODgxIDU2LjMxMTcgMTU0LjA1IDU2LjM3MTYgMTU0LjE5M0M1Ni40MzE1IDE1NC4zMzQgNTYuNTA0NCAxNTQuNDUgNTYuNTkwMyAxNTQuNTQxQzU2LjY3NjIgMTU0LjYzMiA1Ni43NzUyIDE1NC43IDU2Ljg4NzIgMTU0Ljc0NEM1Ny4wMDE4IDE1NC43ODYgNTcuMTI4MSAxNTQuODA3IDU3LjI2NjEgMTU0LjgwN0M1Ny40NDMyIDE1NC44MDcgNTcuNTk4MSAxNTQuNzczIDU3LjczMDkgMTU0LjcwNUM1Ny44NjM3IDE1NC42MzcgNTcuOTc0NCAxNTQuNTMyIDU4LjA2MyAxNTQuMzg5QzU4LjE1NDEgMTU0LjI0MyA1OC4yMjE4IDE1NC4wNTcgNTguMjY2MSAxNTMuODNDNTguMzEwNCAxNTMuNjAxIDU4LjMzMjUgMTUzLjMyNyA1OC4zMzI1IDE1My4wMVpNNjIuNDQ2NCAxNDkuNjA0VjE1NS4zMjJINjEuNzIzN1YxNTAuNTA2TDYwLjI2NjcgMTUxLjAzN1YxNTAuMzg1TDYyLjMzMzEgMTQ5LjYwNEg2Mi40NDY0Wk02Ny42NjI0IDE0OS42MzVWMTU1LjMyMkg2Ni45MDg1VjE0OS42MzVINjcuNjYyNFpNNzAuMDQ1MiAxNTIuMTkzVjE1Mi44MTFINjcuNDk4M1YxNTIuMTkzSDcwLjA0NTJaTTcwLjQzMTkgMTQ5LjYzNVYxNTAuMjUySDY3LjQ5ODNWMTQ5LjYzNUg3MC40MzE5Wk03Mi45NzE2IDE1NS40QzcyLjY3NzMgMTU1LjQgNzIuNDEwNCAxNTUuMzUxIDcyLjE3MDggMTU1LjI1MkM3MS45MzM4IDE1NS4xNSA3MS43Mjk0IDE1NS4wMDggNzEuNTU3NSAxNTQuODI2QzcxLjM4ODMgMTU0LjY0NCA3MS4yNTgxIDE1NC40MjggNzEuMTY2OSAxNTQuMTc4QzcxLjA3NTggMTUzLjkyOCA3MS4wMzAyIDE1My42NTQgNzEuMDMwMiAxNTMuMzU3VjE1My4xOTNDNzEuMDMwMiAxNTIuODUgNzEuMDgxIDE1Mi41NDQgNzEuMTgyNSAxNTIuMjc1QzcxLjI4NDEgMTUyLjAwNSA3MS40MjIxIDE1MS43NzUgNzEuNTk2NiAxNTEuNTg4QzcxLjc3MTEgMTUxLjQgNzEuOTY5IDE1MS4yNTggNzIuMTkwMyAxNTEuMTYyQzcyLjQxMTcgMTUxLjA2NiA3Mi42NDA5IDE1MS4wMTggNzIuODc3OCAxNTEuMDE4QzczLjE3OTkgMTUxLjAxOCA3My40NDAzIDE1MS4wNyA3My42NTkxIDE1MS4xNzRDNzMuODgwNSAxNTEuMjc4IDc0LjA2MTQgMTUxLjQyNCA3NC4yMDIxIDE1MS42MTFDNzQuMzQyNyAxNTEuNzk2IDc0LjQ0NjkgMTUyLjAxNSA3NC41MTQ2IDE1Mi4yNjhDNzQuNTgyMyAxNTIuNTE4IDc0LjYxNjEgMTUyLjc5MSA3NC42MTYxIDE1My4wODhWMTUzLjQxMkg3MS40NTk5VjE1Mi44MjJINzMuODkzNVYxNTIuNzY4QzczLjg4MzEgMTUyLjU4IDczLjg0NCAxNTIuMzk4IDczLjc3NjMgMTUyLjIyMUM3My43MTEyIDE1Mi4wNDQgNzMuNjA3IDE1MS44OTggNzMuNDYzOCAxNTEuNzgzQzczLjMyMDYgMTUxLjY2OSA3My4xMjUyIDE1MS42MTEgNzIuODc3OCAxNTEuNjExQzcyLjcxMzggMTUxLjYxMSA3Mi41NjI3IDE1MS42NDYgNzIuNDI0NyAxNTEuNzE3QzcyLjI4NjcgMTUxLjc4NSA3Mi4xNjgyIDE1MS44ODYgNzIuMDY5MyAxNTIuMDIxQzcxLjk3MDMgMTUyLjE1NyA3MS44OTM1IDE1Mi4zMjIgNzEuODM4OCAxNTIuNTE4QzcxLjc4NDEgMTUyLjcxMyA3MS43NTY4IDE1Mi45MzggNzEuNzU2OCAxNTMuMTkzVjE1My4zNTdDNzEuNzU2OCAxNTMuNTU4IDcxLjc4NDEgMTUzLjc0NyA3MS44Mzg4IDE1My45MjRDNzEuODk2MSAxNTQuMDk4IDcxLjk3ODEgMTU0LjI1MiA3Mi4wODQ5IDE1NC4zODVDNzIuMTk0MyAxNTQuNTE4IDcyLjMyNTggMTU0LjYyMiA3Mi40Nzk0IDE1NC42OTdDNzIuNjM1NyAxNTQuNzczIDcyLjgxMjcgMTU0LjgxMSA3My4wMTA3IDE1NC44MTFDNzMuMjY1OSAxNTQuODExIDczLjQ4MiAxNTQuNzU4IDczLjY1OTEgMTU0LjY1NEM3My44MzYyIDE1NC41NSA3My45OTExIDE1NC40MTEgNzQuMTIzOSAxNTQuMjM2TDc0LjU2MTQgMTU0LjU4NEM3NC40NzAzIDE1NC43MjIgNzQuMzU0NCAxNTQuODU0IDc0LjIxMzggMTU0Ljk3OUM3NC4wNzMyIDE1NS4xMDQgNzMuOSAxNTUuMjA1IDczLjY5NDMgMTU1LjI4M0M3My40OTExIDE1NS4zNjEgNzMuMjUwMiAxNTUuNCA3Mi45NzE2IDE1NS40Wk03NS41Mzg2IDE0OS4zMjJINzYuMjY1MlYxNTQuNTAyTDc2LjIwMjcgMTU1LjMyMkg3NS41Mzg2VjE0OS4zMjJaTTc5LjEyMDYgMTUzLjE3NFYxNTMuMjU2Qzc5LjEyMDYgMTUzLjU2MyA3OS4wODQyIDE1My44NDggNzkuMDExMyAxNTQuMTExQzc4LjkzODMgMTU0LjM3MiA3OC44MzE2IDE1NC41OTggNzguNjkwOSAxNTQuNzkxQzc4LjU1MDMgMTU0Ljk4NCA3OC4zNzg0IDE1NS4xMzMgNzguMTc1MyAxNTUuMjRDNzcuOTcyMiAxNTUuMzQ3IDc3LjczOTEgMTU1LjQgNzcuNDc2MSAxNTUuNEM3Ny4yMDc5IDE1NS40IDc2Ljk3MjIgMTU1LjM1NSA3Ni43NjkxIDE1NS4yNjRDNzYuNTY4NSAxNTUuMTcgNzYuMzk5MyAxNTUuMDM2IDc2LjI2MTMgMTU0Ljg2MUM3Ni4xMjMyIDE1NC42ODcgNzYuMDEyNiAxNTQuNDc2IDc1LjkyOTIgMTU0LjIyOUM3NS44NDg1IDE1My45ODEgNzUuNzkyNSAxNTMuNzAyIDc1Ljc2MTMgMTUzLjM5M1YxNTMuMDMzQzc1Ljc5MjUgMTUyLjcyMSA3NS44NDg1IDE1Mi40NDEgNzUuOTI5MiAxNTIuMTkzQzc2LjAxMjYgMTUxLjk0NiA3Ni4xMjMyIDE1MS43MzUgNzYuMjYxMyAxNTEuNTYxQzc2LjM5OTMgMTUxLjM4MyA3Ni41Njg1IDE1MS4yNDkgNzYuNzY5MSAxNTEuMTU4Qzc2Ljk2OTYgMTUxLjA2NCA3Ny4yMDI3IDE1MS4wMTggNzcuNDY4MyAxNTEuMDE4Qzc3LjczMzkgMTUxLjAxOCA3Ny45Njk2IDE1MS4wNyA3OC4xNzUzIDE1MS4xNzRDNzguMzgxIDE1MS4yNzUgNzguNTUyOSAxNTEuNDIxIDc4LjY5MDkgMTUxLjYxMUM3OC44MzE2IDE1MS44MDEgNzguOTM4MyAxNTIuMDI5IDc5LjAxMTMgMTUyLjI5NUM3OS4wODQyIDE1Mi41NTggNzkuMTIwNiAxNTIuODUxIDc5LjEyMDYgMTUzLjE3NFpNNzguMzk0MSAxNTMuMjU2VjE1My4xNzRDNzguMzk0MSAxNTIuOTYzIDc4LjM3NDUgMTUyLjc2NSA3OC4zMzU1IDE1Mi41OEM3OC4yOTY0IDE1Mi4zOTMgNzguMjMzOSAxNTIuMjI5IDc4LjE0OCAxNTIuMDg4Qzc4LjA2MiAxNTEuOTQ1IDc3Ljk0ODggMTUxLjgzMyA3Ny44MDgxIDE1MS43NTJDNzcuNjY3NSAxNTEuNjY5IDc3LjQ5NDMgMTUxLjYyNyA3Ny4yODg2IDE1MS42MjdDNzcuMTA2MyAxNTEuNjI3IDc2Ljk0NzUgMTUxLjY1OCA3Ni44MTIgMTUxLjcyMUM3Ni42NzkyIDE1MS43ODMgNzYuNTY1OSAxNTEuODY4IDc2LjQ3MjIgMTUxLjk3NUM3Ni4zNzg0IDE1Mi4wNzkgNzYuMzAxNiAxNTIuMTk5IDc2LjI0MTcgMTUyLjMzNEM3Ni4xODQ0IDE1Mi40NjcgNzYuMTQxNSAxNTIuNjA1IDc2LjExMjggMTUyLjc0OFYxNTMuNjg5Qzc2LjE1NDUgMTUzLjg3MiA3Ni4yMjIyIDE1NC4wNDggNzYuMzE1OSAxNTQuMjE3Qzc2LjQxMjMgMTU0LjM4MyA3Ni41Mzk5IDE1NC41MiA3Ni42OTg4IDE1NC42MjdDNzYuODYwMiAxNTQuNzM0IDc3LjA1OTQgMTU0Ljc4NyA3Ny4yOTY0IDE1NC43ODdDNzcuNDkxNyAxNTQuNzg3IDc3LjY1ODQgMTU0Ljc0OCA3Ny43OTY0IDE1NC42N0M3Ny45MzcgMTU0LjU4OSA3OC4wNTAzIDE1NC40NzkgNzguMTM2MyAxNTQuMzM4Qzc4LjIyNDggMTU0LjE5NyA3OC4yODk5IDE1NC4wMzUgNzguMzMxNiAxNTMuODVDNzguMzczMiAxNTMuNjY1IDc4LjM5NDEgMTUzLjQ2NyA3OC4zOTQxIDE1My4yNTZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMV80MTgzXzkwODc3KSI+CjxsaW5lIHgxPSIxNTUuNSIgeTE9IjE0Ny4wNzIiIHgyPSIxNTUuNSIgeTI9IjE0Ni4yNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNSIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPHBhdGggZD0iTTE0Ny41NTkgMTUyLjAyNVYxNTIuODkyQzE0Ny41NTkgMTUzLjM1OCAxNDcuNTE3IDE1My43NTIgMTQ3LjQzNCAxNTQuMDcyQzE0Ny4zNTEgMTU0LjM5MiAxNDcuMjMxIDE1NC42NSAxNDcuMDc1IDE1NC44NDVDMTQ2LjkxOCAxNTUuMDQxIDE0Ni43MyAxNTUuMTgzIDE0Ni41MDggMTU1LjI3MUMxNDYuMjkgMTU1LjM1NyAxNDYuMDQyIDE1NS40IDE0NS43NjYgMTU1LjRDMTQ1LjU0NyAxNTUuNCAxNDUuMzQ2IDE1NS4zNzMgMTQ1LjE2MSAxNTUuMzE4QzE0NC45NzYgMTU1LjI2MyAxNDQuODA5IDE1NS4xNzYgMTQ0LjY2MSAxNTUuMDU2QzE0NC41MTUgMTU0LjkzNCAxNDQuMzkgMTU0Ljc3NSAxNDQuMjg2IDE1NC41OEMxNDQuMTgxIDE1NC4zODUgMTQ0LjEwMiAxNTQuMTQ4IDE0NC4wNDcgMTUzLjg2OUMxNDMuOTkzIDE1My41OSAxNDMuOTY1IDE1My4yNjUgMTQzLjk2NSAxNTIuODkyVjE1Mi4wMjVDMTQzLjk2NSAxNTEuNTU5IDE0NC4wMDcgMTUxLjE2OCAxNDQuMDkgMTUwLjg1M0MxNDQuMTc2IDE1MC41MzggMTQ0LjI5NyAxNTAuMjg2IDE0NC40NTQgMTUwLjA5NUMxNDQuNjEgMTQ5LjkwMyAxNDQuNzk3IDE0OS43NjUgMTQ1LjAxNiAxNDkuNjgxQzE0NS4yMzcgMTQ5LjU5OCAxNDUuNDg1IDE0OS41NTYgMTQ1Ljc1OCAxNDkuNTU2QzE0NS45OCAxNDkuNTU2IDE0Ni4xODMgMTQ5LjU4NCAxNDYuMzY4IDE0OS42MzhDMTQ2LjU1NSAxNDkuNjkxIDE0Ni43MjIgMTQ5Ljc3NSAxNDYuODY4IDE0OS44OTJDMTQ3LjAxMyAxNTAuMDA3IDE0Ny4xMzcgMTUwLjE2MSAxNDcuMjM5IDE1MC4zNTNDMTQ3LjM0MyAxNTAuNTQzIDE0Ny40MjIgMTUwLjc3NiAxNDcuNDc3IDE1MS4wNTJDMTQ3LjUzMiAxNTEuMzI5IDE0Ny41NTkgMTUxLjY1MyAxNDcuNTU5IDE1Mi4wMjVaTTE0Ni44MzIgMTUzLjAxVjE1MS45MDRDMTQ2LjgzMiAxNTEuNjQ5IDE0Ni44MTcgMTUxLjQyNSAxNDYuNzg2IDE1MS4yMzJDMTQ2Ljc1NyAxNTEuMDM3IDE0Ni43MTQgMTUwLjg3IDE0Ni42NTcgMTUwLjczMkMxNDYuNTk5IDE1MC41OTQgMTQ2LjUyNyAxNTAuNDgyIDE0Ni40MzggMTUwLjM5NkMxNDYuMzUyIDE1MC4zMSAxNDYuMjUyIDE1MC4yNDggMTQ2LjEzNyAxNTAuMjA5QzE0Ni4wMjUgMTUwLjE2NyAxNDUuODk5IDE1MC4xNDYgMTQ1Ljc1OCAxNTAuMTQ2QzE0NS41ODYgMTUwLjE0NiAxNDUuNDM0IDE1MC4xNzkgMTQ1LjMwMSAxNTAuMjQ0QzE0NS4xNjggMTUwLjMwNiAxNDUuMDU2IDE1MC40MDcgMTQ0Ljk2NSAxNTAuNTQ1QzE0NC44NzcgMTUwLjY4MyAxNDQuODA5IDE1MC44NjQgMTQ0Ljc2MiAxNTEuMDg4QzE0NC43MTUgMTUxLjMxMiAxNDQuNjkyIDE1MS41ODQgMTQ0LjY5MiAxNTEuOTA0VjE1My4wMUMxNDQuNjkyIDE1My4yNjUgMTQ0LjcwNiAxNTMuNDkgMTQ0LjczNSAxNTMuNjg1QzE0NC43NjYgMTUzLjg4MSAxNDQuODEyIDE1NC4wNSAxNDQuODcyIDE1NC4xOTNDMTQ0LjkzMSAxNTQuMzM0IDE0NS4wMDQgMTU0LjQ1IDE0NS4wOSAxNTQuNTQxQzE0NS4xNzYgMTU0LjYzMiAxNDUuMjc1IDE1NC43IDE0NS4zODcgMTU0Ljc0NEMxNDUuNTAyIDE1NC43ODYgMTQ1LjYyOCAxNTQuODA2IDE0NS43NjYgMTU0LjgwNkMxNDUuOTQzIDE1NC44MDYgMTQ2LjA5OCAxNTQuNzczIDE0Ni4yMzEgMTU0LjcwNUMxNDYuMzY0IDE1NC42MzcgMTQ2LjQ3NCAxNTQuNTMyIDE0Ni41NjMgMTU0LjM4OEMxNDYuNjU0IDE1NC4yNDMgMTQ2LjcyMiAxNTQuMDU2IDE0Ni43NjYgMTUzLjgzQzE0Ni44MSAxNTMuNjAxIDE0Ni44MzIgMTUzLjMyNyAxNDYuODMyIDE1My4wMVpNMTUyLjI5OCAxNTQuNzI4VjE1NS4zMjJIMTQ4LjU3NVYxNTQuODAyTDE1MC40MzkgMTUyLjcyOEMxNTAuNjY4IDE1Mi40NzMgMTUwLjg0NSAxNTIuMjU3IDE1MC45NyAxNTIuMDhDMTUxLjA5NyAxNTEuOSAxNTEuMTg2IDE1MS43NCAxNTEuMjM1IDE1MS41OTlDMTUxLjI4OCAxNTEuNDU2IDE1MS4zMTQgMTUxLjMxIDE1MS4zMTQgMTUxLjE2MkMxNTEuMzE0IDE1MC45NzQgMTUxLjI3NCAxNTAuODA1IDE1MS4xOTYgMTUwLjY1NEMxNTEuMTIxIDE1MC41IDE1MS4wMDkgMTUwLjM3OCAxNTAuODYgMTUwLjI4N0MxNTAuNzEyIDE1MC4xOTYgMTUwLjUzMiAxNTAuMTUgMTUwLjMyMSAxNTAuMTVDMTUwLjA2OSAxNTAuMTUgMTQ5Ljg1OCAxNTAuMiAxNDkuNjg5IDE1MC4yOTlDMTQ5LjUyMiAxNTAuMzk1IDE0OS4zOTcgMTUwLjUzIDE0OS4zMTQgMTUwLjcwNUMxNDkuMjMgMTUwLjg3OSAxNDkuMTg5IDE1MS4wOCAxNDkuMTg5IDE1MS4zMDZIMTQ4LjQ2NkMxNDguNDY2IDE1MC45ODYgMTQ4LjUzNiAxNTAuNjkzIDE0OC42NzcgMTUwLjQyN0MxNDguODE3IDE1MC4xNjIgMTQ5LjAyNiAxNDkuOTUxIDE0OS4zMDIgMTQ5Ljc5NUMxNDkuNTc4IDE0OS42MzYgMTQ5LjkxOCAxNDkuNTU2IDE1MC4zMjEgMTQ5LjU1NkMxNTAuNjgxIDE0OS41NTYgMTUwLjk4OCAxNDkuNjIgMTUxLjI0MyAxNDkuNzQ4QzE1MS40OTggMTQ5Ljg3MyAxNTEuNjk0IDE1MC4wNSAxNTEuODI5IDE1MC4yNzlDMTUxLjk2NyAxNTAuNTA2IDE1Mi4wMzYgMTUwLjc3MSAxNTIuMDM2IDE1MS4wNzZDMTUyLjAzNiAxNTEuMjQzIDE1Mi4wMDggMTUxLjQxMiAxNTEuOTUgMTUxLjU4NEMxNTEuODk2IDE1MS43NTMgMTUxLjgxOSAxNTEuOTIyIDE1MS43MiAxNTIuMDkyQzE1MS42MjMgMTUyLjI2MSAxNTEuNTEgMTUyLjQyNyAxNTEuMzggMTUyLjU5MkMxNTEuMjUyIDE1Mi43NTYgMTUxLjExNiAxNTIuOTE3IDE1MC45NyAxNTMuMDc2TDE0OS40NDYgMTU0LjcyOEgxNTIuMjk4Wk0xNTYuMTYyIDE0OS42MzVWMTU1LjMyMkgxNTUuNDA5VjE0OS42MzVIMTU2LjE2MlpNMTU4LjU0NSAxNTIuMTkzVjE1Mi44MUgxNTUuOTk4VjE1Mi4xOTNIMTU4LjU0NVpNMTU4LjkzMiAxNDkuNjM1VjE1MC4yNTJIMTU1Ljk5OFYxNDkuNjM1SDE1OC45MzJaTTE2MS40NzIgMTU1LjRDMTYxLjE3NyAxNTUuNCAxNjAuOTEgMTU1LjM1MSAxNjAuNjcxIDE1NS4yNTJDMTYwLjQzNCAxNTUuMTUgMTYwLjIyOSAxNTUuMDA4IDE2MC4wNTggMTU0LjgyNkMxNTkuODg4IDE1NC42NDQgMTU5Ljc1OCAxNTQuNDI3IDE1OS42NjcgMTU0LjE3N0MxNTkuNTc2IDE1My45MjcgMTU5LjUzIDE1My42NTQgMTU5LjUzIDE1My4zNTdWMTUzLjE5M0MxNTkuNTMgMTUyLjg0OSAxNTkuNTgxIDE1Mi41NDMgMTU5LjY4MyAxNTIuMjc1QzE1OS43ODQgMTUyLjAwNCAxNTkuOTIyIDE1MS43NzUgMTYwLjA5NyAxNTEuNTg4QzE2MC4yNzEgMTUxLjQgMTYwLjQ2OSAxNTEuMjU4IDE2MC42OSAxNTEuMTYyQzE2MC45MTIgMTUxLjA2NiAxNjEuMTQxIDE1MS4wMTcgMTYxLjM3OCAxNTEuMDE3QzE2MS42OCAxNTEuMDE3IDE2MS45NCAxNTEuMDY5IDE2Mi4xNTkgMTUxLjE3NEMxNjIuMzggMTUxLjI3OCAxNjIuNTYxIDE1MS40MjQgMTYyLjcwMiAxNTEuNjExQzE2Mi44NDMgMTUxLjc5NiAxNjIuOTQ3IDE1Mi4wMTUgMTYzLjAxNSAxNTIuMjY3QzE2My4wODIgMTUyLjUxNyAxNjMuMTE2IDE1Mi43OTEgMTYzLjExNiAxNTMuMDg4VjE1My40MTJIMTU5Ljk2VjE1Mi44MjJIMTYyLjM5M1YxNTIuNzY3QzE2Mi4zODMgMTUyLjU4IDE2Mi4zNDQgMTUyLjM5OCAxNjIuMjc2IDE1Mi4yMkMxNjIuMjExIDE1Mi4wNDMgMTYyLjEwNyAxNTEuODk4IDE2MS45NjQgMTUxLjc4M0MxNjEuODIxIDE1MS42NjggMTYxLjYyNSAxNTEuNjExIDE2MS4zNzggMTUxLjYxMUMxNjEuMjE0IDE1MS42MTEgMTYxLjA2MyAxNTEuNjQ2IDE2MC45MjUgMTUxLjcxN0MxNjAuNzg3IDE1MS43ODQgMTYwLjY2OCAxNTEuODg2IDE2MC41NjkgMTUyLjAyMUMxNjAuNDcgMTUyLjE1NyAxNjAuMzkzIDE1Mi4zMjIgMTYwLjMzOSAxNTIuNTE3QzE2MC4yODQgMTUyLjcxMyAxNjAuMjU3IDE1Mi45MzggMTYwLjI1NyAxNTMuMTkzVjE1My4zNTdDMTYwLjI1NyAxNTMuNTU4IDE2MC4yODQgMTUzLjc0NyAxNjAuMzM5IDE1My45MjRDMTYwLjM5NiAxNTQuMDk4IDE2MC40NzggMTU0LjI1MiAxNjAuNTg1IDE1NC4zODVDMTYwLjY5NCAxNTQuNTE3IDE2MC44MjYgMTU0LjYyMiAxNjAuOTc5IDE1NC42OTdDMTYxLjEzNiAxNTQuNzczIDE2MS4zMTMgMTU0LjgxIDE2MS41MTEgMTU0LjgxQzE2MS43NjYgMTU0LjgxIDE2MS45ODIgMTU0Ljc1OCAxNjIuMTU5IDE1NC42NTRDMTYyLjMzNiAxNTQuNTUgMTYyLjQ5MSAxNTQuNDExIDE2Mi42MjQgMTU0LjIzNkwxNjMuMDYxIDE1NC41ODRDMTYyLjk3IDE1NC43MjIgMTYyLjg1NCAxNTQuODUzIDE2Mi43MTQgMTU0Ljk3OEMxNjIuNTczIDE1NS4xMDMgMTYyLjQgMTU1LjIwNSAxNjIuMTk0IDE1NS4yODNDMTYxLjk5MSAxNTUuMzYxIDE2MS43NSAxNTUuNCAxNjEuNDcyIDE1NS40Wk0xNjQuMDM5IDE0OS4zMjJIMTY0Ljc2NVYxNTQuNTAyTDE2NC43MDMgMTU1LjMyMkgxNjQuMDM5VjE0OS4zMjJaTTE2Ny42MjEgMTUzLjE3NFYxNTMuMjU2QzE2Ny42MjEgMTUzLjU2MyAxNjcuNTg0IDE1My44NDggMTY3LjUxMSAxNTQuMTExQzE2Ny40MzggMTU0LjM3MiAxNjcuMzMyIDE1NC41OTggMTY3LjE5MSAxNTQuNzkxQzE2Ny4wNSAxNTQuOTgzIDE2Ni44NzggMTU1LjEzMyAxNjYuNjc1IDE1NS4yNEMxNjYuNDcyIDE1NS4zNDcgMTY2LjIzOSAxNTUuNCAxNjUuOTc2IDE1NS40QzE2NS43MDggMTU1LjQgMTY1LjQ3MiAxNTUuMzU1IDE2NS4yNjkgMTU1LjI2M0MxNjUuMDY5IDE1NS4xNyAxNjQuODk5IDE1NS4wMzYgMTY0Ljc2MSAxNTQuODYxQzE2NC42MjMgMTU0LjY4NyAxNjQuNTEzIDE1NC40NzYgMTY0LjQyOSAxNTQuMjI4QzE2NC4zNDggMTUzLjk4MSAxNjQuMjkzIDE1My43MDIgMTY0LjI2MSAxNTMuMzkyVjE1My4wMzNDMTY0LjI5MyAxNTIuNzIgMTY0LjM0OCAxNTIuNDQxIDE2NC40MjkgMTUyLjE5M0MxNjQuNTEzIDE1MS45NDYgMTY0LjYyMyAxNTEuNzM1IDE2NC43NjEgMTUxLjU2QzE2NC44OTkgMTUxLjM4MyAxNjUuMDY5IDE1MS4yNDkgMTY1LjI2OSAxNTEuMTU4QzE2NS40NyAxNTEuMDY0IDE2NS43MDMgMTUxLjAxNyAxNjUuOTY4IDE1MS4wMTdDMTY2LjIzNCAxNTEuMDE3IDE2Ni40NyAxNTEuMDY5IDE2Ni42NzUgMTUxLjE3NEMxNjYuODgxIDE1MS4yNzUgMTY3LjA1MyAxNTEuNDIxIDE2Ny4xOTEgMTUxLjYxMUMxNjcuMzMyIDE1MS44MDEgMTY3LjQzOCAxNTIuMDI5IDE2Ny41MTEgMTUyLjI5NUMxNjcuNTg0IDE1Mi41NTggMTY3LjYyMSAxNTIuODUxIDE2Ny42MjEgMTUzLjE3NFpNMTY2Ljg5NCAxNTMuMjU2VjE1My4xNzRDMTY2Ljg5NCAxNTIuOTYzIDE2Ni44NzUgMTUyLjc2NSAxNjYuODM1IDE1Mi41OEMxNjYuNzk2IDE1Mi4zOTIgMTY2LjczNCAxNTIuMjI4IDE2Ni42NDggMTUyLjA4OEMxNjYuNTYyIDE1MS45NDQgMTY2LjQ0OSAxNTEuODMyIDE2Ni4zMDggMTUxLjc1MkMxNjYuMTY4IDE1MS42NjggMTY1Ljk5NCAxNTEuNjI3IDE2NS43ODkgMTUxLjYyN0MxNjUuNjA2IDE1MS42MjcgMTY1LjQ0NyAxNTEuNjU4IDE2NS4zMTIgMTUxLjcyQzE2NS4xNzkgMTUxLjc4MyAxNjUuMDY2IDE1MS44NjggMTY0Ljk3MiAxNTEuOTc0QzE2NC44NzggMTUyLjA3OSAxNjQuODAyIDE1Mi4xOTggMTY0Ljc0MiAxNTIuMzM0QzE2NC42ODQgMTUyLjQ2NyAxNjQuNjQxIDE1Mi42MDUgMTY0LjYxMyAxNTIuNzQ4VjE1My42ODlDMTY0LjY1NCAxNTMuODcyIDE2NC43MjIgMTU0LjA0NyAxNjQuODE2IDE1NC4yMTdDMTY0LjkxMiAxNTQuMzgzIDE2NS4wNCAxNTQuNTIgMTY1LjE5OSAxNTQuNjI3QzE2NS4zNiAxNTQuNzMzIDE2NS41NTkgMTU0Ljc4NyAxNjUuNzk2IDE1NC43ODdDMTY1Ljk5MiAxNTQuNzg3IDE2Ni4xNTggMTU0Ljc0OCAxNjYuMjk2IDE1NC42N0MxNjYuNDM3IDE1NC41ODkgMTY2LjU1IDE1NC40NzggMTY2LjYzNiAxNTQuMzM4QzE2Ni43MjUgMTU0LjE5NyAxNjYuNzkgMTU0LjAzNCAxNjYuODMyIDE1My44NDlDMTY2Ljg3MyAxNTMuNjY0IDE2Ni44OTQgMTUzLjQ2NyAxNjYuODk0IDE1My4yNTZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjwvZz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF80MTgzXzkwODc3Ij4KPHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjxjbGlwUGF0aCBpZD0iY2xpcDFfNDE4M185MDg3NyI+CjxyZWN0IHdpZHRoPSI4OC41IiBoZWlnaHQ9IjEwLjMyMiIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDExMS41IDE0NikiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K",
+ "description": "Displays changes to time-series data over time—for example, temperature or humidity readings.",
+ "descriptor": {
+ "type": "timeseries",
+ "sizeX": 8,
+ "sizeY": 5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n self.ctx.$scope.timeSeriesChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeSeriesChartWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.timeSeriesChartWidget.onLatestDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n chartType: 'bar',\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n dataKeySettingsFunction: TbTimeSeriesChart.dataKeySettings('bar'),\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries', units: '°C', decimals: 0 }];\n }\n };\n}\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}",
+ "latestDataKeySettingsSchema": "{}",
+ "settingsDirective": "tb-time-series-chart-widget-settings",
+ "dataKeySettingsDirective": "tb-time-series-chart-key-settings",
+ "latestDataKeySettingsDirective": "",
+ "hasBasicMode": true,
+ "basicModeDirective": "tb-time-series-chart-basic-config",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"type\":\"bar\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#FFC107\",\"settings\":{\"type\":\"bar\"},\"_hash\":0.5534217244004682,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":null}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":0,\"realtime\":{\"realtimeType\":0,\"timewindowMs\":60000,\"quickInterval\":\"CURRENT_DAY\",\"interval\":1000},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"top\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"yAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"xAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"bottom\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":10,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormat\":{},\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipDateInterval\":true,\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"yAxes\":{\"default\":{\"units\":null,\"decimals\":0,\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormatter\":null,\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\",\"id\":\"default\",\"order\":0}},\"noAggregationBarWidthSettings\":{\"strategy\":\"group\",\"groupWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000},\"barWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000}},\"animation\":{\"animation\":true,\"animationThreshold\":2000,\"animationDuration\":500,\"animationEasing\":\"cubicOut\",\"animationDelay\":0,\"animationDurationUpdate\":300,\"animationEasingUpdate\":\"cubicOut\",\"animationDelayUpdate\":0},\"padding\":\"12px\"},\"title\":\"Bar chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"\",\"decimals\":null,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}"
+ },
+ "tags": [
+ "chart",
+ "time series",
+ "time-series",
+ "bar",
+ "bar chart"
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_types/bar_chart_with_labels.json b/application/src/main/data/json/system/widget_types/bar_chart_with_labels.json
index 75814a547a..356e91f90e 100644
--- a/application/src/main/data/json/system/widget_types/bar_chart_with_labels.json
+++ b/application/src/main/data/json/system/widget_types/bar_chart_with_labels.json
@@ -20,7 +20,7 @@
"latestDataKeySettingsDirective": "",
"hasBasicMode": true,
"basicModeDirective": "tb-bar-chart-with-labels-basic-config",
- "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"rgb(125, 142, 255)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 50) {\\n\\tvalue = 50;\\n} else if (value > 80) {\\n\\tvalue = 80;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Soil moisture\",\"color\":\"rgb(249, 111, 255)\",\"settings\":{},\"_hash\":0.9111685461089025,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 30) {\\n\\tvalue = 30;\\n} else if (value > 90) {\\n\\tvalue = 90;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Leaf wetness\",\"color\":\"rgb(255, 163, 137)\",\"settings\":{},\"_hash\":0.8487533373085416,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 40) {\\n\\tvalue = 40;\\n} else if (value > 70) {\\n\\tvalue = 70;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cloud cover\",\"color\":\"#FFED53\",\"settings\":{},\"_hash\":0.7690144858984289,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 20) {\\n\\tvalue = 20;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":\"MONTH\",\"fixedTimewindow\":{\"startTimeMs\":1704293713163,\"endTimeMs\":1704380113163},\"quickInterval\":\"CURRENT_HALF_YEAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"showBarLabel\":true,\"barLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"12px\"},\"barLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"showBarValue\":true,\"barValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"700\",\"lineHeight\":\"12px\"},\"barValueColor\":\"rgba(0, 0, 0, 0.76)\",\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"MMMM y\",\"lastUpdateAgo\":false,\"custom\":true},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Bar chart with labels\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"public\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"%\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"24px\"}"
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity\",\"color\":\"rgb(125, 142, 255)\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 50) {\\n\\tvalue = 50;\\n} else if (value > 80) {\\n\\tvalue = 80;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Soil moisture\",\"color\":\"rgb(249, 111, 255)\",\"settings\":{},\"_hash\":0.9111685461089025,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 30) {\\n\\tvalue = 30;\\n} else if (value > 90) {\\n\\tvalue = 90;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Leaf wetness\",\"color\":\"rgb(255, 163, 137)\",\"settings\":{},\"_hash\":0.8487533373085416,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 40) {\\n\\tvalue = 40;\\n} else if (value > 70) {\\n\\tvalue = 70;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cloud cover\",\"color\":\"#FFED53\",\"settings\":{},\"_hash\":0.7690144858984289,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < 20) {\\n\\tvalue = 20;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":1,\"history\":{\"historyType\":2,\"timewindowMs\":60000,\"interval\":\"MONTH\",\"fixedTimewindow\":{\"startTimeMs\":1704293713163,\"endTimeMs\":1704380113163},\"quickInterval\":\"CURRENT_HALF_YEAR\"},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"showBarLabel\":true,\"barLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"12px\"},\"barLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"showBarValue\":true,\"barValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"700\",\"lineHeight\":\"12px\"},\"barValueColor\":\"rgba(0, 0, 0, 0.76)\",\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"tooltipDateInterval\":true},\"title\":\"Bar chart with labels\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"public\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"%\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"24px\"}"
},
"tags": [
"bar chart",
diff --git a/application/src/main/data/json/system/widget_types/line_chart.json b/application/src/main/data/json/system/widget_types/line_chart.json
new file mode 100644
index 0000000000..f5b123231c
--- /dev/null
+++ b/application/src/main/data/json/system/widget_types/line_chart.json
@@ -0,0 +1,32 @@
+{
+ "fqn": "line_chart",
+ "name": "Line chart",
+ "deprecated": false,
+ "image": "tb-image:Y2hhcnRfKDEpLnN2Zw==:IkxpbmUgY2hhcnQiIHN5c3RlbSB3aWRnZXQgaW1hZ2U=;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF80MTg0Xzk0NTI3KSI+CjxwYXRoIGQ9Ik0yLjg0NzY2IDEuMjgxMjVWN0gyLjEyNVYyLjE4MzU5TDAuNjY3OTY5IDIuNzE0ODRWMi4wNjI1TDIuNzM0MzggMS4yODEyNUgyLjg0NzY2Wk04LjY3NTE3IDMuNzAzMTJWNC41NzAzMUM4LjY3NTE3IDUuMDM2NDYgOC42MzM1MSA1LjQyOTY5IDguNTUwMTcgNS43NUM4LjQ2Njg0IDYuMDcwMzEgOC4zNDcwNSA2LjMyODEyIDguMTkwOCA2LjUyMzQ0QzguMDM0NTUgNi43MTg3NSA3Ljg0NTc1IDYuODYwNjggNy42MjQzOSA2Ljk0OTIyQzcuNDA1NjQgNy4wMzUxNiA3LjE1ODI1IDcuMDc4MTIgNi44ODIyIDcuMDc4MTJDNi42NjM0NSA3LjA3ODEyIDYuNDYxNjMgNy4wNTA3OCA2LjI3NjczIDYuOTk2MDlDNi4wOTE4NCA2Ljk0MTQxIDUuOTI1MTcgNi44NTQxNyA1Ljc3NjczIDYuNzM0MzhDNS42MzA5IDYuNjExOTggNS41MDU5IDYuNDUzMTIgNS40MDE3MyA2LjI1NzgxQzUuMjk3NTcgNi4wNjI1IDUuMjE4MTQgNS44MjU1MiA1LjE2MzQ1IDUuNTQ2ODhDNS4xMDg3NyA1LjI2ODIzIDUuMDgxNDIgNC45NDI3MSA1LjA4MTQyIDQuNTcwMzFWMy43MDMxMkM1LjA4MTQyIDMuMjM2OTggNS4xMjMwOSAyLjg0NjM1IDUuMjA2NDIgMi41MzEyNUM1LjI5MjM2IDIuMjE2MTUgNS40MTM0NSAxLjk2MzU0IDUuNTY5NyAxLjc3MzQ0QzUuNzI1OTUgMS41ODA3MyA1LjkxMzQ1IDEuNDQyNzEgNi4xMzIyIDEuMzU5MzhDNi4zNTM1NiAxLjI3NjA0IDYuNjAwOTUgMS4yMzQzOCA2Ljg3NDM5IDEuMjM0MzhDNy4wOTU3NSAxLjIzNDM4IDcuMjk4ODcgMS4yNjE3MiA3LjQ4Mzc3IDEuMzE2NDFDNy42NzEyNyAxLjM2ODQ5IDcuODM3OTMgMS40NTMxMiA3Ljk4Mzc3IDEuNTcwMzFDOC4xMjk2IDEuNjg0OSA4LjI1MzMgMS44Mzg1NCA4LjM1NDg2IDIuMDMxMjVDOC40NTkwMyAyLjIyMTM1IDguNTM4NDUgMi40NTQ0MyA4LjU5MzE0IDIuNzMwNDdDOC42NDc4MyAzLjAwNjUxIDguNjc1MTcgMy4zMzA3MyA4LjY3NTE3IDMuNzAzMTJaTTcuOTQ4NjEgNC42ODc1VjMuNTgyMDNDNy45NDg2MSAzLjMyNjgyIDcuOTMyOTggMy4xMDI4NiA3LjkwMTczIDIuOTEwMTZDNy44NzMwOSAyLjcxNDg0IDcuODMwMTIgMi41NDgxOCA3Ljc3MjgzIDIuNDEwMTZDNy43MTU1NCAyLjI3MjE0IDcuNjQyNjIgMi4xNjAxNiA3LjU1NDA4IDIuMDc0MjJDNy40NjgxNCAxLjk4ODI4IDcuMzY3ODggMS45MjU3OCA3LjI1MzMgMS44ODY3MkM3LjE0MTMyIDEuODQ1MDUgNy4wMTUwMiAxLjgyNDIyIDYuODc0MzkgMS44MjQyMkM2LjcwMjUyIDEuODI0MjIgNi41NTAxNyAxLjg1Njc3IDYuNDE3MzYgMS45MjE4OEM2LjI4NDU1IDEuOTg0MzggNi4xNzI1NyAyLjA4NDY0IDYuMDgxNDIgMi4yMjI2NkM1Ljk5Mjg4IDIuMzYwNjggNS45MjUxNyAyLjU0MTY3IDUuODc4MyAyLjc2NTYyQzUuODMxNDIgMi45ODk1OCA1LjgwNzk4IDMuMjYxNzIgNS44MDc5OCAzLjU4MjAzVjQuNjg3NUM1LjgwNzk4IDQuOTQyNzEgNS44MjIzMSA1LjE2Nzk3IDUuODUwOTUgNS4zNjMyOEM1Ljg4MjIgNS41NTg1OSA1LjkyNzc4IDUuNzI3ODYgNS45ODc2NyA1Ljg3MTA5QzYuMDQ3NTcgNi4wMTE3MiA2LjEyMDQ4IDYuMTI3NiA2LjIwNjQyIDYuMjE4NzVDNi4yOTIzNiA2LjMwOTkgNi4zOTEzMiA2LjM3NzYgNi41MDMzIDYuNDIxODhDNi42MTc4OCA2LjQ2MzU0IDYuNzQ0MTggNi40ODQzOCA2Ljg4MjIgNi40ODQzOEM3LjA1OTI5IDYuNDg0MzggNy4yMTQyMyA2LjQ1MDUyIDcuMzQ3MDUgNi4zODI4MUM3LjQ3OTg2IDYuMzE1MSA3LjU5MDU0IDYuMjA5NjQgNy42NzkwOCA2LjA2NjQxQzcuNzcwMjIgNS45MjA1NyA3LjgzNzkzIDUuNzM0MzggNy44ODIyIDUuNTA3ODFDNy45MjY0NyA1LjI3ODY1IDcuOTQ4NjEgNS4wMDUyMSA3Ljk0ODYxIDQuNjg3NVpNMTMuMzA3NCAzLjcwMzEyVjQuNTcwMzFDMTMuMzA3NCA1LjAzNjQ2IDEzLjI2NTcgNS40Mjk2OSAxMy4xODI0IDUuNzVDMTMuMDk5IDYuMDcwMzEgMTIuOTc5MyA2LjMyODEyIDEyLjgyMyA2LjUyMzQ0QzEyLjY2NjggNi43MTg3NSAxMi40Nzc5IDYuODYwNjggMTIuMjU2NiA2Ljk0OTIyQzEyLjAzNzggNy4wMzUxNiAxMS43OTA0IDcuMDc4MTIgMTEuNTE0NCA3LjA3ODEyQzExLjI5NTcgNy4wNzgxMiAxMS4wOTM4IDcuMDUwNzggMTAuOTA4OSA2Ljk5NjA5QzEwLjcyNCA2Ljk0MTQxIDEwLjU1NzQgNi44NTQxNyAxMC40MDg5IDYuNzM0MzhDMTAuMjYzMSA2LjYxMTk4IDEwLjEzODEgNi40NTMxMiAxMC4wMzM5IDYuMjU3ODFDOS45Mjk3NyA2LjA2MjUgOS44NTAzNCA1LjgyNTUyIDkuNzk1NjYgNS41NDY4OEM5Ljc0MDk3IDUuMjY4MjMgOS43MTM2MyA0Ljk0MjcxIDkuNzEzNjMgNC41NzAzMVYzLjcwMzEyQzkuNzEzNjMgMy4yMzY5OCA5Ljc1NTI5IDIuODQ2MzUgOS44Mzg2MyAyLjUzMTI1QzkuOTI0NTYgMi4yMTYxNSAxMC4wNDU3IDEuOTYzNTQgMTAuMjAxOSAxLjc3MzQ0QzEwLjM1ODIgMS41ODA3MyAxMC41NDU3IDEuNDQyNzEgMTAuNzY0NCAxLjM1OTM4QzEwLjk4NTggMS4yNzYwNCAxMS4yMzMyIDEuMjM0MzggMTEuNTA2NiAxLjIzNDM4QzExLjcyNzkgMS4yMzQzOCAxMS45MzExIDEuMjYxNzIgMTIuMTE2IDEuMzE2NDFDMTIuMzAzNSAxLjM2ODQ5IDEyLjQ3MDEgMS40NTMxMiAxMi42MTYgMS41NzAzMUMxMi43NjE4IDEuNjg0OSAxMi44ODU1IDEuODM4NTQgMTIuOTg3MSAyLjAzMTI1QzEzLjA5MTIgMi4yMjEzNSAxMy4xNzA3IDIuNDU0NDMgMTMuMjI1MyAyLjczMDQ3QzEzLjI4IDMuMDA2NTEgMTMuMzA3NCAzLjMzMDczIDEzLjMwNzQgMy43MDMxMlpNMTIuNTgwOCA0LjY4NzVWMy41ODIwM0MxMi41ODA4IDMuMzI2ODIgMTIuNTY1MiAzLjEwMjg2IDEyLjUzMzkgMi45MTAxNkMxMi41MDUzIDIuNzE0ODQgMTIuNDYyMyAyLjU0ODE4IDEyLjQwNSAyLjQxMDE2QzEyLjM0NzcgMi4yNzIxNCAxMi4yNzQ4IDIuMTYwMTYgMTIuMTg2MyAyLjA3NDIyQzEyLjEwMDMgMS45ODgyOCAxMi4wMDAxIDEuOTI1NzggMTEuODg1NSAxLjg4NjcyQzExLjc3MzUgMS44NDUwNSAxMS42NDcyIDEuODI0MjIgMTEuNTA2NiAxLjgyNDIyQzExLjMzNDcgMS44MjQyMiAxMS4xODI0IDEuODU2NzcgMTEuMDQ5NiAxLjkyMTg4QzEwLjkxNjggMS45ODQzOCAxMC44MDQ4IDIuMDg0NjQgMTAuNzEzNiAyLjIyMjY2QzEwLjYyNTEgMi4zNjA2OCAxMC41NTc0IDIuNTQxNjcgMTAuNTEwNSAyLjc2NTYyQzEwLjQ2MzYgMi45ODk1OCAxMC40NDAyIDMuMjYxNzIgMTAuNDQwMiAzLjU4MjAzVjQuNjg3NUMxMC40NDAyIDQuOTQyNzEgMTAuNDU0NSA1LjE2Nzk3IDEwLjQ4MzIgNS4zNjMyOEMxMC41MTQ0IDUuNTU4NTkgMTAuNTYgNS43Mjc4NiAxMC42MTk5IDUuODcxMDlDMTAuNjc5OCA2LjAxMTcyIDEwLjc1MjcgNi4xMjc2IDEwLjgzODYgNi4yMTg3NUMxMC45MjQ2IDYuMzA5OSAxMS4wMjM1IDYuMzc3NiAxMS4xMzU1IDYuNDIxODhDMTEuMjUwMSA2LjQ2MzU0IDExLjM3NjQgNi40ODQzOCAxMS41MTQ0IDYuNDg0MzhDMTEuNjkxNSA2LjQ4NDM4IDExLjg0NjQgNi40NTA1MiAxMS45NzkzIDYuMzgyODFDMTIuMTEyMSA2LjMxNTEgMTIuMjIyNyA2LjIwOTY0IDEyLjMxMTMgNi4wNjY0MUMxMi40MDI0IDUuOTIwNTcgMTIuNDcwMSA1LjczNDM4IDEyLjUxNDQgNS41MDc4MUMxMi41NTg3IDUuMjc4NjUgMTIuNTgwOCA1LjAwNTIxIDEyLjU4MDggNC42ODc1Wk0xNC4zMDY4IDIuNzA3MDNWMi40MDYyNUMxNC4zMDY4IDIuMTkwMSAxNC4zNTM2IDEuOTkzNDkgMTQuNDQ3NCAxLjgxNjQxQzE0LjU0MTEgMS42MzkzMiAxNC42NzUzIDEuNDk3NCAxNC44NDk3IDEuMzkwNjJDMTUuMDI0MiAxLjI4Mzg1IDE1LjIzMTIgMS4yMzA0NyAxNS40NzA4IDEuMjMwNDdDMTUuNzE1NiAxLjIzMDQ3IDE1LjkyNCAxLjI4Mzg1IDE2LjA5NTggMS4zOTA2MkMxNi4yNzAzIDEuNDk3NCAxNi40MDQ0IDEuNjM5MzIgMTYuNDk4MiAxLjgxNjQxQzE2LjU5MTkgMS45OTM0OSAxNi42Mzg4IDIuMTkwMSAxNi42Mzg4IDIuNDA2MjVWMi43MDcwM0MxNi42Mzg4IDIuOTE3OTcgMTYuNTkxOSAzLjExMTk4IDE2LjQ5ODIgMy4yODkwNkMxNi40MDcgMy40NjYxNSAxNi4yNzQyIDMuNjA4MDcgMTYuMDk5NyAzLjcxNDg0QzE1LjkyNzkgMy44MjE2MSAxNS43MjA4IDMuODc1IDE1LjQ3ODYgMy44NzVDMTUuMjM2NSAzLjg3NSAxNS4wMjY4IDMuODIxNjEgMTQuODQ5NyAzLjcxNDg0QzE0LjY3NTMgMy42MDgwNyAxNC41NDExIDMuNDY2MTUgMTQuNDQ3NCAzLjI4OTA2QzE0LjM1MzYgMy4xMTE5OCAxNC4zMDY4IDIuOTE3OTcgMTQuMzA2OCAyLjcwNzAzWk0xNC44NDk3IDIuNDA2MjVWMi43MDcwM0MxNC44NDk3IDIuODI2ODIgMTQuODcxOSAyLjk0MDEgMTQuOTE2MSAzLjA0Njg4QzE0Ljk2MyAzLjE1MzY1IDE1LjAzMzMgMy4yNDA4OSAxNS4xMjcxIDMuMzA4NTlDMTUuMjIwOCAzLjM3MzcgMTUuMzM4IDMuNDA2MjUgMTUuNDc4NiAzLjQwNjI1QzE1LjYxOTMgMy40MDYyNSAxNS43MzUyIDMuMzczNyAxNS44MjYzIDMuMzA4NTlDMTUuOTE3NCAzLjI0MDg5IDE1Ljk4NTIgMy4xNTM2NSAxNi4wMjk0IDMuMDQ2ODhDMTYuMDczNyAyLjk0MDEgMTYuMDk1OCAyLjgyNjgyIDE2LjA5NTggMi43MDcwM1YyLjQwNjI1QzE2LjA5NTggMi4yODM4NSAxNi4wNzI0IDIuMTY5MjcgMTYuMDI1NSAyLjA2MjVDMTUuOTgxMiAxLjk1MzEyIDE1LjkxMjIgMS44NjU4OSAxNS44MTg1IDEuODAwNzhDMTUuNzI3MyAxLjczMzA3IDE1LjYxMTUgMS42OTkyMiAxNS40NzA4IDEuNjk5MjJDMTUuMzMyOCAxLjY5OTIyIDE1LjIxNjkgMS43MzMwNyAxNS4xMjMyIDEuODAwNzhDMTUuMDMyIDEuODY1ODkgMTQuOTYzIDEuOTUzMTIgMTQuOTE2MSAyLjA2MjVDMTQuODcxOSAyLjE2OTI3IDE0Ljg0OTcgMi4yODM4NSAxNC44NDk3IDIuNDA2MjVaTTE3LjA3NjMgNS45MTAxNlY1LjYwNTQ3QzE3LjA3NjMgNS4zOTE5MyAxNy4xMjMyIDUuMTk2NjEgMTcuMjE2OSA1LjAxOTUzQzE3LjMxMDcgNC44NDI0NSAxNy40NDQ4IDQuNzAwNTIgMTcuNjE5MyA0LjU5Mzc1QzE3Ljc5MzcgNC40ODY5OCAxOC4wMDA4IDQuNDMzNTkgMTguMjQwNCA0LjQzMzU5QzE4LjQ4NTIgNC40MzM1OSAxOC42OTM1IDQuNDg2OTggMTguODY1NCA0LjU5Mzc1QzE5LjAzOTggNC43MDA1MiAxOS4xNzQgNC44NDI0NSAxOS4yNjc3IDUuMDE5NTNDMTkuMzYxNSA1LjE5NjYxIDE5LjQwODMgNS4zOTE5MyAxOS40MDgzIDUuNjA1NDdWNS45MTAxNkMxOS40MDgzIDYuMTIzNyAxOS4zNjE1IDYuMzE5MDEgMTkuMjY3NyA2LjQ5NjA5QzE5LjE3NjYgNi42NzMxOCAxOS4wNDM3IDYuODE1MSAxOC44NjkzIDYuOTIxODhDMTguNjk3NCA3LjAyODY1IDE4LjQ5MDQgNy4wODIwMyAxOC4yNDgyIDcuMDgyMDNDMTguMDA2IDcuMDgyMDMgMTcuNzk3NyA3LjAyODY1IDE3LjYyMzIgNi45MjE4OEMxNy40NDg3IDYuODE1MSAxNy4zMTMzIDYuNjczMTggMTcuMjE2OSA2LjQ5NjA5QzE3LjEyMzIgNi4zMTkwMSAxNy4wNzYzIDYuMTIzNyAxNy4wNzYzIDUuOTEwMTZaTTE3LjYxOTMgNS42MDU0N1Y1LjkxMDE2QzE3LjYxOTMgNi4wMjk5NSAxNy42NDE0IDYuMTQ0NTMgMTcuNjg1NyA2LjI1MzkxQzE3LjczMjUgNi4zNjA2OCAxNy44MDI5IDYuNDQ3OTIgMTcuODk2NiA2LjUxNTYyQzE3Ljk5MDQgNi41ODA3MyAxOC4xMDc1IDYuNjEzMjggMTguMjQ4MiA2LjYxMzI4QzE4LjM4ODggNi42MTMyOCAxOC41MDQ3IDYuNTgwNzMgMTguNTk1OCA2LjUxNTYyQzE4LjY4OTYgNi40NDc5MiAxOC43NTg2IDYuMzYwNjggMTguODAyOSA2LjI1MzkxQzE4Ljg0NzEgNi4xNDcxNCAxOC44NjkzIDYuMDMyNTUgMTguODY5MyA1LjkxMDE2VjUuNjA1NDdDMTguODY5MyA1LjQ4MzA3IDE4Ljg0NTggNS4zNjg0OSAxOC43OTkgNS4yNjE3MkMxOC43NTQ3IDUuMTU0OTUgMTguNjg1NyA1LjA2OTAxIDE4LjU5MTkgNS4wMDM5MUMxOC41MDA4IDQuOTM2MiAxOC4zODM2IDQuOTAyMzQgMTguMjQwNCA0LjkwMjM0QzE4LjEwMjMgNC45MDIzNCAxNy45ODY1IDQuOTM2MiAxNy44OTI3IDUuMDAzOTFDMTcuODAxNiA1LjA2OTAxIDE3LjczMjUgNS4xNTQ5NSAxNy42ODU3IDUuMjYxNzJDMTcuNjQxNCA1LjM2ODQ5IDE3LjYxOTMgNS40ODMwNyAxNy42MTkzIDUuNjA1NDdaTTE4LjQyIDIuMTIxMDlMMTUuNjQyNyA2LjU2NjQxTDE1LjIzNjUgNi4zMDg1OUwxOC4wMTM4IDEuODYzMjhMMTguNDIgMi4xMjEwOVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTguMDU4NTkgMzMuNjY3N0M4LjA1ODU5IDM0LjAxNDEgNy45Nzc4NiAzNC4zMDgzIDcuODE2NDEgMzQuNTUwNUM3LjY1NzU1IDM0Ljc5MDEgNy40NDE0MSAzNC45NzI0IDcuMTY3OTcgMzUuMDk3NEM2Ljg5NzE0IDM1LjIyMjQgNi41OTExNSAzNS4yODQ5IDYuMjUgMzUuMjg0OUM1LjkwODg1IDM1LjI4NDkgNS42MDE1NiAzNS4yMjI0IDUuMzI4MTIgMzUuMDk3NEM1LjA1NDY5IDM0Ljk3MjQgNC44Mzg1NCAzNC43OTAxIDQuNjc5NjkgMzQuNTUwNUM0LjUyMDgzIDM0LjMwODMgNC40NDE0MSAzNC4wMTQxIDQuNDQxNDEgMzMuNjY3N0M0LjQ0MTQxIDMzLjQ0MTIgNC40ODQzOCAzMy4yMzQxIDQuNTcwMzEgMzMuMDQ2NkM0LjY1ODg1IDMyLjg1NjUgNC43ODI1NSAzMi42OTEyIDQuOTQxNDEgMzIuNTUwNUM1LjEwMjg2IDMyLjQwOTkgNS4yOTI5NyAzMi4zMDE4IDUuNTExNzIgMzIuMjI2M0M1LjczMzA3IDMyLjE0ODIgNS45NzY1NiAzMi4xMDkxIDYuMjQyMTkgMzIuMTA5MUM2LjU5MTE1IDMyLjEwOTEgNi45MDIzNCAzMi4xNzY4IDcuMTc1NzggMzIuMzEyM0M3LjQ0OTIyIDMyLjQ0NTEgNy42NjQwNiAzMi42Mjg3IDcuODIwMzEgMzIuODYzQzcuOTc5MTcgMzMuMDk3NCA4LjA1ODU5IDMzLjM2NTYgOC4wNTg1OSAzMy42Njc3Wk03LjMzMjAzIDMzLjY1MjFDNy4zMzIwMyAzMy40NDEyIDcuMjg2NDYgMzMuMjU1IDcuMTk1MzEgMzMuMDkzNUM3LjEwNDE3IDMyLjkyOTQgNi45NzY1NiAzMi44MDE4IDYuODEyNSAzMi43MTA3QzYuNjQ4NDQgMzIuNjE5NSA2LjQ1ODMzIDMyLjU3NCA2LjI0MjE5IDMyLjU3NEM2LjAyMDgzIDMyLjU3NCA1LjgyOTQzIDMyLjYxOTUgNS42Njc5NyAzMi43MTA3QzUuNTA5MTEgMzIuODAxOCA1LjM4NTQyIDMyLjkyOTQgNS4yOTY4OCAzMy4wOTM1QzUuMjA4MzMgMzMuMjU1IDUuMTY0MDYgMzMuNDQxMiA1LjE2NDA2IDMzLjY1MjFDNS4xNjQwNiAzMy44NzA4IDUuMjA3MDMgMzQuMDU4MyA1LjI5Mjk3IDM0LjIxNDZDNS4zODE1MSAzNC4zNjgyIDUuNTA2NTEgMzQuNDg2NyA1LjY2Nzk3IDM0LjU3MDFDNS44MzIwMyAzNC42NTA4IDYuMDI2MDQgMzQuNjkxMiA2LjI1IDM0LjY5MTJDNi40NzM5NiAzNC42OTEyIDYuNjY2NjcgMzQuNjUwOCA2LjgyODEyIDM0LjU3MDFDNi45ODk1OCAzNC40ODY3IDcuMTEzMjggMzQuMzY4MiA3LjE5OTIyIDM0LjIxNDZDNy4yODc3NiAzNC4wNTgzIDcuMzMyMDMgMzMuODcwOCA3LjMzMjAzIDMzLjY1MjFaTTcuOTI1NzggMzAuOTk5OEM3LjkyNTc4IDMxLjI3NTggNy44NTI4NiAzMS41MjQ1IDcuNzA3MDMgMzEuNzQ1OEM3LjU2MTIgMzEuOTY3MiA3LjM2MTk4IDMyLjE0MTcgNy4xMDkzOCAzMi4yNjkzQzYuODU2NzcgMzIuMzk2OSA2LjU3MDMxIDMyLjQ2MDcgNi4yNSAzMi40NjA3QzUuOTI0NDggMzIuNDYwNyA1LjYzNDExIDMyLjM5NjkgNS4zNzg5MSAzMi4yNjkzQzUuMTI2MyAzMi4xNDE3IDQuOTI4MzkgMzEuOTY3MiA0Ljc4NTE2IDMxLjc0NThDNC42NDE5MyAzMS41MjQ1IDQuNTcwMzEgMzEuMjc1OCA0LjU3MDMxIDMwLjk5OThDNC41NzAzMSAzMC42NjkgNC42NDE5MyAzMC4zODc4IDQuNzg1MTYgMzAuMTU2QzQuOTMwOTkgMjkuOTI0MiA1LjEzMDIxIDI5Ljc0NzIgNS4zODI4MSAyOS42MjQ4QzUuNjM1NDIgMjkuNTAyNCA1LjkyMzE4IDI5LjQ0MTIgNi4yNDYwOSAyOS40NDEyQzYuNTcxNjEgMjkuNDQxMiA2Ljg2MDY4IDI5LjUwMjQgNy4xMTMyOCAyOS42MjQ4QzcuMzY1ODkgMjkuNzQ3MiA3LjU2MzggMjkuOTI0MiA3LjcwNzAzIDMwLjE1NkM3Ljg1Mjg2IDMwLjM4NzggNy45MjU3OCAzMC42NjkgNy45MjU3OCAzMC45OTk4Wk03LjIwMzEyIDMxLjAxMTVDNy4yMDMxMiAzMC44MjE0IDcuMTYyNzYgMzAuNjUzNCA3LjA4MjAzIDMwLjUwNzZDNy4wMDEzIDMwLjM2MTcgNi44ODkzMiAzMC4yNDcyIDYuNzQ2MDkgMzAuMTYzOEM2LjYwMjg2IDMwLjA3NzkgNi40MzYyIDMwLjAzNDkgNi4yNDYwOSAzMC4wMzQ5QzYuMDU1OTkgMzAuMDM0OSA1Ljg4OTMyIDMwLjA3NTMgNS43NDYwOSAzMC4xNTZDNS42MDU0NyAzMC4yMzQxIDUuNDk0NzkgMzAuMzQ2MSA1LjQxNDA2IDMwLjQ5MTlDNS4zMzU5NCAzMC42Mzc4IDUuMjk2ODggMzAuODExIDUuMjk2ODggMzEuMDExNUM1LjI5Njg4IDMxLjIwNjggNS4zMzU5NCAzMS4zNzc0IDUuNDE0MDYgMzEuNTIzMkM1LjQ5NDc5IDMxLjY2OSA1LjYwNjc3IDMxLjc4MjMgNS43NSAzMS44NjNDNS44OTMyMyAzMS45NDM4IDYuMDU5OSAzMS45ODQxIDYuMjUgMzEuOTg0MUM2LjQ0MDEgMzEuOTg0MSA2LjYwNTQ3IDMxLjk0MzggNi43NDYwOSAzMS44NjNDNi44ODkzMiAzMS43ODIzIDcuMDAxMyAzMS42NjkgNy4wODIwMyAzMS41MjMyQzcuMTYyNzYgMzEuMzc3NCA3LjIwMzEyIDMxLjIwNjggNy4yMDMxMiAzMS4wMTE1Wk0xMi42NzUyIDMxLjkwOTlWMzIuNzc3MUMxMi42NzUyIDMzLjI0MzIgMTIuNjMzNSAzMy42MzY1IDEyLjU1MDIgMzMuOTU2OEMxMi40NjY4IDM0LjI3NzEgMTIuMzQ3IDM0LjUzNDkgMTIuMTkwOCAzNC43MzAyQzEyLjAzNDUgMzQuOTI1NSAxMS44NDU3IDM1LjA2NzUgMTEuNjI0NCAzNS4xNTZDMTEuNDA1NiAzNS4yNDE5IDExLjE1ODIgMzUuMjg0OSAxMC44ODIyIDM1LjI4NDlDMTAuNjYzNSAzNS4yODQ5IDEwLjQ2MTYgMzUuMjU3NiAxMC4yNzY3IDM1LjIwMjlDMTAuMDkxOCAzNS4xNDgyIDkuOTI1MTcgMzUuMDYxIDkuNzc2NzMgMzQuOTQxMkM5LjYzMDkgMzQuODE4OCA5LjUwNTkgMzQuNjU5OSA5LjQwMTczIDM0LjQ2NDZDOS4yOTc1NyAzNC4yNjkzIDkuMjE4MTQgMzQuMDMyMyA5LjE2MzQ1IDMzLjc1MzdDOS4xMDg3NyAzMy40NzUgOS4wODE0MiAzMy4xNDk1IDkuMDgxNDIgMzIuNzc3MVYzMS45MDk5QzkuMDgxNDIgMzEuNDQzOCA5LjEyMzA5IDMxLjA1MzEgOS4yMDY0MiAzMC43MzhDOS4yOTIzNiAzMC40MjI5IDkuNDEzNDUgMzAuMTcwMyA5LjU2OTcgMjkuOTgwMkM5LjcyNTk1IDI5Ljc4NzUgOS45MTM0NSAyOS42NDk1IDEwLjEzMjIgMjkuNTY2MkMxMC4zNTM2IDI5LjQ4MjggMTAuNjAxIDI5LjQ0MTIgMTAuODc0NCAyOS40NDEyQzExLjA5NTcgMjkuNDQxMiAxMS4yOTg5IDI5LjQ2ODUgMTEuNDgzOCAyOS41MjMyQzExLjY3MTMgMjkuNTc1MyAxMS44Mzc5IDI5LjY1OTkgMTEuOTgzOCAyOS43NzcxQzEyLjEyOTYgMjkuODkxNyAxMi4yNTMzIDMwLjA0NTMgMTIuMzU0OSAzMC4yMzhDMTIuNDU5IDMwLjQyODEgMTIuNTM4NSAzMC42NjEyIDEyLjU5MzEgMzAuOTM3M0MxMi42NDc4IDMxLjIxMzMgMTIuNjc1MiAzMS41Mzc1IDEyLjY3NTIgMzEuOTA5OVpNMTEuOTQ4NiAzMi44OTQzVjMxLjc4ODhDMTEuOTQ4NiAzMS41MzM2IDExLjkzMyAzMS4zMDk3IDExLjkwMTcgMzEuMTE2OUMxMS44NzMxIDMwLjkyMTYgMTEuODMwMSAzMC43NTUgMTEuNzcyOCAzMC42MTY5QzExLjcxNTUgMzAuNDc4OSAxMS42NDI2IDMwLjM2NjkgMTEuNTU0MSAzMC4yODFDMTEuNDY4MSAzMC4xOTUxIDExLjM2NzkgMzAuMTMyNiAxMS4yNTMzIDMwLjA5MzVDMTEuMTQxMyAzMC4wNTE4IDExLjAxNSAzMC4wMzEgMTAuODc0NCAzMC4wMzFDMTAuNzAyNSAzMC4wMzEgMTAuNTUwMiAzMC4wNjM2IDEwLjQxNzQgMzAuMTI4N0MxMC4yODQ1IDMwLjE5MTIgMTAuMTcyNiAzMC4yOTE0IDEwLjA4MTQgMzAuNDI5NEM5Ljk5Mjg4IDMwLjU2NzUgOS45MjUxNyAzMC43NDg1IDkuODc4MyAzMC45NzI0QzkuODMxNDIgMzEuMTk2NCA5LjgwNzk4IDMxLjQ2ODUgOS44MDc5OCAzMS43ODg4VjMyLjg5NDNDOS44MDc5OCAzMy4xNDk1IDkuODIyMzEgMzMuMzc0OCA5Ljg1MDk1IDMzLjU3MDFDOS44ODIyIDMzLjc2NTQgOS45Mjc3OCAzMy45MzQ3IDkuOTg3NjcgMzQuMDc3OUMxMC4wNDc2IDM0LjIxODUgMTAuMTIwNSAzNC4zMzQ0IDEwLjIwNjQgMzQuNDI1NUMxMC4yOTI0IDM0LjUxNjcgMTAuMzkxMyAzNC41ODQ0IDEwLjUwMzMgMzQuNjI4N0MxMC42MTc5IDM0LjY3MDMgMTAuNzQ0MiAzNC42OTEyIDEwLjg4MjIgMzQuNjkxMkMxMS4wNTkzIDM0LjY5MTIgMTEuMjE0MiAzNC42NTczIDExLjM0NyAzNC41ODk2QzExLjQ3OTkgMzQuNTIxOSAxMS41OTA1IDM0LjQxNjQgMTEuNjc5MSAzNC4yNzMyQzExLjc3MDIgMzQuMTI3NCAxMS44Mzc5IDMzLjk0MTIgMTEuODgyMiAzMy43MTQ2QzExLjkyNjUgMzMuNDg1NCAxMS45NDg2IDMzLjIxMiAxMS45NDg2IDMyLjg5NDNaTTEzLjY3NDYgMzAuOTEzOFYzMC42MTNDMTMuNjc0NiAzMC4zOTY5IDEzLjcyMTQgMzAuMjAwMyAxMy44MTUyIDMwLjAyMzJDMTMuOTA4OSAyOS44NDYxIDE0LjA0MzEgMjkuNzA0MiAxNC4yMTc1IDI5LjU5NzRDMTQuMzkyIDI5LjQ5MDYgMTQuNTk5IDI5LjQzNzMgMTQuODM4NiAyOS40MzczQzE1LjA4MzQgMjkuNDM3MyAxNS4yOTE4IDI5LjQ5MDYgMTUuNDYzNiAyOS41OTc0QzE1LjYzODEgMjkuNzA0MiAxNS43NzIyIDI5Ljg0NjEgMTUuODY2IDMwLjAyMzJDMTUuOTU5NyAzMC4yMDAzIDE2LjAwNjYgMzAuMzk2OSAxNi4wMDY2IDMwLjYxM1YzMC45MTM4QzE2LjAwNjYgMzEuMTI0OCAxNS45NTk3IDMxLjMxODggMTUuODY2IDMxLjQ5NThDMTUuNzc0OCAzMS42NzI5IDE1LjY0MiAzMS44MTQ5IDE1LjQ2NzUgMzEuOTIxNkMxNS4yOTU3IDMyLjAyODQgMTUuMDg4NiAzMi4wODE4IDE0Ljg0NjQgMzIuMDgxOEMxNC42MDQzIDMyLjA4MTggMTQuMzk0NiAzMi4wMjg0IDE0LjIxNzUgMzEuOTIxNkMxNC4wNDMxIDMxLjgxNDkgMTMuOTA4OSAzMS42NzI5IDEzLjgxNTIgMzEuNDk1OEMxMy43MjE0IDMxLjMxODggMTMuNjc0NiAzMS4xMjQ4IDEzLjY3NDYgMzAuOTEzOFpNMTQuMjE3NSAzMC42MTNWMzAuOTEzOEMxNC4yMTc1IDMxLjAzMzYgMTQuMjM5NyAzMS4xNDY5IDE0LjI4MzkgMzEuMjUzN0MxNC4zMzA4IDMxLjM2MDQgMTQuNDAxMSAzMS40NDc3IDE0LjQ5NDkgMzEuNTE1NEMxNC41ODg2IDMxLjU4MDUgMTQuNzA1OCAzMS42MTMgMTQuODQ2NCAzMS42MTNDMTQuOTg3MSAzMS42MTMgMTUuMTAyOSAzMS41ODA1IDE1LjE5NDEgMzEuNTE1NEMxNS4yODUyIDMxLjQ0NzcgMTUuMzUyOSAzMS4zNjA0IDE1LjM5NzIgMzEuMjUzN0MxNS40NDE1IDMxLjE0NjkgMTUuNDYzNiAzMS4wMzM2IDE1LjQ2MzYgMzAuOTEzOFYzMC42MTNDMTUuNDYzNiAzMC40OTA2IDE1LjQ0MDIgMzAuMzc2MSAxNS4zOTMzIDMwLjI2OTNDMTUuMzQ5IDMwLjE1OTkgMTUuMjggMzAuMDcyNyAxNS4xODYzIDMwLjAwNzZDMTUuMDk1MSAyOS45Mzk5IDE0Ljk3OTMgMjkuOTA2IDE0LjgzODYgMjkuOTA2QzE0LjcwMDYgMjkuOTA2IDE0LjU4NDcgMjkuOTM5OSAxNC40OTEgMzAuMDA3NkMxNC4zOTk4IDMwLjA3MjcgMTQuMzMwOCAzMC4xNTk5IDE0LjI4MzkgMzAuMjY5M0MxNC4yMzk3IDMwLjM3NjEgMTQuMjE3NSAzMC40OTA2IDE0LjIxNzUgMzAuNjEzWk0xNi40NDQxIDM0LjExNjlWMzMuODEyM0MxNi40NDQxIDMzLjU5ODcgMTYuNDkxIDMzLjQwMzQgMTYuNTg0NyAzMy4yMjYzQzE2LjY3ODUgMzMuMDQ5MiAxNi44MTI2IDMyLjkwNzMgMTYuOTg3MSAzMi44MDA1QzE3LjE2MTUgMzIuNjkzOCAxNy4zNjg2IDMyLjY0MDQgMTcuNjA4MiAzMi42NDA0QzE3Ljg1MjkgMzIuNjQwNCAxOC4wNjEzIDMyLjY5MzggMTguMjMzMiAzMi44MDA1QzE4LjQwNzYgMzIuOTA3MyAxOC41NDE4IDMzLjA0OTIgMTguNjM1NSAzMy4yMjYzQzE4LjcyOTMgMzMuNDAzNCAxOC43NzYxIDMzLjU5ODcgMTguNzc2MSAzMy44MTIzVjM0LjExNjlDMTguNzc2MSAzNC4zMzA1IDE4LjcyOTMgMzQuNTI1OCAxOC42MzU1IDM0LjcwMjlDMTguNTQ0NCAzNC44OCAxOC40MTE1IDM1LjAyMTkgMTguMjM3MSAzNS4xMjg3QzE4LjA2NTIgMzUuMjM1NCAxNy44NTgyIDM1LjI4ODggMTcuNjE2IDM1LjI4ODhDMTcuMzczOCAzNS4yODg4IDE3LjE2NTQgMzUuMjM1NCAxNi45OTEgMzUuMTI4N0MxNi44MTY1IDM1LjAyMTkgMTYuNjgxMSAzNC44OCAxNi41ODQ3IDM0LjcwMjlDMTYuNDkxIDM0LjUyNTggMTYuNDQ0MSAzNC4zMzA1IDE2LjQ0NDEgMzQuMTE2OVpNMTYuOTg3MSAzMy44MTIzVjM0LjExNjlDMTYuOTg3MSAzNC4yMzY3IDE3LjAwOTIgMzQuMzUxMyAxNy4wNTM1IDM0LjQ2MDdDMTcuMTAwMyAzNC41Njc1IDE3LjE3MDcgMzQuNjU0NyAxNy4yNjQ0IDM0LjcyMjRDMTcuMzU4MiAzNC43ODc1IDE3LjQ3NTMgMzQuODIwMSAxNy42MTYgMzQuODIwMUMxNy43NTY2IDM0LjgyMDEgMTcuODcyNSAzNC43ODc1IDE3Ljk2MzYgMzQuNzIyNEMxOC4wNTc0IDM0LjY1NDcgMTguMTI2NCAzNC41Njc1IDE4LjE3MDcgMzQuNDYwN0MxOC4yMTQ5IDM0LjM1MzkgMTguMjM3MSAzNC4yMzkzIDE4LjIzNzEgMzQuMTE2OVYzMy44MTIzQzE4LjIzNzEgMzMuNjg5OSAxOC4yMTM2IDMzLjU3NTMgMTguMTY2OCAzMy40Njg1QzE4LjEyMjUgMzMuMzYxNyAxOC4wNTM1IDMzLjI3NTggMTcuOTU5NyAzMy4yMTA3QzE3Ljg2ODYgMzMuMTQzIDE3Ljc1MTQgMzMuMTA5MSAxNy42MDgyIDMzLjEwOTFDMTcuNDcwMSAzMy4xMDkxIDE3LjM1NDMgMzMuMTQzIDE3LjI2MDUgMzMuMjEwN0MxNy4xNjk0IDMzLjI3NTggMTcuMTAwMyAzMy4zNjE3IDE3LjA1MzUgMzMuNDY4NUMxNy4wMDkyIDMzLjU3NTMgMTYuOTg3MSAzMy42ODk5IDE2Ljk4NzEgMzMuODEyM1pNMTcuNzg3OCAzMC4zMjc5TDE1LjAxMDUgMzQuNzczMkwxNC42MDQzIDM0LjUxNTRMMTcuMzgxNiAzMC4wNzAxTDE3Ljc4NzggMzAuMzI3OVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTcuMjQ2MDkgNTcuNzE4M0g3LjMwODU5VjU4LjMzMTVINy4yNDYwOUM2Ljg2MzI4IDU4LjMzMTUgNi41NDI5NyA1OC4zOTQgNi4yODUxNiA1OC41MTlDNi4wMjczNCA1OC42NDE0IDUuODIyOTIgNTguODA2OCA1LjY3MTg4IDU5LjAxNTFDNS41MjA4MyA1OS4yMjA5IDUuNDExNDYgNTkuNDUyNiA1LjM0Mzc1IDU5LjcxMDRDNS4yNzg2NSA1OS45NjgzIDUuMjQ2MDkgNjAuMjMgNS4yNDYwOSA2MC40OTU2VjYxLjMzMTVDNS4yNDYwOSA2MS41ODQxIDUuMjc2MDQgNjEuODA4MSA1LjMzNTk0IDYyLjAwMzRDNS4zOTU4MyA2Mi4xOTYxIDUuNDc3ODYgNjIuMzU4OSA1LjU4MjAzIDYyLjQ5MTdDNS42ODYyIDYyLjYyNDUgNS44MDMzOSA2Mi43MjQ4IDUuOTMzNTkgNjIuNzkyNUM2LjA2NjQxIDYyLjg2MDIgNi4yMDQ0MyA2Mi44OTQgNi4zNDc2NiA2Mi44OTRDNi41MTQzMiA2Mi44OTQgNi42NjI3NiA2Mi44NjI4IDYuNzkyOTcgNjIuODAwM0M2LjkyMzE4IDYyLjczNTIgNy4wMzI1NSA2Mi42NDUzIDcuMTIxMDkgNjIuNTMwOEM3LjIxMjI0IDYyLjQxMzYgNy4yODEyNSA2Mi4yNzU2IDcuMzI4MTIgNjIuMTE2N0M3LjM3NSA2MS45NTc4IDcuMzk4NDQgNjEuNzgzNCA3LjM5ODQ0IDYxLjU5MzNDNy4zOTg0NCA2MS40MjQgNy4zNzc2IDYxLjI2MTIgNy4zMzU5NCA2MS4xMDVDNy4yOTQyNyA2MC45NDYxIDcuMjMwNDcgNjAuODA1NSA3LjE0NDUzIDYwLjY4MzFDNy4wNTg1OSA2MC41NTgxIDYuOTUwNTIgNjAuNDYwNCA2LjgyMDMxIDYwLjM5MDFDNi42OTI3MSA2MC4zMTcyIDYuNTQwMzYgNjAuMjgwOCA2LjM2MzI4IDYwLjI4MDhDNi4xNjI3NiA2MC4yODA4IDUuOTc1MjYgNjAuMzMwMiA1LjgwMDc4IDYwLjQyOTJDNS42Mjg5MSA2MC41MjU2IDUuNDg2OTggNjAuNjUzMiA1LjM3NSA2MC44MTJDNS4yNjU2MiA2MC45NjgzIDUuMjAzMTIgNjEuMTM4OCA1LjE4NzUgNjEuMzIzN0w0LjgwNDY5IDYxLjMxOThDNC44NDExNSA2MS4wMjgyIDQuOTA4ODUgNjAuNzc5NSA1LjAwNzgxIDYwLjU3MzdDNS4xMDkzOCA2MC4zNjU0IDUuMjM0MzggNjAuMTk2MSA1LjM4MjgxIDYwLjA2NTlDNS41MzM4NSA1OS45MzMxIDUuNzAxODIgNTkuODM2OCA1Ljg4NjcyIDU5Ljc3NjlDNi4wNzQyMiA1OS43MTQ0IDYuMjcyMTQgNTkuNjgzMSA2LjQ4MDQ3IDU5LjY4MzFDNi43NjQzMiA1OS42ODMxIDcuMDA5MTEgNTkuNzM2NSA3LjIxNDg0IDU5Ljg0MzNDNy40MjA1NyA1OS45NSA3LjU4OTg0IDYwLjA5MzMgNy43MjI2NiA2MC4yNzI5QzcuODU1NDcgNjAuNDUgNy45NTMxMiA2MC42NTA2IDguMDE1NjIgNjAuODc0NUM4LjA4MDczIDYxLjA5NTkgOC4xMTMyOCA2MS4zMjM3IDguMTEzMjggNjEuNTU4MUM4LjExMzI4IDYxLjgyNjMgOC4wNzU1MiA2Mi4wNzc2IDggNjIuMzEyQzcuOTI0NDggNjIuNTQ2NCA3LjgxMTIgNjIuNzUyMSA3LjY2MDE2IDYyLjkyOTJDNy41MTE3MiA2My4xMDYzIDcuMzI4MTIgNjMuMjQ0MyA3LjEwOTM4IDYzLjM0MzNDNi44OTA2MiA2My40NDIyIDYuNjM2NzIgNjMuNDkxNyA2LjM0NzY2IDYzLjQ5MTdDNi4wNDAzNiA2My40OTE3IDUuNzcyMTQgNjMuNDI5MiA1LjU0Mjk3IDYzLjMwNDJDNS4zMTM4IDYzLjE3NjYgNS4xMjM3IDYzLjAwNzMgNC45NzI2NiA2Mi43OTY0QzQuODIxNjEgNjIuNTg1NCA0LjcwODMzIDYyLjM1MTEgNC42MzI4MSA2Mi4wOTMzQzQuNTU3MjkgNjEuODM1NCA0LjUxOTUzIDYxLjU3MzcgNC41MTk1MyA2MS4zMDgxVjYwLjk2ODNDNC41MTk1MyA2MC41NjcyIDQuNTU5OSA2MC4xNzQgNC42NDA2MiA1OS43ODg2QzQuNzIxMzUgNTkuNDAzMiA0Ljg2MDY4IDU5LjA1NDIgNS4wNTg1OSA1OC43NDE3QzUuMjU5MTEgNTguNDI5MiA1LjUzNjQ2IDU4LjE4MDUgNS44OTA2MiA1Ny45OTU2QzYuMjQ0NzkgNTcuODEwNyA2LjY5NjYxIDU3LjcxODMgNy4yNDYwOSA1Ny43MTgzWk0xMi42NzUyIDYwLjExNjdWNjAuOTgzOUMxMi42NzUyIDYxLjQ1IDEyLjYzMzUgNjEuODQzMyAxMi41NTAyIDYyLjE2MzZDMTIuNDY2OCA2Mi40ODM5IDEyLjM0NyA2Mi43NDE3IDEyLjE5MDggNjIuOTM3QzEyLjAzNDUgNjMuMTMyMyAxMS44NDU3IDYzLjI3NDMgMTEuNjI0NCA2My4zNjI4QzExLjQwNTYgNjMuNDQ4NyAxMS4xNTgyIDYzLjQ5MTcgMTAuODgyMiA2My40OTE3QzEwLjY2MzUgNjMuNDkxNyAxMC40NjE2IDYzLjQ2NDQgMTAuMjc2NyA2My40MDk3QzEwLjA5MTggNjMuMzU1IDkuOTI1MTcgNjMuMjY3NyA5Ljc3NjczIDYzLjE0NzlDOS42MzA5IDYzLjAyNTYgOS41MDU5IDYyLjg2NjcgOS40MDE3MyA2Mi42NzE0QzkuMjk3NTcgNjIuNDc2MSA5LjIxODE0IDYyLjIzOTEgOS4xNjM0NSA2MS45NjA0QzkuMTA4NzcgNjEuNjgxOCA5LjA4MTQyIDYxLjM1NjMgOS4wODE0MiA2MC45ODM5VjYwLjExNjdDOS4wODE0MiA1OS42NTA2IDkuMTIzMDkgNTkuMjU5OSA5LjIwNjQyIDU4Ljk0NDhDOS4yOTIzNiA1OC42Mjk3IDkuNDEzNDUgNTguMzc3MSA5LjU2OTcgNTguMTg3QzkuNzI1OTUgNTcuOTk0MyA5LjkxMzQ1IDU3Ljg1NjMgMTAuMTMyMiA1Ny43NzI5QzEwLjM1MzYgNTcuNjg5NiAxMC42MDEgNTcuNjQ3OSAxMC44NzQ0IDU3LjY0NzlDMTEuMDk1NyA1Ny42NDc5IDExLjI5ODkgNTcuNjc1MyAxMS40ODM4IDU3LjczQzExLjY3MTMgNTcuNzgyMSAxMS44Mzc5IDU3Ljg2NjcgMTEuOTgzOCA1Ny45ODM5QzEyLjEyOTYgNTguMDk4NSAxMi4yNTMzIDU4LjI1MjEgMTIuMzU0OSA1OC40NDQ4QzEyLjQ1OSA1OC42MzQ5IDEyLjUzODUgNTguODY4IDEyLjU5MzEgNTkuMTQ0QzEyLjY0NzggNTkuNDIwMSAxMi42NzUyIDU5Ljc0NDMgMTIuNjc1MiA2MC4xMTY3Wk0xMS45NDg2IDYxLjEwMTFWNTkuOTk1NkMxMS45NDg2IDU5Ljc0MDQgMTEuOTMzIDU5LjUxNjQgMTEuOTAxNyA1OS4zMjM3QzExLjg3MzEgNTkuMTI4NCAxMS44MzAxIDU4Ljk2MTggMTEuNzcyOCA1OC44MjM3QzExLjcxNTUgNTguNjg1NyAxMS42NDI2IDU4LjU3MzcgMTEuNTU0MSA1OC40ODc4QzExLjQ2ODEgNTguNDAxOSAxMS4zNjc5IDU4LjMzOTQgMTEuMjUzMyA1OC4zMDAzQzExLjE0MTMgNTguMjU4NiAxMS4wMTUgNTguMjM3OCAxMC44NzQ0IDU4LjIzNzhDMTAuNzAyNSA1OC4yMzc4IDEwLjU1MDIgNTguMjcwMyAxMC40MTc0IDU4LjMzNTRDMTAuMjg0NSA1OC4zOTc5IDEwLjE3MjYgNTguNDk4MiAxMC4wODE0IDU4LjYzNjJDOS45OTI4OCA1OC43NzQzIDkuOTI1MTcgNTguOTU1MiA5Ljg3ODMgNTkuMTc5MkM5LjgzMTQyIDU5LjQwMzIgOS44MDc5OCA1OS42NzUzIDkuODA3OTggNTkuOTk1NlY2MS4xMDExQzkuODA3OTggNjEuMzU2MyA5LjgyMjMxIDYxLjU4MTUgOS44NTA5NSA2MS43NzY5QzkuODgyMiA2MS45NzIyIDkuOTI3NzggNjIuMTQxNCA5Ljk4NzY3IDYyLjI4NDdDMTAuMDQ3NiA2Mi40MjUzIDEwLjEyMDUgNjIuNTQxMiAxMC4yMDY0IDYyLjYzMjNDMTAuMjkyNCA2Mi43MjM1IDEwLjM5MTMgNjIuNzkxMiAxMC41MDMzIDYyLjgzNTRDMTAuNjE3OSA2Mi44NzcxIDEwLjc0NDIgNjIuODk3OSAxMC44ODIyIDYyLjg5NzlDMTEuMDU5MyA2Mi44OTc5IDExLjIxNDIgNjIuODY0MSAxMS4zNDcgNjIuNzk2NEMxMS40Nzk5IDYyLjcyODcgMTEuNTkwNSA2Mi42MjMyIDExLjY3OTEgNjIuNDhDMTEuNzcwMiA2Mi4zMzQxIDExLjgzNzkgNjIuMTQ3OSAxMS44ODIyIDYxLjkyMTRDMTEuOTI2NSA2MS42OTIyIDExLjk0ODYgNjEuNDE4OCAxMS45NDg2IDYxLjEwMTFaTTEzLjY3NDYgNTkuMTIwNlY1OC44MTk4QzEzLjY3NDYgNTguNjAzNyAxMy43MjE0IDU4LjQwNzEgMTMuODE1MiA1OC4yM0MxMy45MDg5IDU4LjA1MjkgMTQuMDQzMSA1Ny45MTEgMTQuMjE3NSA1Ny44MDQyQzE0LjM5MiA1Ny42OTc0IDE0LjU5OSA1Ny42NDQgMTQuODM4NiA1Ny42NDRDMTUuMDgzNCA1Ny42NDQgMTUuMjkxOCA1Ny42OTc0IDE1LjQ2MzYgNTcuODA0MkMxNS42MzgxIDU3LjkxMSAxNS43NzIyIDU4LjA1MjkgMTUuODY2IDU4LjIzQzE1Ljk1OTcgNTguNDA3MSAxNi4wMDY2IDU4LjYwMzcgMTYuMDA2NiA1OC44MTk4VjU5LjEyMDZDMTYuMDA2NiA1OS4zMzE1IDE1Ljk1OTcgNTkuNTI1NiAxNS44NjYgNTkuNzAyNkMxNS43NzQ4IDU5Ljg3OTcgMTUuNjQyIDYwLjAyMTYgMTUuNDY3NSA2MC4xMjg0QzE1LjI5NTcgNjAuMjM1MiAxNS4wODg2IDYwLjI4ODYgMTQuODQ2NCA2MC4yODg2QzE0LjYwNDMgNjAuMjg4NiAxNC4zOTQ2IDYwLjIzNTIgMTQuMjE3NSA2MC4xMjg0QzE0LjA0MzEgNjAuMDIxNiAxMy45MDg5IDU5Ljg3OTcgMTMuODE1MiA1OS43MDI2QzEzLjcyMTQgNTkuNTI1NiAxMy42NzQ2IDU5LjMzMTUgMTMuNjc0NiA1OS4xMjA2Wk0xNC4yMTc1IDU4LjgxOThWNTkuMTIwNkMxNC4yMTc1IDU5LjI0MDQgMTQuMjM5NyA1OS4zNTM3IDE0LjI4MzkgNTkuNDYwNEMxNC4zMzA4IDU5LjU2NzIgMTQuNDAxMSA1OS42NTQ1IDE0LjQ5NDkgNTkuNzIyMkMxNC41ODg2IDU5Ljc4NzMgMTQuNzA1OCA1OS44MTk4IDE0Ljg0NjQgNTkuODE5OEMxNC45ODcxIDU5LjgxOTggMTUuMTAyOSA1OS43ODczIDE1LjE5NDEgNTkuNzIyMkMxNS4yODUyIDU5LjY1NDUgMTUuMzUyOSA1OS41NjcyIDE1LjM5NzIgNTkuNDYwNEMxNS40NDE1IDU5LjM1MzcgMTUuNDYzNiA1OS4yNDA0IDE1LjQ2MzYgNTkuMTIwNlY1OC44MTk4QzE1LjQ2MzYgNTguNjk3NCAxNS40NDAyIDU4LjU4MjggMTUuMzkzMyA1OC40NzYxQzE1LjM0OSA1OC4zNjY3IDE1LjI4IDU4LjI3OTUgMTUuMTg2MyA1OC4yMTQ0QzE1LjA5NTEgNTguMTQ2NiAxNC45NzkzIDU4LjExMjggMTQuODM4NiA1OC4xMTI4QzE0LjcwMDYgNTguMTEyOCAxNC41ODQ3IDU4LjE0NjYgMTQuNDkxIDU4LjIxNDRDMTQuMzk5OCA1OC4yNzk1IDE0LjMzMDggNTguMzY2NyAxNC4yODM5IDU4LjQ3NjFDMTQuMjM5NyA1OC41ODI4IDE0LjIxNzUgNTguNjk3NCAxNC4yMTc1IDU4LjgxOThaTTE2LjQ0NDEgNjIuMzIzN1Y2Mi4wMTlDMTYuNDQ0MSA2MS44MDU1IDE2LjQ5MSA2MS42MTAyIDE2LjU4NDcgNjEuNDMzMUMxNi42Nzg1IDYxLjI1NiAxNi44MTI2IDYxLjExNDEgMTYuOTg3MSA2MS4wMDczQzE3LjE2MTUgNjAuOTAwNiAxNy4zNjg2IDYwLjg0NzIgMTcuNjA4MiA2MC44NDcyQzE3Ljg1MjkgNjAuODQ3MiAxOC4wNjEzIDYwLjkwMDYgMTguMjMzMiA2MS4wMDczQzE4LjQwNzYgNjEuMTE0MSAxOC41NDE4IDYxLjI1NiAxOC42MzU1IDYxLjQzMzFDMTguNzI5MyA2MS42MTAyIDE4Ljc3NjEgNjEuODA1NSAxOC43NzYxIDYyLjAxOVY2Mi4zMjM3QzE4Ljc3NjEgNjIuNTM3MyAxOC43MjkzIDYyLjczMjYgMTguNjM1NSA2Mi45MDk3QzE4LjU0NDQgNjMuMDg2OCAxOC40MTE1IDYzLjIyODcgMTguMjM3MSA2My4zMzU0QzE4LjA2NTIgNjMuNDQyMiAxNy44NTgyIDYzLjQ5NTYgMTcuNjE2IDYzLjQ5NTZDMTcuMzczOCA2My40OTU2IDE3LjE2NTQgNjMuNDQyMiAxNi45OTEgNjMuMzM1NEMxNi44MTY1IDYzLjIyODcgMTYuNjgxMSA2My4wODY4IDE2LjU4NDcgNjIuOTA5N0MxNi40OTEgNjIuNzMyNiAxNi40NDQxIDYyLjUzNzMgMTYuNDQ0MSA2Mi4zMjM3Wk0xNi45ODcxIDYyLjAxOVY2Mi4zMjM3QzE2Ljk4NzEgNjIuNDQzNSAxNy4wMDkyIDYyLjU1ODEgMTcuMDUzNSA2Mi42Njc1QzE3LjEwMDMgNjIuNzc0MyAxNy4xNzA3IDYyLjg2MTUgMTcuMjY0NCA2Mi45MjkyQzE3LjM1ODIgNjIuOTk0MyAxNy40NzUzIDYzLjAyNjkgMTcuNjE2IDYzLjAyNjlDMTcuNzU2NiA2My4wMjY5IDE3Ljg3MjUgNjIuOTk0MyAxNy45NjM2IDYyLjkyOTJDMTguMDU3NCA2Mi44NjE1IDE4LjEyNjQgNjIuNzc0MyAxOC4xNzA3IDYyLjY2NzVDMTguMjE0OSA2Mi41NjA3IDE4LjIzNzEgNjIuNDQ2MSAxOC4yMzcxIDYyLjMyMzdWNjIuMDE5QzE4LjIzNzEgNjEuODk2NiAxOC4yMTM2IDYxLjc4MjEgMTguMTY2OCA2MS42NzUzQzE4LjEyMjUgNjEuNTY4NSAxOC4wNTM1IDYxLjQ4MjYgMTcuOTU5NyA2MS40MTc1QzE3Ljg2ODYgNjEuMzQ5OCAxNy43NTE0IDYxLjMxNTkgMTcuNjA4MiA2MS4zMTU5QzE3LjQ3MDEgNjEuMzE1OSAxNy4zNTQzIDYxLjM0OTggMTcuMjYwNSA2MS40MTc1QzE3LjE2OTQgNjEuNDgyNiAxNy4xMDAzIDYxLjU2ODUgMTcuMDUzNSA2MS42NzUzQzE3LjAwOTIgNjEuNzgyMSAxNi45ODcxIDYxLjg5NjYgMTYuOTg3MSA2Mi4wMTlaTTE3Ljc4NzggNTguNTM0N0wxNS4wMTA1IDYyLjk4TDE0LjYwNDMgNjIuNzIyMkwxNy4zODE2IDU4LjI3NjlMMTcuNzg3OCA1OC41MzQ3WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNOC4zMTY0MSA4OS43MDYzVjkwLjNINC4yMDcwM1Y4OS44NzQzTDYuNzUzOTEgODUuOTMyOUg3LjM0Mzc1TDYuNzEwOTQgODcuMDczNUw1LjAyNzM0IDg5LjcwNjNIOC4zMTY0MVpNNy41MjM0NCA4NS45MzI5VjkxLjYyMDRINi44MDA3OFY4NS45MzI5SDcuNTIzNDRaTTEyLjY3NTIgODguMzIzNVY4OS4xOTA3QzEyLjY3NTIgODkuNjU2OCAxMi42MzM1IDkwLjA1IDEyLjU1MDIgOTAuMzcwNEMxMi40NjY4IDkwLjY5MDcgMTIuMzQ3IDkwLjk0ODUgMTIuMTkwOCA5MS4xNDM4QzEyLjAzNDUgOTEuMzM5MSAxMS44NDU3IDkxLjQ4MSAxMS42MjQ0IDkxLjU2OTZDMTEuNDA1NiA5MS42NTU1IDExLjE1ODIgOTEuNjk4NSAxMC44ODIyIDkxLjY5ODVDMTAuNjYzNSA5MS42OTg1IDEwLjQ2MTYgOTEuNjcxMSAxMC4yNzY3IDkxLjYxNjVDMTAuMDkxOCA5MS41NjE4IDkuOTI1MTcgOTEuNDc0NSA5Ljc3NjczIDkxLjM1NDdDOS42MzA5IDkxLjIzMjMgOS41MDU5IDkxLjA3MzUgOS40MDE3MyA5MC44NzgyQzkuMjk3NTcgOTAuNjgyOSA5LjIxODE0IDkwLjQ0NTkgOS4xNjM0NSA5MC4xNjcyQzkuMTA4NzcgODkuODg4NiA5LjA4MTQyIDg5LjU2MzEgOS4wODE0MiA4OS4xOTA3Vjg4LjMyMzVDOS4wODE0MiA4Ny44NTczIDkuMTIzMDkgODcuNDY2NyA5LjIwNjQyIDg3LjE1MTZDOS4yOTIzNiA4Ni44MzY1IDkuNDEzNDUgODYuNTgzOSA5LjU2OTcgODYuMzkzOEM5LjcyNTk1IDg2LjIwMTEgOS45MTM0NSA4Ni4wNjMxIDEwLjEzMjIgODUuOTc5N0MxMC4zNTM2IDg1Ljg5NjQgMTAuNjAxIDg1Ljg1NDcgMTAuODc0NCA4NS44NTQ3QzExLjA5NTcgODUuODU0NyAxMS4yOTg5IDg1Ljg4MjEgMTEuNDgzOCA4NS45MzY4QzExLjY3MTMgODUuOTg4OSAxMS44Mzc5IDg2LjA3MzUgMTEuOTgzOCA4Ni4xOTA3QzEyLjEyOTYgODYuMzA1MyAxMi4yNTMzIDg2LjQ1ODkgMTIuMzU0OSA4Ni42NTE2QzEyLjQ1OSA4Ni44NDE3IDEyLjUzODUgODcuMDc0OCAxMi41OTMxIDg3LjM1MDhDMTIuNjQ3OCA4Ny42MjY5IDEyLjY3NTIgODcuOTUxMSAxMi42NzUyIDg4LjMyMzVaTTExLjk0ODYgODkuMzA3OVY4OC4yMDI0QzExLjk0ODYgODcuOTQ3MiAxMS45MzMgODcuNzIzMiAxMS45MDE3IDg3LjUzMDVDMTEuODczMSA4Ny4zMzUyIDExLjgzMDEgODcuMTY4NSAxMS43NzI4IDg3LjAzMDVDMTEuNzE1NSA4Ni44OTI1IDExLjY0MjYgODYuNzgwNSAxMS41NTQxIDg2LjY5NDZDMTEuNDY4MSA4Ni42MDg2IDExLjM2NzkgODYuNTQ2MSAxMS4yNTMzIDg2LjUwNzFDMTEuMTQxMyA4Ni40NjU0IDExLjAxNSA4Ni40NDQ2IDEwLjg3NDQgODYuNDQ0NkMxMC43MDI1IDg2LjQ0NDYgMTAuNTUwMiA4Ni40NzcxIDEwLjQxNzQgODYuNTQyMkMxMC4yODQ1IDg2LjYwNDcgMTAuMTcyNiA4Ni43MDUgMTAuMDgxNCA4Ni44NDNDOS45OTI4OCA4Ni45ODEgOS45MjUxNyA4Ny4xNjIgOS44NzgzIDg3LjM4NkM5LjgzMTQyIDg3LjYwOTkgOS44MDc5OCA4Ny44ODIxIDkuODA3OTggODguMjAyNFY4OS4zMDc5QzkuODA3OTggODkuNTYzMSA5LjgyMjMxIDg5Ljc4ODMgOS44NTA5NSA4OS45ODM2QzkuODgyMiA5MC4xNzkgOS45Mjc3OCA5MC4zNDgyIDkuOTg3NjcgOTAuNDkxNUMxMC4wNDc2IDkwLjYzMjEgMTAuMTIwNSA5MC43NDggMTAuMjA2NCA5MC44MzkxQzEwLjI5MjQgOTAuOTMwMyAxMC4zOTEzIDkwLjk5OCAxMC41MDMzIDkxLjA0MjJDMTAuNjE3OSA5MS4wODM5IDEwLjc0NDIgOTEuMTA0NyAxMC44ODIyIDkxLjEwNDdDMTEuMDU5MyA5MS4xMDQ3IDExLjIxNDIgOTEuMDcwOSAxMS4zNDcgOTEuMDAzMkMxMS40Nzk5IDkwLjkzNTUgMTEuNTkwNSA5MC44MyAxMS42NzkxIDkwLjY4NjhDMTEuNzcwMiA5MC41NDA5IDExLjgzNzkgOTAuMzU0NyAxMS44ODIyIDkwLjEyODJDMTEuOTI2NSA4OS44OTkgMTEuOTQ4NiA4OS42MjU2IDExLjk0ODYgODkuMzA3OVpNMTMuNjc0NiA4Ny4zMjc0Vjg3LjAyNjZDMTMuNjc0NiA4Ni44MTA1IDEzLjcyMTQgODYuNjEzOSAxMy44MTUyIDg2LjQzNjhDMTMuOTA4OSA4Ni4yNTk3IDE0LjA0MzEgODYuMTE3OCAxNC4yMTc1IDg2LjAxMUMxNC4zOTIgODUuOTA0MiAxNC41OTkgODUuODUwOCAxNC44Mzg2IDg1Ljg1MDhDMTUuMDgzNCA4NS44NTA4IDE1LjI5MTggODUuOTA0MiAxNS40NjM2IDg2LjAxMUMxNS42MzgxIDg2LjExNzggMTUuNzcyMiA4Ni4yNTk3IDE1Ljg2NiA4Ni40MzY4QzE1Ljk1OTcgODYuNjEzOSAxNi4wMDY2IDg2LjgxMDUgMTYuMDA2NiA4Ny4wMjY2Vjg3LjMyNzRDMTYuMDA2NiA4Ny41MzgzIDE1Ljk1OTcgODcuNzMyMyAxNS44NjYgODcuOTA5NEMxNS43NzQ4IDg4LjA4NjUgMTUuNjQyIDg4LjIyODQgMTUuNDY3NSA4OC4zMzUyQzE1LjI5NTcgODguNDQyIDE1LjA4ODYgODguNDk1NCAxNC44NDY0IDg4LjQ5NTRDMTQuNjA0MyA4OC40OTU0IDE0LjM5NDYgODguNDQyIDE0LjIxNzUgODguMzM1MkMxNC4wNDMxIDg4LjIyODQgMTMuOTA4OSA4OC4wODY1IDEzLjgxNTIgODcuOTA5NEMxMy43MjE0IDg3LjczMjMgMTMuNjc0NiA4Ny41MzgzIDEzLjY3NDYgODcuMzI3NFpNMTQuMjE3NSA4Ny4wMjY2Vjg3LjMyNzRDMTQuMjE3NSA4Ny40NDcyIDE0LjIzOTcgODcuNTYwNSAxNC4yODM5IDg3LjY2NzJDMTQuMzMwOCA4Ny43NzQgMTQuNDAxMSA4Ny44NjEyIDE0LjQ5NDkgODcuOTI5QzE0LjU4ODYgODcuOTk0MSAxNC43MDU4IDg4LjAyNjYgMTQuODQ2NCA4OC4wMjY2QzE0Ljk4NzEgODguMDI2NiAxNS4xMDI5IDg3Ljk5NDEgMTUuMTk0MSA4Ny45MjlDMTUuMjg1MiA4Ny44NjEyIDE1LjM1MjkgODcuNzc0IDE1LjM5NzIgODcuNjY3MkMxNS40NDE1IDg3LjU2MDUgMTUuNDYzNiA4Ny40NDcyIDE1LjQ2MzYgODcuMzI3NFY4Ny4wMjY2QzE1LjQ2MzYgODYuOTA0MiAxNS40NDAyIDg2Ljc4OTYgMTUuMzkzMyA4Ni42ODI5QzE1LjM0OSA4Ni41NzM1IDE1LjI4IDg2LjQ4NjIgMTUuMTg2MyA4Ni40MjExQzE1LjA5NTEgODYuMzUzNCAxNC45NzkzIDg2LjMxOTYgMTQuODM4NiA4Ni4zMTk2QzE0LjcwMDYgODYuMzE5NiAxNC41ODQ3IDg2LjM1MzQgMTQuNDkxIDg2LjQyMTFDMTQuMzk5OCA4Ni40ODYyIDE0LjMzMDggODYuNTczNSAxNC4yODM5IDg2LjY4MjlDMTQuMjM5NyA4Ni43ODk2IDE0LjIxNzUgODYuOTA0MiAxNC4yMTc1IDg3LjAyNjZaTTE2LjQ0NDEgOTAuNTMwNVY5MC4yMjU4QzE2LjQ0NDEgOTAuMDEyMyAxNi40OTEgODkuODE3IDE2LjU4NDcgODkuNjM5OUMxNi42Nzg1IDg5LjQ2MjggMTYuODEyNiA4OS4zMjA5IDE2Ljk4NzEgODkuMjE0MUMxNy4xNjE1IDg5LjEwNzMgMTcuMzY4NiA4OS4wNTQgMTcuNjA4MiA4OS4wNTRDMTcuODUyOSA4OS4wNTQgMTguMDYxMyA4OS4xMDczIDE4LjIzMzIgODkuMjE0MUMxOC40MDc2IDg5LjMyMDkgMTguNTQxOCA4OS40NjI4IDE4LjYzNTUgODkuNjM5OUMxOC43MjkzIDg5LjgxNyAxOC43NzYxIDkwLjAxMjMgMTguNzc2MSA5MC4yMjU4VjkwLjUzMDVDMTguNzc2MSA5MC43NDQxIDE4LjcyOTMgOTAuOTM5NCAxOC42MzU1IDkxLjExNjVDMTguNTQ0NCA5MS4yOTM1IDE4LjQxMTUgOTEuNDM1NSAxOC4yMzcxIDkxLjU0MjJDMTguMDY1MiA5MS42NDkgMTcuODU4MiA5MS43MDI0IDE3LjYxNiA5MS43MDI0QzE3LjM3MzggOTEuNzAyNCAxNy4xNjU0IDkxLjY0OSAxNi45OTEgOTEuNTQyMkMxNi44MTY1IDkxLjQzNTUgMTYuNjgxMSA5MS4yOTM1IDE2LjU4NDcgOTEuMTE2NUMxNi40OTEgOTAuOTM5NCAxNi40NDQxIDkwLjc0NDEgMTYuNDQ0MSA5MC41MzA1Wk0xNi45ODcxIDkwLjIyNThWOTAuNTMwNUMxNi45ODcxIDkwLjY1MDMgMTcuMDA5MiA5MC43NjQ5IDE3LjA1MzUgOTAuODc0M0MxNy4xMDAzIDkwLjk4MSAxNy4xNzA3IDkxLjA2ODMgMTcuMjY0NCA5MS4xMzZDMTcuMzU4MiA5MS4yMDExIDE3LjQ3NTMgOTEuMjMzNiAxNy42MTYgOTEuMjMzNkMxNy43NTY2IDkxLjIzMzYgMTcuODcyNSA5MS4yMDExIDE3Ljk2MzYgOTEuMTM2QzE4LjA1NzQgOTEuMDY4MyAxOC4xMjY0IDkwLjk4MSAxOC4xNzA3IDkwLjg3NDNDMTguMjE0OSA5MC43Njc1IDE4LjIzNzEgOTAuNjUyOSAxOC4yMzcxIDkwLjUzMDVWOTAuMjI1OEMxOC4yMzcxIDkwLjEwMzQgMTguMjEzNiA4OS45ODg5IDE4LjE2NjggODkuODgyMUMxOC4xMjI1IDg5Ljc3NTMgMTguMDUzNSA4OS42ODk0IDE3Ljk1OTcgODkuNjI0M0MxNy44Njg2IDg5LjU1NjYgMTcuNzUxNCA4OS41MjI3IDE3LjYwODIgODkuNTIyN0MxNy40NzAxIDg5LjUyMjcgMTcuMzU0MyA4OS41NTY2IDE3LjI2MDUgODkuNjI0M0MxNy4xNjk0IDg5LjY4OTQgMTcuMTAwMyA4OS43NzUzIDE3LjA1MzUgODkuODgyMUMxNy4wMDkyIDg5Ljk4ODkgMTYuOTg3MSA5MC4xMDM0IDE2Ljk4NzEgOTAuMjI1OFpNMTcuNzg3OCA4Ni43NDE1TDE1LjAxMDUgOTEuMTg2OEwxNC42MDQzIDkwLjkyOUwxNy4zODE2IDg2LjQ4MzZMMTcuNzg3OCA4Ni43NDE1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNOC4xOTkyMiAxMTkuMjMzVjExOS44MjdINC40NzY1NlYxMTkuMzA4TDYuMzM5ODQgMTE3LjIzM0M2LjU2OTAxIDExNi45NzggNi43NDYwOSAxMTYuNzYyIDYuODcxMDkgMTE2LjU4NUM2Ljk5ODcgMTE2LjQwNSA3LjA4NzI0IDExNi4yNDUgNy4xMzY3MiAxMTYuMTA0QzcuMTg4OCAxMTUuOTYxIDcuMjE0ODQgMTE1LjgxNSA3LjIxNDg0IDExNS42NjdDNy4yMTQ4NCAxMTUuNDc5IDcuMTc1NzggMTE1LjMxIDcuMDk3NjYgMTE1LjE1OUM3LjAyMjE0IDExNS4wMDYgNi45MTAxNiAxMTQuODgzIDYuNzYxNzIgMTE0Ljc5MkM2LjYxMzI4IDExNC43MDEgNi40MzM1OSAxMTQuNjU1IDYuMjIyNjYgMTE0LjY1NUM1Ljk3MDA1IDExNC42NTUgNS43NTkxMSAxMTQuNzA1IDUuNTg5ODQgMTE0LjgwNEM1LjQyMzE4IDExNC45IDUuMjk4MTggMTE1LjAzNSA1LjIxNDg0IDExNS4yMUM1LjEzMTUxIDExNS4zODQgNS4wODk4NCAxMTUuNTg1IDUuMDg5ODQgMTE1LjgxMkg0LjM2NzE5QzQuMzY3MTkgMTE1LjQ5MSA0LjQzNzUgMTE1LjE5OCA0LjU3ODEyIDExNC45MzNDNC43MTg3NSAxMTQuNjY3IDQuOTI3MDggMTE0LjQ1NiA1LjIwMzEyIDExNC4zQzUuNDc5MTcgMTE0LjE0MSA1LjgxOTAxIDExNC4wNjIgNi4yMjI2NiAxMTQuMDYyQzYuNTgyMDMgMTE0LjA2MiA2Ljg4OTMyIDExNC4xMjUgNy4xNDQ1MyAxMTQuMjUzQzcuMzk5NzQgMTE0LjM3OCA3LjU5NTA1IDExNC41NTUgNy43MzA0NyAxMTQuNzg0QzcuODY4NDkgMTE1LjAxMSA3LjkzNzUgMTE1LjI3NiA3LjkzNzUgMTE1LjU4MUM3LjkzNzUgMTE1Ljc0OCA3LjkwODg1IDExNS45MTcgNy44NTE1NiAxMTYuMDg5QzcuNzk2ODggMTE2LjI1OCA3LjcyMDA1IDExNi40MjcgNy42MjEwOSAxMTYuNTk3QzcuNTI0NzQgMTE2Ljc2NiA3LjQxMTQ2IDExNi45MzMgNy4yODEyNSAxMTcuMDk3QzcuMTUzNjUgMTE3LjI2MSA3LjAxNjkzIDExNy40MjIgNi44NzEwOSAxMTcuNTgxTDUuMzQ3NjYgMTE5LjIzM0g4LjE5OTIyWk0xMi42NzUyIDExNi41M1YxMTcuMzk3QzEyLjY3NTIgMTE3Ljg2NCAxMi42MzM1IDExOC4yNTcgMTIuNTUwMiAxMTguNTc3QzEyLjQ2NjggMTE4Ljg5NyAxMi4zNDcgMTE5LjE1NSAxMi4xOTA4IDExOS4zNTFDMTIuMDM0NSAxMTkuNTQ2IDExLjg0NTcgMTE5LjY4OCAxMS42MjQ0IDExOS43NzZDMTEuNDA1NiAxMTkuODYyIDExLjE1ODIgMTE5LjkwNSAxMC44ODIyIDExOS45MDVDMTAuNjYzNSAxMTkuOTA1IDEwLjQ2MTYgMTE5Ljg3OCAxMC4yNzY3IDExOS44MjNDMTAuMDkxOCAxMTkuNzY5IDkuOTI1MTcgMTE5LjY4MSA5Ljc3NjczIDExOS41NjJDOS42MzA5IDExOS40MzkgOS41MDU5IDExOS4yOCA5LjQwMTczIDExOS4wODVDOS4yOTc1NyAxMTguODkgOS4yMTgxNCAxMTguNjUzIDkuMTYzNDUgMTE4LjM3NEM5LjEwODc3IDExOC4wOTUgOS4wODE0MiAxMTcuNzcgOS4wODE0MiAxMTcuMzk3VjExNi41M0M5LjA4MTQyIDExNi4wNjQgOS4xMjMwOSAxMTUuNjc0IDkuMjA2NDIgMTE1LjM1OEM5LjI5MjM2IDExNS4wNDMgOS40MTM0NSAxMTQuNzkxIDkuNTY5NyAxMTQuNjAxQzkuNzI1OTUgMTE0LjQwOCA5LjkxMzQ1IDExNC4yNyAxMC4xMzIyIDExNC4xODdDMTAuMzUzNiAxMTQuMTAzIDEwLjYwMSAxMTQuMDYyIDEwLjg3NDQgMTE0LjA2MkMxMS4wOTU3IDExNC4wNjIgMTEuMjk4OSAxMTQuMDg5IDExLjQ4MzggMTE0LjE0NEMxMS42NzEzIDExNC4xOTYgMTEuODM3OSAxMTQuMjggMTEuOTgzOCAxMTQuMzk3QzEyLjEyOTYgMTE0LjUxMiAxMi4yNTMzIDExNC42NjYgMTIuMzU0OSAxMTQuODU4QzEyLjQ1OSAxMTUuMDQ5IDEyLjUzODUgMTE1LjI4MiAxMi41OTMxIDExNS41NThDMTIuNjQ3OCAxMTUuODM0IDEyLjY3NTIgMTE2LjE1OCAxMi42NzUyIDExNi41M1pNMTEuOTQ4NiAxMTcuNTE1VjExNi40MDlDMTEuOTQ4NiAxMTYuMTU0IDExLjkzMyAxMTUuOTMgMTEuOTAxNyAxMTUuNzM3QzExLjg3MzEgMTE1LjU0MiAxMS44MzAxIDExNS4zNzUgMTEuNzcyOCAxMTUuMjM3QzExLjcxNTUgMTE1LjA5OSAxMS42NDI2IDExNC45ODcgMTEuNTU0MSAxMTQuOTAxQzExLjQ2ODEgMTE0LjgxNSAxMS4zNjc5IDExNC43NTMgMTEuMjUzMyAxMTQuNzE0QzExLjE0MTMgMTE0LjY3MiAxMS4wMTUgMTE0LjY1MSAxMC44NzQ0IDExNC42NTFDMTAuNzAyNSAxMTQuNjUxIDEwLjU1MDIgMTE0LjY4NCAxMC40MTc0IDExNC43NDlDMTAuMjg0NSAxMTQuODEyIDEwLjE3MjYgMTE0LjkxMiAxMC4wODE0IDExNS4wNUM5Ljk5Mjg4IDExNS4xODggOS45MjUxNyAxMTUuMzY5IDkuODc4MyAxMTUuNTkzQzkuODMxNDIgMTE1LjgxNyA5LjgwNzk4IDExNi4wODkgOS44MDc5OCAxMTYuNDA5VjExNy41MTVDOS44MDc5OCAxMTcuNzcgOS44MjIzMSAxMTcuOTk1IDkuODUwOTUgMTE4LjE5QzkuODgyMiAxMTguMzg2IDkuOTI3NzggMTE4LjU1NSA5Ljk4NzY3IDExOC42OThDMTAuMDQ3NiAxMTguODM5IDEwLjEyMDUgMTE4Ljk1NSAxMC4yMDY0IDExOS4wNDZDMTAuMjkyNCAxMTkuMTM3IDEwLjM5MTMgMTE5LjIwNSAxMC41MDMzIDExOS4yNDlDMTAuNjE3OSAxMTkuMjkxIDEwLjc0NDIgMTE5LjMxMiAxMC44ODIyIDExOS4zMTJDMTEuMDU5MyAxMTkuMzEyIDExLjIxNDIgMTE5LjI3OCAxMS4zNDcgMTE5LjIxQzExLjQ3OTkgMTE5LjE0MiAxMS41OTA1IDExOS4wMzcgMTEuNjc5MSAxMTguODk0QzExLjc3MDIgMTE4Ljc0OCAxMS44Mzc5IDExOC41NjIgMTEuODgyMiAxMTguMzM1QzExLjkyNjUgMTE4LjEwNiAxMS45NDg2IDExNy44MzIgMTEuOTQ4NiAxMTcuNTE1Wk0xMy42NzQ2IDExNS41MzRWMTE1LjIzM0MxMy42NzQ2IDExNS4wMTcgMTMuNzIxNCAxMTQuODIxIDEzLjgxNTIgMTE0LjY0NEMxMy45MDg5IDExNC40NjYgMTQuMDQzMSAxMTQuMzI1IDE0LjIxNzUgMTE0LjIxOEMxNC4zOTIgMTE0LjExMSAxNC41OTkgMTE0LjA1OCAxNC44Mzg2IDExNC4wNThDMTUuMDgzNCAxMTQuMDU4IDE1LjI5MTggMTE0LjExMSAxNS40NjM2IDExNC4yMThDMTUuNjM4MSAxMTQuMzI1IDE1Ljc3MjIgMTE0LjQ2NiAxNS44NjYgMTE0LjY0NEMxNS45NTk3IDExNC44MjEgMTYuMDA2NiAxMTUuMDE3IDE2LjAwNjYgMTE1LjIzM1YxMTUuNTM0QzE2LjAwNjYgMTE1Ljc0NSAxNS45NTk3IDExNS45MzkgMTUuODY2IDExNi4xMTZDMTUuNzc0OCAxMTYuMjkzIDE1LjY0MiAxMTYuNDM1IDE1LjQ2NzUgMTE2LjU0MkMxNS4yOTU3IDExNi42NDkgMTUuMDg4NiAxMTYuNzAyIDE0Ljg0NjQgMTE2LjcwMkMxNC42MDQzIDExNi43MDIgMTQuMzk0NiAxMTYuNjQ5IDE0LjIxNzUgMTE2LjU0MkMxNC4wNDMxIDExNi40MzUgMTMuOTA4OSAxMTYuMjkzIDEzLjgxNTIgMTE2LjExNkMxMy43MjE0IDExNS45MzkgMTMuNjc0NiAxMTUuNzQ1IDEzLjY3NDYgMTE1LjUzNFpNMTQuMjE3NSAxMTUuMjMzVjExNS41MzRDMTQuMjE3NSAxMTUuNjU0IDE0LjIzOTcgMTE1Ljc2NyAxNC4yODM5IDExNS44NzRDMTQuMzMwOCAxMTUuOTgxIDE0LjQwMTEgMTE2LjA2OCAxNC40OTQ5IDExNi4xMzZDMTQuNTg4NiAxMTYuMjAxIDE0LjcwNTggMTE2LjIzMyAxNC44NDY0IDExNi4yMzNDMTQuOTg3MSAxMTYuMjMzIDE1LjEwMjkgMTE2LjIwMSAxNS4xOTQxIDExNi4xMzZDMTUuMjg1MiAxMTYuMDY4IDE1LjM1MjkgMTE1Ljk4MSAxNS4zOTcyIDExNS44NzRDMTUuNDQxNSAxMTUuNzY3IDE1LjQ2MzYgMTE1LjY1NCAxNS40NjM2IDExNS41MzRWMTE1LjIzM0MxNS40NjM2IDExNS4xMTEgMTUuNDQwMiAxMTQuOTk2IDE1LjM5MzMgMTE0Ljg5QzE1LjM0OSAxMTQuNzggMTUuMjggMTE0LjY5MyAxNS4xODYzIDExNC42MjhDMTUuMDk1MSAxMTQuNTYgMTQuOTc5MyAxMTQuNTI2IDE0LjgzODYgMTE0LjUyNkMxNC43MDA2IDExNC41MjYgMTQuNTg0NyAxMTQuNTYgMTQuNDkxIDExNC42MjhDMTQuMzk5OCAxMTQuNjkzIDE0LjMzMDggMTE0Ljc4IDE0LjI4MzkgMTE0Ljg5QzE0LjIzOTcgMTE0Ljk5NiAxNC4yMTc1IDExNS4xMTEgMTQuMjE3NSAxMTUuMjMzWk0xNi40NDQxIDExOC43MzdWMTE4LjQzM0MxNi40NDQxIDExOC4yMTkgMTYuNDkxIDExOC4wMjQgMTYuNTg0NyAxMTcuODQ3QzE2LjY3ODUgMTE3LjY3IDE2LjgxMjYgMTE3LjUyOCAxNi45ODcxIDExNy40MjFDMTcuMTYxNSAxMTcuMzE0IDE3LjM2ODYgMTE3LjI2MSAxNy42MDgyIDExNy4yNjFDMTcuODUyOSAxMTcuMjYxIDE4LjA2MTMgMTE3LjMxNCAxOC4yMzMyIDExNy40MjFDMTguNDA3NiAxMTcuNTI4IDE4LjU0MTggMTE3LjY3IDE4LjYzNTUgMTE3Ljg0N0MxOC43MjkzIDExOC4wMjQgMTguNzc2MSAxMTguMjE5IDE4Ljc3NjEgMTE4LjQzM1YxMTguNzM3QzE4Ljc3NjEgMTE4Ljk1MSAxOC43MjkzIDExOS4xNDYgMTguNjM1NSAxMTkuMzIzQzE4LjU0NDQgMTE5LjUgMTguNDExNSAxMTkuNjQyIDE4LjIzNzEgMTE5Ljc0OUMxOC4wNjUyIDExOS44NTYgMTcuODU4MiAxMTkuOTA5IDE3LjYxNiAxMTkuOTA5QzE3LjM3MzggMTE5LjkwOSAxNy4xNjU0IDExOS44NTYgMTYuOTkxIDExOS43NDlDMTYuODE2NSAxMTkuNjQyIDE2LjY4MTEgMTE5LjUgMTYuNTg0NyAxMTkuMzIzQzE2LjQ5MSAxMTkuMTQ2IDE2LjQ0NDEgMTE4Ljk1MSAxNi40NDQxIDExOC43MzdaTTE2Ljk4NzEgMTE4LjQzM1YxMTguNzM3QzE2Ljk4NzEgMTE4Ljg1NyAxNy4wMDkyIDExOC45NzIgMTcuMDUzNSAxMTkuMDgxQzE3LjEwMDMgMTE5LjE4OCAxNy4xNzA3IDExOS4yNzUgMTcuMjY0NCAxMTkuMzQzQzE3LjM1ODIgMTE5LjQwOCAxNy40NzUzIDExOS40NCAxNy42MTYgMTE5LjQ0QzE3Ljc1NjYgMTE5LjQ0IDE3Ljg3MjUgMTE5LjQwOCAxNy45NjM2IDExOS4zNDNDMTguMDU3NCAxMTkuMjc1IDE4LjEyNjQgMTE5LjE4OCAxOC4xNzA3IDExOS4wODFDMTguMjE0OSAxMTguOTc0IDE4LjIzNzEgMTE4Ljg2IDE4LjIzNzEgMTE4LjczN1YxMTguNDMzQzE4LjIzNzEgMTE4LjMxIDE4LjIxMzYgMTE4LjE5NiAxOC4xNjY4IDExOC4wODlDMTguMTIyNSAxMTcuOTgyIDE4LjA1MzUgMTE3Ljg5NiAxNy45NTk3IDExNy44MzFDMTcuODY4NiAxMTcuNzYzIDE3Ljc1MTQgMTE3LjcyOSAxNy42MDgyIDExNy43MjlDMTcuNDcwMSAxMTcuNzI5IDE3LjM1NDMgMTE3Ljc2MyAxNy4yNjA1IDExNy44MzFDMTcuMTY5NCAxMTcuODk2IDE3LjEwMDMgMTE3Ljk4MiAxNy4wNTM1IDExOC4wODlDMTcuMDA5MiAxMTguMTk2IDE2Ljk4NzEgMTE4LjMxIDE2Ljk4NzEgMTE4LjQzM1pNMTcuNzg3OCAxMTQuOTQ4TDE1LjAxMDUgMTE5LjM5NEwxNC42MDQzIDExOS4xMzZMMTcuMzgxNiAxMTQuNjlMMTcuNzg3OCAxMTQuOTQ4WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNMTMuMDQzIDE0NC43MzdWMTQ1LjYwNEMxMy4wNDMgMTQ2LjA3IDEzLjAwMTMgMTQ2LjQ2NCAxMi45MTggMTQ2Ljc4NEMxMi44MzQ2IDE0Ny4xMDQgMTIuNzE0OCAxNDcuMzYyIDEyLjU1ODYgMTQ3LjU1N0MxMi40MDIzIDE0Ny43NTMgMTIuMjEzNSAxNDcuODk1IDExLjk5MjIgMTQ3Ljk4M0MxMS43NzM0IDE0OC4wNjkgMTEuNTI2IDE0OC4xMTIgMTEuMjUgMTQ4LjExMkMxMS4wMzEyIDE0OC4xMTIgMTAuODI5NCAxNDguMDg1IDEwLjY0NDUgMTQ4LjAzQzEwLjQ1OTYgMTQ3Ljk3NSAxMC4yOTMgMTQ3Ljg4OCAxMC4xNDQ1IDE0Ny43NjhDOS45OTg3IDE0Ny42NDYgOS44NzM3IDE0Ny40ODcgOS43Njk1MyAxNDcuMjkyQzkuNjY1MzYgMTQ3LjA5NiA5LjU4NTk0IDE0Ni44NTkgOS41MzEyNSAxNDYuNTgxQzkuNDc2NTYgMTQ2LjMwMiA5LjQ0OTIyIDE0NS45NzcgOS40NDkyMiAxNDUuNjA0VjE0NC43MzdDOS40NDkyMiAxNDQuMjcxIDkuNDkwODkgMTQzLjg4IDkuNTc0MjIgMTQzLjU2NUM5LjY2MDE2IDE0My4yNSA5Ljc4MTI1IDE0Mi45OTcgOS45Mzc1IDE0Mi44MDdDMTAuMDkzOCAxNDIuNjE1IDEwLjI4MTIgMTQyLjQ3NyAxMC41IDE0Mi4zOTNDMTAuNzIxNCAxNDIuMzEgMTAuOTY4OCAxNDIuMjY4IDExLjI0MjIgMTQyLjI2OEMxMS40NjM1IDE0Mi4yNjggMTEuNjY2NyAxNDIuMjk2IDExLjg1MTYgMTQyLjM1QzEyLjAzOTEgMTQyLjQwMiAxMi4yMDU3IDE0Mi40ODcgMTIuMzUxNiAxNDIuNjA0QzEyLjQ5NzQgMTQyLjcxOSAxMi42MjExIDE0Mi44NzIgMTIuNzIyNyAxNDMuMDY1QzEyLjgyNjggMTQzLjI1NSAxMi45MDYyIDE0My40ODggMTIuOTYwOSAxNDMuNzY0QzEzLjAxNTYgMTQ0LjA0IDEzLjA0MyAxNDQuMzY1IDEzLjA0MyAxNDQuNzM3Wk0xMi4zMTY0IDE0NS43MjFWMTQ0LjYxNkMxMi4zMTY0IDE0NC4zNjEgMTIuMzAwOCAxNDQuMTM3IDEyLjI2OTUgMTQzLjk0NEMxMi4yNDA5IDE0My43NDkgMTIuMTk3OSAxNDMuNTgyIDEyLjE0MDYgMTQzLjQ0NEMxMi4wODMzIDE0My4zMDYgMTIuMDEwNCAxNDMuMTk0IDExLjkyMTkgMTQzLjEwOEMxMS44MzU5IDE0My4wMjIgMTEuNzM1NyAxNDIuOTYgMTEuNjIxMSAxNDIuOTIxQzExLjUwOTEgMTQyLjg3OSAxMS4zODI4IDE0Mi44NTggMTEuMjQyMiAxNDIuODU4QzExLjA3MDMgMTQyLjg1OCAxMC45MTggMTQyLjg5MSAxMC43ODUyIDE0Mi45NTZDMTAuNjUyMyAxNDMuMDE4IDEwLjU0MDQgMTQzLjExOSAxMC40NDkyIDE0My4yNTdDMTAuMzYwNyAxNDMuMzk1IDEwLjI5MyAxNDMuNTc2IDEwLjI0NjEgMTQzLjhDMTAuMTk5MiAxNDQuMDI0IDEwLjE3NTggMTQ0LjI5NiAxMC4xNzU4IDE0NC42MTZWMTQ1LjcyMUMxMC4xNzU4IDE0NS45NzcgMTAuMTkwMSAxNDYuMjAyIDEwLjIxODggMTQ2LjM5N0MxMC4yNSAxNDYuNTkzIDEwLjI5NTYgMTQ2Ljc2MiAxMC4zNTU1IDE0Ni45MDVDMTAuNDE1NCAxNDcuMDQ2IDEwLjQ4ODMgMTQ3LjE2MiAxMC41NzQyIDE0Ny4yNTNDMTAuNjYwMiAxNDcuMzQ0IDEwLjc1OTEgMTQ3LjQxMiAxMC44NzExIDE0Ny40NTZDMTAuOTg1NyAxNDcuNDk3IDExLjExMiAxNDcuNTE4IDExLjI1IDE0Ny41MThDMTEuNDI3MSAxNDcuNTE4IDExLjU4MiAxNDcuNDg0IDExLjcxNDggMTQ3LjQxN0MxMS44NDc3IDE0Ny4zNDkgMTEuOTU4MyAxNDcuMjQ0IDEyLjA0NjkgMTQ3LjFDMTIuMTM4IDE0Ni45NTUgMTIuMjA1NyAxNDYuNzY4IDEyLjI1IDE0Ni41NDJDMTIuMjk0MyAxNDYuMzEzIDEyLjMxNjQgMTQ2LjAzOSAxMi4zMTY0IDE0NS43MjFaTTE0LjA0MjQgMTQzLjc0MVYxNDMuNDRDMTQuMDQyNCAxNDMuMjI0IDE0LjA4OTIgMTQzLjAyNyAxNC4xODMgMTQyLjg1QzE0LjI3NjcgMTQyLjY3MyAxNC40MTA4IDE0Mi41MzEgMTQuNTg1MyAxNDIuNDI1QzE0Ljc1OTggMTQyLjMxOCAxNC45NjY4IDE0Mi4yNjQgMTUuMjA2NCAxNDIuMjY0QzE1LjQ1MTIgMTQyLjI2NCAxNS42NTk1IDE0Mi4zMTggMTUuODMxNCAxNDIuNDI1QzE2LjAwNTkgMTQyLjUzMSAxNi4xNCAxNDIuNjczIDE2LjIzMzggMTQyLjg1QzE2LjMyNzUgMTQzLjAyNyAxNi4zNzQ0IDE0My4yMjQgMTYuMzc0NCAxNDMuNDRWMTQzLjc0MUMxNi4zNzQ0IDE0My45NTIgMTYuMzI3NSAxNDQuMTQ2IDE2LjIzMzggMTQ0LjMyM0MxNi4xNDI2IDE0NC41IDE2LjAwOTggMTQ0LjY0MiAxNS44MzUzIDE0NC43NDlDMTUuNjYzNSAxNDQuODU2IDE1LjQ1NjQgMTQ0LjkwOSAxNS4yMTQyIDE0NC45MDlDMTQuOTcyIDE0NC45MDkgMTQuNzYyNCAxNDQuODU2IDE0LjU4NTMgMTQ0Ljc0OUMxNC40MTA4IDE0NC42NDIgMTQuMjc2NyAxNDQuNSAxNC4xODMgMTQ0LjMyM0MxNC4wODkyIDE0NC4xNDYgMTQuMDQyNCAxNDMuOTUyIDE0LjA0MjQgMTQzLjc0MVpNMTQuNTg1MyAxNDMuNDRWMTQzLjc0MUMxNC41ODUzIDE0My44NjEgMTQuNjA3NSAxNDMuOTc0IDE0LjY1MTcgMTQ0LjA4MUMxNC42OTg2IDE0NC4xODggMTQuNzY4OSAxNDQuMjc1IDE0Ljg2MjcgMTQ0LjM0M0MxNC45NTY0IDE0NC40MDggMTUuMDczNiAxNDQuNDQgMTUuMjE0MiAxNDQuNDRDMTUuMzU0OSAxNDQuNDQgMTUuNDcwNyAxNDQuNDA4IDE1LjU2MTkgMTQ0LjM0M0MxNS42NTMgMTQ0LjI3NSAxNS43MjA3IDE0NC4xODggMTUuNzY1IDE0NC4wODFDMTUuODA5MyAxNDMuOTc0IDE1LjgzMTQgMTQzLjg2MSAxNS44MzE0IDE0My43NDFWMTQzLjQ0QzE1LjgzMTQgMTQzLjMxOCAxNS44MDggMTQzLjIwMyAxNS43NjExIDE0My4wOTZDMTUuNzE2OCAxNDIuOTg3IDE1LjY0NzggMTQyLjkgMTUuNTU0MSAxNDIuODM1QzE1LjQ2MjkgMTQyLjc2NyAxNS4zNDcgMTQyLjczMyAxNS4yMDY0IDE0Mi43MzNDMTUuMDY4NCAxNDIuNzMzIDE0Ljk1MjUgMTQyLjc2NyAxNC44NTg4IDE0Mi44MzVDMTQuNzY3NiAxNDIuOSAxNC42OTg2IDE0Mi45ODcgMTQuNjUxNyAxNDMuMDk2QzE0LjYwNzUgMTQzLjIwMyAxNC41ODUzIDE0My4zMTggMTQuNTg1MyAxNDMuNDRaTTE2LjgxMTkgMTQ2Ljk0NFYxNDYuNjM5QzE2LjgxMTkgMTQ2LjQyNiAxNi44NTg4IDE0Ni4yMzEgMTYuOTUyNSAxNDYuMDUzQzE3LjA0NjMgMTQ1Ljg3NiAxNy4xODA0IDE0NS43MzQgMTcuMzU0OSAxNDUuNjI4QzE3LjUyOTMgMTQ1LjUyMSAxNy43MzY0IDE0NS40NjggMTcuOTc2IDE0NS40NjhDMTguMjIwNyAxNDUuNDY4IDE4LjQyOTEgMTQ1LjUyMSAxOC42MDEgMTQ1LjYyOEMxOC43NzU0IDE0NS43MzQgMTguOTA5NSAxNDUuODc2IDE5LjAwMzMgMTQ2LjA1M0MxOS4wOTcgMTQ2LjIzMSAxOS4xNDM5IDE0Ni40MjYgMTkuMTQzOSAxNDYuNjM5VjE0Ni45NDRDMTkuMTQzOSAxNDcuMTU4IDE5LjA5NyAxNDcuMzUzIDE5LjAwMzMgMTQ3LjUzQzE4LjkxMjIgMTQ3LjcwNyAxOC43NzkzIDE0Ny44NDkgMTguNjA0OSAxNDcuOTU2QzE4LjQzMyAxNDguMDYzIDE4LjIyNiAxNDguMTE2IDE3Ljk4MzggMTQ4LjExNkMxNy43NDE2IDE0OC4xMTYgMTcuNTMzMiAxNDguMDYzIDE3LjM1ODggMTQ3Ljk1NkMxNy4xODQzIDE0Ny44NDkgMTcuMDQ4OSAxNDcuNzA3IDE2Ljk1MjUgMTQ3LjUzQzE2Ljg1ODggMTQ3LjM1MyAxNi44MTE5IDE0Ny4xNTggMTYuODExOSAxNDYuOTQ0Wk0xNy4zNTQ5IDE0Ni42MzlWMTQ2Ljk0NEMxNy4zNTQ5IDE0Ny4wNjQgMTcuMzc3IDE0Ny4xNzggMTcuNDIxMyAxNDcuMjg4QzE3LjQ2ODEgMTQ3LjM5NSAxNy41Mzg1IDE0Ny40ODIgMTcuNjMyMiAxNDcuNTVDMTcuNzI2IDE0Ny42MTUgMTcuODQzMSAxNDcuNjQ3IDE3Ljk4MzggMTQ3LjY0N0MxOC4xMjQ0IDE0Ny42NDcgMTguMjQwMyAxNDcuNjE1IDE4LjMzMTQgMTQ3LjU1QzE4LjQyNTIgMTQ3LjQ4MiAxOC40OTQyIDE0Ny4zOTUgMTguNTM4NSAxNDcuMjg4QzE4LjU4MjcgMTQ3LjE4MSAxOC42MDQ5IDE0Ny4wNjYgMTguNjA0OSAxNDYuOTQ0VjE0Ni42MzlDMTguNjA0OSAxNDYuNTE3IDE4LjU4MTQgMTQ2LjQwMiAxOC41MzQ1IDE0Ni4yOTZDMTguNDkwMyAxNDYuMTg5IDE4LjQyMTMgMTQ2LjEwMyAxOC4zMjc1IDE0Ni4wMzhDMTguMjM2NCAxNDUuOTcgMTguMTE5MiAxNDUuOTM2IDE3Ljk3NiAxNDUuOTM2QzE3LjgzNzkgMTQ1LjkzNiAxNy43MjIgMTQ1Ljk3IDE3LjYyODMgMTQ2LjAzOEMxNy41MzcyIDE0Ni4xMDMgMTcuNDY4MSAxNDYuMTg5IDE3LjQyMTMgMTQ2LjI5NkMxNy4zNzcgMTQ2LjQwMiAxNy4zNTQ5IDE0Ni41MTcgMTcuMzU0OSAxNDYuNjM5Wk0xOC4xNTU2IDE0My4xNTVMMTUuMzc4MyAxNDcuNkwxNC45NzIgMTQ3LjM0M0wxNy43NDk0IDE0Mi44OTdMMTguMTU1NiAxNDMuMTU1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNMjUgNC4xNjExM0wyMDAgNC4xNjExNiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTI1IDMzLjE2MTFMMjAwIDMzLjE2MTIiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIi8+CjxwYXRoIGQ9Ik0yNSA2MS4xNjExTDIwMCA2MS4xNjEyIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMjUgODkuMTYxMUwyMDAgODkuMTYxMiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTI1IDExOC4xNjFMMjAwIDExOC4xNjEiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIi8+CjxsaW5lIHgxPSIyMy4yIiB5MT0iMTQ1Ljk2MSIgeDI9IjIwMi44IiB5Mj0iMTQ1Ljk2MSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNyIgc3Ryb2tlLXdpZHRoPSIwLjQiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPGxpbmUgeDE9IjQwLjQ1IiB5MT0iMTQ4LjA3MiIgeDI9IjQwLjQ1IiB5Mj0iMTQ3LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNMzIuNTA5MSAxNTIuMDI1VjE1Mi44OTNDMzIuNTA5MSAxNTMuMzU5IDMyLjQ2NzQgMTUzLjc1MiAzMi4zODQxIDE1NC4wNzJDMzIuMzAwNyAxNTQuMzkzIDMyLjE4MDkgMTU0LjY1IDMyLjAyNDcgMTU0Ljg0NkMzMS44Njg0IDE1NS4wNDEgMzEuNjc5NiAxNTUuMTgzIDMxLjQ1ODMgMTU1LjI3MUMzMS4yMzk1IDE1NS4zNTcgMzAuOTkyMSAxNTUuNCAzMC43MTYxIDE1NS40QzMwLjQ5NzMgMTU1LjQgMzAuMjk1NSAxNTUuMzczIDMwLjExMDYgMTU1LjMxOEMyOS45MjU3IDE1NS4yNjQgMjkuNzU5MSAxNTUuMTc2IDI5LjYxMDYgMTU1LjA1N0MyOS40NjQ4IDE1NC45MzQgMjkuMzM5OCAxNTQuNzc1IDI5LjIzNTYgMTU0LjU4QzI5LjEzMTUgMTU0LjM4NSAyOS4wNTIgMTU0LjE0OCAyOC45OTczIDE1My44NjlDMjguOTQyNyAxNTMuNTkgMjguOTE1MyAxNTMuMjY1IDI4LjkxNTMgMTUyLjg5M1YxNTIuMDI1QzI4LjkxNTMgMTUxLjU1OSAyOC45NTcgMTUxLjE2OSAyOS4wNDAzIDE1MC44NTRDMjkuMTI2MiAxNTAuNTM4IDI5LjI0NzMgMTUwLjI4NiAyOS40MDM2IDE1MC4wOTZDMjkuNTU5OCAxNDkuOTAzIDI5Ljc0NzMgMTQ5Ljc2NSAyOS45NjYxIDE0OS42ODJDMzAuMTg3NCAxNDkuNTk4IDMwLjQzNDggMTQ5LjU1NyAzMC43MDgzIDE0OS41NTdDMzAuOTI5NiAxNDkuNTU3IDMxLjEzMjggMTQ5LjU4NCAzMS4zMTc3IDE0OS42MzlDMzEuNTA1MiAxNDkuNjkxIDMxLjY3MTggMTQ5Ljc3NSAzMS44MTc3IDE0OS44OTNDMzEuOTYzNSAxNTAuMDA3IDMyLjA4NzIgMTUwLjE2MSAzMi4xODg3IDE1MC4zNTRDMzIuMjkyOSAxNTAuNTQ0IDMyLjM3MjMgMTUwLjc3NyAzMi40MjcgMTUxLjA1M0MzMi40ODE3IDE1MS4zMjkgMzIuNTA5MSAxNTEuNjUzIDMyLjUwOTEgMTUyLjAyNVpNMzEuNzgyNSAxNTMuMDFWMTUxLjkwNEMzMS43ODI1IDE1MS42NDkgMzEuNzY2OSAxNTEuNDI1IDMxLjczNTYgMTUxLjIzMkMzMS43MDcgMTUxLjAzNyAzMS42NjQgMTUwLjg3IDMxLjYwNjcgMTUwLjczMkMzMS41NDk0IDE1MC41OTQgMzEuNDc2NSAxNTAuNDgyIDMxLjM4OCAxNTAuMzk2QzMxLjMwMiAxNTAuMzExIDMxLjIwMTggMTUwLjI0OCAzMS4wODcyIDE1MC4yMDlDMzAuOTc1MiAxNTAuMTY3IDMwLjg0ODkgMTUwLjE0NiAzMC43MDgzIDE1MC4xNDZDMzAuNTM2NCAxNTAuMTQ2IDMwLjM4NDEgMTUwLjE3OSAzMC4yNTEyIDE1MC4yNDRDMzAuMTE4NCAxNTAuMzA3IDMwLjAwNjUgMTUwLjQwNyAyOS45MTUzIDE1MC41NDVDMjkuODI2OCAxNTAuNjgzIDI5Ljc1OTEgMTUwLjg2NCAyOS43MTIyIDE1MS4wODhDMjkuNjY1MyAxNTEuMzEyIDI5LjY0MTkgMTUxLjU4NCAyOS42NDE5IDE1MS45MDRWMTUzLjAxQzI5LjY0MTkgMTUzLjI2NSAyOS42NTYyIDE1My40OSAyOS42ODQ4IDE1My42ODZDMjkuNzE2MSAxNTMuODgxIDI5Ljc2MTcgMTU0LjA1IDI5LjgyMTYgMTU0LjE5M0MyOS44ODE1IDE1NC4zMzQgMjkuOTU0NCAxNTQuNDUgMzAuMDQwMyAxNTQuNTQxQzMwLjEyNjIgMTU0LjYzMiAzMC4yMjUyIDE1NC43IDMwLjMzNzIgMTU0Ljc0NEMzMC40NTE4IDE1NC43ODYgMzAuNTc4MSAxNTQuODA3IDMwLjcxNjEgMTU0LjgwN0MzMC44OTMyIDE1NC44MDcgMzEuMDQ4MSAxNTQuNzczIDMxLjE4MDkgMTU0LjcwNUMzMS4zMTM3IDE1NC42MzcgMzEuNDI0NCAxNTQuNTMyIDMxLjUxMyAxNTQuMzg5QzMxLjYwNDEgMTU0LjI0MyAzMS42NzE4IDE1NC4wNTcgMzEuNzE2MSAxNTMuODNDMzEuNzYwNCAxNTMuNjAxIDMxLjc4MjUgMTUzLjMyNyAzMS43ODI1IDE1My4wMVpNMzUuODk2NCAxNDkuNjA0VjE1NS4zMjJIMzUuMTczN1YxNTAuNTA2TDMzLjcxNjcgMTUxLjAzN1YxNTAuMzg1TDM1Ljc4MzEgMTQ5LjYwNEgzNS44OTY0Wk00MS4xMTI0IDE0OS42MzVWMTU1LjMyMkg0MC4zNTg1VjE0OS42MzVINDEuMTEyNFpNNDMuNDk1MiAxNTIuMTkzVjE1Mi44MTFINDAuOTQ4M1YxNTIuMTkzSDQzLjQ5NTJaTTQzLjg4MTkgMTQ5LjYzNVYxNTAuMjUySDQwLjk0ODNWMTQ5LjYzNUg0My44ODE5Wk00Ni40MjE2IDE1NS40QzQ2LjEyNzMgMTU1LjQgNDUuODYwNCAxNTUuMzUxIDQ1LjYyMDggMTU1LjI1MkM0NS4zODM4IDE1NS4xNSA0NS4xNzk0IDE1NS4wMDggNDUuMDA3NSAxNTQuODI2QzQ0LjgzODMgMTU0LjY0NCA0NC43MDgxIDE1NC40MjggNDQuNjE2OSAxNTQuMTc4QzQ0LjUyNTggMTUzLjkyOCA0NC40ODAyIDE1My42NTQgNDQuNDgwMiAxNTMuMzU3VjE1My4xOTNDNDQuNDgwMiAxNTIuODUgNDQuNTMxIDE1Mi41NDQgNDQuNjMyNSAxNTIuMjc1QzQ0LjczNDEgMTUyLjAwNSA0NC44NzIxIDE1MS43NzUgNDUuMDQ2NiAxNTEuNTg4QzQ1LjIyMTEgMTUxLjQgNDUuNDE5IDE1MS4yNTggNDUuNjQwMyAxNTEuMTYyQzQ1Ljg2MTcgMTUxLjA2NiA0Ni4wOTA5IDE1MS4wMTggNDYuMzI3OCAxNTEuMDE4QzQ2LjYyOTkgMTUxLjAxOCA0Ni44OTAzIDE1MS4wNyA0Ny4xMDkxIDE1MS4xNzRDNDcuMzMwNSAxNTEuMjc4IDQ3LjUxMTQgMTUxLjQyNCA0Ny42NTIxIDE1MS42MTFDNDcuNzkyNyAxNTEuNzk2IDQ3Ljg5NjkgMTUyLjAxNSA0Ny45NjQ2IDE1Mi4yNjhDNDguMDMyMyAxNTIuNTE4IDQ4LjA2NjEgMTUyLjc5MSA0OC4wNjYxIDE1My4wODhWMTUzLjQxMkg0NC45MDk5VjE1Mi44MjJINDcuMzQzNVYxNTIuNzY4QzQ3LjMzMzEgMTUyLjU4IDQ3LjI5NCAxNTIuMzk4IDQ3LjIyNjMgMTUyLjIyMUM0Ny4xNjEyIDE1Mi4wNDQgNDcuMDU3IDE1MS44OTggNDYuOTEzOCAxNTEuNzgzQzQ2Ljc3MDYgMTUxLjY2OSA0Ni41NzUyIDE1MS42MTEgNDYuMzI3OCAxNTEuNjExQzQ2LjE2MzggMTUxLjYxMSA0Ni4wMTI3IDE1MS42NDYgNDUuODc0NyAxNTEuNzE3QzQ1LjczNjcgMTUxLjc4NSA0NS42MTgyIDE1MS44ODYgNDUuNTE5MyAxNTIuMDIxQzQ1LjQyMDMgMTUyLjE1NyA0NS4zNDM1IDE1Mi4zMjIgNDUuMjg4OCAxNTIuNTE4QzQ1LjIzNDEgMTUyLjcxMyA0NS4yMDY4IDE1Mi45MzggNDUuMjA2OCAxNTMuMTkzVjE1My4zNTdDNDUuMjA2OCAxNTMuNTU4IDQ1LjIzNDEgMTUzLjc0NyA0NS4yODg4IDE1My45MjRDNDUuMzQ2MSAxNTQuMDk4IDQ1LjQyODEgMTU0LjI1MiA0NS41MzQ5IDE1NC4zODVDNDUuNjQ0MyAxNTQuNTE4IDQ1Ljc3NTggMTU0LjYyMiA0NS45Mjk0IDE1NC42OTdDNDYuMDg1NyAxNTQuNzczIDQ2LjI2MjcgMTU0LjgxMSA0Ni40NjA3IDE1NC44MTFDNDYuNzE1OSAxNTQuODExIDQ2LjkzMiAxNTQuNzU4IDQ3LjEwOTEgMTU0LjY1NEM0Ny4yODYyIDE1NC41NSA0Ny40NDExIDE1NC40MTEgNDcuNTczOSAxNTQuMjM2TDQ4LjAxMTQgMTU0LjU4NEM0Ny45MjAzIDE1NC43MjIgNDcuODA0NCAxNTQuODU0IDQ3LjY2MzggMTU0Ljk3OUM0Ny41MjMyIDE1NS4xMDQgNDcuMzUgMTU1LjIwNSA0Ny4xNDQzIDE1NS4yODNDNDYuOTQxMSAxNTUuMzYxIDQ2LjcwMDIgMTU1LjQgNDYuNDIxNiAxNTUuNFpNNDguOTg4NiAxNDkuMzIySDQ5LjcxNTJWMTU0LjUwMkw0OS42NTI3IDE1NS4zMjJINDguOTg4NlYxNDkuMzIyWk01Mi41NzA2IDE1My4xNzRWMTUzLjI1NkM1Mi41NzA2IDE1My41NjMgNTIuNTM0MiAxNTMuODQ4IDUyLjQ2MTMgMTU0LjExMUM1Mi4zODgzIDE1NC4zNzIgNTIuMjgxNiAxNTQuNTk4IDUyLjE0MDkgMTU0Ljc5MUM1Mi4wMDAzIDE1NC45ODQgNTEuODI4NCAxNTUuMTMzIDUxLjYyNTMgMTU1LjI0QzUxLjQyMjIgMTU1LjM0NyA1MS4xODkxIDE1NS40IDUwLjkyNjEgMTU1LjRDNTAuNjU3OSAxNTUuNCA1MC40MjIyIDE1NS4zNTUgNTAuMjE5MSAxNTUuMjY0QzUwLjAxODUgMTU1LjE3IDQ5Ljg0OTMgMTU1LjAzNiA0OS43MTEzIDE1NC44NjFDNDkuNTczMiAxNTQuNjg3IDQ5LjQ2MjYgMTU0LjQ3NiA0OS4zNzkyIDE1NC4yMjlDNDkuMjk4NSAxNTMuOTgxIDQ5LjI0MjUgMTUzLjcwMiA0OS4yMTEzIDE1My4zOTNWMTUzLjAzM0M0OS4yNDI1IDE1Mi43MjEgNDkuMjk4NSAxNTIuNDQxIDQ5LjM3OTIgMTUyLjE5M0M0OS40NjI2IDE1MS45NDYgNDkuNTczMiAxNTEuNzM1IDQ5LjcxMTMgMTUxLjU2MUM0OS44NDkzIDE1MS4zODMgNTAuMDE4NSAxNTEuMjQ5IDUwLjIxOTEgMTUxLjE1OEM1MC40MTk2IDE1MS4wNjQgNTAuNjUyNyAxNTEuMDE4IDUwLjkxODMgMTUxLjAxOEM1MS4xODM5IDE1MS4wMTggNTEuNDE5NiAxNTEuMDcgNTEuNjI1MyAxNTEuMTc0QzUxLjgzMSAxNTEuMjc1IDUyLjAwMjkgMTUxLjQyMSA1Mi4xNDA5IDE1MS42MTFDNTIuMjgxNiAxNTEuODAxIDUyLjM4ODMgMTUyLjAyOSA1Mi40NjEzIDE1Mi4yOTVDNTIuNTM0MiAxNTIuNTU4IDUyLjU3MDYgMTUyLjg1MSA1Mi41NzA2IDE1My4xNzRaTTUxLjg0NDEgMTUzLjI1NlYxNTMuMTc0QzUxLjg0NDEgMTUyLjk2MyA1MS44MjQ1IDE1Mi43NjUgNTEuNzg1NSAxNTIuNThDNTEuNzQ2NCAxNTIuMzkzIDUxLjY4MzkgMTUyLjIyOSA1MS41OTggMTUyLjA4OEM1MS41MTIgMTUxLjk0NSA1MS4zOTg4IDE1MS44MzMgNTEuMjU4MSAxNTEuNzUyQzUxLjExNzUgMTUxLjY2OSA1MC45NDQzIDE1MS42MjcgNTAuNzM4NiAxNTEuNjI3QzUwLjU1NjMgMTUxLjYyNyA1MC4zOTc1IDE1MS42NTggNTAuMjYyIDE1MS43MjFDNTAuMTI5MiAxNTEuNzgzIDUwLjAxNTkgMTUxLjg2OCA0OS45MjIyIDE1MS45NzVDNDkuODI4NCAxNTIuMDc5IDQ5Ljc1MTYgMTUyLjE5OSA0OS42OTE3IDE1Mi4zMzRDNDkuNjM0NCAxNTIuNDY3IDQ5LjU5MTUgMTUyLjYwNSA0OS41NjI4IDE1Mi43NDhWMTUzLjY4OUM0OS42MDQ1IDE1My44NzIgNDkuNjcyMiAxNTQuMDQ4IDQ5Ljc2NTkgMTU0LjIxN0M0OS44NjIzIDE1NC4zODMgNDkuOTg5OSAxNTQuNTIgNTAuMTQ4OCAxNTQuNjI3QzUwLjMxMDIgMTU0LjczNCA1MC41MDk0IDE1NC43ODcgNTAuNzQ2NCAxNTQuNzg3QzUwLjk0MTcgMTU0Ljc4NyA1MS4xMDg0IDE1NC43NDggNTEuMjQ2NCAxNTQuNjdDNTEuMzg3IDE1NC41ODkgNTEuNTAwMyAxNTQuNDc5IDUxLjU4NjMgMTU0LjMzOEM1MS42NzQ4IDE1NC4xOTcgNTEuNzM5OSAxNTQuMDM1IDUxLjc4MTYgMTUzLjg1QzUxLjgyMzIgMTUzLjY2NSA1MS44NDQxIDE1My40NjcgNTEuODQ0MSAxNTMuMjU2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDFfNDE4NF85NDUyNykiPgo8bGluZSB4MT0iNzUuODQ5OSIgeTE9IjE0Ny4wNzIiIHgyPSI3NS44NDk5IiB5Mj0iMTQ2LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNNjcuOTA5IDE1Mi4wMjVWMTUyLjg5MkM2Ny45MDkgMTUzLjM1OCA2Ny44NjczIDE1My43NTIgNjcuNzg0IDE1NC4wNzJDNjcuNzAwNiAxNTQuMzkyIDY3LjU4MDggMTU0LjY1IDY3LjQyNDYgMTU0Ljg0NUM2Ny4yNjgzIDE1NS4wNDEgNjcuMDc5NSAxNTUuMTgzIDY2Ljg1ODIgMTU1LjI3MUM2Ni42Mzk0IDE1NS4zNTcgNjYuMzkyIDE1NS40IDY2LjExNiAxNTUuNEM2NS44OTcyIDE1NS40IDY1LjY5NTQgMTU1LjM3MyA2NS41MTA1IDE1NS4zMThDNjUuMzI1NiAxNTUuMjYzIDY1LjE1OSAxNTUuMTc2IDY1LjAxMDUgMTU1LjA1NkM2NC44NjQ3IDE1NC45MzQgNjQuNzM5NyAxNTQuNzc1IDY0LjYzNTUgMTU0LjU4QzY0LjUzMTQgMTU0LjM4NSA2NC40NTE5IDE1NC4xNDggNjQuMzk3MiAxNTMuODY5QzY0LjM0MjYgMTUzLjU5IDY0LjMxNTIgMTUzLjI2NSA2NC4zMTUyIDE1Mi44OTJWMTUyLjAyNUM2NC4zMTUyIDE1MS41NTkgNjQuMzU2OSAxNTEuMTY4IDY0LjQ0MDIgMTUwLjg1M0M2NC41MjYxIDE1MC41MzggNjQuNjQ3MiAxNTAuMjg2IDY0LjgwMzUgMTUwLjA5NUM2NC45NTk3IDE0OS45MDMgNjUuMTQ3MiAxNDkuNzY1IDY1LjM2NiAxNDkuNjgxQzY1LjU4NzMgMTQ5LjU5OCA2NS44MzQ3IDE0OS41NTYgNjYuMTA4MiAxNDkuNTU2QzY2LjMyOTUgMTQ5LjU1NiA2Ni41MzI3IDE0OS41ODQgNjYuNzE3NiAxNDkuNjM4QzY2LjkwNTEgMTQ5LjY5MSA2Ny4wNzE3IDE0OS43NzUgNjcuMjE3NiAxNDkuODkyQzY3LjM2MzQgMTUwLjAwNyA2Ny40ODcxIDE1MC4xNjEgNjcuNTg4NiAxNTAuMzUzQzY3LjY5MjggMTUwLjU0MyA2Ny43NzIyIDE1MC43NzYgNjcuODI2OSAxNTEuMDUyQzY3Ljg4MTYgMTUxLjMyOSA2Ny45MDkgMTUxLjY1MyA2Ny45MDkgMTUyLjAyNVpNNjcuMTgyNCAxNTMuMDFWMTUxLjkwNEM2Ny4xODI0IDE1MS42NDkgNjcuMTY2OCAxNTEuNDI1IDY3LjEzNTUgMTUxLjIzMkM2Ny4xMDY5IDE1MS4wMzcgNjcuMDYzOSAxNTAuODcgNjcuMDA2NiAxNTAuNzMyQzY2Ljk0OTMgMTUwLjU5NCA2Ni44NzY0IDE1MC40ODIgNjYuNzg3OSAxNTAuMzk2QzY2LjcwMTkgMTUwLjMxIDY2LjYwMTcgMTUwLjI0OCA2Ni40ODcxIDE1MC4yMDlDNjYuMzc1MSAxNTAuMTY3IDY2LjI0ODggMTUwLjE0NiA2Ni4xMDgyIDE1MC4xNDZDNjUuOTM2MyAxNTAuMTQ2IDY1Ljc4NCAxNTAuMTc5IDY1LjY1MTEgMTUwLjI0NEM2NS41MTgzIDE1MC4zMDYgNjUuNDA2NCAxNTAuNDA3IDY1LjMxNTIgMTUwLjU0NUM2NS4yMjY3IDE1MC42ODMgNjUuMTU5IDE1MC44NjQgNjUuMTEyMSAxNTEuMDg4QzY1LjA2NTIgMTUxLjMxMiA2NS4wNDE4IDE1MS41ODQgNjUuMDQxOCAxNTEuOTA0VjE1My4wMUM2NS4wNDE4IDE1My4yNjUgNjUuMDU2MSAxNTMuNDkgNjUuMDg0NyAxNTMuNjg1QzY1LjExNiAxNTMuODgxIDY1LjE2MTYgMTU0LjA1IDY1LjIyMTUgMTU0LjE5M0M2NS4yODE0IDE1NC4zMzQgNjUuMzU0MyAxNTQuNDUgNjUuNDQwMiAxNTQuNTQxQzY1LjUyNjEgMTU0LjYzMiA2NS42MjUxIDE1NC43IDY1LjczNzEgMTU0Ljc0NEM2NS44NTE3IDE1NC43ODYgNjUuOTc4IDE1NC44MDYgNjYuMTE2IDE1NC44MDZDNjYuMjkzMSAxNTQuODA2IDY2LjQ0OCAxNTQuNzczIDY2LjU4MDggMTU0LjcwNUM2Ni43MTM2IDE1NC42MzcgNjYuODI0MyAxNTQuNTMyIDY2LjkxMjkgMTU0LjM4OEM2Ny4wMDQgMTU0LjI0MyA2Ny4wNzE3IDE1NC4wNTYgNjcuMTE2IDE1My44M0M2Ny4xNjAzIDE1My42MDEgNjcuMTgyNCAxNTMuMzI3IDY3LjE4MjQgMTUzLjAxWk03Mi42NDc4IDE1NC43MjhWMTU1LjMyMkg2OC45MjUyVjE1NC44MDJMNzAuNzg4NSAxNTIuNzI4QzcxLjAxNzYgMTUyLjQ3MyA3MS4xOTQ3IDE1Mi4yNTcgNzEuMzE5NyAxNTIuMDhDNzEuNDQ3MyAxNTEuOSA3MS41MzU5IDE1MS43NCA3MS41ODUzIDE1MS41OTlDNzEuNjM3NCAxNTEuNDU2IDcxLjY2MzUgMTUxLjMxIDcxLjY2MzUgMTUxLjE2MkM3MS42NjM1IDE1MC45NzQgNzEuNjI0NCAxNTAuODA1IDcxLjU0NjMgMTUwLjY1NEM3MS40NzA4IDE1MC41IDcxLjM1ODggMTUwLjM3OCA3MS4yMTAzIDE1MC4yODdDNzEuMDYxOSAxNTAuMTk2IDcwLjg4MjIgMTUwLjE1IDcwLjY3MTMgMTUwLjE1QzcwLjQxODcgMTUwLjE1IDcwLjIwNzcgMTUwLjIgNzAuMDM4NSAxNTAuMjk5QzY5Ljg3MTggMTUwLjM5NSA2OS43NDY4IDE1MC41MyA2OS42NjM1IDE1MC43MDVDNjkuNTgwMSAxNTAuODc5IDY5LjUzODUgMTUxLjA4IDY5LjUzODUgMTUxLjMwNkg2OC44MTU4QzY4LjgxNTggMTUwLjk4NiA2OC44ODYxIDE1MC42OTMgNjkuMDI2NyAxNTAuNDI3QzY5LjE2NzQgMTUwLjE2MiA2OS4zNzU3IDE0OS45NTEgNjkuNjUxNyAxNDkuNzk1QzY5LjkyNzggMTQ5LjYzNiA3MC4yNjc2IDE0OS41NTYgNzAuNjcxMyAxNDkuNTU2QzcxLjAzMDcgMTQ5LjU1NiA3MS4zMzc5IDE0OS42MiA3MS41OTMyIDE0OS43NDhDNzEuODQ4NCAxNDkuODczIDcyLjA0MzcgMTUwLjA1IDcyLjE3OTEgMTUwLjI3OUM3Mi4zMTcxIDE1MC41MDYgNzIuMzg2MSAxNTAuNzcxIDcyLjM4NjEgMTUxLjA3NkM3Mi4zODYxIDE1MS4yNDMgNzIuMzU3NSAxNTEuNDEyIDcyLjMwMDIgMTUxLjU4NEM3Mi4yNDU1IDE1MS43NTMgNzIuMTY4NyAxNTEuOTIyIDcyLjA2OTcgMTUyLjA5MkM3MS45NzM0IDE1Mi4yNjEgNzEuODYwMSAxNTIuNDI3IDcxLjcyOTkgMTUyLjU5MkM3MS42MDIzIDE1Mi43NTYgNzEuNDY1NSAxNTIuOTE3IDcxLjMxOTcgMTUzLjA3Nkw2OS43OTYzIDE1NC43MjhINzIuNjQ3OFpNNzYuNTEyMyAxNDkuNjM1VjE1NS4zMjJINzUuNzU4NFYxNDkuNjM1SDc2LjUxMjNaTTc4Ljg5NTEgMTUyLjE5M1YxNTIuODFINzYuMzQ4MlYxNTIuMTkzSDc4Ljg5NTFaTTc5LjI4MTggMTQ5LjYzNVYxNTAuMjUySDc2LjM0ODJWMTQ5LjYzNUg3OS4yODE4Wk04MS44MjE1IDE1NS40QzgxLjUyNzIgMTU1LjQgODEuMjYwMyAxNTUuMzUxIDgxLjAyMDcgMTU1LjI1MkM4MC43ODM3IDE1NS4xNSA4MC41NzkzIDE1NS4wMDggODAuNDA3NCAxNTQuODI2QzgwLjIzODIgMTU0LjY0NCA4MC4xMDggMTU0LjQyNyA4MC4wMTY4IDE1NC4xNzdDNzkuOTI1NyAxNTMuOTI3IDc5Ljg4MDEgMTUzLjY1NCA3OS44ODAxIDE1My4zNTdWMTUzLjE5M0M3OS44ODAxIDE1Mi44NDkgNzkuOTMwOSAxNTIuNTQzIDgwLjAzMjQgMTUyLjI3NUM4MC4xMzQgMTUyLjAwNCA4MC4yNzIgMTUxLjc3NSA4MC40NDY1IDE1MS41ODhDODAuNjIxIDE1MS40IDgwLjgxODkgMTUxLjI1OCA4MS4wNDAyIDE1MS4xNjJDODEuMjYxNiAxNTEuMDY2IDgxLjQ5MDggMTUxLjAxNyA4MS43Mjc3IDE1MS4wMTdDODIuMDI5OCAxNTEuMDE3IDgyLjI5MDIgMTUxLjA2OSA4Mi41MDkgMTUxLjE3NEM4Mi43MzA0IDE1MS4yNzggODIuOTExMyAxNTEuNDI0IDgzLjA1MiAxNTEuNjExQzgzLjE5MjYgMTUxLjc5NiA4My4yOTY4IDE1Mi4wMTUgODMuMzY0NSAxNTIuMjY3QzgzLjQzMjIgMTUyLjUxNyA4My40NjYgMTUyLjc5MSA4My40NjYgMTUzLjA4OFYxNTMuNDEySDgwLjMwOThWMTUyLjgyMkg4Mi43NDM0VjE1Mi43NjdDODIuNzMzIDE1Mi41OCA4Mi42OTM5IDE1Mi4zOTggODIuNjI2MiAxNTIuMjJDODIuNTYxMSAxNTIuMDQzIDgyLjQ1NjkgMTUxLjg5OCA4Mi4zMTM3IDE1MS43ODNDODIuMTcwNSAxNTEuNjY4IDgxLjk3NTEgMTUxLjYxMSA4MS43Mjc3IDE1MS42MTFDODEuNTYzNyAxNTEuNjExIDgxLjQxMjYgMTUxLjY0NiA4MS4yNzQ2IDE1MS43MTdDODEuMTM2NiAxNTEuNzg0IDgxLjAxODEgMTUxLjg4NiA4MC45MTkyIDE1Mi4wMjFDODAuODIwMiAxNTIuMTU3IDgwLjc0MzQgMTUyLjMyMiA4MC42ODg3IDE1Mi41MTdDODAuNjM0IDE1Mi43MTMgODAuNjA2NyAxNTIuOTM4IDgwLjYwNjcgMTUzLjE5M1YxNTMuMzU3QzgwLjYwNjcgMTUzLjU1OCA4MC42MzQgMTUzLjc0NyA4MC42ODg3IDE1My45MjRDODAuNzQ2IDE1NC4wOTggODAuODI4IDE1NC4yNTIgODAuOTM0OCAxNTQuMzg1QzgxLjA0NDIgMTU0LjUxNyA4MS4xNzU3IDE1NC42MjIgODEuMzI5MyAxNTQuNjk3QzgxLjQ4NTYgMTU0Ljc3MyA4MS42NjI2IDE1NC44MSA4MS44NjA2IDE1NC44MUM4Mi4xMTU4IDE1NC44MSA4Mi4zMzE5IDE1NC43NTggODIuNTA5IDE1NC42NTRDODIuNjg2MSAxNTQuNTUgODIuODQxIDE1NC40MTEgODIuOTczOCAxNTQuMjM2TDgzLjQxMTMgMTU0LjU4NEM4My4zMjAyIDE1NC43MjIgODMuMjA0MyAxNTQuODUzIDgzLjA2MzcgMTU0Ljk3OEM4Mi45MjMxIDE1NS4xMDMgODIuNzQ5OSAxNTUuMjA1IDgyLjU0NDIgMTU1LjI4M0M4Mi4zNDEgMTU1LjM2MSA4Mi4xMDAxIDE1NS40IDgxLjgyMTUgMTU1LjRaTTg0LjM4ODUgMTQ5LjMyMkg4NS4xMTUxVjE1NC41MDJMODUuMDUyNiAxNTUuMzIySDg0LjM4ODVWMTQ5LjMyMlpNODcuOTcwNSAxNTMuMTc0VjE1My4yNTZDODcuOTcwNSAxNTMuNTYzIDg3LjkzNDEgMTUzLjg0OCA4Ny44NjEyIDE1NC4xMTFDODcuNzg4MiAxNTQuMzcyIDg3LjY4MTUgMTU0LjU5OCA4Ny41NDA4IDE1NC43OTFDODcuNDAwMiAxNTQuOTgzIDg3LjIyODMgMTU1LjEzMyA4Ny4wMjUyIDE1NS4yNEM4Ni44MjIxIDE1NS4zNDcgODYuNTg5IDE1NS40IDg2LjMyNiAxNTUuNEM4Ni4wNTc4IDE1NS40IDg1LjgyMjEgMTU1LjM1NSA4NS42MTkgMTU1LjI2M0M4NS40MTg1IDE1NS4xNyA4NS4yNDkyIDE1NS4wMzYgODUuMTExMiAxNTQuODYxQzg0Ljk3MzEgMTU0LjY4NyA4NC44NjI1IDE1NC40NzYgODQuNzc5MSAxNTQuMjI4Qzg0LjY5ODQgMTUzLjk4MSA4NC42NDI0IDE1My43MDIgODQuNjExMiAxNTMuMzkyVjE1My4wMzNDODQuNjQyNCAxNTIuNzIgODQuNjk4NCAxNTIuNDQxIDg0Ljc3OTEgMTUyLjE5M0M4NC44NjI1IDE1MS45NDYgODQuOTczMSAxNTEuNzM1IDg1LjExMTIgMTUxLjU2Qzg1LjI0OTIgMTUxLjM4MyA4NS40MTg1IDE1MS4yNDkgODUuNjE5IDE1MS4xNThDODUuODE5NSAxNTEuMDY0IDg2LjA1MjYgMTUxLjAxNyA4Ni4zMTgyIDE1MS4wMTdDODYuNTgzOCAxNTEuMDE3IDg2LjgxOTUgMTUxLjA2OSA4Ny4wMjUyIDE1MS4xNzRDODcuMjMxIDE1MS4yNzUgODcuNDAyOCAxNTEuNDIxIDg3LjU0MDggMTUxLjYxMUM4Ny42ODE1IDE1MS44MDEgODcuNzg4MiAxNTIuMDI5IDg3Ljg2MTIgMTUyLjI5NUM4Ny45MzQxIDE1Mi41NTggODcuOTcwNSAxNTIuODUxIDg3Ljk3MDUgMTUzLjE3NFpNODcuMjQ0IDE1My4yNTZWMTUzLjE3NEM4Ny4yNDQgMTUyLjk2MyA4Ny4yMjQ0IDE1Mi43NjUgODcuMTg1NCAxNTIuNThDODcuMTQ2MyAxNTIuMzkyIDg3LjA4MzggMTUyLjIyOCA4Ni45OTc5IDE1Mi4wODhDODYuOTExOSAxNTEuOTQ0IDg2Ljc5ODcgMTUxLjgzMiA4Ni42NTggMTUxLjc1MkM4Ni41MTc0IDE1MS42NjggODYuMzQ0MiAxNTEuNjI3IDg2LjEzODUgMTUxLjYyN0M4NS45NTYyIDE1MS42MjcgODUuNzk3NCAxNTEuNjU4IDg1LjY2MTkgMTUxLjcyQzg1LjUyOTEgMTUxLjc4MyA4NS40MTU4IDE1MS44NjggODUuMzIyMSAxNTEuOTc0Qzg1LjIyODMgMTUyLjA3OSA4NS4xNTE1IDE1Mi4xOTggODUuMDkxNiAxNTIuMzM0Qzg1LjAzNDMgMTUyLjQ2NyA4NC45OTE0IDE1Mi42MDUgODQuOTYyNyAxNTIuNzQ4VjE1My42ODlDODUuMDA0NCAxNTMuODcyIDg1LjA3MjEgMTU0LjA0NyA4NS4xNjU4IDE1NC4yMTdDODUuMjYyMiAxNTQuMzgzIDg1LjM4OTggMTU0LjUyIDg1LjU0ODcgMTU0LjYyN0M4NS43MTAxIDE1NC43MzMgODUuOTA5MyAxNTQuNzg3IDg2LjE0NjMgMTU0Ljc4N0M4Ni4zNDE2IDE1NC43ODcgODYuNTA4MyAxNTQuNzQ4IDg2LjY0NjMgMTU0LjY3Qzg2Ljc4NjkgMTU0LjU4OSA4Ni45MDAyIDE1NC40NzggODYuOTg2MiAxNTQuMzM4Qzg3LjA3NDcgMTU0LjE5NyA4Ny4xMzk4IDE1NC4wMzQgODcuMTgxNSAxNTMuODQ5Qzg3LjIyMzEgMTUzLjY2NCA4Ny4yNDQgMTUzLjQ2NyA4Ny4yNDQgMTUzLjI1NloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPC9nPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDJfNDE4NF85NDUyNykiPgo8bGluZSB4MT0iMTExLjI1IiB5MT0iMTQ3LjA3MiIgeDI9IjExMS4yNSIgeTI9IjE0Ni4yNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNSIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPHBhdGggZD0iTTEwMy4zMDkgMTUyLjAyNVYxNTIuODkyQzEwMy4zMDkgMTUzLjM1OCAxMDMuMjY3IDE1My43NTIgMTAzLjE4NCAxNTQuMDcyQzEwMy4xMDEgMTU0LjM5MiAxMDIuOTgxIDE1NC42NSAxMDIuODI1IDE1NC44NDVDMTAyLjY2OCAxNTUuMDQxIDEwMi40OCAxNTUuMTgzIDEwMi4yNTggMTU1LjI3MUMxMDIuMDQgMTU1LjM1NyAxMDEuNzkyIDE1NS40IDEwMS41MTYgMTU1LjRDMTAxLjI5NyAxNTUuNCAxMDEuMDk2IDE1NS4zNzMgMTAwLjkxMSAxNTUuMzE4QzEwMC43MjYgMTU1LjI2MyAxMDAuNTU5IDE1NS4xNzYgMTAwLjQxMSAxNTUuMDU2QzEwMC4yNjUgMTU0LjkzNCAxMDAuMTQgMTU0Ljc3NSAxMDAuMDM2IDE1NC41OEM5OS45MzE1IDE1NC4zODUgOTkuODUyMSAxNTQuMTQ4IDk5Ljc5NzQgMTUzLjg2OUM5OS43NDI3IDE1My41OSA5OS43MTU0IDE1My4yNjUgOTkuNzE1NCAxNTIuODkyVjE1Mi4wMjVDOTkuNzE1NCAxNTEuNTU5IDk5Ljc1NyAxNTEuMTY4IDk5Ljg0MDQgMTUwLjg1M0M5OS45MjYzIDE1MC41MzggMTAwLjA0NyAxNTAuMjg2IDEwMC4yMDQgMTUwLjA5NUMxMDAuMzYgMTQ5LjkwMyAxMDAuNTQ3IDE0OS43NjUgMTAwLjc2NiAxNDkuNjgxQzEwMC45ODcgMTQ5LjU5OCAxMDEuMjM1IDE0OS41NTYgMTAxLjUwOCAxNDkuNTU2QzEwMS43MyAxNDkuNTU2IDEwMS45MzMgMTQ5LjU4NCAxMDIuMTE4IDE0OS42MzhDMTAyLjMwNSAxNDkuNjkxIDEwMi40NzIgMTQ5Ljc3NSAxMDIuNjE4IDE0OS44OTJDMTAyLjc2NCAxNTAuMDA3IDEwMi44ODcgMTUwLjE2MSAxMDIuOTg5IDE1MC4zNTNDMTAzLjA5MyAxNTAuNTQzIDEwMy4xNzIgMTUwLjc3NiAxMDMuMjI3IDE1MS4wNTJDMTAzLjI4MiAxNTEuMzI5IDEwMy4zMDkgMTUxLjY1MyAxMDMuMzA5IDE1Mi4wMjVaTTEwMi41ODMgMTUzLjAxVjE1MS45MDRDMTAyLjU4MyAxNTEuNjQ5IDEwMi41NjcgMTUxLjQyNSAxMDIuNTM2IDE1MS4yMzJDMTAyLjUwNyAxNTEuMDM3IDEwMi40NjQgMTUwLjg3IDEwMi40MDcgMTUwLjczMkMxMDIuMzQ5IDE1MC41OTQgMTAyLjI3NyAxNTAuNDgyIDEwMi4xODggMTUwLjM5NkMxMDIuMTAyIDE1MC4zMSAxMDIuMDAyIDE1MC4yNDggMTAxLjg4NyAxNTAuMjA5QzEwMS43NzUgMTUwLjE2NyAxMDEuNjQ5IDE1MC4xNDYgMTAxLjUwOCAxNTAuMTQ2QzEwMS4zMzYgMTUwLjE0NiAxMDEuMTg0IDE1MC4xNzkgMTAxLjA1MSAxNTAuMjQ0QzEwMC45MTggMTUwLjMwNiAxMDAuODA3IDE1MC40MDcgMTAwLjcxNSAxNTAuNTQ1QzEwMC42MjcgMTUwLjY4MyAxMDAuNTU5IDE1MC44NjQgMTAwLjUxMiAxNTEuMDg4QzEwMC40NjUgMTUxLjMxMiAxMDAuNDQyIDE1MS41ODQgMTAwLjQ0MiAxNTEuOTA0VjE1My4wMUMxMDAuNDQyIDE1My4yNjUgMTAwLjQ1NiAxNTMuNDkgMTAwLjQ4NSAxNTMuNjg1QzEwMC41MTYgMTUzLjg4MSAxMDAuNTYyIDE1NC4wNSAxMDAuNjIyIDE1NC4xOTNDMTAwLjY4MiAxNTQuMzM0IDEwMC43NTQgMTU0LjQ1IDEwMC44NCAxNTQuNTQxQzEwMC45MjYgMTU0LjYzMiAxMDEuMDI1IDE1NC43IDEwMS4xMzcgMTU0Ljc0NEMxMDEuMjUyIDE1NC43ODYgMTAxLjM3OCAxNTQuODA2IDEwMS41MTYgMTU0LjgwNkMxMDEuNjkzIDE1NC44MDYgMTAxLjg0OCAxNTQuNzczIDEwMS45ODEgMTU0LjcwNUMxMDIuMTE0IDE1NC42MzcgMTAyLjIyNCAxNTQuNTMyIDEwMi4zMTMgMTU0LjM4OEMxMDIuNDA0IDE1NC4yNDMgMTAyLjQ3MiAxNTQuMDU2IDEwMi41MTYgMTUzLjgzQzEwMi41NiAxNTMuNjAxIDEwMi41ODMgMTUzLjMyNyAxMDIuNTgzIDE1My4wMVpNMTA1LjM3NiAxNTIuMTIzSDEwNS44OTJDMTA2LjE0NCAxNTIuMTIzIDEwNi4zNTMgMTUyLjA4MSAxMDYuNTE3IDE1MS45OThDMTA2LjY4MyAxNTEuOTEyIDEwNi44MDcgMTUxLjc5NiAxMDYuODg4IDE1MS42NUMxMDYuOTcxIDE1MS41MDIgMTA3LjAxMyAxNTEuMzM1IDEwNy4wMTMgMTUxLjE1QzEwNy4wMTMgMTUwLjkzMSAxMDYuOTc2IDE1MC43NDggMTA2LjkwMyAxNTAuNTk5QzEwNi44MzEgMTUwLjQ1MSAxMDYuNzIxIDE1MC4zMzkgMTA2LjU3NSAxNTAuMjYzQzEwNi40MjkgMTUwLjE4OCAxMDYuMjQ1IDE1MC4xNSAxMDYuMDIxIDE1MC4xNUMxMDUuODE4IDE1MC4xNSAxMDUuNjM4IDE1MC4xOTEgMTA1LjQ4MiAxNTAuMjcxQzEwNS4zMjggMTUwLjM0OSAxMDUuMjA3IDE1MC40NjEgMTA1LjExOCAxNTAuNjA3QzEwNS4wMzIgMTUwLjc1MyAxMDQuOTg5IDE1MC45MjUgMTA0Ljk4OSAxNTEuMTIzSDEwNC4yNjdDMTA0LjI2NyAxNTAuODM0IDEwNC4zNCAxNTAuNTcxIDEwNC40ODUgMTUwLjMzNEMxMDQuNjMxIDE1MC4wOTcgMTA0LjgzNiAxNDkuOTA4IDEwNS4wOTkgMTQ5Ljc2N0MxMDUuMzY0IDE0OS42MjcgMTA1LjY3MiAxNDkuNTU2IDEwNi4wMjEgMTQ5LjU1NkMxMDYuMzY0IDE0OS41NTYgMTA2LjY2NSAxNDkuNjE4IDEwNi45MjMgMTQ5Ljc0QzEwNy4xODEgMTQ5Ljg2IDEwNy4zODEgMTUwLjAzOSAxMDcuNTI1IDE1MC4yNzlDMTA3LjY2OCAxNTAuNTE2IDEwNy43MzkgMTUwLjgxMiAxMDcuNzM5IDE1MS4xNjZDMTA3LjczOSAxNTEuMzA5IDEwNy43MDYgMTUxLjQ2MyAxMDcuNjM4IDE1MS42MjdDMTA3LjU3MyAxNTEuNzg4IDEwNy40NyAxNTEuOTM5IDEwNy4zMjkgMTUyLjA4QzEwNy4xOTEgMTUyLjIyIDEwNy4wMTIgMTUyLjMzNiAxMDYuNzkgMTUyLjQyN0MxMDYuNTY5IDE1Mi41MTYgMTA2LjMwMyAxNTIuNTYgMTA1Ljk5MyAxNTIuNTZIMTA1LjM3NlYxNTIuMTIzWk0xMDUuMzc2IDE1Mi43MTdWMTUyLjI4M0gxMDUuOTkzQzEwNi4zNTUgMTUyLjI4MyAxMDYuNjU1IDE1Mi4zMjYgMTA2Ljg5MiAxNTIuNDEyQzEwNy4xMjkgMTUyLjQ5OCAxMDcuMzE1IDE1Mi42MTIgMTA3LjQ1IDE1Mi43NTZDMTA3LjU4OCAxNTIuODk5IDEwNy42ODUgMTUzLjA1NiAxMDcuNzM5IDE1My4yMjhDMTA3Ljc5NyAxNTMuMzk4IDEwNy44MjUgMTUzLjU2NyAxMDcuODI1IDE1My43MzZDMTA3LjgyNSAxNTQuMDAyIDEwNy43OCAxNTQuMjM3IDEwNy42ODkgMTU0LjQ0M0MxMDcuNiAxNTQuNjQ5IDEwNy40NzQgMTU0LjgyMyAxMDcuMzEgMTU0Ljk2N0MxMDcuMTQ4IDE1NS4xMSAxMDYuOTU4IDE1NS4yMTggMTA2LjczOSAxNTUuMjkxQzEwNi41MjEgMTU1LjM2NCAxMDYuMjgyIDE1NS40IDEwNi4wMjUgMTU1LjRDMTA1Ljc3NyAxNTUuNCAxMDUuNTQ0IDE1NS4zNjUgMTA1LjMyNSAxNTUuMjk1QzEwNS4xMDkgMTU1LjIyNCAxMDQuOTE4IDE1NS4xMjMgMTA0Ljc1MSAxNTQuOTlDMTA0LjU4NCAxNTQuODU1IDEwNC40NTQgMTU0LjY4OSAxMDQuMzYgMTU0LjQ5NEMxMDQuMjY3IDE1NC4yOTYgMTA0LjIyIDE1NC4wNzEgMTA0LjIyIDE1My44MThIMTA0Ljk0M0MxMDQuOTQzIDE1NC4wMTYgMTA0Ljk4NSAxNTQuMTg5IDEwNS4wNzEgMTU0LjMzOEMxMDUuMTYgMTU0LjQ4NiAxMDUuMjg1IDE1NC42MDIgMTA1LjQ0NiAxNTQuNjg1QzEwNS42MSAxNTQuNzY2IDEwNS44MDMgMTU0LjgwNiAxMDYuMDI1IDE1NC44MDZDMTA2LjI0NiAxNTQuODA2IDEwNi40MzYgMTU0Ljc2OSAxMDYuNTk1IDE1NC42OTNDMTA2Ljc1NiAxNTQuNjE1IDEwNi44OCAxNTQuNDk4IDEwNi45NjYgMTU0LjM0MkMxMDcuMDU0IDE1NC4xODUgMTA3LjA5OSAxNTMuOTg5IDEwNy4wOTkgMTUzLjc1MkMxMDcuMDk5IDE1My41MTUgMTA3LjA0OSAxNTMuMzIxIDEwNi45NSAxNTMuMTdDMTA2Ljg1MSAxNTMuMDE2IDEwNi43MTEgMTUyLjkwMyAxMDYuNTI4IDE1Mi44M0MxMDYuMzQ5IDE1Mi43NTQgMTA2LjEzNyAxNTIuNzE3IDEwNS44OTIgMTUyLjcxN0gxMDUuMzc2Wk0xMTEuOTEyIDE0OS42MzVWMTU1LjMyMkgxMTEuMTU5VjE0OS42MzVIMTExLjkxMlpNMTE0LjI5NSAxNTIuMTkzVjE1Mi44MUgxMTEuNzQ4VjE1Mi4xOTNIMTE0LjI5NVpNMTE0LjY4MiAxNDkuNjM1VjE1MC4yNTJIMTExLjc0OFYxNDkuNjM1SDExNC42ODJaTTExNy4yMjIgMTU1LjRDMTE2LjkyNyAxNTUuNCAxMTYuNjYgMTU1LjM1MSAxMTYuNDIxIDE1NS4yNTJDMTE2LjE4NCAxNTUuMTUgMTE1Ljk3OSAxNTUuMDA4IDExNS44MDggMTU0LjgyNkMxMTUuNjM4IDE1NC42NDQgMTE1LjUwOCAxNTQuNDI3IDExNS40MTcgMTU0LjE3N0MxMTUuMzI2IDE1My45MjcgMTE1LjI4IDE1My42NTQgMTE1LjI4IDE1My4zNTdWMTUzLjE5M0MxMTUuMjggMTUyLjg0OSAxMTUuMzMxIDE1Mi41NDMgMTE1LjQzMyAxNTIuMjc1QzExNS41MzQgMTUyLjAwNCAxMTUuNjcyIDE1MS43NzUgMTE1Ljg0NyAxNTEuNTg4QzExNi4wMjEgMTUxLjQgMTE2LjIxOSAxNTEuMjU4IDExNi40NCAxNTEuMTYyQzExNi42NjIgMTUxLjA2NiAxMTYuODkxIDE1MS4wMTcgMTE3LjEyOCAxNTEuMDE3QzExNy40MyAxNTEuMDE3IDExNy42OSAxNTEuMDY5IDExNy45MDkgMTUxLjE3NEMxMTguMTMgMTUxLjI3OCAxMTguMzExIDE1MS40MjQgMTE4LjQ1MiAxNTEuNjExQzExOC41OTMgMTUxLjc5NiAxMTguNjk3IDE1Mi4wMTUgMTE4Ljc2NSAxNTIuMjY3QzExOC44MzIgMTUyLjUxNyAxMTguODY2IDE1Mi43OTEgMTE4Ljg2NiAxNTMuMDg4VjE1My40MTJIMTE1LjcxVjE1Mi44MjJIMTE4LjE0NFYxNTIuNzY3QzExOC4xMzMgMTUyLjU4IDExOC4wOTQgMTUyLjM5OCAxMTguMDI2IDE1Mi4yMkMxMTcuOTYxIDE1Mi4wNDMgMTE3Ljg1NyAxNTEuODk4IDExNy43MTQgMTUxLjc4M0MxMTcuNTcxIDE1MS42NjggMTE3LjM3NSAxNTEuNjExIDExNy4xMjggMTUxLjYxMUMxMTYuOTY0IDE1MS42MTEgMTE2LjgxMyAxNTEuNjQ2IDExNi42NzUgMTUxLjcxN0MxMTYuNTM3IDE1MS43ODQgMTE2LjQxOCAxNTEuODg2IDExNi4zMTkgMTUyLjAyMUMxMTYuMjIgMTUyLjE1NyAxMTYuMTQ0IDE1Mi4zMjIgMTE2LjA4OSAxNTIuNTE3QzExNi4wMzQgMTUyLjcxMyAxMTYuMDA3IDE1Mi45MzggMTE2LjAwNyAxNTMuMTkzVjE1My4zNTdDMTE2LjAwNyAxNTMuNTU4IDExNi4wMzQgMTUzLjc0NyAxMTYuMDg5IDE1My45MjRDMTE2LjE0NiAxNTQuMDk4IDExNi4yMjggMTU0LjI1MiAxMTYuMzM1IDE1NC4zODVDMTE2LjQ0NCAxNTQuNTE3IDExNi41NzYgMTU0LjYyMiAxMTYuNzI5IDE1NC42OTdDMTE2Ljg4NiAxNTQuNzczIDExNy4wNjMgMTU0LjgxIDExNy4yNjEgMTU0LjgxQzExNy41MTYgMTU0LjgxIDExNy43MzIgMTU0Ljc1OCAxMTcuOTA5IDE1NC42NTRDMTE4LjA4NiAxNTQuNTUgMTE4LjI0MSAxNTQuNDExIDExOC4zNzQgMTU0LjIzNkwxMTguODExIDE1NC41ODRDMTE4LjcyIDE1NC43MjIgMTE4LjYwNCAxNTQuODUzIDExOC40NjQgMTU0Ljk3OEMxMTguMzIzIDE1NS4xMDMgMTE4LjE1IDE1NS4yMDUgMTE3Ljk0NCAxNTUuMjgzQzExNy43NDEgMTU1LjM2MSAxMTcuNSAxNTUuNCAxMTcuMjIyIDE1NS40Wk0xMTkuNzg5IDE0OS4zMjJIMTIwLjUxNVYxNTQuNTAyTDEyMC40NTMgMTU1LjMyMkgxMTkuNzg5VjE0OS4zMjJaTTEyMy4zNzEgMTUzLjE3NFYxNTMuMjU2QzEyMy4zNzEgMTUzLjU2MyAxMjMuMzM0IDE1My44NDggMTIzLjI2MSAxNTQuMTExQzEyMy4xODggMTU0LjM3MiAxMjMuMDgyIDE1NC41OTggMTIyLjk0MSAxNTQuNzkxQzEyMi44IDE1NC45ODMgMTIyLjYyOCAxNTUuMTMzIDEyMi40MjUgMTU1LjI0QzEyMi4yMjIgMTU1LjM0NyAxMjEuOTg5IDE1NS40IDEyMS43MjYgMTU1LjRDMTIxLjQ1OCAxNTUuNCAxMjEuMjIyIDE1NS4zNTUgMTIxLjAxOSAxNTUuMjYzQzEyMC44MTkgMTU1LjE3IDEyMC42NDkgMTU1LjAzNiAxMjAuNTExIDE1NC44NjFDMTIwLjM3MyAxNTQuNjg3IDEyMC4yNjMgMTU0LjQ3NiAxMjAuMTc5IDE1NC4yMjhDMTIwLjA5OSAxNTMuOTgxIDEyMC4wNDMgMTUzLjcwMiAxMjAuMDExIDE1My4zOTJWMTUzLjAzM0MxMjAuMDQzIDE1Mi43MiAxMjAuMDk5IDE1Mi40NDEgMTIwLjE3OSAxNTIuMTkzQzEyMC4yNjMgMTUxLjk0NiAxMjAuMzczIDE1MS43MzUgMTIwLjUxMSAxNTEuNTZDMTIwLjY0OSAxNTEuMzgzIDEyMC44MTkgMTUxLjI0OSAxMjEuMDE5IDE1MS4xNThDMTIxLjIyIDE1MS4wNjQgMTIxLjQ1MyAxNTEuMDE3IDEyMS43MTggMTUxLjAxN0MxMjEuOTg0IDE1MS4wMTcgMTIyLjIyIDE1MS4wNjkgMTIyLjQyNSAxNTEuMTc0QzEyMi42MzEgMTUxLjI3NSAxMjIuODAzIDE1MS40MjEgMTIyLjk0MSAxNTEuNjExQzEyMy4wODIgMTUxLjgwMSAxMjMuMTg4IDE1Mi4wMjkgMTIzLjI2MSAxNTIuMjk1QzEyMy4zMzQgMTUyLjU1OCAxMjMuMzcxIDE1Mi44NTEgMTIzLjM3MSAxNTMuMTc0Wk0xMjIuNjQ0IDE1My4yNTZWMTUzLjE3NEMxMjIuNjQ0IDE1Mi45NjMgMTIyLjYyNSAxNTIuNzY1IDEyMi41ODYgMTUyLjU4QzEyMi41NDYgMTUyLjM5MiAxMjIuNDg0IDE1Mi4yMjggMTIyLjM5OCAxNTIuMDg4QzEyMi4zMTIgMTUxLjk0NCAxMjIuMTk5IDE1MS44MzIgMTIyLjA1OCAxNTEuNzUyQzEyMS45MTggMTUxLjY2OCAxMjEuNzQ0IDE1MS42MjcgMTIxLjUzOSAxNTEuNjI3QzEyMS4zNTYgMTUxLjYyNyAxMjEuMTk4IDE1MS42NTggMTIxLjA2MiAxNTEuNzJDMTIwLjkyOSAxNTEuNzgzIDEyMC44MTYgMTUxLjg2OCAxMjAuNzIyIDE1MS45NzRDMTIwLjYyOCAxNTIuMDc5IDEyMC41NTIgMTUyLjE5OCAxMjAuNDkyIDE1Mi4zMzRDMTIwLjQzNCAxNTIuNDY3IDEyMC4zOTIgMTUyLjYwNSAxMjAuMzYzIDE1Mi43NDhWMTUzLjY4OUMxMjAuNDA1IDE1My44NzIgMTIwLjQ3MiAxNTQuMDQ3IDEyMC41NjYgMTU0LjIxN0MxMjAuNjYyIDE1NC4zODMgMTIwLjc5IDE1NC41MiAxMjAuOTQ5IDE1NC42MjdDMTIxLjExIDE1NC43MzMgMTIxLjMwOSAxNTQuNzg3IDEyMS41NDYgMTU0Ljc4N0MxMjEuNzQyIDE1NC43ODcgMTIxLjkwOCAxNTQuNzQ4IDEyMi4wNDYgMTU0LjY3QzEyMi4xODcgMTU0LjU4OSAxMjIuMyAxNTQuNDc4IDEyMi4zODYgMTU0LjMzOEMxMjIuNDc1IDE1NC4xOTcgMTIyLjU0IDE1NC4wMzQgMTIyLjU4MiAxNTMuODQ5QzEyMi42MjMgMTUzLjY2NCAxMjIuNjQ0IDE1My40NjcgMTIyLjY0NCAxNTMuMjU2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8L2c+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwM180MTg0Xzk0NTI3KSI+CjxsaW5lIHgxPSIxNDYuNjUiIHkxPSIxNDcuMDcyIiB4Mj0iMTQ2LjY1IiB5Mj0iMTQ2LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNMTM4LjcwOSAxNTIuMDI1VjE1Mi44OTJDMTM4LjcwOSAxNTMuMzU4IDEzOC42NjcgMTUzLjc1MiAxMzguNTg0IDE1NC4wNzJDMTM4LjUwMSAxNTQuMzkyIDEzOC4zODEgMTU0LjY1IDEzOC4yMjUgMTU0Ljg0NUMxMzguMDY4IDE1NS4wNDEgMTM3Ljg4IDE1NS4xODMgMTM3LjY1OCAxNTUuMjcxQzEzNy40MzkgMTU1LjM1NyAxMzcuMTkyIDE1NS40IDEzNi45MTYgMTU1LjRDMTM2LjY5NyAxNTUuNCAxMzYuNDk1IDE1NS4zNzMgMTM2LjMxMSAxNTUuMzE4QzEzNi4xMjYgMTU1LjI2MyAxMzUuOTU5IDE1NS4xNzYgMTM1LjgxMSAxNTUuMDU2QzEzNS42NjUgMTU0LjkzNCAxMzUuNTQgMTU0Ljc3NSAxMzUuNDM2IDE1NC41OEMxMzUuMzMxIDE1NC4zODUgMTM1LjI1MiAxNTQuMTQ4IDEzNS4xOTcgMTUzLjg2OUMxMzUuMTQzIDE1My41OSAxMzUuMTE1IDE1My4yNjUgMTM1LjExNSAxNTIuODkyVjE1Mi4wMjVDMTM1LjExNSAxNTEuNTU5IDEzNS4xNTcgMTUxLjE2OCAxMzUuMjQgMTUwLjg1M0MxMzUuMzI2IDE1MC41MzggMTM1LjQ0NyAxNTAuMjg2IDEzNS42MDQgMTUwLjA5NUMxMzUuNzYgMTQ5LjkwMyAxMzUuOTQ3IDE0OS43NjUgMTM2LjE2NiAxNDkuNjgxQzEzNi4zODcgMTQ5LjU5OCAxMzYuNjM1IDE0OS41NTYgMTM2LjkwOCAxNDkuNTU2QzEzNy4xMyAxNDkuNTU2IDEzNy4zMzMgMTQ5LjU4NCAxMzcuNTE4IDE0OS42MzhDMTM3LjcwNSAxNDkuNjkxIDEzNy44NzIgMTQ5Ljc3NSAxMzguMDE4IDE0OS44OTJDMTM4LjE2MyAxNTAuMDA3IDEzOC4yODcgMTUwLjE2MSAxMzguMzg5IDE1MC4zNTNDMTM4LjQ5MyAxNTAuNTQzIDEzOC41NzIgMTUwLjc3NiAxMzguNjI3IDE1MS4wNTJDMTM4LjY4MiAxNTEuMzI5IDEzOC43MDkgMTUxLjY1MyAxMzguNzA5IDE1Mi4wMjVaTTEzNy45ODIgMTUzLjAxVjE1MS45MDRDMTM3Ljk4MiAxNTEuNjQ5IDEzNy45NjcgMTUxLjQyNSAxMzcuOTM2IDE1MS4yMzJDMTM3LjkwNyAxNTEuMDM3IDEzNy44NjQgMTUwLjg3IDEzNy44MDcgMTUwLjczMkMxMzcuNzQ5IDE1MC41OTQgMTM3LjY3NiAxNTAuNDgyIDEzNy41ODggMTUwLjM5NkMxMzcuNTAyIDE1MC4zMSAxMzcuNDAyIDE1MC4yNDggMTM3LjI4NyAxNTAuMjA5QzEzNy4xNzUgMTUwLjE2NyAxMzcuMDQ5IDE1MC4xNDYgMTM2LjkwOCAxNTAuMTQ2QzEzNi43MzYgMTUwLjE0NiAxMzYuNTg0IDE1MC4xNzkgMTM2LjQ1MSAxNTAuMjQ0QzEzNi4zMTggMTUwLjMwNiAxMzYuMjA2IDE1MC40MDcgMTM2LjExNSAxNTAuNTQ1QzEzNi4wMjcgMTUwLjY4MyAxMzUuOTU5IDE1MC44NjQgMTM1LjkxMiAxNTEuMDg4QzEzNS44NjUgMTUxLjMxMiAxMzUuODQyIDE1MS41ODQgMTM1Ljg0MiAxNTEuOTA0VjE1My4wMUMxMzUuODQyIDE1My4yNjUgMTM1Ljg1NiAxNTMuNDkgMTM1Ljg4NSAxNTMuNjg1QzEzNS45MTYgMTUzLjg4MSAxMzUuOTYyIDE1NC4wNSAxMzYuMDIyIDE1NC4xOTNDMTM2LjA4MSAxNTQuMzM0IDEzNi4xNTQgMTU0LjQ1IDEzNi4yNCAxNTQuNTQxQzEzNi4zMjYgMTU0LjYzMiAxMzYuNDI1IDE1NC43IDEzNi41MzcgMTU0Ljc0NEMxMzYuNjUyIDE1NC43ODYgMTM2Ljc3OCAxNTQuODA2IDEzNi45MTYgMTU0LjgwNkMxMzcuMDkzIDE1NC44MDYgMTM3LjI0OCAxNTQuNzczIDEzNy4zODEgMTU0LjcwNUMxMzcuNTE0IDE1NC42MzcgMTM3LjYyNCAxNTQuNTMyIDEzNy43MTMgMTU0LjM4OEMxMzcuODA0IDE1NC4yNDMgMTM3Ljg3MiAxNTQuMDU2IDEzNy45MTYgMTUzLjgzQzEzNy45NiAxNTMuNjAxIDEzNy45ODIgMTUzLjMyNyAxMzcuOTgyIDE1My4wMVpNMTQzLjU2NSAxNTMuNDA4VjE1NC4wMDJIMTM5LjQ1NlYxNTMuNTc2TDE0Mi4wMDMgMTQ5LjYzNUgxNDIuNTkyTDE0MS45NiAxNTAuNzc1TDE0MC4yNzYgMTUzLjQwOEgxNDMuNTY1Wk0xNDIuNzcyIDE0OS42MzVWMTU1LjMyMkgxNDIuMDQ5VjE0OS42MzVIMTQyLjc3MlpNMTQ3LjMxMiAxNDkuNjM1VjE1NS4zMjJIMTQ2LjU1OFYxNDkuNjM1SDE0Ny4zMTJaTTE0OS42OTUgMTUyLjE5M1YxNTIuODFIMTQ3LjE0OFYxNTIuMTkzSDE0OS42OTVaTTE1MC4wODIgMTQ5LjYzNVYxNTAuMjUySDE0Ny4xNDhWMTQ5LjYzNUgxNTAuMDgyWk0xNTIuNjIyIDE1NS40QzE1Mi4zMjcgMTU1LjQgMTUyLjA2IDE1NS4zNTEgMTUxLjgyMSAxNTUuMjUyQzE1MS41ODQgMTU1LjE1IDE1MS4zNzkgMTU1LjAwOCAxNTEuMjA3IDE1NC44MjZDMTUxLjAzOCAxNTQuNjQ0IDE1MC45MDggMTU0LjQyNyAxNTAuODE3IDE1NC4xNzdDMTUwLjcyNiAxNTMuOTI3IDE1MC42OCAxNTMuNjU0IDE1MC42OCAxNTMuMzU3VjE1My4xOTNDMTUwLjY4IDE1Mi44NDkgMTUwLjczMSAxNTIuNTQzIDE1MC44MzIgMTUyLjI3NUMxNTAuOTM0IDE1Mi4wMDQgMTUxLjA3MiAxNTEuNzc1IDE1MS4yNDcgMTUxLjU4OEMxNTEuNDIxIDE1MS40IDE1MS42MTkgMTUxLjI1OCAxNTEuODQgMTUxLjE2MkMxNTIuMDYyIDE1MS4wNjYgMTUyLjI5MSAxNTEuMDE3IDE1Mi41MjggMTUxLjAxN0MxNTIuODMgMTUxLjAxNyAxNTMuMDkgMTUxLjA2OSAxNTMuMzA5IDE1MS4xNzRDMTUzLjUzIDE1MS4yNzggMTUzLjcxMSAxNTEuNDI0IDE1My44NTIgMTUxLjYxMUMxNTMuOTkzIDE1MS43OTYgMTU0LjA5NyAxNTIuMDE1IDE1NC4xNjUgMTUyLjI2N0MxNTQuMjMyIDE1Mi41MTcgMTU0LjI2NiAxNTIuNzkxIDE1NC4yNjYgMTUzLjA4OFYxNTMuNDEySDE1MS4xMVYxNTIuODIySDE1My41NDNWMTUyLjc2N0MxNTMuNTMzIDE1Mi41OCAxNTMuNDk0IDE1Mi4zOTggMTUzLjQyNiAxNTIuMjJDMTUzLjM2MSAxNTIuMDQzIDE1My4yNTcgMTUxLjg5OCAxNTMuMTE0IDE1MS43ODNDMTUyLjk3MSAxNTEuNjY4IDE1Mi43NzUgMTUxLjYxMSAxNTIuNTI4IDE1MS42MTFDMTUyLjM2NCAxNTEuNjExIDE1Mi4yMTMgMTUxLjY0NiAxNTIuMDc1IDE1MS43MTdDMTUxLjkzNyAxNTEuNzg0IDE1MS44MTggMTUxLjg4NiAxNTEuNzE5IDE1Mi4wMjFDMTUxLjYyIDE1Mi4xNTcgMTUxLjU0MyAxNTIuMzIyIDE1MS40ODkgMTUyLjUxN0MxNTEuNDM0IDE1Mi43MTMgMTUxLjQwNyAxNTIuOTM4IDE1MS40MDcgMTUzLjE5M1YxNTMuMzU3QzE1MS40MDcgMTUzLjU1OCAxNTEuNDM0IDE1My43NDcgMTUxLjQ4OSAxNTMuOTI0QzE1MS41NDYgMTU0LjA5OCAxNTEuNjI4IDE1NC4yNTIgMTUxLjczNSAxNTQuMzg1QzE1MS44NDQgMTU0LjUxNyAxNTEuOTc2IDE1NC42MjIgMTUyLjEyOSAxNTQuNjk3QzE1Mi4yODYgMTU0Ljc3MyAxNTIuNDYzIDE1NC44MSAxNTIuNjYxIDE1NC44MUMxNTIuOTE2IDE1NC44MSAxNTMuMTMyIDE1NC43NTggMTUzLjMwOSAxNTQuNjU0QzE1My40ODYgMTU0LjU1IDE1My42NDEgMTU0LjQxMSAxNTMuNzc0IDE1NC4yMzZMMTU0LjIxMSAxNTQuNTg0QzE1NC4xMiAxNTQuNzIyIDE1NC4wMDQgMTU0Ljg1MyAxNTMuODY0IDE1NC45NzhDMTUzLjcyMyAxNTUuMTAzIDE1My41NSAxNTUuMjA1IDE1My4zNDQgMTU1LjI4M0MxNTMuMTQxIDE1NS4zNjEgMTUyLjkgMTU1LjQgMTUyLjYyMiAxNTUuNFpNMTU1LjE4OSAxNDkuMzIySDE1NS45MTVWMTU0LjUwMkwxNTUuODUzIDE1NS4zMjJIMTU1LjE4OVYxNDkuMzIyWk0xNTguNzcxIDE1My4xNzRWMTUzLjI1NkMxNTguNzcxIDE1My41NjMgMTU4LjczNCAxNTMuODQ4IDE1OC42NjEgMTU0LjExMUMxNTguNTg4IDE1NC4zNzIgMTU4LjQ4MiAxNTQuNTk4IDE1OC4zNDEgMTU0Ljc5MUMxNTguMiAxNTQuOTgzIDE1OC4wMjggMTU1LjEzMyAxNTcuODI1IDE1NS4yNEMxNTcuNjIyIDE1NS4zNDcgMTU3LjM4OSAxNTUuNCAxNTcuMTI2IDE1NS40QzE1Ni44NTggMTU1LjQgMTU2LjYyMiAxNTUuMzU1IDE1Ni40MTkgMTU1LjI2M0MxNTYuMjE4IDE1NS4xNyAxNTYuMDQ5IDE1NS4wMzYgMTU1LjkxMSAxNTQuODYxQzE1NS43NzMgMTU0LjY4NyAxNTUuNjYzIDE1NC40NzYgMTU1LjU3OSAxNTQuMjI4QzE1NS40OTggMTUzLjk4MSAxNTUuNDQyIDE1My43MDIgMTU1LjQxMSAxNTMuMzkyVjE1My4wMzNDMTU1LjQ0MiAxNTIuNzIgMTU1LjQ5OCAxNTIuNDQxIDE1NS41NzkgMTUyLjE5M0MxNTUuNjYzIDE1MS45NDYgMTU1Ljc3MyAxNTEuNzM1IDE1NS45MTEgMTUxLjU2QzE1Ni4wNDkgMTUxLjM4MyAxNTYuMjE4IDE1MS4yNDkgMTU2LjQxOSAxNTEuMTU4QzE1Ni42MiAxNTEuMDY0IDE1Ni44NTMgMTUxLjAxNyAxNTcuMTE4IDE1MS4wMTdDMTU3LjM4NCAxNTEuMDE3IDE1Ny42MiAxNTEuMDY5IDE1Ny44MjUgMTUxLjE3NEMxNTguMDMxIDE1MS4yNzUgMTU4LjIwMyAxNTEuNDIxIDE1OC4zNDEgMTUxLjYxMUMxNTguNDgyIDE1MS44MDEgMTU4LjU4OCAxNTIuMDI5IDE1OC42NjEgMTUyLjI5NUMxNTguNzM0IDE1Mi41NTggMTU4Ljc3MSAxNTIuODUxIDE1OC43NzEgMTUzLjE3NFpNMTU4LjA0NCAxNTMuMjU2VjE1My4xNzRDMTU4LjA0NCAxNTIuOTYzIDE1OC4wMjQgMTUyLjc2NSAxNTcuOTg1IDE1Mi41OEMxNTcuOTQ2IDE1Mi4zOTIgMTU3Ljg4NCAxNTIuMjI4IDE1Ny43OTggMTUyLjA4OEMxNTcuNzEyIDE1MS45NDQgMTU3LjU5OSAxNTEuODMyIDE1Ny40NTggMTUxLjc1MkMxNTcuMzE3IDE1MS42NjggMTU3LjE0NCAxNTEuNjI3IDE1Ni45MzkgMTUxLjYyN0MxNTYuNzU2IDE1MS42MjcgMTU2LjU5NyAxNTEuNjU4IDE1Ni40NjIgMTUxLjcyQzE1Ni4zMjkgMTUxLjc4MyAxNTYuMjE2IDE1MS44NjggMTU2LjEyMiAxNTEuOTc0QzE1Ni4wMjggMTUyLjA3OSAxNTUuOTUyIDE1Mi4xOTggMTU1Ljg5MiAxNTIuMzM0QzE1NS44MzQgMTUyLjQ2NyAxNTUuNzkxIDE1Mi42MDUgMTU1Ljc2MyAxNTIuNzQ4VjE1My42ODlDMTU1LjgwNCAxNTMuODcyIDE1NS44NzIgMTU0LjA0NyAxNTUuOTY2IDE1NC4yMTdDMTU2LjA2MiAxNTQuMzgzIDE1Ni4xOSAxNTQuNTIgMTU2LjM0OSAxNTQuNjI3QzE1Ni41MSAxNTQuNzMzIDE1Ni43MDkgMTU0Ljc4NyAxNTYuOTQ2IDE1NC43ODdDMTU3LjE0MiAxNTQuNzg3IDE1Ny4zMDggMTU0Ljc0OCAxNTcuNDQ2IDE1NC42N0MxNTcuNTg3IDE1NC41ODkgMTU3LjcgMTU0LjQ3OCAxNTcuNzg2IDE1NC4zMzhDMTU3Ljg3NSAxNTQuMTk3IDE1Ny45NCAxNTQuMDM0IDE1Ny45ODIgMTUzLjg0OUMxNTguMDIzIDE1My42NjQgMTU4LjA0NCAxNTMuNDY3IDE1OC4wNDQgMTUzLjI1NloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPC9nPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDRfNDE4NF85NDUyNykiPgo8bGluZSB4MT0iMTgyLjA1IiB5MT0iMTQ3LjA3MiIgeDI9IjE4Mi4wNSIgeTI9IjE0Ni4yNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNSIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPHBhdGggZD0iTTE3NC4xMDkgMTUyLjAyNVYxNTIuODkyQzE3NC4xMDkgMTUzLjM1OCAxNzQuMDY3IDE1My43NTIgMTczLjk4NCAxNTQuMDcyQzE3My45MDEgMTU0LjM5MiAxNzMuNzgxIDE1NC42NSAxNzMuNjI1IDE1NC44NDVDMTczLjQ2OSAxNTUuMDQxIDE3My4yOCAxNTUuMTgzIDE3My4wNTggMTU1LjI3MUMxNzIuODQgMTU1LjM1NyAxNzIuNTkyIDE1NS40IDE3Mi4zMTYgMTU1LjRDMTcyLjA5NyAxNTUuNCAxNzEuODk2IDE1NS4zNzMgMTcxLjcxMSAxNTUuMzE4QzE3MS41MjYgMTU1LjI2MyAxNzEuMzU5IDE1NS4xNzYgMTcxLjIxMSAxNTUuMDU2QzE3MS4wNjUgMTU0LjkzNCAxNzAuOTQgMTU0Ljc3NSAxNzAuODM2IDE1NC41OEMxNzAuNzMyIDE1NC4zODUgMTcwLjY1MiAxNTQuMTQ4IDE3MC41OTcgMTUzLjg2OUMxNzAuNTQzIDE1My41OSAxNzAuNTE1IDE1My4yNjUgMTcwLjUxNSAxNTIuODkyVjE1Mi4wMjVDMTcwLjUxNSAxNTEuNTU5IDE3MC41NTcgMTUxLjE2OCAxNzAuNjQgMTUwLjg1M0MxNzAuNzI2IDE1MC41MzggMTcwLjg0NyAxNTAuMjg2IDE3MS4wMDQgMTUwLjA5NUMxNzEuMTYgMTQ5LjkwMyAxNzEuMzQ3IDE0OS43NjUgMTcxLjU2NiAxNDkuNjgxQzE3MS43ODggMTQ5LjU5OCAxNzIuMDM1IDE0OS41NTYgMTcyLjMwOCAxNDkuNTU2QzE3Mi41MyAxNDkuNTU2IDE3Mi43MzMgMTQ5LjU4NCAxNzIuOTE4IDE0OS42MzhDMTczLjEwNSAxNDkuNjkxIDE3My4yNzIgMTQ5Ljc3NSAxNzMuNDE4IDE0OS44OTJDMTczLjU2NCAxNTAuMDA3IDE3My42ODcgMTUwLjE2MSAxNzMuNzg5IDE1MC4zNTNDMTczLjg5MyAxNTAuNTQzIDE3My45NzIgMTUwLjc3NiAxNzQuMDI3IDE1MS4wNTJDMTc0LjA4MiAxNTEuMzI5IDE3NC4xMDkgMTUxLjY1MyAxNzQuMTA5IDE1Mi4wMjVaTTE3My4zODMgMTUzLjAxVjE1MS45MDRDMTczLjM4MyAxNTEuNjQ5IDE3My4zNjcgMTUxLjQyNSAxNzMuMzM2IDE1MS4yMzJDMTczLjMwNyAxNTEuMDM3IDE3My4yNjQgMTUwLjg3IDE3My4yMDcgMTUwLjczMkMxNzMuMTUgMTUwLjU5NCAxNzMuMDc3IDE1MC40ODIgMTcyLjk4OCAxNTAuMzk2QzE3Mi45MDIgMTUwLjMxIDE3Mi44MDIgMTUwLjI0OCAxNzIuNjg3IDE1MC4yMDlDMTcyLjU3NSAxNTAuMTY3IDE3Mi40NDkgMTUwLjE0NiAxNzIuMzA4IDE1MC4xNDZDMTcyLjEzNiAxNTAuMTQ2IDE3MS45ODQgMTUwLjE3OSAxNzEuODUxIDE1MC4yNDRDMTcxLjcxOSAxNTAuMzA2IDE3MS42MDcgMTUwLjQwNyAxNzEuNTE1IDE1MC41NDVDMTcxLjQyNyAxNTAuNjgzIDE3MS4zNTkgMTUwLjg2NCAxNzEuMzEyIDE1MS4wODhDMTcxLjI2NSAxNTEuMzEyIDE3MS4yNDIgMTUxLjU4NCAxNzEuMjQyIDE1MS45MDRWMTUzLjAxQzE3MS4yNDIgMTUzLjI2NSAxNzEuMjU2IDE1My40OSAxNzEuMjg1IDE1My42ODVDMTcxLjMxNiAxNTMuODgxIDE3MS4zNjIgMTU0LjA1IDE3MS40MjIgMTU0LjE5M0MxNzEuNDgyIDE1NC4zMzQgMTcxLjU1NCAxNTQuNDUgMTcxLjY0IDE1NC41NDFDMTcxLjcyNiAxNTQuNjMyIDE3MS44MjUgMTU0LjcgMTcxLjkzNyAxNTQuNzQ0QzE3Mi4wNTIgMTU0Ljc4NiAxNzIuMTc4IDE1NC44MDYgMTcyLjMxNiAxNTQuODA2QzE3Mi40OTMgMTU0LjgwNiAxNzIuNjQ4IDE1NC43NzMgMTcyLjc4MSAxNTQuNzA1QzE3Mi45MTQgMTU0LjYzNyAxNzMuMDI1IDE1NC41MzIgMTczLjExMyAxNTQuMzg4QzE3My4yMDQgMTU0LjI0MyAxNzMuMjcyIDE1NC4wNTYgMTczLjMxNiAxNTMuODNDMTczLjM2IDE1My42MDEgMTczLjM4MyAxNTMuMzI3IDE3My4zODMgMTUzLjAxWk0xNzYuMDM2IDE1Mi42MTVMMTc1LjQ1NyAxNTIuNDY3TDE3NS43NDMgMTQ5LjYzNUgxNzguNjYxVjE1MC4zMDJIMTc2LjM1NkwxNzYuMTg0IDE1MS44NDlDMTc2LjI4OCAxNTEuNzg5IDE3Ni40MiAxNTEuNzMzIDE3Ni41NzkgMTUxLjY4MUMxNzYuNzQgMTUxLjYyOSAxNzYuOTI1IDE1MS42MDMgMTc3LjEzMyAxNTEuNjAzQzE3Ny4zOTYgMTUxLjYwMyAxNzcuNjMyIDE1MS42NDkgMTc3Ljg0IDE1MS43NEMxNzguMDQ5IDE1MS44MjkgMTc4LjIyNiAxNTEuOTU2IDE3OC4zNzEgMTUyLjEyM0MxNzguNTIgMTUyLjI4OSAxNzguNjMzIDE1Mi40OSAxNzguNzExIDE1Mi43MjRDMTc4Ljc4OSAxNTIuOTU5IDE3OC44MjkgMTUzLjIyIDE3OC44MjkgMTUzLjUxQzE3OC44MjkgMTUzLjc4MyAxNzguNzkxIDE1NC4wMzQgMTc4LjcxNSAxNTQuMjYzQzE3OC42NDIgMTU0LjQ5MyAxNzguNTMyIDE1NC42OTMgMTc4LjM4MyAxNTQuODY1QzE3OC4yMzUgMTU1LjAzNCAxNzguMDQ3IDE1NS4xNjYgMTc3LjgyMSAxNTUuMjZDMTc3LjU5NyAxNTUuMzUzIDE3Ny4zMzIgMTU1LjQgMTc3LjAyOCAxNTUuNEMxNzYuNzk5IDE1NS40IDE3Ni41ODEgMTU1LjM2OSAxNzYuMzc1IDE1NS4zMDZDMTc2LjE3MiAxNTUuMjQxIDE3NS45OSAxNTUuMTQ0IDE3NS44MjkgMTU1LjAxM0MxNzUuNjcgMTU0Ljg4MSAxNzUuNTM5IDE1NC43MTcgMTc1LjQzOCAxNTQuNTIxQzE3NS4zMzkgMTU0LjMyMyAxNzUuMjc2IDE1NC4wOTIgMTc1LjI1IDE1My44MjZIMTc1LjkzOEMxNzUuOTY5IDE1NC4wMzkgMTc2LjAzMiAxNTQuMjE5IDE3Ni4xMjUgMTU0LjM2NUMxNzYuMjE5IDE1NC41MTEgMTc2LjM0MiAxNTQuNjIyIDE3Ni40OTMgMTU0LjY5N0MxNzYuNjQ2IDE1NC43NyAxNzYuODI1IDE1NC44MDYgMTc3LjAyOCAxNTQuODA2QzE3Ny4yIDE1NC44MDYgMTc3LjM1MiAxNTQuNzc2IDE3Ny40ODUgMTU0LjcxN0MxNzcuNjE4IDE1NC42NTcgMTc3LjczIDE1NC41NzEgMTc3LjgyMSAxNTQuNDU5QzE3Ny45MTIgMTU0LjM0NyAxNzcuOTgxIDE1NC4yMTEgMTc4LjAyOCAxNTQuMDUyQzE3OC4wNzcgMTUzLjg5NCAxNzguMTAyIDE1My43MTUgMTc4LjEwMiAxNTMuNTE3QzE3OC4xMDIgMTUzLjMzOCAxNzguMDc3IDE1My4xNzEgMTc4LjAyOCAxNTMuMDE3QzE3Ny45NzggMTUyLjg2NCAxNzcuOTA0IDE1Mi43MyAxNzcuODA1IDE1Mi42MTVDMTc3LjcwOSAxNTIuNSAxNzcuNTkgMTUyLjQxMiAxNzcuNDUgMTUyLjM0OUMxNzcuMzA5IDE1Mi4yODQgMTc3LjE0OCAxNTIuMjUyIDE3Ni45NjUgMTUyLjI1MkMxNzYuNzIzIDE1Mi4yNTIgMTc2LjUzOSAxNTIuMjg0IDE3Ni40MTQgMTUyLjM0OUMxNzYuMjkyIDE1Mi40MTQgMTc2LjE2NiAxNTIuNTAzIDE3Ni4wMzYgMTUyLjYxNVpNMTgyLjcxMyAxNDkuNjM1VjE1NS4zMjJIMTgxLjk1OVYxNDkuNjM1SDE4Mi43MTNaTTE4NS4wOTUgMTUyLjE5M1YxNTIuODFIMTgyLjU0OFYxNTIuMTkzSDE4NS4wOTVaTTE4NS40ODIgMTQ5LjYzNVYxNTAuMjUySDE4Mi41NDhWMTQ5LjYzNUgxODUuNDgyWk0xODguMDIyIDE1NS40QzE4Ny43MjcgMTU1LjQgMTg3LjQ2IDE1NS4zNTEgMTg3LjIyMSAxNTUuMjUyQzE4Ni45ODQgMTU1LjE1IDE4Ni43OCAxNTUuMDA4IDE4Ni42MDggMTU0LjgyNkMxODYuNDM4IDE1NC42NDQgMTg2LjMwOCAxNTQuNDI3IDE4Ni4yMTcgMTU0LjE3N0MxODYuMTI2IDE1My45MjcgMTg2LjA4IDE1My42NTQgMTg2LjA4IDE1My4zNTdWMTUzLjE5M0MxODYuMDggMTUyLjg0OSAxODYuMTMxIDE1Mi41NDMgMTg2LjIzMyAxNTIuMjc1QzE4Ni4zMzQgMTUyLjAwNCAxODYuNDcyIDE1MS43NzUgMTg2LjY0NyAxNTEuNTg4QzE4Ni44MjEgMTUxLjQgMTg3LjAxOSAxNTEuMjU4IDE4Ny4yNCAxNTEuMTYyQzE4Ny40NjIgMTUxLjA2NiAxODcuNjkxIDE1MS4wMTcgMTg3LjkyOCAxNTEuMDE3QzE4OC4yMyAxNTEuMDE3IDE4OC40OSAxNTEuMDY5IDE4OC43MDkgMTUxLjE3NEMxODguOTMxIDE1MS4yNzggMTg5LjExMiAxNTEuNDI0IDE4OS4yNTIgMTUxLjYxMUMxODkuMzkzIDE1MS43OTYgMTg5LjQ5NyAxNTIuMDE1IDE4OS41NjUgMTUyLjI2N0MxODkuNjMyIDE1Mi41MTcgMTg5LjY2NiAxNTIuNzkxIDE4OS42NjYgMTUzLjA4OFYxNTMuNDEySDE4Ni41MVYxNTIuODIySDE4OC45NDRWMTUyLjc2N0MxODguOTMzIDE1Mi41OCAxODguODk0IDE1Mi4zOTggMTg4LjgyNiAxNTIuMjJDMTg4Ljc2MSAxNTIuMDQzIDE4OC42NTcgMTUxLjg5OCAxODguNTE0IDE1MS43ODNDMTg4LjM3MSAxNTEuNjY4IDE4OC4xNzUgMTUxLjYxMSAxODcuOTI4IDE1MS42MTFDMTg3Ljc2NCAxNTEuNjExIDE4Ny42MTMgMTUxLjY0NiAxODcuNDc1IDE1MS43MTdDMTg3LjMzNyAxNTEuNzg0IDE4Ny4yMTggMTUxLjg4NiAxODcuMTE5IDE1Mi4wMjFDMTg3LjAyIDE1Mi4xNTcgMTg2Ljk0NCAxNTIuMzIyIDE4Ni44ODkgMTUyLjUxN0MxODYuODM0IDE1Mi43MTMgMTg2LjgwNyAxNTIuOTM4IDE4Ni44MDcgMTUzLjE5M1YxNTMuMzU3QzE4Ni44MDcgMTUzLjU1OCAxODYuODM0IDE1My43NDcgMTg2Ljg4OSAxNTMuOTI0QzE4Ni45NDYgMTU0LjA5OCAxODcuMDI4IDE1NC4yNTIgMTg3LjEzNSAxNTQuMzg1QzE4Ny4yNDQgMTU0LjUxNyAxODcuMzc2IDE1NC42MjIgMTg3LjUzIDE1NC42OTdDMTg3LjY4NiAxNTQuNzczIDE4Ny44NjMgMTU0LjgxIDE4OC4wNjEgMTU0LjgxQzE4OC4zMTYgMTU0LjgxIDE4OC41MzIgMTU0Ljc1OCAxODguNzA5IDE1NC42NTRDMTg4Ljg4NiAxNTQuNTUgMTg5LjA0MSAxNTQuNDExIDE4OS4xNzQgMTU0LjIzNkwxODkuNjEyIDE1NC41ODRDMTg5LjUyIDE1NC43MjIgMTg5LjQwNSAxNTQuODUzIDE4OS4yNjQgMTU0Ljk3OEMxODkuMTIzIDE1NS4xMDMgMTg4Ljk1IDE1NS4yMDUgMTg4Ljc0NCAxNTUuMjgzQzE4OC41NDEgMTU1LjM2MSAxODguMyAxNTUuNCAxODguMDIyIDE1NS40Wk0xOTAuNTg5IDE0OS4zMjJIMTkxLjMxNVYxNTQuNTAyTDE5MS4yNTMgMTU1LjMyMkgxOTAuNTg5VjE0OS4zMjJaTTE5NC4xNzEgMTUzLjE3NFYxNTMuMjU2QzE5NC4xNzEgMTUzLjU2MyAxOTQuMTM0IDE1My44NDggMTk0LjA2MSAxNTQuMTExQzE5My45ODggMTU0LjM3MiAxOTMuODgyIDE1NC41OTggMTkzLjc0MSAxNTQuNzkxQzE5My42IDE1NC45ODMgMTkzLjQyOSAxNTUuMTMzIDE5My4yMjUgMTU1LjI0QzE5My4wMjIgMTU1LjM0NyAxOTIuNzg5IDE1NS40IDE5Mi41MjYgMTU1LjRDMTkyLjI1OCAxNTUuNCAxOTIuMDIyIDE1NS4zNTUgMTkxLjgxOSAxNTUuMjYzQzE5MS42MTkgMTU1LjE3IDE5MS40NDkgMTU1LjAzNiAxOTEuMzExIDE1NC44NjFDMTkxLjE3MyAxNTQuNjg3IDE5MS4wNjMgMTU0LjQ3NiAxOTAuOTc5IDE1NC4yMjhDMTkwLjg5OSAxNTMuOTgxIDE5MC44NDMgMTUzLjcwMiAxOTAuODExIDE1My4zOTJWMTUzLjAzM0MxOTAuODQzIDE1Mi43MiAxOTAuODk5IDE1Mi40NDEgMTkwLjk3OSAxNTIuMTkzQzE5MS4wNjMgMTUxLjk0NiAxOTEuMTczIDE1MS43MzUgMTkxLjMxMSAxNTEuNTZDMTkxLjQ0OSAxNTEuMzgzIDE5MS42MTkgMTUxLjI0OSAxOTEuODE5IDE1MS4xNThDMTkyLjAyIDE1MS4wNjQgMTkyLjI1MyAxNTEuMDE3IDE5Mi41MTggMTUxLjAxN0MxOTIuNzg0IDE1MS4wMTcgMTkzLjAyIDE1MS4wNjkgMTkzLjIyNSAxNTEuMTc0QzE5My40MzEgMTUxLjI3NSAxOTMuNjAzIDE1MS40MjEgMTkzLjc0MSAxNTEuNjExQzE5My44ODIgMTUxLjgwMSAxOTMuOTg4IDE1Mi4wMjkgMTk0LjA2MSAxNTIuMjk1QzE5NC4xMzQgMTUyLjU1OCAxOTQuMTcxIDE1Mi44NTEgMTk0LjE3MSAxNTMuMTc0Wk0xOTMuNDQ0IDE1My4yNTZWMTUzLjE3NEMxOTMuNDQ0IDE1Mi45NjMgMTkzLjQyNSAxNTIuNzY1IDE5My4zODYgMTUyLjU4QzE5My4zNDcgMTUyLjM5MiAxOTMuMjg0IDE1Mi4yMjggMTkzLjE5OCAxNTIuMDg4QzE5My4xMTIgMTUxLjk0NCAxOTIuOTk5IDE1MS44MzIgMTkyLjg1OCAxNTEuNzUyQzE5Mi43MTggMTUxLjY2OCAxOTIuNTQ0IDE1MS42MjcgMTkyLjMzOSAxNTEuNjI3QzE5Mi4xNTYgMTUxLjYyNyAxOTEuOTk4IDE1MS42NTggMTkxLjg2MiAxNTEuNzJDMTkxLjcyOSAxNTEuNzgzIDE5MS42MTYgMTUxLjg2OCAxOTEuNTIyIDE1MS45NzRDMTkxLjQyOSAxNTIuMDc5IDE5MS4zNTIgMTUyLjE5OCAxOTEuMjkyIDE1Mi4zMzRDMTkxLjIzNSAxNTIuNDY3IDE5MS4xOTIgMTUyLjYwNSAxOTEuMTYzIDE1Mi43NDhWMTUzLjY4OUMxOTEuMjA1IDE1My44NzIgMTkxLjI3MiAxNTQuMDQ3IDE5MS4zNjYgMTU0LjIxN0MxOTEuNDYyIDE1NC4zODMgMTkxLjU5IDE1NC41MiAxOTEuNzQ5IDE1NC42MjdDMTkxLjkxIDE1NC43MzMgMTkyLjExIDE1NC43ODcgMTkyLjM0NyAxNTQuNzg3QzE5Mi41NDIgMTU0Ljc4NyAxOTIuNzA4IDE1NC43NDggMTkyLjg0NyAxNTQuNjdDMTkyLjk4NyAxNTQuNTg5IDE5My4xIDE1NC40NzggMTkzLjE4NiAxNTQuMzM4QzE5My4yNzUgMTU0LjE5NyAxOTMuMzQgMTU0LjAzNCAxOTMuMzgyIDE1My44NDlDMTkzLjQyMyAxNTMuNjY0IDE5My40NDQgMTUzLjQ2NyAxOTMuNDQ0IDE1My4yNTZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjwvZz4KPHBhdGggZD0iTTI3IDEwNy40NDVMNDAgODguMDk2Mkw3NS45NDA3IDQ0Ljk4OTVDNzYuMjYxMSA0NC42MDUyIDc2LjgxNjEgNDQuNTE2OCA3Ny4yNCA0NC43ODI2TDExMC41NTYgNjUuNjcxNkMxMTAuODM0IDY1Ljg0NiAxMTEuMTggNjUuODcyOCAxMTEuNDgyIDY1Ljc0MzNMMTQ3IDUwLjQ5OUwxODQuMDMyIDM5LjgxODNDMTg0LjMyNyAzOS43MzMxIDE4NC41NjcgMzkuNTE2OCAxODQuNjgyIDM5LjIzMThMMTk4LjUgNSIgc3Ryb2tlPSIjRkZDMTA3IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cGF0aCBkPSJNMjggMTI5LjE2MUw0MCAxMTIuMTRMNzYgNzkuMTYxNEwxMTEgODkuMjY3OEwxNDcgNzkuMTYxNEwxNzggMTIwLjY1MUwxOTkgODkuMjY3OCIgc3Ryb2tlPSIjNENBRjUwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cGF0aCBkPSJNMjcgMTE4TDQwIDcxLjc3OTJMNzYgNjQuNTYzNUwxMTAuNSAxNy42NjExTDE0Ni41IDQyLjkxNjJMMTgyIDcxLjc3OTJMMTk4LjUgMTA4IiBzdHJva2U9IiMyMTk2RjMiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjwvZz4KPGRlZnM+CjxjbGlwUGF0aCBpZD0iY2xpcDBfNDE4NF85NDUyNyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8Y2xpcFBhdGggaWQ9ImNsaXAxXzQxODRfOTQ1MjciPgo8cmVjdCB3aWR0aD0iMzUuNCIgaGVpZ2h0PSIxMC4zMjIiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1OC4zOTk5IDE0NikiLz4KPC9jbGlwUGF0aD4KPGNsaXBQYXRoIGlkPSJjbGlwMl80MTg0Xzk0NTI3Ij4KPHJlY3Qgd2lkdGg9IjM1LjQiIGhlaWdodD0iMTAuMzIyIiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOTMuOCAxNDYpIi8+CjwvY2xpcFBhdGg+CjxjbGlwUGF0aCBpZD0iY2xpcDNfNDE4NF85NDUyNyI+CjxyZWN0IHdpZHRoPSIzNS40IiBoZWlnaHQ9IjEwLjMyMiIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyOS4yIDE0NikiLz4KPC9jbGlwUGF0aD4KPGNsaXBQYXRoIGlkPSJjbGlwNF80MTg0Xzk0NTI3Ij4KPHJlY3Qgd2lkdGg9IjM1LjQiIGhlaWdodD0iMTAuMzIyIiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTY0LjYgMTQ2KSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo=",
+ "description": "Displays changes to time-series data over time—for example, temperature or humidity readings.",
+ "descriptor": {
+ "type": "timeseries",
+ "sizeX": 8,
+ "sizeY": 5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n self.ctx.$scope.timeSeriesChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeSeriesChartWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.timeSeriesChartWidget.onLatestDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n chartType: 'line',\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n dataKeySettingsFunction: TbTimeSeriesChart.dataKeySettings('line'),\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries', units: '°C', decimals: 0 }];\n }\n };\n}\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}",
+ "latestDataKeySettingsSchema": "{}",
+ "settingsDirective": "tb-time-series-chart-widget-settings",
+ "dataKeySettingsDirective": "tb-time-series-chart-key-settings",
+ "latestDataKeySettingsDirective": "",
+ "hasBasicMode": true,
+ "basicModeDirective": "tb-time-series-chart-basic-config",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":true,\"step\":false,\"stepType\":\"start\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":false,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"pointShape\":\"emptyCircle\",\"pointSize\":4,\"fillAreaSettings\":{\"type\":\"opacity\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}}},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#FFC107\",\"settings\":{\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":true,\"step\":false,\"stepType\":\"start\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":false,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"pointShape\":\"emptyCircle\",\"pointSize\":4,\"fillAreaSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}}},\"_hash\":0.3409583261715494,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"decimals\":null,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":null}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":0,\"realtime\":{\"realtimeType\":0,\"timewindowMs\":60000,\"quickInterval\":\"CURRENT_DAY\",\"interval\":1000},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"top\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"yAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"xAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"bottom\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":10,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormat\":{},\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipDateInterval\":true,\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"yAxes\":{\"default\":{\"units\":null,\"decimals\":0,\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormatter\":null,\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\",\"id\":\"default\",\"order\":0}},\"noAggregationBarWidthSettings\":{\"strategy\":\"group\",\"groupWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000},\"barWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000}},\"animation\":{\"animation\":true,\"animationThreshold\":2000,\"animationDuration\":500,\"animationEasing\":\"cubicOut\",\"animationDelay\":0,\"animationDurationUpdate\":300,\"animationEasingUpdate\":\"cubicOut\",\"animationDelayUpdate\":0},\"padding\":\"12px\"},\"title\":\"Line chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"\",\"decimals\":null,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}"
+ },
+ "tags": [
+ "chart",
+ "time series",
+ "time-series",
+ "line",
+ "line chart"
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_types/point_chart.json b/application/src/main/data/json/system/widget_types/point_chart.json
new file mode 100644
index 0000000000..beb99116c9
--- /dev/null
+++ b/application/src/main/data/json/system/widget_types/point_chart.json
@@ -0,0 +1,32 @@
+{
+ "fqn": "point_chart",
+ "name": "Point chart",
+ "deprecated": false,
+ "image": "tb-image:Y2hhcnRfKDMpLnN2Zw==:IlBvaW50IGNoYXJ0IiBzeXN0ZW0gd2lkZ2V0IGltYWdl;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF80MTgyXzExMTkzKSI+CjxwYXRoIGQ9Ik0yLjg0NzY2IDEuMjgxMjVWN0gyLjEyNVYyLjE4MzU5TDAuNjY3OTY5IDIuNzE0ODRWMi4wNjI1TDIuNzM0MzggMS4yODEyNUgyLjg0NzY2Wk04LjY3NTE3IDMuNzAzMTJWNC41NzAzMUM4LjY3NTE3IDUuMDM2NDYgOC42MzM1MSA1LjQyOTY5IDguNTUwMTcgNS43NUM4LjQ2Njg0IDYuMDcwMzEgOC4zNDcwNSA2LjMyODEyIDguMTkwOCA2LjUyMzQ0QzguMDM0NTUgNi43MTg3NSA3Ljg0NTc1IDYuODYwNjggNy42MjQzOSA2Ljk0OTIyQzcuNDA1NjQgNy4wMzUxNiA3LjE1ODI1IDcuMDc4MTIgNi44ODIyIDcuMDc4MTJDNi42NjM0NSA3LjA3ODEyIDYuNDYxNjMgNy4wNTA3OCA2LjI3NjczIDYuOTk2MDlDNi4wOTE4NCA2Ljk0MTQxIDUuOTI1MTcgNi44NTQxNyA1Ljc3NjczIDYuNzM0MzhDNS42MzA5IDYuNjExOTggNS41MDU5IDYuNDUzMTIgNS40MDE3MyA2LjI1NzgxQzUuMjk3NTcgNi4wNjI1IDUuMjE4MTQgNS44MjU1MiA1LjE2MzQ1IDUuNTQ2ODhDNS4xMDg3NyA1LjI2ODIzIDUuMDgxNDIgNC45NDI3MSA1LjA4MTQyIDQuNTcwMzFWMy43MDMxMkM1LjA4MTQyIDMuMjM2OTggNS4xMjMwOSAyLjg0NjM1IDUuMjA2NDIgMi41MzEyNUM1LjI5MjM2IDIuMjE2MTUgNS40MTM0NSAxLjk2MzU0IDUuNTY5NyAxLjc3MzQ0QzUuNzI1OTUgMS41ODA3MyA1LjkxMzQ1IDEuNDQyNzEgNi4xMzIyIDEuMzU5MzhDNi4zNTM1NiAxLjI3NjA0IDYuNjAwOTUgMS4yMzQzOCA2Ljg3NDM5IDEuMjM0MzhDNy4wOTU3NSAxLjIzNDM4IDcuMjk4ODcgMS4yNjE3MiA3LjQ4Mzc3IDEuMzE2NDFDNy42NzEyNyAxLjM2ODQ5IDcuODM3OTMgMS40NTMxMiA3Ljk4Mzc3IDEuNTcwMzFDOC4xMjk2IDEuNjg0OSA4LjI1MzMgMS44Mzg1NCA4LjM1NDg2IDIuMDMxMjVDOC40NTkwMyAyLjIyMTM1IDguNTM4NDUgMi40NTQ0MyA4LjU5MzE0IDIuNzMwNDdDOC42NDc4MyAzLjAwNjUxIDguNjc1MTcgMy4zMzA3MyA4LjY3NTE3IDMuNzAzMTJaTTcuOTQ4NjEgNC42ODc1VjMuNTgyMDNDNy45NDg2MSAzLjMyNjgyIDcuOTMyOTggMy4xMDI4NiA3LjkwMTczIDIuOTEwMTZDNy44NzMwOSAyLjcxNDg0IDcuODMwMTIgMi41NDgxOCA3Ljc3MjgzIDIuNDEwMTZDNy43MTU1NCAyLjI3MjE0IDcuNjQyNjIgMi4xNjAxNiA3LjU1NDA4IDIuMDc0MjJDNy40NjgxNCAxLjk4ODI4IDcuMzY3ODggMS45MjU3OCA3LjI1MzMgMS44ODY3MkM3LjE0MTMyIDEuODQ1MDUgNy4wMTUwMiAxLjgyNDIyIDYuODc0MzkgMS44MjQyMkM2LjcwMjUyIDEuODI0MjIgNi41NTAxNyAxLjg1Njc3IDYuNDE3MzYgMS45MjE4OEM2LjI4NDU1IDEuOTg0MzggNi4xNzI1NyAyLjA4NDY0IDYuMDgxNDIgMi4yMjI2NkM1Ljk5Mjg4IDIuMzYwNjggNS45MjUxNyAyLjU0MTY3IDUuODc4MyAyLjc2NTYyQzUuODMxNDIgMi45ODk1OCA1LjgwNzk4IDMuMjYxNzIgNS44MDc5OCAzLjU4MjAzVjQuNjg3NUM1LjgwNzk4IDQuOTQyNzEgNS44MjIzMSA1LjE2Nzk3IDUuODUwOTUgNS4zNjMyOEM1Ljg4MjIgNS41NTg1OSA1LjkyNzc4IDUuNzI3ODYgNS45ODc2NyA1Ljg3MTA5QzYuMDQ3NTcgNi4wMTE3MiA2LjEyMDQ4IDYuMTI3NiA2LjIwNjQyIDYuMjE4NzVDNi4yOTIzNiA2LjMwOTkgNi4zOTEzMiA2LjM3NzYgNi41MDMzIDYuNDIxODhDNi42MTc4OCA2LjQ2MzU0IDYuNzQ0MTggNi40ODQzOCA2Ljg4MjIgNi40ODQzOEM3LjA1OTI5IDYuNDg0MzggNy4yMTQyMyA2LjQ1MDUyIDcuMzQ3MDUgNi4zODI4MUM3LjQ3OTg2IDYuMzE1MSA3LjU5MDU0IDYuMjA5NjQgNy42NzkwOCA2LjA2NjQxQzcuNzcwMjIgNS45MjA1NyA3LjgzNzkzIDUuNzM0MzggNy44ODIyIDUuNTA3ODFDNy45MjY0NyA1LjI3ODY1IDcuOTQ4NjEgNS4wMDUyMSA3Ljk0ODYxIDQuNjg3NVpNMTMuMzA3NCAzLjcwMzEyVjQuNTcwMzFDMTMuMzA3NCA1LjAzNjQ2IDEzLjI2NTcgNS40Mjk2OSAxMy4xODI0IDUuNzVDMTMuMDk5IDYuMDcwMzEgMTIuOTc5MyA2LjMyODEyIDEyLjgyMyA2LjUyMzQ0QzEyLjY2NjggNi43MTg3NSAxMi40Nzc5IDYuODYwNjggMTIuMjU2NiA2Ljk0OTIyQzEyLjAzNzggNy4wMzUxNiAxMS43OTA0IDcuMDc4MTIgMTEuNTE0NCA3LjA3ODEyQzExLjI5NTcgNy4wNzgxMiAxMS4wOTM4IDcuMDUwNzggMTAuOTA4OSA2Ljk5NjA5QzEwLjcyNCA2Ljk0MTQxIDEwLjU1NzQgNi44NTQxNyAxMC40MDg5IDYuNzM0MzhDMTAuMjYzMSA2LjYxMTk4IDEwLjEzODEgNi40NTMxMiAxMC4wMzM5IDYuMjU3ODFDOS45Mjk3NyA2LjA2MjUgOS44NTAzNCA1LjgyNTUyIDkuNzk1NjYgNS41NDY4OEM5Ljc0MDk3IDUuMjY4MjMgOS43MTM2MyA0Ljk0MjcxIDkuNzEzNjMgNC41NzAzMVYzLjcwMzEyQzkuNzEzNjMgMy4yMzY5OCA5Ljc1NTI5IDIuODQ2MzUgOS44Mzg2MyAyLjUzMTI1QzkuOTI0NTYgMi4yMTYxNSAxMC4wNDU3IDEuOTYzNTQgMTAuMjAxOSAxLjc3MzQ0QzEwLjM1ODIgMS41ODA3MyAxMC41NDU3IDEuNDQyNzEgMTAuNzY0NCAxLjM1OTM4QzEwLjk4NTggMS4yNzYwNCAxMS4yMzMyIDEuMjM0MzggMTEuNTA2NiAxLjIzNDM4QzExLjcyNzkgMS4yMzQzOCAxMS45MzExIDEuMjYxNzIgMTIuMTE2IDEuMzE2NDFDMTIuMzAzNSAxLjM2ODQ5IDEyLjQ3MDEgMS40NTMxMiAxMi42MTYgMS41NzAzMUMxMi43NjE4IDEuNjg0OSAxMi44ODU1IDEuODM4NTQgMTIuOTg3MSAyLjAzMTI1QzEzLjA5MTIgMi4yMjEzNSAxMy4xNzA3IDIuNDU0NDMgMTMuMjI1MyAyLjczMDQ3QzEzLjI4IDMuMDA2NTEgMTMuMzA3NCAzLjMzMDczIDEzLjMwNzQgMy43MDMxMlpNMTIuNTgwOCA0LjY4NzVWMy41ODIwM0MxMi41ODA4IDMuMzI2ODIgMTIuNTY1MiAzLjEwMjg2IDEyLjUzMzkgMi45MTAxNkMxMi41MDUzIDIuNzE0ODQgMTIuNDYyMyAyLjU0ODE4IDEyLjQwNSAyLjQxMDE2QzEyLjM0NzcgMi4yNzIxNCAxMi4yNzQ4IDIuMTYwMTYgMTIuMTg2MyAyLjA3NDIyQzEyLjEwMDMgMS45ODgyOCAxMi4wMDAxIDEuOTI1NzggMTEuODg1NSAxLjg4NjcyQzExLjc3MzUgMS44NDUwNSAxMS42NDcyIDEuODI0MjIgMTEuNTA2NiAxLjgyNDIyQzExLjMzNDcgMS44MjQyMiAxMS4xODI0IDEuODU2NzcgMTEuMDQ5NiAxLjkyMTg4QzEwLjkxNjggMS45ODQzOCAxMC44MDQ4IDIuMDg0NjQgMTAuNzEzNiAyLjIyMjY2QzEwLjYyNTEgMi4zNjA2OCAxMC41NTc0IDIuNTQxNjcgMTAuNTEwNSAyLjc2NTYyQzEwLjQ2MzYgMi45ODk1OCAxMC40NDAyIDMuMjYxNzIgMTAuNDQwMiAzLjU4MjAzVjQuNjg3NUMxMC40NDAyIDQuOTQyNzEgMTAuNDU0NSA1LjE2Nzk3IDEwLjQ4MzIgNS4zNjMyOEMxMC41MTQ0IDUuNTU4NTkgMTAuNTYgNS43Mjc4NiAxMC42MTk5IDUuODcxMDlDMTAuNjc5OCA2LjAxMTcyIDEwLjc1MjcgNi4xMjc2IDEwLjgzODYgNi4yMTg3NUMxMC45MjQ2IDYuMzA5OSAxMS4wMjM1IDYuMzc3NiAxMS4xMzU1IDYuNDIxODhDMTEuMjUwMSA2LjQ2MzU0IDExLjM3NjQgNi40ODQzOCAxMS41MTQ0IDYuNDg0MzhDMTEuNjkxNSA2LjQ4NDM4IDExLjg0NjQgNi40NTA1MiAxMS45NzkzIDYuMzgyODFDMTIuMTEyMSA2LjMxNTEgMTIuMjIyNyA2LjIwOTY0IDEyLjMxMTMgNi4wNjY0MUMxMi40MDI0IDUuOTIwNTcgMTIuNDcwMSA1LjczNDM4IDEyLjUxNDQgNS41MDc4MUMxMi41NTg3IDUuMjc4NjUgMTIuNTgwOCA1LjAwNTIxIDEyLjU4MDggNC42ODc1Wk0xNC4zMDY4IDIuNzA3MDNWMi40MDYyNUMxNC4zMDY4IDIuMTkwMSAxNC4zNTM2IDEuOTkzNDkgMTQuNDQ3NCAxLjgxNjQxQzE0LjU0MTEgMS42MzkzMiAxNC42NzUzIDEuNDk3NCAxNC44NDk3IDEuMzkwNjJDMTUuMDI0MiAxLjI4Mzg1IDE1LjIzMTIgMS4yMzA0NyAxNS40NzA4IDEuMjMwNDdDMTUuNzE1NiAxLjIzMDQ3IDE1LjkyNCAxLjI4Mzg1IDE2LjA5NTggMS4zOTA2MkMxNi4yNzAzIDEuNDk3NCAxNi40MDQ0IDEuNjM5MzIgMTYuNDk4MiAxLjgxNjQxQzE2LjU5MTkgMS45OTM0OSAxNi42Mzg4IDIuMTkwMSAxNi42Mzg4IDIuNDA2MjVWMi43MDcwM0MxNi42Mzg4IDIuOTE3OTcgMTYuNTkxOSAzLjExMTk4IDE2LjQ5ODIgMy4yODkwNkMxNi40MDcgMy40NjYxNSAxNi4yNzQyIDMuNjA4MDcgMTYuMDk5NyAzLjcxNDg0QzE1LjkyNzkgMy44MjE2MSAxNS43MjA4IDMuODc1IDE1LjQ3ODYgMy44NzVDMTUuMjM2NSAzLjg3NSAxNS4wMjY4IDMuODIxNjEgMTQuODQ5NyAzLjcxNDg0QzE0LjY3NTMgMy42MDgwNyAxNC41NDExIDMuNDY2MTUgMTQuNDQ3NCAzLjI4OTA2QzE0LjM1MzYgMy4xMTE5OCAxNC4zMDY4IDIuOTE3OTcgMTQuMzA2OCAyLjcwNzAzWk0xNC44NDk3IDIuNDA2MjVWMi43MDcwM0MxNC44NDk3IDIuODI2ODIgMTQuODcxOSAyLjk0MDEgMTQuOTE2MSAzLjA0Njg4QzE0Ljk2MyAzLjE1MzY1IDE1LjAzMzMgMy4yNDA4OSAxNS4xMjcxIDMuMzA4NTlDMTUuMjIwOCAzLjM3MzcgMTUuMzM4IDMuNDA2MjUgMTUuNDc4NiAzLjQwNjI1QzE1LjYxOTMgMy40MDYyNSAxNS43MzUyIDMuMzczNyAxNS44MjYzIDMuMzA4NTlDMTUuOTE3NCAzLjI0MDg5IDE1Ljk4NTIgMy4xNTM2NSAxNi4wMjk0IDMuMDQ2ODhDMTYuMDczNyAyLjk0MDEgMTYuMDk1OCAyLjgyNjgyIDE2LjA5NTggMi43MDcwM1YyLjQwNjI1QzE2LjA5NTggMi4yODM4NSAxNi4wNzI0IDIuMTY5MjcgMTYuMDI1NSAyLjA2MjVDMTUuOTgxMiAxLjk1MzEyIDE1LjkxMjIgMS44NjU4OSAxNS44MTg1IDEuODAwNzhDMTUuNzI3MyAxLjczMzA3IDE1LjYxMTUgMS42OTkyMiAxNS40NzA4IDEuNjk5MjJDMTUuMzMyOCAxLjY5OTIyIDE1LjIxNjkgMS43MzMwNyAxNS4xMjMyIDEuODAwNzhDMTUuMDMyIDEuODY1ODkgMTQuOTYzIDEuOTUzMTIgMTQuOTE2MSAyLjA2MjVDMTQuODcxOSAyLjE2OTI3IDE0Ljg0OTcgMi4yODM4NSAxNC44NDk3IDIuNDA2MjVaTTE3LjA3NjMgNS45MTAxNlY1LjYwNTQ3QzE3LjA3NjMgNS4zOTE5MyAxNy4xMjMyIDUuMTk2NjEgMTcuMjE2OSA1LjAxOTUzQzE3LjMxMDcgNC44NDI0NSAxNy40NDQ4IDQuNzAwNTIgMTcuNjE5MyA0LjU5Mzc1QzE3Ljc5MzcgNC40ODY5OCAxOC4wMDA4IDQuNDMzNTkgMTguMjQwNCA0LjQzMzU5QzE4LjQ4NTIgNC40MzM1OSAxOC42OTM1IDQuNDg2OTggMTguODY1NCA0LjU5Mzc1QzE5LjAzOTggNC43MDA1MiAxOS4xNzQgNC44NDI0NSAxOS4yNjc3IDUuMDE5NTNDMTkuMzYxNSA1LjE5NjYxIDE5LjQwODMgNS4zOTE5MyAxOS40MDgzIDUuNjA1NDdWNS45MTAxNkMxOS40MDgzIDYuMTIzNyAxOS4zNjE1IDYuMzE5MDEgMTkuMjY3NyA2LjQ5NjA5QzE5LjE3NjYgNi42NzMxOCAxOS4wNDM3IDYuODE1MSAxOC44NjkzIDYuOTIxODhDMTguNjk3NCA3LjAyODY1IDE4LjQ5MDQgNy4wODIwMyAxOC4yNDgyIDcuMDgyMDNDMTguMDA2IDcuMDgyMDMgMTcuNzk3NyA3LjAyODY1IDE3LjYyMzIgNi45MjE4OEMxNy40NDg3IDYuODE1MSAxNy4zMTMzIDYuNjczMTggMTcuMjE2OSA2LjQ5NjA5QzE3LjEyMzIgNi4zMTkwMSAxNy4wNzYzIDYuMTIzNyAxNy4wNzYzIDUuOTEwMTZaTTE3LjYxOTMgNS42MDU0N1Y1LjkxMDE2QzE3LjYxOTMgNi4wMjk5NSAxNy42NDE0IDYuMTQ0NTMgMTcuNjg1NyA2LjI1MzkxQzE3LjczMjUgNi4zNjA2OCAxNy44MDI5IDYuNDQ3OTIgMTcuODk2NiA2LjUxNTYyQzE3Ljk5MDQgNi41ODA3MyAxOC4xMDc1IDYuNjEzMjggMTguMjQ4MiA2LjYxMzI4QzE4LjM4ODggNi42MTMyOCAxOC41MDQ3IDYuNTgwNzMgMTguNTk1OCA2LjUxNTYyQzE4LjY4OTYgNi40NDc5MiAxOC43NTg2IDYuMzYwNjggMTguODAyOSA2LjI1MzkxQzE4Ljg0NzEgNi4xNDcxNCAxOC44NjkzIDYuMDMyNTUgMTguODY5MyA1LjkxMDE2VjUuNjA1NDdDMTguODY5MyA1LjQ4MzA3IDE4Ljg0NTggNS4zNjg0OSAxOC43OTkgNS4yNjE3MkMxOC43NTQ3IDUuMTU0OTUgMTguNjg1NyA1LjA2OTAxIDE4LjU5MTkgNS4wMDM5MUMxOC41MDA4IDQuOTM2MiAxOC4zODM2IDQuOTAyMzQgMTguMjQwNCA0LjkwMjM0QzE4LjEwMjMgNC45MDIzNCAxNy45ODY1IDQuOTM2MiAxNy44OTI3IDUuMDAzOTFDMTcuODAxNiA1LjA2OTAxIDE3LjczMjUgNS4xNTQ5NSAxNy42ODU3IDUuMjYxNzJDMTcuNjQxNCA1LjM2ODQ5IDE3LjYxOTMgNS40ODMwNyAxNy42MTkzIDUuNjA1NDdaTTE4LjQyIDIuMTIxMDlMMTUuNjQyNyA2LjU2NjQxTDE1LjIzNjUgNi4zMDg1OUwxOC4wMTM4IDEuODYzMjhMMTguNDIgMi4xMjEwOVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTguMDU4NTkgMzMuNjY3N0M4LjA1ODU5IDM0LjAxNDEgNy45Nzc4NiAzNC4zMDgzIDcuODE2NDEgMzQuNTUwNUM3LjY1NzU1IDM0Ljc5MDEgNy40NDE0MSAzNC45NzI0IDcuMTY3OTcgMzUuMDk3NEM2Ljg5NzE0IDM1LjIyMjQgNi41OTExNSAzNS4yODQ5IDYuMjUgMzUuMjg0OUM1LjkwODg1IDM1LjI4NDkgNS42MDE1NiAzNS4yMjI0IDUuMzI4MTIgMzUuMDk3NEM1LjA1NDY5IDM0Ljk3MjQgNC44Mzg1NCAzNC43OTAxIDQuNjc5NjkgMzQuNTUwNUM0LjUyMDgzIDM0LjMwODMgNC40NDE0MSAzNC4wMTQxIDQuNDQxNDEgMzMuNjY3N0M0LjQ0MTQxIDMzLjQ0MTIgNC40ODQzOCAzMy4yMzQxIDQuNTcwMzEgMzMuMDQ2NkM0LjY1ODg1IDMyLjg1NjUgNC43ODI1NSAzMi42OTEyIDQuOTQxNDEgMzIuNTUwNUM1LjEwMjg2IDMyLjQwOTkgNS4yOTI5NyAzMi4zMDE4IDUuNTExNzIgMzIuMjI2M0M1LjczMzA3IDMyLjE0ODIgNS45NzY1NiAzMi4xMDkxIDYuMjQyMTkgMzIuMTA5MUM2LjU5MTE1IDMyLjEwOTEgNi45MDIzNCAzMi4xNzY4IDcuMTc1NzggMzIuMzEyM0M3LjQ0OTIyIDMyLjQ0NTEgNy42NjQwNiAzMi42Mjg3IDcuODIwMzEgMzIuODYzQzcuOTc5MTcgMzMuMDk3NCA4LjA1ODU5IDMzLjM2NTYgOC4wNTg1OSAzMy42Njc3Wk03LjMzMjAzIDMzLjY1MjFDNy4zMzIwMyAzMy40NDEyIDcuMjg2NDYgMzMuMjU1IDcuMTk1MzEgMzMuMDkzNUM3LjEwNDE3IDMyLjkyOTQgNi45NzY1NiAzMi44MDE4IDYuODEyNSAzMi43MTA3QzYuNjQ4NDQgMzIuNjE5NSA2LjQ1ODMzIDMyLjU3NCA2LjI0MjE5IDMyLjU3NEM2LjAyMDgzIDMyLjU3NCA1LjgyOTQzIDMyLjYxOTUgNS42Njc5NyAzMi43MTA3QzUuNTA5MTEgMzIuODAxOCA1LjM4NTQyIDMyLjkyOTQgNS4yOTY4OCAzMy4wOTM1QzUuMjA4MzMgMzMuMjU1IDUuMTY0MDYgMzMuNDQxMiA1LjE2NDA2IDMzLjY1MjFDNS4xNjQwNiAzMy44NzA4IDUuMjA3MDMgMzQuMDU4MyA1LjI5Mjk3IDM0LjIxNDZDNS4zODE1MSAzNC4zNjgyIDUuNTA2NTEgMzQuNDg2NyA1LjY2Nzk3IDM0LjU3MDFDNS44MzIwMyAzNC42NTA4IDYuMDI2MDQgMzQuNjkxMiA2LjI1IDM0LjY5MTJDNi40NzM5NiAzNC42OTEyIDYuNjY2NjcgMzQuNjUwOCA2LjgyODEyIDM0LjU3MDFDNi45ODk1OCAzNC40ODY3IDcuMTEzMjggMzQuMzY4MiA3LjE5OTIyIDM0LjIxNDZDNy4yODc3NiAzNC4wNTgzIDcuMzMyMDMgMzMuODcwOCA3LjMzMjAzIDMzLjY1MjFaTTcuOTI1NzggMzAuOTk5OEM3LjkyNTc4IDMxLjI3NTggNy44NTI4NiAzMS41MjQ1IDcuNzA3MDMgMzEuNzQ1OEM3LjU2MTIgMzEuOTY3MiA3LjM2MTk4IDMyLjE0MTcgNy4xMDkzOCAzMi4yNjkzQzYuODU2NzcgMzIuMzk2OSA2LjU3MDMxIDMyLjQ2MDcgNi4yNSAzMi40NjA3QzUuOTI0NDggMzIuNDYwNyA1LjYzNDExIDMyLjM5NjkgNS4zNzg5MSAzMi4yNjkzQzUuMTI2MyAzMi4xNDE3IDQuOTI4MzkgMzEuOTY3MiA0Ljc4NTE2IDMxLjc0NThDNC42NDE5MyAzMS41MjQ1IDQuNTcwMzEgMzEuMjc1OCA0LjU3MDMxIDMwLjk5OThDNC41NzAzMSAzMC42NjkgNC42NDE5MyAzMC4zODc4IDQuNzg1MTYgMzAuMTU2QzQuOTMwOTkgMjkuOTI0MiA1LjEzMDIxIDI5Ljc0NzIgNS4zODI4MSAyOS42MjQ4QzUuNjM1NDIgMjkuNTAyNCA1LjkyMzE4IDI5LjQ0MTIgNi4yNDYwOSAyOS40NDEyQzYuNTcxNjEgMjkuNDQxMiA2Ljg2MDY4IDI5LjUwMjQgNy4xMTMyOCAyOS42MjQ4QzcuMzY1ODkgMjkuNzQ3MiA3LjU2MzggMjkuOTI0MiA3LjcwNzAzIDMwLjE1NkM3Ljg1Mjg2IDMwLjM4NzggNy45MjU3OCAzMC42NjkgNy45MjU3OCAzMC45OTk4Wk03LjIwMzEyIDMxLjAxMTVDNy4yMDMxMiAzMC44MjE0IDcuMTYyNzYgMzAuNjUzNCA3LjA4MjAzIDMwLjUwNzZDNy4wMDEzIDMwLjM2MTcgNi44ODkzMiAzMC4yNDcyIDYuNzQ2MDkgMzAuMTYzOEM2LjYwMjg2IDMwLjA3NzkgNi40MzYyIDMwLjAzNDkgNi4yNDYwOSAzMC4wMzQ5QzYuMDU1OTkgMzAuMDM0OSA1Ljg4OTMyIDMwLjA3NTMgNS43NDYwOSAzMC4xNTZDNS42MDU0NyAzMC4yMzQxIDUuNDk0NzkgMzAuMzQ2MSA1LjQxNDA2IDMwLjQ5MTlDNS4zMzU5NCAzMC42Mzc4IDUuMjk2ODggMzAuODExIDUuMjk2ODggMzEuMDExNUM1LjI5Njg4IDMxLjIwNjggNS4zMzU5NCAzMS4zNzc0IDUuNDE0MDYgMzEuNTIzMkM1LjQ5NDc5IDMxLjY2OSA1LjYwNjc3IDMxLjc4MjMgNS43NSAzMS44NjNDNS44OTMyMyAzMS45NDM4IDYuMDU5OSAzMS45ODQxIDYuMjUgMzEuOTg0MUM2LjQ0MDEgMzEuOTg0MSA2LjYwNTQ3IDMxLjk0MzggNi43NDYwOSAzMS44NjNDNi44ODkzMiAzMS43ODIzIDcuMDAxMyAzMS42NjkgNy4wODIwMyAzMS41MjMyQzcuMTYyNzYgMzEuMzc3NCA3LjIwMzEyIDMxLjIwNjggNy4yMDMxMiAzMS4wMTE1Wk0xMi42NzUyIDMxLjkwOTlWMzIuNzc3MUMxMi42NzUyIDMzLjI0MzIgMTIuNjMzNSAzMy42MzY1IDEyLjU1MDIgMzMuOTU2OEMxMi40NjY4IDM0LjI3NzEgMTIuMzQ3IDM0LjUzNDkgMTIuMTkwOCAzNC43MzAyQzEyLjAzNDUgMzQuOTI1NSAxMS44NDU3IDM1LjA2NzUgMTEuNjI0NCAzNS4xNTZDMTEuNDA1NiAzNS4yNDE5IDExLjE1ODIgMzUuMjg0OSAxMC44ODIyIDM1LjI4NDlDMTAuNjYzNSAzNS4yODQ5IDEwLjQ2MTYgMzUuMjU3NiAxMC4yNzY3IDM1LjIwMjlDMTAuMDkxOCAzNS4xNDgyIDkuOTI1MTcgMzUuMDYxIDkuNzc2NzMgMzQuOTQxMkM5LjYzMDkgMzQuODE4OCA5LjUwNTkgMzQuNjU5OSA5LjQwMTczIDM0LjQ2NDZDOS4yOTc1NyAzNC4yNjkzIDkuMjE4MTQgMzQuMDMyMyA5LjE2MzQ1IDMzLjc1MzdDOS4xMDg3NyAzMy40NzUgOS4wODE0MiAzMy4xNDk1IDkuMDgxNDIgMzIuNzc3MVYzMS45MDk5QzkuMDgxNDIgMzEuNDQzOCA5LjEyMzA5IDMxLjA1MzEgOS4yMDY0MiAzMC43MzhDOS4yOTIzNiAzMC40MjI5IDkuNDEzNDUgMzAuMTcwMyA5LjU2OTcgMjkuOTgwMkM5LjcyNTk1IDI5Ljc4NzUgOS45MTM0NSAyOS42NDk1IDEwLjEzMjIgMjkuNTY2MkMxMC4zNTM2IDI5LjQ4MjggMTAuNjAxIDI5LjQ0MTIgMTAuODc0NCAyOS40NDEyQzExLjA5NTcgMjkuNDQxMiAxMS4yOTg5IDI5LjQ2ODUgMTEuNDgzOCAyOS41MjMyQzExLjY3MTMgMjkuNTc1MyAxMS44Mzc5IDI5LjY1OTkgMTEuOTgzOCAyOS43NzcxQzEyLjEyOTYgMjkuODkxNyAxMi4yNTMzIDMwLjA0NTMgMTIuMzU0OSAzMC4yMzhDMTIuNDU5IDMwLjQyODEgMTIuNTM4NSAzMC42NjEyIDEyLjU5MzEgMzAuOTM3M0MxMi42NDc4IDMxLjIxMzMgMTIuNjc1MiAzMS41Mzc1IDEyLjY3NTIgMzEuOTA5OVpNMTEuOTQ4NiAzMi44OTQzVjMxLjc4ODhDMTEuOTQ4NiAzMS41MzM2IDExLjkzMyAzMS4zMDk3IDExLjkwMTcgMzEuMTE2OUMxMS44NzMxIDMwLjkyMTYgMTEuODMwMSAzMC43NTUgMTEuNzcyOCAzMC42MTY5QzExLjcxNTUgMzAuNDc4OSAxMS42NDI2IDMwLjM2NjkgMTEuNTU0MSAzMC4yODFDMTEuNDY4MSAzMC4xOTUxIDExLjM2NzkgMzAuMTMyNiAxMS4yNTMzIDMwLjA5MzVDMTEuMTQxMyAzMC4wNTE4IDExLjAxNSAzMC4wMzEgMTAuODc0NCAzMC4wMzFDMTAuNzAyNSAzMC4wMzEgMTAuNTUwMiAzMC4wNjM2IDEwLjQxNzQgMzAuMTI4N0MxMC4yODQ1IDMwLjE5MTIgMTAuMTcyNiAzMC4yOTE0IDEwLjA4MTQgMzAuNDI5NEM5Ljk5Mjg4IDMwLjU2NzUgOS45MjUxNyAzMC43NDg1IDkuODc4MyAzMC45NzI0QzkuODMxNDIgMzEuMTk2NCA5LjgwNzk4IDMxLjQ2ODUgOS44MDc5OCAzMS43ODg4VjMyLjg5NDNDOS44MDc5OCAzMy4xNDk1IDkuODIyMzEgMzMuMzc0OCA5Ljg1MDk1IDMzLjU3MDFDOS44ODIyIDMzLjc2NTQgOS45Mjc3OCAzMy45MzQ3IDkuOTg3NjcgMzQuMDc3OUMxMC4wNDc2IDM0LjIxODUgMTAuMTIwNSAzNC4zMzQ0IDEwLjIwNjQgMzQuNDI1NUMxMC4yOTI0IDM0LjUxNjcgMTAuMzkxMyAzNC41ODQ0IDEwLjUwMzMgMzQuNjI4N0MxMC42MTc5IDM0LjY3MDMgMTAuNzQ0MiAzNC42OTEyIDEwLjg4MjIgMzQuNjkxMkMxMS4wNTkzIDM0LjY5MTIgMTEuMjE0MiAzNC42NTczIDExLjM0NyAzNC41ODk2QzExLjQ3OTkgMzQuNTIxOSAxMS41OTA1IDM0LjQxNjQgMTEuNjc5MSAzNC4yNzMyQzExLjc3MDIgMzQuMTI3NCAxMS44Mzc5IDMzLjk0MTIgMTEuODgyMiAzMy43MTQ2QzExLjkyNjUgMzMuNDg1NCAxMS45NDg2IDMzLjIxMiAxMS45NDg2IDMyLjg5NDNaTTEzLjY3NDYgMzAuOTEzOFYzMC42MTNDMTMuNjc0NiAzMC4zOTY5IDEzLjcyMTQgMzAuMjAwMyAxMy44MTUyIDMwLjAyMzJDMTMuOTA4OSAyOS44NDYxIDE0LjA0MzEgMjkuNzA0MiAxNC4yMTc1IDI5LjU5NzRDMTQuMzkyIDI5LjQ5MDYgMTQuNTk5IDI5LjQzNzMgMTQuODM4NiAyOS40MzczQzE1LjA4MzQgMjkuNDM3MyAxNS4yOTE4IDI5LjQ5MDYgMTUuNDYzNiAyOS41OTc0QzE1LjYzODEgMjkuNzA0MiAxNS43NzIyIDI5Ljg0NjEgMTUuODY2IDMwLjAyMzJDMTUuOTU5NyAzMC4yMDAzIDE2LjAwNjYgMzAuMzk2OSAxNi4wMDY2IDMwLjYxM1YzMC45MTM4QzE2LjAwNjYgMzEuMTI0OCAxNS45NTk3IDMxLjMxODggMTUuODY2IDMxLjQ5NThDMTUuNzc0OCAzMS42NzI5IDE1LjY0MiAzMS44MTQ5IDE1LjQ2NzUgMzEuOTIxNkMxNS4yOTU3IDMyLjAyODQgMTUuMDg4NiAzMi4wODE4IDE0Ljg0NjQgMzIuMDgxOEMxNC42MDQzIDMyLjA4MTggMTQuMzk0NiAzMi4wMjg0IDE0LjIxNzUgMzEuOTIxNkMxNC4wNDMxIDMxLjgxNDkgMTMuOTA4OSAzMS42NzI5IDEzLjgxNTIgMzEuNDk1OEMxMy43MjE0IDMxLjMxODggMTMuNjc0NiAzMS4xMjQ4IDEzLjY3NDYgMzAuOTEzOFpNMTQuMjE3NSAzMC42MTNWMzAuOTEzOEMxNC4yMTc1IDMxLjAzMzYgMTQuMjM5NyAzMS4xNDY5IDE0LjI4MzkgMzEuMjUzN0MxNC4zMzA4IDMxLjM2MDQgMTQuNDAxMSAzMS40NDc3IDE0LjQ5NDkgMzEuNTE1NEMxNC41ODg2IDMxLjU4MDUgMTQuNzA1OCAzMS42MTMgMTQuODQ2NCAzMS42MTNDMTQuOTg3MSAzMS42MTMgMTUuMTAyOSAzMS41ODA1IDE1LjE5NDEgMzEuNTE1NEMxNS4yODUyIDMxLjQ0NzcgMTUuMzUyOSAzMS4zNjA0IDE1LjM5NzIgMzEuMjUzN0MxNS40NDE1IDMxLjE0NjkgMTUuNDYzNiAzMS4wMzM2IDE1LjQ2MzYgMzAuOTEzOFYzMC42MTNDMTUuNDYzNiAzMC40OTA2IDE1LjQ0MDIgMzAuMzc2MSAxNS4zOTMzIDMwLjI2OTNDMTUuMzQ5IDMwLjE1OTkgMTUuMjggMzAuMDcyNyAxNS4xODYzIDMwLjAwNzZDMTUuMDk1MSAyOS45Mzk5IDE0Ljk3OTMgMjkuOTA2IDE0LjgzODYgMjkuOTA2QzE0LjcwMDYgMjkuOTA2IDE0LjU4NDcgMjkuOTM5OSAxNC40OTEgMzAuMDA3NkMxNC4zOTk4IDMwLjA3MjcgMTQuMzMwOCAzMC4xNTk5IDE0LjI4MzkgMzAuMjY5M0MxNC4yMzk3IDMwLjM3NjEgMTQuMjE3NSAzMC40OTA2IDE0LjIxNzUgMzAuNjEzWk0xNi40NDQxIDM0LjExNjlWMzMuODEyM0MxNi40NDQxIDMzLjU5ODcgMTYuNDkxIDMzLjQwMzQgMTYuNTg0NyAzMy4yMjYzQzE2LjY3ODUgMzMuMDQ5MiAxNi44MTI2IDMyLjkwNzMgMTYuOTg3MSAzMi44MDA1QzE3LjE2MTUgMzIuNjkzOCAxNy4zNjg2IDMyLjY0MDQgMTcuNjA4MiAzMi42NDA0QzE3Ljg1MjkgMzIuNjQwNCAxOC4wNjEzIDMyLjY5MzggMTguMjMzMiAzMi44MDA1QzE4LjQwNzYgMzIuOTA3MyAxOC41NDE4IDMzLjA0OTIgMTguNjM1NSAzMy4yMjYzQzE4LjcyOTMgMzMuNDAzNCAxOC43NzYxIDMzLjU5ODcgMTguNzc2MSAzMy44MTIzVjM0LjExNjlDMTguNzc2MSAzNC4zMzA1IDE4LjcyOTMgMzQuNTI1OCAxOC42MzU1IDM0LjcwMjlDMTguNTQ0NCAzNC44OCAxOC40MTE1IDM1LjAyMTkgMTguMjM3MSAzNS4xMjg3QzE4LjA2NTIgMzUuMjM1NCAxNy44NTgyIDM1LjI4ODggMTcuNjE2IDM1LjI4ODhDMTcuMzczOCAzNS4yODg4IDE3LjE2NTQgMzUuMjM1NCAxNi45OTEgMzUuMTI4N0MxNi44MTY1IDM1LjAyMTkgMTYuNjgxMSAzNC44OCAxNi41ODQ3IDM0LjcwMjlDMTYuNDkxIDM0LjUyNTggMTYuNDQ0MSAzNC4zMzA1IDE2LjQ0NDEgMzQuMTE2OVpNMTYuOTg3MSAzMy44MTIzVjM0LjExNjlDMTYuOTg3MSAzNC4yMzY3IDE3LjAwOTIgMzQuMzUxMyAxNy4wNTM1IDM0LjQ2MDdDMTcuMTAwMyAzNC41Njc1IDE3LjE3MDcgMzQuNjU0NyAxNy4yNjQ0IDM0LjcyMjRDMTcuMzU4MiAzNC43ODc1IDE3LjQ3NTMgMzQuODIwMSAxNy42MTYgMzQuODIwMUMxNy43NTY2IDM0LjgyMDEgMTcuODcyNSAzNC43ODc1IDE3Ljk2MzYgMzQuNzIyNEMxOC4wNTc0IDM0LjY1NDcgMTguMTI2NCAzNC41Njc1IDE4LjE3MDcgMzQuNDYwN0MxOC4yMTQ5IDM0LjM1MzkgMTguMjM3MSAzNC4yMzkzIDE4LjIzNzEgMzQuMTE2OVYzMy44MTIzQzE4LjIzNzEgMzMuNjg5OSAxOC4yMTM2IDMzLjU3NTMgMTguMTY2OCAzMy40Njg1QzE4LjEyMjUgMzMuMzYxNyAxOC4wNTM1IDMzLjI3NTggMTcuOTU5NyAzMy4yMTA3QzE3Ljg2ODYgMzMuMTQzIDE3Ljc1MTQgMzMuMTA5MSAxNy42MDgyIDMzLjEwOTFDMTcuNDcwMSAzMy4xMDkxIDE3LjM1NDMgMzMuMTQzIDE3LjI2MDUgMzMuMjEwN0MxNy4xNjk0IDMzLjI3NTggMTcuMTAwMyAzMy4zNjE3IDE3LjA1MzUgMzMuNDY4NUMxNy4wMDkyIDMzLjU3NTMgMTYuOTg3MSAzMy42ODk5IDE2Ljk4NzEgMzMuODEyM1pNMTcuNzg3OCAzMC4zMjc5TDE1LjAxMDUgMzQuNzczMkwxNC42MDQzIDM0LjUxNTRMMTcuMzgxNiAzMC4wNzAxTDE3Ljc4NzggMzAuMzI3OVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTcuMjQ2MDkgNTcuNzE4M0g3LjMwODU5VjU4LjMzMTVINy4yNDYwOUM2Ljg2MzI4IDU4LjMzMTUgNi41NDI5NyA1OC4zOTQgNi4yODUxNiA1OC41MTlDNi4wMjczNCA1OC42NDE0IDUuODIyOTIgNTguODA2OCA1LjY3MTg4IDU5LjAxNTFDNS41MjA4MyA1OS4yMjA5IDUuNDExNDYgNTkuNDUyNiA1LjM0Mzc1IDU5LjcxMDRDNS4yNzg2NSA1OS45NjgzIDUuMjQ2MDkgNjAuMjMgNS4yNDYwOSA2MC40OTU2VjYxLjMzMTVDNS4yNDYwOSA2MS41ODQxIDUuMjc2MDQgNjEuODA4MSA1LjMzNTk0IDYyLjAwMzRDNS4zOTU4MyA2Mi4xOTYxIDUuNDc3ODYgNjIuMzU4OSA1LjU4MjAzIDYyLjQ5MTdDNS42ODYyIDYyLjYyNDUgNS44MDMzOSA2Mi43MjQ4IDUuOTMzNTkgNjIuNzkyNUM2LjA2NjQxIDYyLjg2MDIgNi4yMDQ0MyA2Mi44OTQgNi4zNDc2NiA2Mi44OTRDNi41MTQzMiA2Mi44OTQgNi42NjI3NiA2Mi44NjI4IDYuNzkyOTcgNjIuODAwM0M2LjkyMzE4IDYyLjczNTIgNy4wMzI1NSA2Mi42NDUzIDcuMTIxMDkgNjIuNTMwOEM3LjIxMjI0IDYyLjQxMzYgNy4yODEyNSA2Mi4yNzU2IDcuMzI4MTIgNjIuMTE2N0M3LjM3NSA2MS45NTc4IDcuMzk4NDQgNjEuNzgzNCA3LjM5ODQ0IDYxLjU5MzNDNy4zOTg0NCA2MS40MjQgNy4zNzc2IDYxLjI2MTIgNy4zMzU5NCA2MS4xMDVDNy4yOTQyNyA2MC45NDYxIDcuMjMwNDcgNjAuODA1NSA3LjE0NDUzIDYwLjY4MzFDNy4wNTg1OSA2MC41NTgxIDYuOTUwNTIgNjAuNDYwNCA2LjgyMDMxIDYwLjM5MDFDNi42OTI3MSA2MC4zMTcyIDYuNTQwMzYgNjAuMjgwOCA2LjM2MzI4IDYwLjI4MDhDNi4xNjI3NiA2MC4yODA4IDUuOTc1MjYgNjAuMzMwMiA1LjgwMDc4IDYwLjQyOTJDNS42Mjg5MSA2MC41MjU2IDUuNDg2OTggNjAuNjUzMiA1LjM3NSA2MC44MTJDNS4yNjU2MiA2MC45NjgzIDUuMjAzMTIgNjEuMTM4OCA1LjE4NzUgNjEuMzIzN0w0LjgwNDY5IDYxLjMxOThDNC44NDExNSA2MS4wMjgyIDQuOTA4ODUgNjAuNzc5NSA1LjAwNzgxIDYwLjU3MzdDNS4xMDkzOCA2MC4zNjU0IDUuMjM0MzggNjAuMTk2MSA1LjM4MjgxIDYwLjA2NTlDNS41MzM4NSA1OS45MzMxIDUuNzAxODIgNTkuODM2OCA1Ljg4NjcyIDU5Ljc3NjlDNi4wNzQyMiA1OS43MTQ0IDYuMjcyMTQgNTkuNjgzMSA2LjQ4MDQ3IDU5LjY4MzFDNi43NjQzMiA1OS42ODMxIDcuMDA5MTEgNTkuNzM2NSA3LjIxNDg0IDU5Ljg0MzNDNy40MjA1NyA1OS45NSA3LjU4OTg0IDYwLjA5MzMgNy43MjI2NiA2MC4yNzI5QzcuODU1NDcgNjAuNDUgNy45NTMxMiA2MC42NTA2IDguMDE1NjIgNjAuODc0NUM4LjA4MDczIDYxLjA5NTkgOC4xMTMyOCA2MS4zMjM3IDguMTEzMjggNjEuNTU4MUM4LjExMzI4IDYxLjgyNjMgOC4wNzU1MiA2Mi4wNzc2IDggNjIuMzEyQzcuOTI0NDggNjIuNTQ2NCA3LjgxMTIgNjIuNzUyMSA3LjY2MDE2IDYyLjkyOTJDNy41MTE3MiA2My4xMDYzIDcuMzI4MTIgNjMuMjQ0MyA3LjEwOTM4IDYzLjM0MzNDNi44OTA2MiA2My40NDIyIDYuNjM2NzIgNjMuNDkxNyA2LjM0NzY2IDYzLjQ5MTdDNi4wNDAzNiA2My40OTE3IDUuNzcyMTQgNjMuNDI5MiA1LjU0Mjk3IDYzLjMwNDJDNS4zMTM4IDYzLjE3NjYgNS4xMjM3IDYzLjAwNzMgNC45NzI2NiA2Mi43OTY0QzQuODIxNjEgNjIuNTg1NCA0LjcwODMzIDYyLjM1MTEgNC42MzI4MSA2Mi4wOTMzQzQuNTU3MjkgNjEuODM1NCA0LjUxOTUzIDYxLjU3MzcgNC41MTk1MyA2MS4zMDgxVjYwLjk2ODNDNC41MTk1MyA2MC41NjcyIDQuNTU5OSA2MC4xNzQgNC42NDA2MiA1OS43ODg2QzQuNzIxMzUgNTkuNDAzMiA0Ljg2MDY4IDU5LjA1NDIgNS4wNTg1OSA1OC43NDE3QzUuMjU5MTEgNTguNDI5MiA1LjUzNjQ2IDU4LjE4MDUgNS44OTA2MiA1Ny45OTU2QzYuMjQ0NzkgNTcuODEwNyA2LjY5NjYxIDU3LjcxODMgNy4yNDYwOSA1Ny43MTgzWk0xMi42NzUyIDYwLjExNjdWNjAuOTgzOUMxMi42NzUyIDYxLjQ1IDEyLjYzMzUgNjEuODQzMyAxMi41NTAyIDYyLjE2MzZDMTIuNDY2OCA2Mi40ODM5IDEyLjM0NyA2Mi43NDE3IDEyLjE5MDggNjIuOTM3QzEyLjAzNDUgNjMuMTMyMyAxMS44NDU3IDYzLjI3NDMgMTEuNjI0NCA2My4zNjI4QzExLjQwNTYgNjMuNDQ4NyAxMS4xNTgyIDYzLjQ5MTcgMTAuODgyMiA2My40OTE3QzEwLjY2MzUgNjMuNDkxNyAxMC40NjE2IDYzLjQ2NDQgMTAuMjc2NyA2My40MDk3QzEwLjA5MTggNjMuMzU1IDkuOTI1MTcgNjMuMjY3NyA5Ljc3NjczIDYzLjE0NzlDOS42MzA5IDYzLjAyNTYgOS41MDU5IDYyLjg2NjcgOS40MDE3MyA2Mi42NzE0QzkuMjk3NTcgNjIuNDc2MSA5LjIxODE0IDYyLjIzOTEgOS4xNjM0NSA2MS45NjA0QzkuMTA4NzcgNjEuNjgxOCA5LjA4MTQyIDYxLjM1NjMgOS4wODE0MiA2MC45ODM5VjYwLjExNjdDOS4wODE0MiA1OS42NTA2IDkuMTIzMDkgNTkuMjU5OSA5LjIwNjQyIDU4Ljk0NDhDOS4yOTIzNiA1OC42Mjk3IDkuNDEzNDUgNTguMzc3MSA5LjU2OTcgNTguMTg3QzkuNzI1OTUgNTcuOTk0MyA5LjkxMzQ1IDU3Ljg1NjMgMTAuMTMyMiA1Ny43NzI5QzEwLjM1MzYgNTcuNjg5NiAxMC42MDEgNTcuNjQ3OSAxMC44NzQ0IDU3LjY0NzlDMTEuMDk1NyA1Ny42NDc5IDExLjI5ODkgNTcuNjc1MyAxMS40ODM4IDU3LjczQzExLjY3MTMgNTcuNzgyMSAxMS44Mzc5IDU3Ljg2NjcgMTEuOTgzOCA1Ny45ODM5QzEyLjEyOTYgNTguMDk4NSAxMi4yNTMzIDU4LjI1MjEgMTIuMzU0OSA1OC40NDQ4QzEyLjQ1OSA1OC42MzQ5IDEyLjUzODUgNTguODY4IDEyLjU5MzEgNTkuMTQ0QzEyLjY0NzggNTkuNDIwMSAxMi42NzUyIDU5Ljc0NDMgMTIuNjc1MiA2MC4xMTY3Wk0xMS45NDg2IDYxLjEwMTFWNTkuOTk1NkMxMS45NDg2IDU5Ljc0MDQgMTEuOTMzIDU5LjUxNjQgMTEuOTAxNyA1OS4zMjM3QzExLjg3MzEgNTkuMTI4NCAxMS44MzAxIDU4Ljk2MTggMTEuNzcyOCA1OC44MjM3QzExLjcxNTUgNTguNjg1NyAxMS42NDI2IDU4LjU3MzcgMTEuNTU0MSA1OC40ODc4QzExLjQ2ODEgNTguNDAxOSAxMS4zNjc5IDU4LjMzOTQgMTEuMjUzMyA1OC4zMDAzQzExLjE0MTMgNTguMjU4NiAxMS4wMTUgNTguMjM3OCAxMC44NzQ0IDU4LjIzNzhDMTAuNzAyNSA1OC4yMzc4IDEwLjU1MDIgNTguMjcwMyAxMC40MTc0IDU4LjMzNTRDMTAuMjg0NSA1OC4zOTc5IDEwLjE3MjYgNTguNDk4MiAxMC4wODE0IDU4LjYzNjJDOS45OTI4OCA1OC43NzQzIDkuOTI1MTcgNTguOTU1MiA5Ljg3ODMgNTkuMTc5MkM5LjgzMTQyIDU5LjQwMzIgOS44MDc5OCA1OS42NzUzIDkuODA3OTggNTkuOTk1NlY2MS4xMDExQzkuODA3OTggNjEuMzU2MyA5LjgyMjMxIDYxLjU4MTUgOS44NTA5NSA2MS43NzY5QzkuODgyMiA2MS45NzIyIDkuOTI3NzggNjIuMTQxNCA5Ljk4NzY3IDYyLjI4NDdDMTAuMDQ3NiA2Mi40MjUzIDEwLjEyMDUgNjIuNTQxMiAxMC4yMDY0IDYyLjYzMjNDMTAuMjkyNCA2Mi43MjM1IDEwLjM5MTMgNjIuNzkxMiAxMC41MDMzIDYyLjgzNTRDMTAuNjE3OSA2Mi44NzcxIDEwLjc0NDIgNjIuODk3OSAxMC44ODIyIDYyLjg5NzlDMTEuMDU5MyA2Mi44OTc5IDExLjIxNDIgNjIuODY0MSAxMS4zNDcgNjIuNzk2NEMxMS40Nzk5IDYyLjcyODcgMTEuNTkwNSA2Mi42MjMyIDExLjY3OTEgNjIuNDhDMTEuNzcwMiA2Mi4zMzQxIDExLjgzNzkgNjIuMTQ3OSAxMS44ODIyIDYxLjkyMTRDMTEuOTI2NSA2MS42OTIyIDExLjk0ODYgNjEuNDE4OCAxMS45NDg2IDYxLjEwMTFaTTEzLjY3NDYgNTkuMTIwNlY1OC44MTk4QzEzLjY3NDYgNTguNjAzNyAxMy43MjE0IDU4LjQwNzEgMTMuODE1MiA1OC4yM0MxMy45MDg5IDU4LjA1MjkgMTQuMDQzMSA1Ny45MTEgMTQuMjE3NSA1Ny44MDQyQzE0LjM5MiA1Ny42OTc0IDE0LjU5OSA1Ny42NDQgMTQuODM4NiA1Ny42NDRDMTUuMDgzNCA1Ny42NDQgMTUuMjkxOCA1Ny42OTc0IDE1LjQ2MzYgNTcuODA0MkMxNS42MzgxIDU3LjkxMSAxNS43NzIyIDU4LjA1MjkgMTUuODY2IDU4LjIzQzE1Ljk1OTcgNTguNDA3MSAxNi4wMDY2IDU4LjYwMzcgMTYuMDA2NiA1OC44MTk4VjU5LjEyMDZDMTYuMDA2NiA1OS4zMzE1IDE1Ljk1OTcgNTkuNTI1NiAxNS44NjYgNTkuNzAyNkMxNS43NzQ4IDU5Ljg3OTcgMTUuNjQyIDYwLjAyMTYgMTUuNDY3NSA2MC4xMjg0QzE1LjI5NTcgNjAuMjM1MiAxNS4wODg2IDYwLjI4ODYgMTQuODQ2NCA2MC4yODg2QzE0LjYwNDMgNjAuMjg4NiAxNC4zOTQ2IDYwLjIzNTIgMTQuMjE3NSA2MC4xMjg0QzE0LjA0MzEgNjAuMDIxNiAxMy45MDg5IDU5Ljg3OTcgMTMuODE1MiA1OS43MDI2QzEzLjcyMTQgNTkuNTI1NiAxMy42NzQ2IDU5LjMzMTUgMTMuNjc0NiA1OS4xMjA2Wk0xNC4yMTc1IDU4LjgxOThWNTkuMTIwNkMxNC4yMTc1IDU5LjI0MDQgMTQuMjM5NyA1OS4zNTM3IDE0LjI4MzkgNTkuNDYwNEMxNC4zMzA4IDU5LjU2NzIgMTQuNDAxMSA1OS42NTQ1IDE0LjQ5NDkgNTkuNzIyMkMxNC41ODg2IDU5Ljc4NzMgMTQuNzA1OCA1OS44MTk4IDE0Ljg0NjQgNTkuODE5OEMxNC45ODcxIDU5LjgxOTggMTUuMTAyOSA1OS43ODczIDE1LjE5NDEgNTkuNzIyMkMxNS4yODUyIDU5LjY1NDUgMTUuMzUyOSA1OS41NjcyIDE1LjM5NzIgNTkuNDYwNEMxNS40NDE1IDU5LjM1MzcgMTUuNDYzNiA1OS4yNDA0IDE1LjQ2MzYgNTkuMTIwNlY1OC44MTk4QzE1LjQ2MzYgNTguNjk3NCAxNS40NDAyIDU4LjU4MjggMTUuMzkzMyA1OC40NzYxQzE1LjM0OSA1OC4zNjY3IDE1LjI4IDU4LjI3OTUgMTUuMTg2MyA1OC4yMTQ0QzE1LjA5NTEgNTguMTQ2NiAxNC45NzkzIDU4LjExMjggMTQuODM4NiA1OC4xMTI4QzE0LjcwMDYgNTguMTEyOCAxNC41ODQ3IDU4LjE0NjYgMTQuNDkxIDU4LjIxNDRDMTQuMzk5OCA1OC4yNzk1IDE0LjMzMDggNTguMzY2NyAxNC4yODM5IDU4LjQ3NjFDMTQuMjM5NyA1OC41ODI4IDE0LjIxNzUgNTguNjk3NCAxNC4yMTc1IDU4LjgxOThaTTE2LjQ0NDEgNjIuMzIzN1Y2Mi4wMTlDMTYuNDQ0MSA2MS44MDU1IDE2LjQ5MSA2MS42MTAyIDE2LjU4NDcgNjEuNDMzMUMxNi42Nzg1IDYxLjI1NiAxNi44MTI2IDYxLjExNDEgMTYuOTg3MSA2MS4wMDczQzE3LjE2MTUgNjAuOTAwNiAxNy4zNjg2IDYwLjg0NzIgMTcuNjA4MiA2MC44NDcyQzE3Ljg1MjkgNjAuODQ3MiAxOC4wNjEzIDYwLjkwMDYgMTguMjMzMiA2MS4wMDczQzE4LjQwNzYgNjEuMTE0MSAxOC41NDE4IDYxLjI1NiAxOC42MzU1IDYxLjQzMzFDMTguNzI5MyA2MS42MTAyIDE4Ljc3NjEgNjEuODA1NSAxOC43NzYxIDYyLjAxOVY2Mi4zMjM3QzE4Ljc3NjEgNjIuNTM3MyAxOC43MjkzIDYyLjczMjYgMTguNjM1NSA2Mi45MDk3QzE4LjU0NDQgNjMuMDg2OCAxOC40MTE1IDYzLjIyODcgMTguMjM3MSA2My4zMzU0QzE4LjA2NTIgNjMuNDQyMiAxNy44NTgyIDYzLjQ5NTYgMTcuNjE2IDYzLjQ5NTZDMTcuMzczOCA2My40OTU2IDE3LjE2NTQgNjMuNDQyMiAxNi45OTEgNjMuMzM1NEMxNi44MTY1IDYzLjIyODcgMTYuNjgxMSA2My4wODY4IDE2LjU4NDcgNjIuOTA5N0MxNi40OTEgNjIuNzMyNiAxNi40NDQxIDYyLjUzNzMgMTYuNDQ0MSA2Mi4zMjM3Wk0xNi45ODcxIDYyLjAxOVY2Mi4zMjM3QzE2Ljk4NzEgNjIuNDQzNSAxNy4wMDkyIDYyLjU1ODEgMTcuMDUzNSA2Mi42Njc1QzE3LjEwMDMgNjIuNzc0MyAxNy4xNzA3IDYyLjg2MTUgMTcuMjY0NCA2Mi45MjkyQzE3LjM1ODIgNjIuOTk0MyAxNy40NzUzIDYzLjAyNjkgMTcuNjE2IDYzLjAyNjlDMTcuNzU2NiA2My4wMjY5IDE3Ljg3MjUgNjIuOTk0MyAxNy45NjM2IDYyLjkyOTJDMTguMDU3NCA2Mi44NjE1IDE4LjEyNjQgNjIuNzc0MyAxOC4xNzA3IDYyLjY2NzVDMTguMjE0OSA2Mi41NjA3IDE4LjIzNzEgNjIuNDQ2MSAxOC4yMzcxIDYyLjMyMzdWNjIuMDE5QzE4LjIzNzEgNjEuODk2NiAxOC4yMTM2IDYxLjc4MjEgMTguMTY2OCA2MS42NzUzQzE4LjEyMjUgNjEuNTY4NSAxOC4wNTM1IDYxLjQ4MjYgMTcuOTU5NyA2MS40MTc1QzE3Ljg2ODYgNjEuMzQ5OCAxNy43NTE0IDYxLjMxNTkgMTcuNjA4MiA2MS4zMTU5QzE3LjQ3MDEgNjEuMzE1OSAxNy4zNTQzIDYxLjM0OTggMTcuMjYwNSA2MS40MTc1QzE3LjE2OTQgNjEuNDgyNiAxNy4xMDAzIDYxLjU2ODUgMTcuMDUzNSA2MS42NzUzQzE3LjAwOTIgNjEuNzgyMSAxNi45ODcxIDYxLjg5NjYgMTYuOTg3MSA2Mi4wMTlaTTE3Ljc4NzggNTguNTM0N0wxNS4wMTA1IDYyLjk4TDE0LjYwNDMgNjIuNzIyMkwxNy4zODE2IDU4LjI3NjlMMTcuNzg3OCA1OC41MzQ3WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNOC4zMTY0MSA4OS43MDYzVjkwLjNINC4yMDcwM1Y4OS44NzQzTDYuNzUzOTEgODUuOTMyOUg3LjM0Mzc1TDYuNzEwOTQgODcuMDczNUw1LjAyNzM0IDg5LjcwNjNIOC4zMTY0MVpNNy41MjM0NCA4NS45MzI5VjkxLjYyMDRINi44MDA3OFY4NS45MzI5SDcuNTIzNDRaTTEyLjY3NTIgODguMzIzNVY4OS4xOTA3QzEyLjY3NTIgODkuNjU2OCAxMi42MzM1IDkwLjA1IDEyLjU1MDIgOTAuMzcwNEMxMi40NjY4IDkwLjY5MDcgMTIuMzQ3IDkwLjk0ODUgMTIuMTkwOCA5MS4xNDM4QzEyLjAzNDUgOTEuMzM5MSAxMS44NDU3IDkxLjQ4MSAxMS42MjQ0IDkxLjU2OTZDMTEuNDA1NiA5MS42NTU1IDExLjE1ODIgOTEuNjk4NSAxMC44ODIyIDkxLjY5ODVDMTAuNjYzNSA5MS42OTg1IDEwLjQ2MTYgOTEuNjcxMSAxMC4yNzY3IDkxLjYxNjVDMTAuMDkxOCA5MS41NjE4IDkuOTI1MTcgOTEuNDc0NSA5Ljc3NjczIDkxLjM1NDdDOS42MzA5IDkxLjIzMjMgOS41MDU5IDkxLjA3MzUgOS40MDE3MyA5MC44NzgyQzkuMjk3NTcgOTAuNjgyOSA5LjIxODE0IDkwLjQ0NTkgOS4xNjM0NSA5MC4xNjcyQzkuMTA4NzcgODkuODg4NiA5LjA4MTQyIDg5LjU2MzEgOS4wODE0MiA4OS4xOTA3Vjg4LjMyMzVDOS4wODE0MiA4Ny44NTczIDkuMTIzMDkgODcuNDY2NyA5LjIwNjQyIDg3LjE1MTZDOS4yOTIzNiA4Ni44MzY1IDkuNDEzNDUgODYuNTgzOSA5LjU2OTcgODYuMzkzOEM5LjcyNTk1IDg2LjIwMTEgOS45MTM0NSA4Ni4wNjMxIDEwLjEzMjIgODUuOTc5N0MxMC4zNTM2IDg1Ljg5NjQgMTAuNjAxIDg1Ljg1NDcgMTAuODc0NCA4NS44NTQ3QzExLjA5NTcgODUuODU0NyAxMS4yOTg5IDg1Ljg4MjEgMTEuNDgzOCA4NS45MzY4QzExLjY3MTMgODUuOTg4OSAxMS44Mzc5IDg2LjA3MzUgMTEuOTgzOCA4Ni4xOTA3QzEyLjEyOTYgODYuMzA1MyAxMi4yNTMzIDg2LjQ1ODkgMTIuMzU0OSA4Ni42NTE2QzEyLjQ1OSA4Ni44NDE3IDEyLjUzODUgODcuMDc0OCAxMi41OTMxIDg3LjM1MDhDMTIuNjQ3OCA4Ny42MjY5IDEyLjY3NTIgODcuOTUxMSAxMi42NzUyIDg4LjMyMzVaTTExLjk0ODYgODkuMzA3OVY4OC4yMDI0QzExLjk0ODYgODcuOTQ3MiAxMS45MzMgODcuNzIzMiAxMS45MDE3IDg3LjUzMDVDMTEuODczMSA4Ny4zMzUyIDExLjgzMDEgODcuMTY4NSAxMS43NzI4IDg3LjAzMDVDMTEuNzE1NSA4Ni44OTI1IDExLjY0MjYgODYuNzgwNSAxMS41NTQxIDg2LjY5NDZDMTEuNDY4MSA4Ni42MDg2IDExLjM2NzkgODYuNTQ2MSAxMS4yNTMzIDg2LjUwNzFDMTEuMTQxMyA4Ni40NjU0IDExLjAxNSA4Ni40NDQ2IDEwLjg3NDQgODYuNDQ0NkMxMC43MDI1IDg2LjQ0NDYgMTAuNTUwMiA4Ni40NzcxIDEwLjQxNzQgODYuNTQyMkMxMC4yODQ1IDg2LjYwNDcgMTAuMTcyNiA4Ni43MDUgMTAuMDgxNCA4Ni44NDNDOS45OTI4OCA4Ni45ODEgOS45MjUxNyA4Ny4xNjIgOS44NzgzIDg3LjM4NkM5LjgzMTQyIDg3LjYwOTkgOS44MDc5OCA4Ny44ODIxIDkuODA3OTggODguMjAyNFY4OS4zMDc5QzkuODA3OTggODkuNTYzMSA5LjgyMjMxIDg5Ljc4ODMgOS44NTA5NSA4OS45ODM2QzkuODgyMiA5MC4xNzkgOS45Mjc3OCA5MC4zNDgyIDkuOTg3NjcgOTAuNDkxNUMxMC4wNDc2IDkwLjYzMjEgMTAuMTIwNSA5MC43NDggMTAuMjA2NCA5MC44MzkxQzEwLjI5MjQgOTAuOTMwMyAxMC4zOTEzIDkwLjk5OCAxMC41MDMzIDkxLjA0MjJDMTAuNjE3OSA5MS4wODM5IDEwLjc0NDIgOTEuMTA0NyAxMC44ODIyIDkxLjEwNDdDMTEuMDU5MyA5MS4xMDQ3IDExLjIxNDIgOTEuMDcwOSAxMS4zNDcgOTEuMDAzMkMxMS40Nzk5IDkwLjkzNTUgMTEuNTkwNSA5MC44MyAxMS42NzkxIDkwLjY4NjhDMTEuNzcwMiA5MC41NDA5IDExLjgzNzkgOTAuMzU0NyAxMS44ODIyIDkwLjEyODJDMTEuOTI2NSA4OS44OTkgMTEuOTQ4NiA4OS42MjU2IDExLjk0ODYgODkuMzA3OVpNMTMuNjc0NiA4Ny4zMjc0Vjg3LjAyNjZDMTMuNjc0NiA4Ni44MTA1IDEzLjcyMTQgODYuNjEzOSAxMy44MTUyIDg2LjQzNjhDMTMuOTA4OSA4Ni4yNTk3IDE0LjA0MzEgODYuMTE3OCAxNC4yMTc1IDg2LjAxMUMxNC4zOTIgODUuOTA0MiAxNC41OTkgODUuODUwOCAxNC44Mzg2IDg1Ljg1MDhDMTUuMDgzNCA4NS44NTA4IDE1LjI5MTggODUuOTA0MiAxNS40NjM2IDg2LjAxMUMxNS42MzgxIDg2LjExNzggMTUuNzcyMiA4Ni4yNTk3IDE1Ljg2NiA4Ni40MzY4QzE1Ljk1OTcgODYuNjEzOSAxNi4wMDY2IDg2LjgxMDUgMTYuMDA2NiA4Ny4wMjY2Vjg3LjMyNzRDMTYuMDA2NiA4Ny41MzgzIDE1Ljk1OTcgODcuNzMyMyAxNS44NjYgODcuOTA5NEMxNS43NzQ4IDg4LjA4NjUgMTUuNjQyIDg4LjIyODQgMTUuNDY3NSA4OC4zMzUyQzE1LjI5NTcgODguNDQyIDE1LjA4ODYgODguNDk1NCAxNC44NDY0IDg4LjQ5NTRDMTQuNjA0MyA4OC40OTU0IDE0LjM5NDYgODguNDQyIDE0LjIxNzUgODguMzM1MkMxNC4wNDMxIDg4LjIyODQgMTMuOTA4OSA4OC4wODY1IDEzLjgxNTIgODcuOTA5NEMxMy43MjE0IDg3LjczMjMgMTMuNjc0NiA4Ny41MzgzIDEzLjY3NDYgODcuMzI3NFpNMTQuMjE3NSA4Ny4wMjY2Vjg3LjMyNzRDMTQuMjE3NSA4Ny40NDcyIDE0LjIzOTcgODcuNTYwNSAxNC4yODM5IDg3LjY2NzJDMTQuMzMwOCA4Ny43NzQgMTQuNDAxMSA4Ny44NjEyIDE0LjQ5NDkgODcuOTI5QzE0LjU4ODYgODcuOTk0MSAxNC43MDU4IDg4LjAyNjYgMTQuODQ2NCA4OC4wMjY2QzE0Ljk4NzEgODguMDI2NiAxNS4xMDI5IDg3Ljk5NDEgMTUuMTk0MSA4Ny45MjlDMTUuMjg1MiA4Ny44NjEyIDE1LjM1MjkgODcuNzc0IDE1LjM5NzIgODcuNjY3MkMxNS40NDE1IDg3LjU2MDUgMTUuNDYzNiA4Ny40NDcyIDE1LjQ2MzYgODcuMzI3NFY4Ny4wMjY2QzE1LjQ2MzYgODYuOTA0MiAxNS40NDAyIDg2Ljc4OTYgMTUuMzkzMyA4Ni42ODI5QzE1LjM0OSA4Ni41NzM1IDE1LjI4IDg2LjQ4NjIgMTUuMTg2MyA4Ni40MjExQzE1LjA5NTEgODYuMzUzNCAxNC45NzkzIDg2LjMxOTYgMTQuODM4NiA4Ni4zMTk2QzE0LjcwMDYgODYuMzE5NiAxNC41ODQ3IDg2LjM1MzQgMTQuNDkxIDg2LjQyMTFDMTQuMzk5OCA4Ni40ODYyIDE0LjMzMDggODYuNTczNSAxNC4yODM5IDg2LjY4MjlDMTQuMjM5NyA4Ni43ODk2IDE0LjIxNzUgODYuOTA0MiAxNC4yMTc1IDg3LjAyNjZaTTE2LjQ0NDEgOTAuNTMwNVY5MC4yMjU4QzE2LjQ0NDEgOTAuMDEyMyAxNi40OTEgODkuODE3IDE2LjU4NDcgODkuNjM5OUMxNi42Nzg1IDg5LjQ2MjggMTYuODEyNiA4OS4zMjA5IDE2Ljk4NzEgODkuMjE0MUMxNy4xNjE1IDg5LjEwNzMgMTcuMzY4NiA4OS4wNTQgMTcuNjA4MiA4OS4wNTRDMTcuODUyOSA4OS4wNTQgMTguMDYxMyA4OS4xMDczIDE4LjIzMzIgODkuMjE0MUMxOC40MDc2IDg5LjMyMDkgMTguNTQxOCA4OS40NjI4IDE4LjYzNTUgODkuNjM5OUMxOC43MjkzIDg5LjgxNyAxOC43NzYxIDkwLjAxMjMgMTguNzc2MSA5MC4yMjU4VjkwLjUzMDVDMTguNzc2MSA5MC43NDQxIDE4LjcyOTMgOTAuOTM5NCAxOC42MzU1IDkxLjExNjVDMTguNTQ0NCA5MS4yOTM1IDE4LjQxMTUgOTEuNDM1NSAxOC4yMzcxIDkxLjU0MjJDMTguMDY1MiA5MS42NDkgMTcuODU4MiA5MS43MDI0IDE3LjYxNiA5MS43MDI0QzE3LjM3MzggOTEuNzAyNCAxNy4xNjU0IDkxLjY0OSAxNi45OTEgOTEuNTQyMkMxNi44MTY1IDkxLjQzNTUgMTYuNjgxMSA5MS4yOTM1IDE2LjU4NDcgOTEuMTE2NUMxNi40OTEgOTAuOTM5NCAxNi40NDQxIDkwLjc0NDEgMTYuNDQ0MSA5MC41MzA1Wk0xNi45ODcxIDkwLjIyNThWOTAuNTMwNUMxNi45ODcxIDkwLjY1MDMgMTcuMDA5MiA5MC43NjQ5IDE3LjA1MzUgOTAuODc0M0MxNy4xMDAzIDkwLjk4MSAxNy4xNzA3IDkxLjA2ODMgMTcuMjY0NCA5MS4xMzZDMTcuMzU4MiA5MS4yMDExIDE3LjQ3NTMgOTEuMjMzNiAxNy42MTYgOTEuMjMzNkMxNy43NTY2IDkxLjIzMzYgMTcuODcyNSA5MS4yMDExIDE3Ljk2MzYgOTEuMTM2QzE4LjA1NzQgOTEuMDY4MyAxOC4xMjY0IDkwLjk4MSAxOC4xNzA3IDkwLjg3NDNDMTguMjE0OSA5MC43Njc1IDE4LjIzNzEgOTAuNjUyOSAxOC4yMzcxIDkwLjUzMDVWOTAuMjI1OEMxOC4yMzcxIDkwLjEwMzQgMTguMjEzNiA4OS45ODg5IDE4LjE2NjggODkuODgyMUMxOC4xMjI1IDg5Ljc3NTMgMTguMDUzNSA4OS42ODk0IDE3Ljk1OTcgODkuNjI0M0MxNy44Njg2IDg5LjU1NjYgMTcuNzUxNCA4OS41MjI3IDE3LjYwODIgODkuNTIyN0MxNy40NzAxIDg5LjUyMjcgMTcuMzU0MyA4OS41NTY2IDE3LjI2MDUgODkuNjI0M0MxNy4xNjk0IDg5LjY4OTQgMTcuMTAwMyA4OS43NzUzIDE3LjA1MzUgODkuODgyMUMxNy4wMDkyIDg5Ljk4ODkgMTYuOTg3MSA5MC4xMDM0IDE2Ljk4NzEgOTAuMjI1OFpNMTcuNzg3OCA4Ni43NDE1TDE1LjAxMDUgOTEuMTg2OEwxNC42MDQzIDkwLjkyOUwxNy4zODE2IDg2LjQ4MzZMMTcuNzg3OCA4Ni43NDE1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNOC4xOTkyMiAxMTkuMjMzVjExOS44MjdINC40NzY1NlYxMTkuMzA4TDYuMzM5ODQgMTE3LjIzM0M2LjU2OTAxIDExNi45NzggNi43NDYwOSAxMTYuNzYyIDYuODcxMDkgMTE2LjU4NUM2Ljk5ODcgMTE2LjQwNSA3LjA4NzI0IDExNi4yNDUgNy4xMzY3MiAxMTYuMTA0QzcuMTg4OCAxMTUuOTYxIDcuMjE0ODQgMTE1LjgxNSA3LjIxNDg0IDExNS42NjdDNy4yMTQ4NCAxMTUuNDc5IDcuMTc1NzggMTE1LjMxIDcuMDk3NjYgMTE1LjE1OUM3LjAyMjE0IDExNS4wMDYgNi45MTAxNiAxMTQuODgzIDYuNzYxNzIgMTE0Ljc5MkM2LjYxMzI4IDExNC43MDEgNi40MzM1OSAxMTQuNjU1IDYuMjIyNjYgMTE0LjY1NUM1Ljk3MDA1IDExNC42NTUgNS43NTkxMSAxMTQuNzA1IDUuNTg5ODQgMTE0LjgwNEM1LjQyMzE4IDExNC45IDUuMjk4MTggMTE1LjAzNSA1LjIxNDg0IDExNS4yMUM1LjEzMTUxIDExNS4zODQgNS4wODk4NCAxMTUuNTg1IDUuMDg5ODQgMTE1LjgxMkg0LjM2NzE5QzQuMzY3MTkgMTE1LjQ5MSA0LjQzNzUgMTE1LjE5OCA0LjU3ODEyIDExNC45MzNDNC43MTg3NSAxMTQuNjY3IDQuOTI3MDggMTE0LjQ1NiA1LjIwMzEyIDExNC4zQzUuNDc5MTcgMTE0LjE0MSA1LjgxOTAxIDExNC4wNjIgNi4yMjI2NiAxMTQuMDYyQzYuNTgyMDMgMTE0LjA2MiA2Ljg4OTMyIDExNC4xMjUgNy4xNDQ1MyAxMTQuMjUzQzcuMzk5NzQgMTE0LjM3OCA3LjU5NTA1IDExNC41NTUgNy43MzA0NyAxMTQuNzg0QzcuODY4NDkgMTE1LjAxMSA3LjkzNzUgMTE1LjI3NiA3LjkzNzUgMTE1LjU4MUM3LjkzNzUgMTE1Ljc0OCA3LjkwODg1IDExNS45MTcgNy44NTE1NiAxMTYuMDg5QzcuNzk2ODggMTE2LjI1OCA3LjcyMDA1IDExNi40MjcgNy42MjEwOSAxMTYuNTk3QzcuNTI0NzQgMTE2Ljc2NiA3LjQxMTQ2IDExNi45MzMgNy4yODEyNSAxMTcuMDk3QzcuMTUzNjUgMTE3LjI2MSA3LjAxNjkzIDExNy40MjIgNi44NzEwOSAxMTcuNTgxTDUuMzQ3NjYgMTE5LjIzM0g4LjE5OTIyWk0xMi42NzUyIDExNi41M1YxMTcuMzk3QzEyLjY3NTIgMTE3Ljg2NCAxMi42MzM1IDExOC4yNTcgMTIuNTUwMiAxMTguNTc3QzEyLjQ2NjggMTE4Ljg5NyAxMi4zNDcgMTE5LjE1NSAxMi4xOTA4IDExOS4zNTFDMTIuMDM0NSAxMTkuNTQ2IDExLjg0NTcgMTE5LjY4OCAxMS42MjQ0IDExOS43NzZDMTEuNDA1NiAxMTkuODYyIDExLjE1ODIgMTE5LjkwNSAxMC44ODIyIDExOS45MDVDMTAuNjYzNSAxMTkuOTA1IDEwLjQ2MTYgMTE5Ljg3OCAxMC4yNzY3IDExOS44MjNDMTAuMDkxOCAxMTkuNzY5IDkuOTI1MTcgMTE5LjY4MSA5Ljc3NjczIDExOS41NjJDOS42MzA5IDExOS40MzkgOS41MDU5IDExOS4yOCA5LjQwMTczIDExOS4wODVDOS4yOTc1NyAxMTguODkgOS4yMTgxNCAxMTguNjUzIDkuMTYzNDUgMTE4LjM3NEM5LjEwODc3IDExOC4wOTUgOS4wODE0MiAxMTcuNzcgOS4wODE0MiAxMTcuMzk3VjExNi41M0M5LjA4MTQyIDExNi4wNjQgOS4xMjMwOSAxMTUuNjc0IDkuMjA2NDIgMTE1LjM1OEM5LjI5MjM2IDExNS4wNDMgOS40MTM0NSAxMTQuNzkxIDkuNTY5NyAxMTQuNjAxQzkuNzI1OTUgMTE0LjQwOCA5LjkxMzQ1IDExNC4yNyAxMC4xMzIyIDExNC4xODdDMTAuMzUzNiAxMTQuMTAzIDEwLjYwMSAxMTQuMDYyIDEwLjg3NDQgMTE0LjA2MkMxMS4wOTU3IDExNC4wNjIgMTEuMjk4OSAxMTQuMDg5IDExLjQ4MzggMTE0LjE0NEMxMS42NzEzIDExNC4xOTYgMTEuODM3OSAxMTQuMjggMTEuOTgzOCAxMTQuMzk3QzEyLjEyOTYgMTE0LjUxMiAxMi4yNTMzIDExNC42NjYgMTIuMzU0OSAxMTQuODU4QzEyLjQ1OSAxMTUuMDQ5IDEyLjUzODUgMTE1LjI4MiAxMi41OTMxIDExNS41NThDMTIuNjQ3OCAxMTUuODM0IDEyLjY3NTIgMTE2LjE1OCAxMi42NzUyIDExNi41M1pNMTEuOTQ4NiAxMTcuNTE1VjExNi40MDlDMTEuOTQ4NiAxMTYuMTU0IDExLjkzMyAxMTUuOTMgMTEuOTAxNyAxMTUuNzM3QzExLjg3MzEgMTE1LjU0MiAxMS44MzAxIDExNS4zNzUgMTEuNzcyOCAxMTUuMjM3QzExLjcxNTUgMTE1LjA5OSAxMS42NDI2IDExNC45ODcgMTEuNTU0MSAxMTQuOTAxQzExLjQ2ODEgMTE0LjgxNSAxMS4zNjc5IDExNC43NTMgMTEuMjUzMyAxMTQuNzE0QzExLjE0MTMgMTE0LjY3MiAxMS4wMTUgMTE0LjY1MSAxMC44NzQ0IDExNC42NTFDMTAuNzAyNSAxMTQuNjUxIDEwLjU1MDIgMTE0LjY4NCAxMC40MTc0IDExNC43NDlDMTAuMjg0NSAxMTQuODEyIDEwLjE3MjYgMTE0LjkxMiAxMC4wODE0IDExNS4wNUM5Ljk5Mjg4IDExNS4xODggOS45MjUxNyAxMTUuMzY5IDkuODc4MyAxMTUuNTkzQzkuODMxNDIgMTE1LjgxNyA5LjgwNzk4IDExNi4wODkgOS44MDc5OCAxMTYuNDA5VjExNy41MTVDOS44MDc5OCAxMTcuNzcgOS44MjIzMSAxMTcuOTk1IDkuODUwOTUgMTE4LjE5QzkuODgyMiAxMTguMzg2IDkuOTI3NzggMTE4LjU1NSA5Ljk4NzY3IDExOC42OThDMTAuMDQ3NiAxMTguODM5IDEwLjEyMDUgMTE4Ljk1NSAxMC4yMDY0IDExOS4wNDZDMTAuMjkyNCAxMTkuMTM3IDEwLjM5MTMgMTE5LjIwNSAxMC41MDMzIDExOS4yNDlDMTAuNjE3OSAxMTkuMjkxIDEwLjc0NDIgMTE5LjMxMiAxMC44ODIyIDExOS4zMTJDMTEuMDU5MyAxMTkuMzEyIDExLjIxNDIgMTE5LjI3OCAxMS4zNDcgMTE5LjIxQzExLjQ3OTkgMTE5LjE0MiAxMS41OTA1IDExOS4wMzcgMTEuNjc5MSAxMTguODk0QzExLjc3MDIgMTE4Ljc0OCAxMS44Mzc5IDExOC41NjIgMTEuODgyMiAxMTguMzM1QzExLjkyNjUgMTE4LjEwNiAxMS45NDg2IDExNy44MzIgMTEuOTQ4NiAxMTcuNTE1Wk0xMy42NzQ2IDExNS41MzRWMTE1LjIzM0MxMy42NzQ2IDExNS4wMTcgMTMuNzIxNCAxMTQuODIxIDEzLjgxNTIgMTE0LjY0NEMxMy45MDg5IDExNC40NjYgMTQuMDQzMSAxMTQuMzI1IDE0LjIxNzUgMTE0LjIxOEMxNC4zOTIgMTE0LjExMSAxNC41OTkgMTE0LjA1OCAxNC44Mzg2IDExNC4wNThDMTUuMDgzNCAxMTQuMDU4IDE1LjI5MTggMTE0LjExMSAxNS40NjM2IDExNC4yMThDMTUuNjM4MSAxMTQuMzI1IDE1Ljc3MjIgMTE0LjQ2NiAxNS44NjYgMTE0LjY0NEMxNS45NTk3IDExNC44MjEgMTYuMDA2NiAxMTUuMDE3IDE2LjAwNjYgMTE1LjIzM1YxMTUuNTM0QzE2LjAwNjYgMTE1Ljc0NSAxNS45NTk3IDExNS45MzkgMTUuODY2IDExNi4xMTZDMTUuNzc0OCAxMTYuMjkzIDE1LjY0MiAxMTYuNDM1IDE1LjQ2NzUgMTE2LjU0MkMxNS4yOTU3IDExNi42NDkgMTUuMDg4NiAxMTYuNzAyIDE0Ljg0NjQgMTE2LjcwMkMxNC42MDQzIDExNi43MDIgMTQuMzk0NiAxMTYuNjQ5IDE0LjIxNzUgMTE2LjU0MkMxNC4wNDMxIDExNi40MzUgMTMuOTA4OSAxMTYuMjkzIDEzLjgxNTIgMTE2LjExNkMxMy43MjE0IDExNS45MzkgMTMuNjc0NiAxMTUuNzQ1IDEzLjY3NDYgMTE1LjUzNFpNMTQuMjE3NSAxMTUuMjMzVjExNS41MzRDMTQuMjE3NSAxMTUuNjU0IDE0LjIzOTcgMTE1Ljc2NyAxNC4yODM5IDExNS44NzRDMTQuMzMwOCAxMTUuOTgxIDE0LjQwMTEgMTE2LjA2OCAxNC40OTQ5IDExNi4xMzZDMTQuNTg4NiAxMTYuMjAxIDE0LjcwNTggMTE2LjIzMyAxNC44NDY0IDExNi4yMzNDMTQuOTg3MSAxMTYuMjMzIDE1LjEwMjkgMTE2LjIwMSAxNS4xOTQxIDExNi4xMzZDMTUuMjg1MiAxMTYuMDY4IDE1LjM1MjkgMTE1Ljk4MSAxNS4zOTcyIDExNS44NzRDMTUuNDQxNSAxMTUuNzY3IDE1LjQ2MzYgMTE1LjY1NCAxNS40NjM2IDExNS41MzRWMTE1LjIzM0MxNS40NjM2IDExNS4xMTEgMTUuNDQwMiAxMTQuOTk2IDE1LjM5MzMgMTE0Ljg5QzE1LjM0OSAxMTQuNzggMTUuMjggMTE0LjY5MyAxNS4xODYzIDExNC42MjhDMTUuMDk1MSAxMTQuNTYgMTQuOTc5MyAxMTQuNTI2IDE0LjgzODYgMTE0LjUyNkMxNC43MDA2IDExNC41MjYgMTQuNTg0NyAxMTQuNTYgMTQuNDkxIDExNC42MjhDMTQuMzk5OCAxMTQuNjkzIDE0LjMzMDggMTE0Ljc4IDE0LjI4MzkgMTE0Ljg5QzE0LjIzOTcgMTE0Ljk5NiAxNC4yMTc1IDExNS4xMTEgMTQuMjE3NSAxMTUuMjMzWk0xNi40NDQxIDExOC43MzdWMTE4LjQzM0MxNi40NDQxIDExOC4yMTkgMTYuNDkxIDExOC4wMjQgMTYuNTg0NyAxMTcuODQ3QzE2LjY3ODUgMTE3LjY3IDE2LjgxMjYgMTE3LjUyOCAxNi45ODcxIDExNy40MjFDMTcuMTYxNSAxMTcuMzE0IDE3LjM2ODYgMTE3LjI2MSAxNy42MDgyIDExNy4yNjFDMTcuODUyOSAxMTcuMjYxIDE4LjA2MTMgMTE3LjMxNCAxOC4yMzMyIDExNy40MjFDMTguNDA3NiAxMTcuNTI4IDE4LjU0MTggMTE3LjY3IDE4LjYzNTUgMTE3Ljg0N0MxOC43MjkzIDExOC4wMjQgMTguNzc2MSAxMTguMjE5IDE4Ljc3NjEgMTE4LjQzM1YxMTguNzM3QzE4Ljc3NjEgMTE4Ljk1MSAxOC43MjkzIDExOS4xNDYgMTguNjM1NSAxMTkuMzIzQzE4LjU0NDQgMTE5LjUgMTguNDExNSAxMTkuNjQyIDE4LjIzNzEgMTE5Ljc0OUMxOC4wNjUyIDExOS44NTYgMTcuODU4MiAxMTkuOTA5IDE3LjYxNiAxMTkuOTA5QzE3LjM3MzggMTE5LjkwOSAxNy4xNjU0IDExOS44NTYgMTYuOTkxIDExOS43NDlDMTYuODE2NSAxMTkuNjQyIDE2LjY4MTEgMTE5LjUgMTYuNTg0NyAxMTkuMzIzQzE2LjQ5MSAxMTkuMTQ2IDE2LjQ0NDEgMTE4Ljk1MSAxNi40NDQxIDExOC43MzdaTTE2Ljk4NzEgMTE4LjQzM1YxMTguNzM3QzE2Ljk4NzEgMTE4Ljg1NyAxNy4wMDkyIDExOC45NzIgMTcuMDUzNSAxMTkuMDgxQzE3LjEwMDMgMTE5LjE4OCAxNy4xNzA3IDExOS4yNzUgMTcuMjY0NCAxMTkuMzQzQzE3LjM1ODIgMTE5LjQwOCAxNy40NzUzIDExOS40NCAxNy42MTYgMTE5LjQ0QzE3Ljc1NjYgMTE5LjQ0IDE3Ljg3MjUgMTE5LjQwOCAxNy45NjM2IDExOS4zNDNDMTguMDU3NCAxMTkuMjc1IDE4LjEyNjQgMTE5LjE4OCAxOC4xNzA3IDExOS4wODFDMTguMjE0OSAxMTguOTc0IDE4LjIzNzEgMTE4Ljg2IDE4LjIzNzEgMTE4LjczN1YxMTguNDMzQzE4LjIzNzEgMTE4LjMxIDE4LjIxMzYgMTE4LjE5NiAxOC4xNjY4IDExOC4wODlDMTguMTIyNSAxMTcuOTgyIDE4LjA1MzUgMTE3Ljg5NiAxNy45NTk3IDExNy44MzFDMTcuODY4NiAxMTcuNzYzIDE3Ljc1MTQgMTE3LjcyOSAxNy42MDgyIDExNy43MjlDMTcuNDcwMSAxMTcuNzI5IDE3LjM1NDMgMTE3Ljc2MyAxNy4yNjA1IDExNy44MzFDMTcuMTY5NCAxMTcuODk2IDE3LjEwMDMgMTE3Ljk4MiAxNy4wNTM1IDExOC4wODlDMTcuMDA5MiAxMTguMTk2IDE2Ljk4NzEgMTE4LjMxIDE2Ljk4NzEgMTE4LjQzM1pNMTcuNzg3OCAxMTQuOTQ4TDE1LjAxMDUgMTE5LjM5NEwxNC42MDQzIDExOS4xMzZMMTcuMzgxNiAxMTQuNjlMMTcuNzg3OCAxMTQuOTQ4WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNMTMuMDQzIDE0NC43MzdWMTQ1LjYwNEMxMy4wNDMgMTQ2LjA3IDEzLjAwMTMgMTQ2LjQ2NCAxMi45MTggMTQ2Ljc4NEMxMi44MzQ2IDE0Ny4xMDQgMTIuNzE0OCAxNDcuMzYyIDEyLjU1ODYgMTQ3LjU1N0MxMi40MDIzIDE0Ny43NTMgMTIuMjEzNSAxNDcuODk1IDExLjk5MjIgMTQ3Ljk4M0MxMS43NzM0IDE0OC4wNjkgMTEuNTI2IDE0OC4xMTIgMTEuMjUgMTQ4LjExMkMxMS4wMzEyIDE0OC4xMTIgMTAuODI5NCAxNDguMDg1IDEwLjY0NDUgMTQ4LjAzQzEwLjQ1OTYgMTQ3Ljk3NSAxMC4yOTMgMTQ3Ljg4OCAxMC4xNDQ1IDE0Ny43NjhDOS45OTg3IDE0Ny42NDYgOS44NzM3IDE0Ny40ODcgOS43Njk1MyAxNDcuMjkyQzkuNjY1MzYgMTQ3LjA5NiA5LjU4NTk0IDE0Ni44NTkgOS41MzEyNSAxNDYuNTgxQzkuNDc2NTYgMTQ2LjMwMiA5LjQ0OTIyIDE0NS45NzcgOS40NDkyMiAxNDUuNjA0VjE0NC43MzdDOS40NDkyMiAxNDQuMjcxIDkuNDkwODkgMTQzLjg4IDkuNTc0MjIgMTQzLjU2NUM5LjY2MDE2IDE0My4yNSA5Ljc4MTI1IDE0Mi45OTcgOS45Mzc1IDE0Mi44MDdDMTAuMDkzOCAxNDIuNjE1IDEwLjI4MTIgMTQyLjQ3NyAxMC41IDE0Mi4zOTNDMTAuNzIxNCAxNDIuMzEgMTAuOTY4OCAxNDIuMjY4IDExLjI0MjIgMTQyLjI2OEMxMS40NjM1IDE0Mi4yNjggMTEuNjY2NyAxNDIuMjk2IDExLjg1MTYgMTQyLjM1QzEyLjAzOTEgMTQyLjQwMiAxMi4yMDU3IDE0Mi40ODcgMTIuMzUxNiAxNDIuNjA0QzEyLjQ5NzQgMTQyLjcxOSAxMi42MjExIDE0Mi44NzIgMTIuNzIyNyAxNDMuMDY1QzEyLjgyNjggMTQzLjI1NSAxMi45MDYyIDE0My40ODggMTIuOTYwOSAxNDMuNzY0QzEzLjAxNTYgMTQ0LjA0IDEzLjA0MyAxNDQuMzY1IDEzLjA0MyAxNDQuNzM3Wk0xMi4zMTY0IDE0NS43MjFWMTQ0LjYxNkMxMi4zMTY0IDE0NC4zNjEgMTIuMzAwOCAxNDQuMTM3IDEyLjI2OTUgMTQzLjk0NEMxMi4yNDA5IDE0My43NDkgMTIuMTk3OSAxNDMuNTgyIDEyLjE0MDYgMTQzLjQ0NEMxMi4wODMzIDE0My4zMDYgMTIuMDEwNCAxNDMuMTk0IDExLjkyMTkgMTQzLjEwOEMxMS44MzU5IDE0My4wMjIgMTEuNzM1NyAxNDIuOTYgMTEuNjIxMSAxNDIuOTIxQzExLjUwOTEgMTQyLjg3OSAxMS4zODI4IDE0Mi44NTggMTEuMjQyMiAxNDIuODU4QzExLjA3MDMgMTQyLjg1OCAxMC45MTggMTQyLjg5MSAxMC43ODUyIDE0Mi45NTZDMTAuNjUyMyAxNDMuMDE4IDEwLjU0MDQgMTQzLjExOSAxMC40NDkyIDE0My4yNTdDMTAuMzYwNyAxNDMuMzk1IDEwLjI5MyAxNDMuNTc2IDEwLjI0NjEgMTQzLjhDMTAuMTk5MiAxNDQuMDI0IDEwLjE3NTggMTQ0LjI5NiAxMC4xNzU4IDE0NC42MTZWMTQ1LjcyMUMxMC4xNzU4IDE0NS45NzcgMTAuMTkwMSAxNDYuMjAyIDEwLjIxODggMTQ2LjM5N0MxMC4yNSAxNDYuNTkzIDEwLjI5NTYgMTQ2Ljc2MiAxMC4zNTU1IDE0Ni45MDVDMTAuNDE1NCAxNDcuMDQ2IDEwLjQ4ODMgMTQ3LjE2MiAxMC41NzQyIDE0Ny4yNTNDMTAuNjYwMiAxNDcuMzQ0IDEwLjc1OTEgMTQ3LjQxMiAxMC44NzExIDE0Ny40NTZDMTAuOTg1NyAxNDcuNDk3IDExLjExMiAxNDcuNTE4IDExLjI1IDE0Ny41MThDMTEuNDI3MSAxNDcuNTE4IDExLjU4MiAxNDcuNDg0IDExLjcxNDggMTQ3LjQxN0MxMS44NDc3IDE0Ny4zNDkgMTEuOTU4MyAxNDcuMjQ0IDEyLjA0NjkgMTQ3LjFDMTIuMTM4IDE0Ni45NTUgMTIuMjA1NyAxNDYuNzY4IDEyLjI1IDE0Ni41NDJDMTIuMjk0MyAxNDYuMzEzIDEyLjMxNjQgMTQ2LjAzOSAxMi4zMTY0IDE0NS43MjFaTTE0LjA0MjQgMTQzLjc0MVYxNDMuNDRDMTQuMDQyNCAxNDMuMjI0IDE0LjA4OTIgMTQzLjAyNyAxNC4xODMgMTQyLjg1QzE0LjI3NjcgMTQyLjY3MyAxNC40MTA4IDE0Mi41MzEgMTQuNTg1MyAxNDIuNDI1QzE0Ljc1OTggMTQyLjMxOCAxNC45NjY4IDE0Mi4yNjQgMTUuMjA2NCAxNDIuMjY0QzE1LjQ1MTIgMTQyLjI2NCAxNS42NTk1IDE0Mi4zMTggMTUuODMxNCAxNDIuNDI1QzE2LjAwNTkgMTQyLjUzMSAxNi4xNCAxNDIuNjczIDE2LjIzMzggMTQyLjg1QzE2LjMyNzUgMTQzLjAyNyAxNi4zNzQ0IDE0My4yMjQgMTYuMzc0NCAxNDMuNDRWMTQzLjc0MUMxNi4zNzQ0IDE0My45NTIgMTYuMzI3NSAxNDQuMTQ2IDE2LjIzMzggMTQ0LjMyM0MxNi4xNDI2IDE0NC41IDE2LjAwOTggMTQ0LjY0MiAxNS44MzUzIDE0NC43NDlDMTUuNjYzNSAxNDQuODU2IDE1LjQ1NjQgMTQ0LjkwOSAxNS4yMTQyIDE0NC45MDlDMTQuOTcyIDE0NC45MDkgMTQuNzYyNCAxNDQuODU2IDE0LjU4NTMgMTQ0Ljc0OUMxNC40MTA4IDE0NC42NDIgMTQuMjc2NyAxNDQuNSAxNC4xODMgMTQ0LjMyM0MxNC4wODkyIDE0NC4xNDYgMTQuMDQyNCAxNDMuOTUyIDE0LjA0MjQgMTQzLjc0MVpNMTQuNTg1MyAxNDMuNDRWMTQzLjc0MUMxNC41ODUzIDE0My44NjEgMTQuNjA3NSAxNDMuOTc0IDE0LjY1MTcgMTQ0LjA4MUMxNC42OTg2IDE0NC4xODggMTQuNzY4OSAxNDQuMjc1IDE0Ljg2MjcgMTQ0LjM0M0MxNC45NTY0IDE0NC40MDggMTUuMDczNiAxNDQuNDQgMTUuMjE0MiAxNDQuNDRDMTUuMzU0OSAxNDQuNDQgMTUuNDcwNyAxNDQuNDA4IDE1LjU2MTkgMTQ0LjM0M0MxNS42NTMgMTQ0LjI3NSAxNS43MjA3IDE0NC4xODggMTUuNzY1IDE0NC4wODFDMTUuODA5MyAxNDMuOTc0IDE1LjgzMTQgMTQzLjg2MSAxNS44MzE0IDE0My43NDFWMTQzLjQ0QzE1LjgzMTQgMTQzLjMxOCAxNS44MDggMTQzLjIwMyAxNS43NjExIDE0My4wOTZDMTUuNzE2OCAxNDIuOTg3IDE1LjY0NzggMTQyLjkgMTUuNTU0MSAxNDIuODM1QzE1LjQ2MjkgMTQyLjc2NyAxNS4zNDcgMTQyLjczMyAxNS4yMDY0IDE0Mi43MzNDMTUuMDY4NCAxNDIuNzMzIDE0Ljk1MjUgMTQyLjc2NyAxNC44NTg4IDE0Mi44MzVDMTQuNzY3NiAxNDIuOSAxNC42OTg2IDE0Mi45ODcgMTQuNjUxNyAxNDMuMDk2QzE0LjYwNzUgMTQzLjIwMyAxNC41ODUzIDE0My4zMTggMTQuNTg1MyAxNDMuNDRaTTE2LjgxMTkgMTQ2Ljk0NFYxNDYuNjM5QzE2LjgxMTkgMTQ2LjQyNiAxNi44NTg4IDE0Ni4yMzEgMTYuOTUyNSAxNDYuMDUzQzE3LjA0NjMgMTQ1Ljg3NiAxNy4xODA0IDE0NS43MzQgMTcuMzU0OSAxNDUuNjI4QzE3LjUyOTMgMTQ1LjUyMSAxNy43MzY0IDE0NS40NjggMTcuOTc2IDE0NS40NjhDMTguMjIwNyAxNDUuNDY4IDE4LjQyOTEgMTQ1LjUyMSAxOC42MDEgMTQ1LjYyOEMxOC43NzU0IDE0NS43MzQgMTguOTA5NSAxNDUuODc2IDE5LjAwMzMgMTQ2LjA1M0MxOS4wOTcgMTQ2LjIzMSAxOS4xNDM5IDE0Ni40MjYgMTkuMTQzOSAxNDYuNjM5VjE0Ni45NDRDMTkuMTQzOSAxNDcuMTU4IDE5LjA5NyAxNDcuMzUzIDE5LjAwMzMgMTQ3LjUzQzE4LjkxMjIgMTQ3LjcwNyAxOC43NzkzIDE0Ny44NDkgMTguNjA0OSAxNDcuOTU2QzE4LjQzMyAxNDguMDYzIDE4LjIyNiAxNDguMTE2IDE3Ljk4MzggMTQ4LjExNkMxNy43NDE2IDE0OC4xMTYgMTcuNTMzMiAxNDguMDYzIDE3LjM1ODggMTQ3Ljk1NkMxNy4xODQzIDE0Ny44NDkgMTcuMDQ4OSAxNDcuNzA3IDE2Ljk1MjUgMTQ3LjUzQzE2Ljg1ODggMTQ3LjM1MyAxNi44MTE5IDE0Ny4xNTggMTYuODExOSAxNDYuOTQ0Wk0xNy4zNTQ5IDE0Ni42MzlWMTQ2Ljk0NEMxNy4zNTQ5IDE0Ny4wNjQgMTcuMzc3IDE0Ny4xNzggMTcuNDIxMyAxNDcuMjg4QzE3LjQ2ODEgMTQ3LjM5NSAxNy41Mzg1IDE0Ny40ODIgMTcuNjMyMiAxNDcuNTVDMTcuNzI2IDE0Ny42MTUgMTcuODQzMSAxNDcuNjQ3IDE3Ljk4MzggMTQ3LjY0N0MxOC4xMjQ0IDE0Ny42NDcgMTguMjQwMyAxNDcuNjE1IDE4LjMzMTQgMTQ3LjU1QzE4LjQyNTIgMTQ3LjQ4MiAxOC40OTQyIDE0Ny4zOTUgMTguNTM4NSAxNDcuMjg4QzE4LjU4MjcgMTQ3LjE4MSAxOC42MDQ5IDE0Ny4wNjYgMTguNjA0OSAxNDYuOTQ0VjE0Ni42MzlDMTguNjA0OSAxNDYuNTE3IDE4LjU4MTQgMTQ2LjQwMiAxOC41MzQ1IDE0Ni4yOTZDMTguNDkwMyAxNDYuMTg5IDE4LjQyMTMgMTQ2LjEwMyAxOC4zMjc1IDE0Ni4wMzhDMTguMjM2NCAxNDUuOTcgMTguMTE5MiAxNDUuOTM2IDE3Ljk3NiAxNDUuOTM2QzE3LjgzNzkgMTQ1LjkzNiAxNy43MjIgMTQ1Ljk3IDE3LjYyODMgMTQ2LjAzOEMxNy41MzcyIDE0Ni4xMDMgMTcuNDY4MSAxNDYuMTg5IDE3LjQyMTMgMTQ2LjI5NkMxNy4zNzcgMTQ2LjQwMiAxNy4zNTQ5IDE0Ni41MTcgMTcuMzU0OSAxNDYuNjM5Wk0xOC4xNTU2IDE0My4xNTVMMTUuMzc4MyAxNDcuNkwxNC45NzIgMTQ3LjM0M0wxNy43NDk0IDE0Mi44OTdMMTguMTU1NiAxNDMuMTU1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNMjUgNC4xNjExM0wyMDAgNC4xNjExNiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTI1IDMzLjE2MTFMMjAwIDMzLjE2MTIiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIi8+CjxwYXRoIGQ9Ik0yNSA2MS4xNjExTDIwMCA2MS4xNjEyIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMjUgODkuMTYxMUwyMDAgODkuMTYxMiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTI1IDExOC4xNjFMMjAwIDExOC4xNjEiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIi8+CjxsaW5lIHgxPSIyMy4yIiB5MT0iMTQ1Ljk2MSIgeDI9IjIwMi44IiB5Mj0iMTQ1Ljk2MSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNyIgc3Ryb2tlLXdpZHRoPSIwLjQiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAxXzQxODJfMTExOTMpIj4KPGxpbmUgeDE9IjQwLjQ1IiB5MT0iMTQ3LjA3MiIgeDI9IjQwLjQ1IiB5Mj0iMTQ2LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNMzIuNTA5MSAxNTIuMDI1VjE1Mi44OTNDMzIuNTA5MSAxNTMuMzU5IDMyLjQ2NzQgMTUzLjc1MiAzMi4zODQxIDE1NC4wNzJDMzIuMzAwNyAxNTQuMzkzIDMyLjE4MDkgMTU0LjY1IDMyLjAyNDcgMTU0Ljg0NkMzMS44Njg0IDE1NS4wNDEgMzEuNjc5NiAxNTUuMTgzIDMxLjQ1ODMgMTU1LjI3MUMzMS4yMzk1IDE1NS4zNTcgMzAuOTkyMSAxNTUuNCAzMC43MTYxIDE1NS40QzMwLjQ5NzMgMTU1LjQgMzAuMjk1NSAxNTUuMzczIDMwLjExMDYgMTU1LjMxOEMyOS45MjU3IDE1NS4yNjQgMjkuNzU5MSAxNTUuMTc2IDI5LjYxMDYgMTU1LjA1N0MyOS40NjQ4IDE1NC45MzQgMjkuMzM5OCAxNTQuNzc1IDI5LjIzNTYgMTU0LjU4QzI5LjEzMTUgMTU0LjM4NSAyOS4wNTIgMTU0LjE0OCAyOC45OTczIDE1My44NjlDMjguOTQyNyAxNTMuNTkgMjguOTE1MyAxNTMuMjY1IDI4LjkxNTMgMTUyLjg5M1YxNTIuMDI1QzI4LjkxNTMgMTUxLjU1OSAyOC45NTcgMTUxLjE2OSAyOS4wNDAzIDE1MC44NTRDMjkuMTI2MiAxNTAuNTM4IDI5LjI0NzMgMTUwLjI4NiAyOS40MDM2IDE1MC4wOTZDMjkuNTU5OCAxNDkuOTAzIDI5Ljc0NzMgMTQ5Ljc2NSAyOS45NjYxIDE0OS42ODJDMzAuMTg3NCAxNDkuNTk4IDMwLjQzNDggMTQ5LjU1NyAzMC43MDgzIDE0OS41NTdDMzAuOTI5NiAxNDkuNTU3IDMxLjEzMjggMTQ5LjU4NCAzMS4zMTc3IDE0OS42MzlDMzEuNTA1MiAxNDkuNjkxIDMxLjY3MTggMTQ5Ljc3NSAzMS44MTc3IDE0OS44OTNDMzEuOTYzNSAxNTAuMDA3IDMyLjA4NzIgMTUwLjE2MSAzMi4xODg3IDE1MC4zNTRDMzIuMjkyOSAxNTAuNTQ0IDMyLjM3MjMgMTUwLjc3NyAzMi40MjcgMTUxLjA1M0MzMi40ODE3IDE1MS4zMjkgMzIuNTA5MSAxNTEuNjUzIDMyLjUwOTEgMTUyLjAyNVpNMzEuNzgyNSAxNTMuMDFWMTUxLjkwNEMzMS43ODI1IDE1MS42NDkgMzEuNzY2OSAxNTEuNDI1IDMxLjczNTYgMTUxLjIzMkMzMS43MDcgMTUxLjAzNyAzMS42NjQgMTUwLjg3IDMxLjYwNjcgMTUwLjczMkMzMS41NDk0IDE1MC41OTQgMzEuNDc2NSAxNTAuNDgyIDMxLjM4OCAxNTAuMzk2QzMxLjMwMiAxNTAuMzExIDMxLjIwMTggMTUwLjI0OCAzMS4wODcyIDE1MC4yMDlDMzAuOTc1MiAxNTAuMTY3IDMwLjg0ODkgMTUwLjE0NiAzMC43MDgzIDE1MC4xNDZDMzAuNTM2NCAxNTAuMTQ2IDMwLjM4NDEgMTUwLjE3OSAzMC4yNTEyIDE1MC4yNDRDMzAuMTE4NCAxNTAuMzA3IDMwLjAwNjUgMTUwLjQwNyAyOS45MTUzIDE1MC41NDVDMjkuODI2OCAxNTAuNjgzIDI5Ljc1OTEgMTUwLjg2NCAyOS43MTIyIDE1MS4wODhDMjkuNjY1MyAxNTEuMzEyIDI5LjY0MTkgMTUxLjU4NCAyOS42NDE5IDE1MS45MDRWMTUzLjAxQzI5LjY0MTkgMTUzLjI2NSAyOS42NTYyIDE1My40OSAyOS42ODQ4IDE1My42ODZDMjkuNzE2MSAxNTMuODgxIDI5Ljc2MTcgMTU0LjA1IDI5LjgyMTYgMTU0LjE5M0MyOS44ODE1IDE1NC4zMzQgMjkuOTU0NCAxNTQuNDUgMzAuMDQwMyAxNTQuNTQxQzMwLjEyNjIgMTU0LjYzMiAzMC4yMjUyIDE1NC43IDMwLjMzNzIgMTU0Ljc0NEMzMC40NTE4IDE1NC43ODYgMzAuNTc4MSAxNTQuODA3IDMwLjcxNjEgMTU0LjgwN0MzMC44OTMyIDE1NC44MDcgMzEuMDQ4MSAxNTQuNzczIDMxLjE4MDkgMTU0LjcwNUMzMS4zMTM3IDE1NC42MzcgMzEuNDI0NCAxNTQuNTMyIDMxLjUxMyAxNTQuMzg5QzMxLjYwNDEgMTU0LjI0MyAzMS42NzE4IDE1NC4wNTcgMzEuNzE2MSAxNTMuODNDMzEuNzYwNCAxNTMuNjAxIDMxLjc4MjUgMTUzLjMyNyAzMS43ODI1IDE1My4wMVpNMzUuODk2NCAxNDkuNjA0VjE1NS4zMjJIMzUuMTczN1YxNTAuNTA2TDMzLjcxNjcgMTUxLjAzN1YxNTAuMzg1TDM1Ljc4MzEgMTQ5LjYwNEgzNS44OTY0Wk00MS4xMTI0IDE0OS42MzVWMTU1LjMyMkg0MC4zNTg1VjE0OS42MzVINDEuMTEyNFpNNDMuNDk1MiAxNTIuMTkzVjE1Mi44MTFINDAuOTQ4M1YxNTIuMTkzSDQzLjQ5NTJaTTQzLjg4MTkgMTQ5LjYzNVYxNTAuMjUySDQwLjk0ODNWMTQ5LjYzNUg0My44ODE5Wk00Ni40MjE2IDE1NS40QzQ2LjEyNzMgMTU1LjQgNDUuODYwNCAxNTUuMzUxIDQ1LjYyMDggMTU1LjI1MkM0NS4zODM4IDE1NS4xNSA0NS4xNzk0IDE1NS4wMDggNDUuMDA3NSAxNTQuODI2QzQ0LjgzODMgMTU0LjY0NCA0NC43MDgxIDE1NC40MjggNDQuNjE2OSAxNTQuMTc4QzQ0LjUyNTggMTUzLjkyOCA0NC40ODAyIDE1My42NTQgNDQuNDgwMiAxNTMuMzU3VjE1My4xOTNDNDQuNDgwMiAxNTIuODUgNDQuNTMxIDE1Mi41NDQgNDQuNjMyNSAxNTIuMjc1QzQ0LjczNDEgMTUyLjAwNSA0NC44NzIxIDE1MS43NzUgNDUuMDQ2NiAxNTEuNTg4QzQ1LjIyMTEgMTUxLjQgNDUuNDE5IDE1MS4yNTggNDUuNjQwMyAxNTEuMTYyQzQ1Ljg2MTcgMTUxLjA2NiA0Ni4wOTA5IDE1MS4wMTggNDYuMzI3OCAxNTEuMDE4QzQ2LjYyOTkgMTUxLjAxOCA0Ni44OTAzIDE1MS4wNyA0Ny4xMDkxIDE1MS4xNzRDNDcuMzMwNSAxNTEuMjc4IDQ3LjUxMTQgMTUxLjQyNCA0Ny42NTIxIDE1MS42MTFDNDcuNzkyNyAxNTEuNzk2IDQ3Ljg5NjkgMTUyLjAxNSA0Ny45NjQ2IDE1Mi4yNjhDNDguMDMyMyAxNTIuNTE4IDQ4LjA2NjEgMTUyLjc5MSA0OC4wNjYxIDE1My4wODhWMTUzLjQxMkg0NC45MDk5VjE1Mi44MjJINDcuMzQzNVYxNTIuNzY4QzQ3LjMzMzEgMTUyLjU4IDQ3LjI5NCAxNTIuMzk4IDQ3LjIyNjMgMTUyLjIyMUM0Ny4xNjEyIDE1Mi4wNDQgNDcuMDU3IDE1MS44OTggNDYuOTEzOCAxNTEuNzgzQzQ2Ljc3MDYgMTUxLjY2OSA0Ni41NzUyIDE1MS42MTEgNDYuMzI3OCAxNTEuNjExQzQ2LjE2MzggMTUxLjYxMSA0Ni4wMTI3IDE1MS42NDYgNDUuODc0NyAxNTEuNzE3QzQ1LjczNjcgMTUxLjc4NSA0NS42MTgyIDE1MS44ODYgNDUuNTE5MyAxNTIuMDIxQzQ1LjQyMDMgMTUyLjE1NyA0NS4zNDM1IDE1Mi4zMjIgNDUuMjg4OCAxNTIuNTE4QzQ1LjIzNDEgMTUyLjcxMyA0NS4yMDY4IDE1Mi45MzggNDUuMjA2OCAxNTMuMTkzVjE1My4zNTdDNDUuMjA2OCAxNTMuNTU4IDQ1LjIzNDEgMTUzLjc0NyA0NS4yODg4IDE1My45MjRDNDUuMzQ2MSAxNTQuMDk4IDQ1LjQyODEgMTU0LjI1MiA0NS41MzQ5IDE1NC4zODVDNDUuNjQ0MyAxNTQuNTE4IDQ1Ljc3NTggMTU0LjYyMiA0NS45Mjk0IDE1NC42OTdDNDYuMDg1NyAxNTQuNzczIDQ2LjI2MjcgMTU0LjgxMSA0Ni40NjA3IDE1NC44MTFDNDYuNzE1OSAxNTQuODExIDQ2LjkzMiAxNTQuNzU4IDQ3LjEwOTEgMTU0LjY1NEM0Ny4yODYyIDE1NC41NSA0Ny40NDExIDE1NC40MTEgNDcuNTczOSAxNTQuMjM2TDQ4LjAxMTQgMTU0LjU4NEM0Ny45MjAzIDE1NC43MjIgNDcuODA0NCAxNTQuODU0IDQ3LjY2MzggMTU0Ljk3OUM0Ny41MjMyIDE1NS4xMDQgNDcuMzUgMTU1LjIwNSA0Ny4xNDQzIDE1NS4yODNDNDYuOTQxMSAxNTUuMzYxIDQ2LjcwMDIgMTU1LjQgNDYuNDIxNiAxNTUuNFpNNDguOTg4NiAxNDkuMzIySDQ5LjcxNTJWMTU0LjUwMkw0OS42NTI3IDE1NS4zMjJINDguOTg4NlYxNDkuMzIyWk01Mi41NzA2IDE1My4xNzRWMTUzLjI1NkM1Mi41NzA2IDE1My41NjMgNTIuNTM0MiAxNTMuODQ4IDUyLjQ2MTMgMTU0LjExMUM1Mi4zODgzIDE1NC4zNzIgNTIuMjgxNiAxNTQuNTk4IDUyLjE0MDkgMTU0Ljc5MUM1Mi4wMDAzIDE1NC45ODQgNTEuODI4NCAxNTUuMTMzIDUxLjYyNTMgMTU1LjI0QzUxLjQyMjIgMTU1LjM0NyA1MS4xODkxIDE1NS40IDUwLjkyNjEgMTU1LjRDNTAuNjU3OSAxNTUuNCA1MC40MjIyIDE1NS4zNTUgNTAuMjE5MSAxNTUuMjY0QzUwLjAxODUgMTU1LjE3IDQ5Ljg0OTMgMTU1LjAzNiA0OS43MTEzIDE1NC44NjFDNDkuNTczMiAxNTQuNjg3IDQ5LjQ2MjYgMTU0LjQ3NiA0OS4zNzkyIDE1NC4yMjlDNDkuMjk4NSAxNTMuOTgxIDQ5LjI0MjUgMTUzLjcwMiA0OS4yMTEzIDE1My4zOTNWMTUzLjAzM0M0OS4yNDI1IDE1Mi43MjEgNDkuMjk4NSAxNTIuNDQxIDQ5LjM3OTIgMTUyLjE5M0M0OS40NjI2IDE1MS45NDYgNDkuNTczMiAxNTEuNzM1IDQ5LjcxMTMgMTUxLjU2MUM0OS44NDkzIDE1MS4zODMgNTAuMDE4NSAxNTEuMjQ5IDUwLjIxOTEgMTUxLjE1OEM1MC40MTk2IDE1MS4wNjQgNTAuNjUyNyAxNTEuMDE4IDUwLjkxODMgMTUxLjAxOEM1MS4xODM5IDE1MS4wMTggNTEuNDE5NiAxNTEuMDcgNTEuNjI1MyAxNTEuMTc0QzUxLjgzMSAxNTEuMjc1IDUyLjAwMjkgMTUxLjQyMSA1Mi4xNDA5IDE1MS42MTFDNTIuMjgxNiAxNTEuODAxIDUyLjM4ODMgMTUyLjAyOSA1Mi40NjEzIDE1Mi4yOTVDNTIuNTM0MiAxNTIuNTU4IDUyLjU3MDYgMTUyLjg1MSA1Mi41NzA2IDE1My4xNzRaTTUxLjg0NDEgMTUzLjI1NlYxNTMuMTc0QzUxLjg0NDEgMTUyLjk2MyA1MS44MjQ1IDE1Mi43NjUgNTEuNzg1NSAxNTIuNThDNTEuNzQ2NCAxNTIuMzkzIDUxLjY4MzkgMTUyLjIyOSA1MS41OTggMTUyLjA4OEM1MS41MTIgMTUxLjk0NSA1MS4zOTg4IDE1MS44MzMgNTEuMjU4MSAxNTEuNzUyQzUxLjExNzUgMTUxLjY2OSA1MC45NDQzIDE1MS42MjcgNTAuNzM4NiAxNTEuNjI3QzUwLjU1NjMgMTUxLjYyNyA1MC4zOTc1IDE1MS42NTggNTAuMjYyIDE1MS43MjFDNTAuMTI5MiAxNTEuNzgzIDUwLjAxNTkgMTUxLjg2OCA0OS45MjIyIDE1MS45NzVDNDkuODI4NCAxNTIuMDc5IDQ5Ljc1MTYgMTUyLjE5OSA0OS42OTE3IDE1Mi4zMzRDNDkuNjM0NCAxNTIuNDY3IDQ5LjU5MTUgMTUyLjYwNSA0OS41NjI4IDE1Mi43NDhWMTUzLjY4OUM0OS42MDQ1IDE1My44NzIgNDkuNjcyMiAxNTQuMDQ4IDQ5Ljc2NTkgMTU0LjIxN0M0OS44NjIzIDE1NC4zODMgNDkuOTg5OSAxNTQuNTIgNTAuMTQ4OCAxNTQuNjI3QzUwLjMxMDIgMTU0LjczNCA1MC41MDk0IDE1NC43ODcgNTAuNzQ2NCAxNTQuNzg3QzUwLjk0MTcgMTU0Ljc4NyA1MS4xMDg0IDE1NC43NDggNTEuMjQ2NCAxNTQuNjdDNTEuMzg3IDE1NC41ODkgNTEuNTAwMyAxNTQuNDc5IDUxLjU4NjMgMTU0LjMzOEM1MS42NzQ4IDE1NC4xOTcgNTEuNzM5OSAxNTQuMDM1IDUxLjc4MTYgMTUzLjg1QzUxLjgyMzIgMTUzLjY2NSA1MS44NDQxIDE1My40NjcgNTEuODQ0MSAxNTMuMjU2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8L2c+CjxsaW5lIHgxPSI1OC4xNDk5IiB5MT0iMTQ3LjIzMyIgeDI9IjU4LjE0OTkiIHkyPSIxNDYuNDExIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDJfNDE4Ml8xMTE5MykiPgo8bGluZSB4MT0iNzUuODQ5OSIgeTE9IjE0Ny4yMzMiIHgyPSI3NS44NDk5IiB5Mj0iMTQ2LjQxMSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNSIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPHBhdGggZD0iTTY3LjkwOSAxNTIuMTg2VjE1My4wNTNDNjcuOTA5IDE1My41MiA2Ny44NjczIDE1My45MTMgNjcuNzg0IDE1NC4yMzNDNjcuNzAwNiAxNTQuNTUzIDY3LjU4MDggMTU0LjgxMSA2Ny40MjQ2IDE1NS4wMDdDNjcuMjY4MyAxNTUuMjAyIDY3LjA3OTUgMTU1LjM0NCA2Ni44NTgyIDE1NS40MzJDNjYuNjM5NCAxNTUuNTE4IDY2LjM5MiAxNTUuNTYxIDY2LjExNiAxNTUuNTYxQzY1Ljg5NzIgMTU1LjU2MSA2NS42OTU0IDE1NS41MzQgNjUuNTEwNSAxNTUuNDc5QzY1LjMyNTYgMTU1LjQyNSA2NS4xNTkgMTU1LjMzNyA2NS4wMTA1IDE1NS4yMThDNjQuODY0NyAxNTUuMDk1IDY0LjczOTcgMTU0LjkzNiA2NC42MzU1IDE1NC43NDFDNjQuNTMxNCAxNTQuNTQ2IDY0LjQ1MTkgMTU0LjMwOSA2NC4zOTcyIDE1NC4wM0M2NC4zNDI2IDE1My43NTEgNjQuMzE1MiAxNTMuNDI2IDY0LjMxNTIgMTUzLjA1M1YxNTIuMTg2QzY0LjMxNTIgMTUxLjcyIDY0LjM1NjkgMTUxLjMzIDY0LjQ0MDIgMTUxLjAxNEM2NC41MjYxIDE1MC42OTkgNjQuNjQ3MiAxNTAuNDQ3IDY0LjgwMzUgMTUwLjI1N0M2NC45NTk3IDE1MC4wNjQgNjUuMTQ3MiAxNDkuOTI2IDY1LjM2NiAxNDkuODQzQzY1LjU4NzMgMTQ5Ljc1OSA2NS44MzQ3IDE0OS43MTggNjYuMTA4MiAxNDkuNzE4QzY2LjMyOTUgMTQ5LjcxOCA2Ni41MzI3IDE0OS43NDUgNjYuNzE3NiAxNDkuOEM2Ni45MDUxIDE0OS44NTIgNjcuMDcxNyAxNDkuOTM2IDY3LjIxNzYgMTUwLjA1M0M2Ny4zNjM0IDE1MC4xNjggNjcuNDg3MSAxNTAuMzIyIDY3LjU4ODYgMTUwLjUxNEM2Ny42OTI4IDE1MC43MDUgNjcuNzcyMiAxNTAuOTM4IDY3LjgyNjkgMTUxLjIxNEM2Ny44ODE2IDE1MS40OSA2Ny45MDkgMTUxLjgxNCA2Ny45MDkgMTUyLjE4NlpNNjcuMTgyNCAxNTMuMTcxVjE1Mi4wNjVDNjcuMTgyNCAxNTEuODEgNjcuMTY2OCAxNTEuNTg2IDY3LjEzNTUgMTUxLjM5M0M2Ny4xMDY5IDE1MS4xOTggNjcuMDYzOSAxNTEuMDMxIDY3LjAwNjYgMTUwLjg5M0M2Ni45NDkzIDE1MC43NTUgNjYuODc2NCAxNTAuNjQzIDY2Ljc4NzkgMTUwLjU1N0M2Ni43MDE5IDE1MC40NzEgNjYuNjAxNyAxNTAuNDA5IDY2LjQ4NzEgMTUwLjM3QzY2LjM3NTEgMTUwLjMyOCA2Ni4yNDg4IDE1MC4zMDcgNjYuMTA4MiAxNTAuMzA3QzY1LjkzNjMgMTUwLjMwNyA2NS43ODQgMTUwLjM0IDY1LjY1MTEgMTUwLjQwNUM2NS41MTgzIDE1MC40NjggNjUuNDA2NCAxNTAuNTY4IDY1LjMxNTIgMTUwLjcwNkM2NS4yMjY3IDE1MC44NDQgNjUuMTU5IDE1MS4wMjUgNjUuMTEyMSAxNTEuMjQ5QzY1LjA2NTIgMTUxLjQ3MyA2NS4wNDE4IDE1MS43NDUgNjUuMDQxOCAxNTIuMDY1VjE1My4xNzFDNjUuMDQxOCAxNTMuNDI2IDY1LjA1NjEgMTUzLjY1MSA2NS4wODQ3IDE1My44NDZDNjUuMTE2IDE1NC4wNDIgNjUuMTYxNiAxNTQuMjExIDY1LjIyMTUgMTU0LjM1NEM2NS4yODE0IDE1NC40OTUgNjUuMzU0MyAxNTQuNjExIDY1LjQ0MDIgMTU0LjcwMkM2NS41MjYxIDE1NC43OTMgNjUuNjI1MSAxNTQuODYxIDY1LjczNzEgMTU0LjkwNUM2NS44NTE3IDE1NC45NDcgNjUuOTc4IDE1NC45NjggNjYuMTE2IDE1NC45NjhDNjYuMjkzMSAxNTQuOTY4IDY2LjQ0OCAxNTQuOTM0IDY2LjU4MDggMTU0Ljg2NkM2Ni43MTM2IDE1NC43OTggNjYuODI0MyAxNTQuNjkzIDY2LjkxMjkgMTU0LjU1QzY3LjAwNCAxNTQuNDA0IDY3LjA3MTcgMTU0LjIxOCA2Ny4xMTYgMTUzLjk5MUM2Ny4xNjAzIDE1My43NjIgNjcuMTgyNCAxNTMuNDg4IDY3LjE4MjQgMTUzLjE3MVpNNjkuOTc2IDE1Mi4yODRINzAuNDkxNkM3MC43NDQyIDE1Mi4yODQgNzAuOTUyNSAxNTIuMjQyIDcxLjExNjYgMTUyLjE1OUM3MS4yODMzIDE1Mi4wNzMgNzEuNDA3IDE1MS45NTcgNzEuNDg3NyAxNTEuODExQzcxLjU3MSAxNTEuNjYzIDcxLjYxMjcgMTUxLjQ5NiA3MS42MTI3IDE1MS4zMTFDNzEuNjEyNyAxNTEuMDkzIDcxLjU3NjIgMTUwLjkwOSA3MS41MDMzIDE1MC43NkM3MS40MzA0IDE1MC42MTIgNzEuMzIxIDE1MC41IDcxLjE3NTIgMTUwLjQyNUM3MS4wMjkzIDE1MC4zNDkgNzAuODQ0NSAxNTAuMzExIDcwLjYyMDUgMTUwLjMxMUM3MC40MTc0IDE1MC4zMTEgNzAuMjM3NyAxNTAuMzUyIDcwLjA4MTQgMTUwLjQzMkM2OS45Mjc4IDE1MC41MSA2OS44MDY3IDE1MC42MjIgNjkuNzE4MiAxNTAuNzY4QzY5LjYzMjIgMTUwLjkxNCA2OS41ODkyIDE1MS4wODYgNjkuNTg5MiAxNTEuMjg0SDY4Ljg2NjZDNjguODY2NiAxNTAuOTk1IDY4LjkzOTUgMTUwLjczMiA2OS4wODUzIDE1MC40OTVDNjkuMjMxMiAxNTAuMjU4IDY5LjQzNTYgMTUwLjA2OSA2OS42OTg2IDE0OS45MjhDNjkuOTY0MiAxNDkuNzg4IDcwLjI3MTUgMTQ5LjcxOCA3MC42MjA1IDE0OS43MThDNzAuOTY0MiAxNDkuNzE4IDcxLjI2NSAxNDkuNzc5IDcxLjUyMjggMTQ5LjkwMUM3MS43ODA3IDE1MC4wMjEgNzEuOTgxMiAxNTAuMjAxIDcyLjEyNDQgMTUwLjQ0QzcyLjI2NzYgMTUwLjY3NyA3Mi4zMzkyIDE1MC45NzMgNzIuMzM5MiAxNTEuMzI3QzcyLjMzOTIgMTUxLjQ3IDcyLjMwNTQgMTUxLjYyNCA3Mi4yMzc3IDE1MS43ODhDNzIuMTcyNiAxNTEuOTQ5IDcyLjA2OTcgMTUyLjEgNzEuOTI5MSAxNTIuMjQxQzcxLjc5MTEgMTUyLjM4MiA3MS42MTE0IDE1Mi40OTcgNzEuMzkgMTUyLjU4OUM3MS4xNjg3IDE1Mi42NzcgNzAuOTAzIDE1Mi43MjEgNzAuNTkzMiAxNTIuNzIxSDY5Ljk3NlYxNTIuMjg0Wk02OS45NzYgMTUyLjg3OFYxNTIuNDQ0SDcwLjU5MzJDNzAuOTU1MSAxNTIuNDQ0IDcxLjI1NDYgMTUyLjQ4NyA3MS40OTE2IDE1Mi41NzNDNzEuNzI4NiAxNTIuNjU5IDcxLjkxNDggMTUyLjc3NCA3Mi4wNTAyIDE1Mi45MTdDNzIuMTg4MiAxNTMuMDYgNzIuMjg0NiAxNTMuMjE4IDcyLjMzOTIgMTUzLjM4OUM3Mi4zOTY1IDE1My41NTkgNzIuNDI1MiAxNTMuNzI4IDcyLjQyNTIgMTUzLjg5N0M3Mi40MjUyIDE1NC4xNjMgNzIuMzc5NiAxNTQuMzk5IDcyLjI4ODUgMTU0LjYwNEM3Mi4xOTk5IDE1NC44MSA3Mi4wNzM2IDE1NC45ODQgNzEuOTA5NiAxNTUuMTI4QzcxLjc0ODEgMTU1LjI3MSA3MS41NTggMTU1LjM3OSA3MS4zMzkyIDE1NS40NTJDNzEuMTIwNSAxNTUuNTI1IDcwLjg4MjIgMTU1LjU2MSA3MC42MjQ0IDE1NS41NjFDNzAuMzc3IDE1NS41NjEgNzAuMTQzOSAxNTUuNTI2IDY5LjkyNTIgMTU1LjQ1NkM2OS43MDkgMTU1LjM4NSA2OS41MTc2IDE1NS4yODQgNjkuMzUxIDE1NS4xNTFDNjkuMTg0MyAxNTUuMDE2IDY5LjA1NDEgMTU0Ljg1IDY4Ljk2MDMgMTU0LjY1NUM2OC44NjY2IDE1NC40NTcgNjguODE5NyAxNTQuMjMyIDY4LjgxOTcgMTUzLjk3OUg2OS41NDI0QzY5LjU0MjQgMTU0LjE3NyA2OS41ODUzIDE1NC4zNSA2OS42NzEzIDE1NC40OTlDNjkuNzU5OCAxNTQuNjQ3IDY5Ljg4NDggMTU0Ljc2MyA3MC4wNDYzIDE1NC44NDZDNzAuMjEwMyAxNTQuOTI3IDcwLjQwMyAxNTQuOTY4IDcwLjYyNDQgMTU0Ljk2OEM3MC44NDU4IDE1NC45NjggNzEuMDM1OSAxNTQuOTMgNzEuMTk0NyAxNTQuODU0QzcxLjM1NjIgMTU0Ljc3NiA3MS40Nzk5IDE1NC42NTkgNzEuNTY1OCAxNTQuNTAzQzcxLjY1NDMgMTU0LjM0NiA3MS42OTg2IDE1NC4xNSA3MS42OTg2IDE1My45MTNDNzEuNjk4NiAxNTMuNjc2IDcxLjY0OTEgMTUzLjQ4MiA3MS41NTAyIDE1My4zMzFDNzEuNDUxMiAxNTMuMTc3IDcxLjMxMDYgMTUzLjA2NCA3MS4xMjgzIDE1Mi45OTFDNzAuOTQ4NiAxNTIuOTE1IDcwLjczNjQgMTUyLjg3OCA3MC40OTE2IDE1Mi44NzhINjkuOTc2Wk03Ni41MTIzIDE0OS43OTZWMTU1LjQ4M0g3NS43NTg0VjE0OS43OTZINzYuNTEyM1pNNzguODk1MSAxNTIuMzU0VjE1Mi45NzFINzYuMzQ4MlYxNTIuMzU0SDc4Ljg5NTFaTTc5LjI4MTggMTQ5Ljc5NlYxNTAuNDEzSDc2LjM0ODJWMTQ5Ljc5Nkg3OS4yODE4Wk04MS44MjE1IDE1NS41NjFDODEuNTI3MiAxNTUuNTYxIDgxLjI2MDMgMTU1LjUxMiA4MS4wMjA3IDE1NS40MTNDODAuNzgzNyAxNTUuMzExIDgwLjU3OTMgMTU1LjE2OSA4MC40MDc0IDE1NC45ODdDODAuMjM4MiAxNTQuODA1IDgwLjEwOCAxNTQuNTg5IDgwLjAxNjggMTU0LjMzOUM3OS45MjU3IDE1NC4wODkgNzkuODgwMSAxNTMuODE1IDc5Ljg4MDEgMTUzLjUxOFYxNTMuMzU0Qzc5Ljg4MDEgMTUzLjAxIDc5LjkzMDkgMTUyLjcwNSA4MC4wMzI0IDE1Mi40MzZDODAuMTM0IDE1Mi4xNjUgODAuMjcyIDE1MS45MzYgODAuNDQ2NSAxNTEuNzQ5QzgwLjYyMSAxNTEuNTYxIDgwLjgxODkgMTUxLjQxOSA4MS4wNDAyIDE1MS4zMjNDODEuMjYxNiAxNTEuMjI3IDgxLjQ5MDggMTUxLjE3OCA4MS43Mjc3IDE1MS4xNzhDODIuMDI5OCAxNTEuMTc4IDgyLjI5MDIgMTUxLjIzMSA4Mi41MDkgMTUxLjMzNUM4Mi43MzA0IDE1MS40MzkgODIuOTExMyAxNTEuNTg1IDgzLjA1MiAxNTEuNzcyQzgzLjE5MjYgMTUxLjk1NyA4My4yOTY4IDE1Mi4xNzYgODMuMzY0NSAxNTIuNDI4QzgzLjQzMjIgMTUyLjY3OCA4My40NjYgMTUyLjk1MiA4My40NjYgMTUzLjI0OVYxNTMuNTczSDgwLjMwOThWMTUyLjk4M0g4Mi43NDM0VjE1Mi45MjhDODIuNzMzIDE1Mi43NDEgODIuNjkzOSAxNTIuNTU5IDgyLjYyNjIgMTUyLjM4MkM4Mi41NjExIDE1Mi4yMDUgODIuNDU2OSAxNTIuMDU5IDgyLjMxMzcgMTUxLjk0NEM4Mi4xNzA1IDE1MS44MyA4MS45NzUxIDE1MS43NzIgODEuNzI3NyAxNTEuNzcyQzgxLjU2MzcgMTUxLjc3MiA4MS40MTI2IDE1MS44MDcgODEuMjc0NiAxNTEuODc4QzgxLjEzNjYgMTUxLjk0NSA4MS4wMTgxIDE1Mi4wNDcgODAuOTE5MiAxNTIuMTgyQzgwLjgyMDIgMTUyLjMxOCA4MC43NDM0IDE1Mi40ODMgODAuNjg4NyAxNTIuNjc4QzgwLjYzNCAxNTIuODc0IDgwLjYwNjcgMTUzLjA5OSA4MC42MDY3IDE1My4zNTRWMTUzLjUxOEM4MC42MDY3IDE1My43MTkgODAuNjM0IDE1My45MDggODAuNjg4NyAxNTQuMDg1QzgwLjc0NiAxNTQuMjU5IDgwLjgyOCAxNTQuNDEzIDgwLjkzNDggMTU0LjU0NkM4MS4wNDQyIDE1NC42NzggODEuMTc1NyAxNTQuNzgzIDgxLjMyOTMgMTU0Ljg1OEM4MS40ODU2IDE1NC45MzQgODEuNjYyNiAxNTQuOTcxIDgxLjg2MDYgMTU0Ljk3MUM4Mi4xMTU4IDE1NC45NzEgODIuMzMxOSAxNTQuOTE5IDgyLjUwOSAxNTQuODE1QzgyLjY4NjEgMTU0LjcxMSA4Mi44NDEgMTU0LjU3MiA4Mi45NzM4IDE1NC4zOTdMODMuNDExMyAxNTQuNzQ1QzgzLjMyMDIgMTU0Ljg4MyA4My4yMDQzIDE1NS4wMTQgODMuMDYzNyAxNTUuMTM5QzgyLjkyMzEgMTU1LjI2NCA4Mi43NDk5IDE1NS4zNjYgODIuNTQ0MiAxNTUuNDQ0QzgyLjM0MSAxNTUuNTIyIDgyLjEwMDEgMTU1LjU2MSA4MS44MjE1IDE1NS41NjFaTTg0LjM4ODUgMTQ5LjQ4M0g4NS4xMTUxVjE1NC42NjNMODUuMDUyNiAxNTUuNDgzSDg0LjM4ODVWMTQ5LjQ4M1pNODcuOTcwNSAxNTMuMzM1VjE1My40MTdDODcuOTcwNSAxNTMuNzI0IDg3LjkzNDEgMTU0LjAwOSA4Ny44NjEyIDE1NC4yNzJDODcuNzg4MiAxNTQuNTMzIDg3LjY4MTUgMTU0Ljc1OSA4Ny41NDA4IDE1NC45NTJDODcuNDAwMiAxNTUuMTQ1IDg3LjIyODMgMTU1LjI5NCA4Ny4wMjUyIDE1NS40MDFDODYuODIyMSAxNTUuNTA4IDg2LjU4OSAxNTUuNTYxIDg2LjMyNiAxNTUuNTYxQzg2LjA1NzggMTU1LjU2MSA4NS44MjIxIDE1NS41MTYgODUuNjE5IDE1NS40MjVDODUuNDE4NSAxNTUuMzMxIDg1LjI0OTIgMTU1LjE5NyA4NS4xMTEyIDE1NS4wMjJDODQuOTczMSAxNTQuODQ4IDg0Ljg2MjUgMTU0LjYzNyA4NC43NzkxIDE1NC4zODlDODQuNjk4NCAxNTQuMTQyIDg0LjY0MjQgMTUzLjg2MyA4NC42MTEyIDE1My41NTNWMTUzLjE5NEM4NC42NDI0IDE1Mi44ODIgODQuNjk4NCAxNTIuNjAyIDg0Ljc3OTEgMTUyLjM1NEM4NC44NjI1IDE1Mi4xMDcgODQuOTczMSAxNTEuODk2IDg1LjExMTIgMTUxLjcyMUM4NS4yNDkyIDE1MS41NDQgODUuNDE4NSAxNTEuNDEgODUuNjE5IDE1MS4zMTlDODUuODE5NSAxNTEuMjI1IDg2LjA1MjYgMTUxLjE3OCA4Ni4zMTgyIDE1MS4xNzhDODYuNTgzOCAxNTEuMTc4IDg2LjgxOTUgMTUxLjIzMSA4Ny4wMjUyIDE1MS4zMzVDODcuMjMxIDE1MS40MzYgODcuNDAyOCAxNTEuNTgyIDg3LjU0MDggMTUxLjc3MkM4Ny42ODE1IDE1MS45NjIgODcuNzg4MiAxNTIuMTkgODcuODYxMiAxNTIuNDU2Qzg3LjkzNDEgMTUyLjcxOSA4Ny45NzA1IDE1My4wMTIgODcuOTcwNSAxNTMuMzM1Wk04Ny4yNDQgMTUzLjQxN1YxNTMuMzM1Qzg3LjI0NCAxNTMuMTI0IDg3LjIyNDQgMTUyLjkyNiA4Ny4xODU0IDE1Mi43NDFDODcuMTQ2MyAxNTIuNTUzIDg3LjA4MzggMTUyLjM4OSA4Ni45OTc5IDE1Mi4yNDlDODYuOTExOSAxNTIuMTA2IDg2Ljc5ODcgMTUxLjk5NCA4Ni42NTggMTUxLjkxM0M4Ni41MTc0IDE1MS44MyA4Ni4zNDQyIDE1MS43ODggODYuMTM4NSAxNTEuNzg4Qzg1Ljk1NjIgMTUxLjc4OCA4NS43OTc0IDE1MS44MTkgODUuNjYxOSAxNTEuODgyQzg1LjUyOTEgMTUxLjk0NCA4NS40MTU4IDE1Mi4wMjkgODUuMzIyMSAxNTIuMTM1Qzg1LjIyODMgMTUyLjI0IDg1LjE1MTUgMTUyLjM1OSA4NS4wOTE2IDE1Mi40OTVDODUuMDM0MyAxNTIuNjI4IDg0Ljk5MTQgMTUyLjc2NiA4NC45NjI3IDE1Mi45MDlWMTUzLjg1Qzg1LjAwNDQgMTU0LjAzMyA4NS4wNzIxIDE1NC4yMDggODUuMTY1OCAxNTQuMzc4Qzg1LjI2MjIgMTU0LjU0NCA4NS4zODk4IDE1NC42ODEgODUuNTQ4NyAxNTQuNzg4Qzg1LjcxMDEgMTU0Ljg5NSA4NS45MDkzIDE1NC45NDggODYuMTQ2MyAxNTQuOTQ4Qzg2LjM0MTYgMTU0Ljk0OCA4Ni41MDgzIDE1NC45MDkgODYuNjQ2MyAxNTQuODMxQzg2Ljc4NjkgMTU0Ljc1IDg2LjkwMDIgMTU0LjYzOSA4Ni45ODYyIDE1NC40OTlDODcuMDc0NyAxNTQuMzU4IDg3LjEzOTggMTU0LjE5NSA4Ny4xODE1IDE1NC4wMUM4Ny4yMjMxIDE1My44MjYgODcuMjQ0IDE1My42MjggODcuMjQ0IDE1My40MTdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjwvZz4KPGxpbmUgeDE9IjkzLjU1IiB5MT0iMTQ3LjIzMyIgeDI9IjkzLjU1IiB5Mj0iMTQ2LjQxMSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNSIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAzXzQxODJfMTExOTMpIj4KPGxpbmUgeDE9IjExMS4yNSIgeTE9IjE0Ny4yMzMiIHgyPSIxMTEuMjUiIHkyPSIxNDYuNDExIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNMTAzLjMwOSAxNTIuMTg2VjE1My4wNTNDMTAzLjMwOSAxNTMuNTIgMTAzLjI2NyAxNTMuOTEzIDEwMy4xODQgMTU0LjIzM0MxMDMuMTAxIDE1NC41NTMgMTAyLjk4MSAxNTQuODExIDEwMi44MjUgMTU1LjAwN0MxMDIuNjY4IDE1NS4yMDIgMTAyLjQ4IDE1NS4zNDQgMTAyLjI1OCAxNTUuNDMyQzEwMi4wNCAxNTUuNTE4IDEwMS43OTIgMTU1LjU2MSAxMDEuNTE2IDE1NS41NjFDMTAxLjI5NyAxNTUuNTYxIDEwMS4wOTYgMTU1LjUzNCAxMDAuOTExIDE1NS40NzlDMTAwLjcyNiAxNTUuNDI1IDEwMC41NTkgMTU1LjMzNyAxMDAuNDExIDE1NS4yMThDMTAwLjI2NSAxNTUuMDk1IDEwMC4xNCAxNTQuOTM2IDEwMC4wMzYgMTU0Ljc0MUM5OS45MzE1IDE1NC41NDYgOTkuODUyMSAxNTQuMzA5IDk5Ljc5NzQgMTU0LjAzQzk5Ljc0MjcgMTUzLjc1MSA5OS43MTU0IDE1My40MjYgOTkuNzE1NCAxNTMuMDUzVjE1Mi4xODZDOTkuNzE1NCAxNTEuNzIgOTkuNzU3IDE1MS4zMyA5OS44NDA0IDE1MS4wMTRDOTkuOTI2MyAxNTAuNjk5IDEwMC4wNDcgMTUwLjQ0NyAxMDAuMjA0IDE1MC4yNTdDMTAwLjM2IDE1MC4wNjQgMTAwLjU0NyAxNDkuOTI2IDEwMC43NjYgMTQ5Ljg0M0MxMDAuOTg3IDE0OS43NTkgMTAxLjIzNSAxNDkuNzE4IDEwMS41MDggMTQ5LjcxOEMxMDEuNzMgMTQ5LjcxOCAxMDEuOTMzIDE0OS43NDUgMTAyLjExOCAxNDkuOEMxMDIuMzA1IDE0OS44NTIgMTAyLjQ3MiAxNDkuOTM2IDEwMi42MTggMTUwLjA1M0MxMDIuNzY0IDE1MC4xNjggMTAyLjg4NyAxNTAuMzIyIDEwMi45ODkgMTUwLjUxNEMxMDMuMDkzIDE1MC43MDUgMTAzLjE3MiAxNTAuOTM4IDEwMy4yMjcgMTUxLjIxNEMxMDMuMjgyIDE1MS40OSAxMDMuMzA5IDE1MS44MTQgMTAzLjMwOSAxNTIuMTg2Wk0xMDIuNTgzIDE1My4xNzFWMTUyLjA2NUMxMDIuNTgzIDE1MS44MSAxMDIuNTY3IDE1MS41ODYgMTAyLjUzNiAxNTEuMzkzQzEwMi41MDcgMTUxLjE5OCAxMDIuNDY0IDE1MS4wMzEgMTAyLjQwNyAxNTAuODkzQzEwMi4zNDkgMTUwLjc1NSAxMDIuMjc3IDE1MC42NDMgMTAyLjE4OCAxNTAuNTU3QzEwMi4xMDIgMTUwLjQ3MSAxMDIuMDAyIDE1MC40MDkgMTAxLjg4NyAxNTAuMzdDMTAxLjc3NSAxNTAuMzI4IDEwMS42NDkgMTUwLjMwNyAxMDEuNTA4IDE1MC4zMDdDMTAxLjMzNiAxNTAuMzA3IDEwMS4xODQgMTUwLjM0IDEwMS4wNTEgMTUwLjQwNUMxMDAuOTE4IDE1MC40NjggMTAwLjgwNyAxNTAuNTY4IDEwMC43MTUgMTUwLjcwNkMxMDAuNjI3IDE1MC44NDQgMTAwLjU1OSAxNTEuMDI1IDEwMC41MTIgMTUxLjI0OUMxMDAuNDY1IDE1MS40NzMgMTAwLjQ0MiAxNTEuNzQ1IDEwMC40NDIgMTUyLjA2NVYxNTMuMTcxQzEwMC40NDIgMTUzLjQyNiAxMDAuNDU2IDE1My42NTEgMTAwLjQ4NSAxNTMuODQ2QzEwMC41MTYgMTU0LjA0MiAxMDAuNTYyIDE1NC4yMTEgMTAwLjYyMiAxNTQuMzU0QzEwMC42ODIgMTU0LjQ5NSAxMDAuNzU0IDE1NC42MTEgMTAwLjg0IDE1NC43MDJDMTAwLjkyNiAxNTQuNzkzIDEwMS4wMjUgMTU0Ljg2MSAxMDEuMTM3IDE1NC45MDVDMTAxLjI1MiAxNTQuOTQ3IDEwMS4zNzggMTU0Ljk2OCAxMDEuNTE2IDE1NC45NjhDMTAxLjY5MyAxNTQuOTY4IDEwMS44NDggMTU0LjkzNCAxMDEuOTgxIDE1NC44NjZDMTAyLjExNCAxNTQuNzk4IDEwMi4yMjQgMTU0LjY5MyAxMDIuMzEzIDE1NC41NUMxMDIuNDA0IDE1NC40MDQgMTAyLjQ3MiAxNTQuMjE4IDEwMi41MTYgMTUzLjk5MUMxMDIuNTYgMTUzLjc2MiAxMDIuNTgzIDE1My40ODggMTAyLjU4MyAxNTMuMTcxWk0xMDUuMjM1IDE1Mi43NzZMMTA0LjY1NyAxNTIuNjI4TDEwNC45NDMgMTQ5Ljc5NkgxMDcuODZWMTUwLjQ2NEgxMDUuNTU2TDEwNS4zODQgMTUyLjAxQzEwNS40ODggMTUxLjk1MSAxMDUuNjIgMTUxLjg5NSAxMDUuNzc4IDE1MS44NDNDMTA1Ljk0IDE1MS43OSAxMDYuMTI1IDE1MS43NjQgMTA2LjMzMyAxNTEuNzY0QzEwNi41OTYgMTUxLjc2NCAxMDYuODMyIDE1MS44MSAxMDcuMDQgMTUxLjkwMUMxMDcuMjQ5IDE1MS45OSAxMDcuNDI2IDE1Mi4xMTcgMTA3LjU3MSAxNTIuMjg0QzEwNy43MiAxNTIuNDUxIDEwNy44MzMgMTUyLjY1MSAxMDcuOTExIDE1Mi44ODVDMTA3Ljk4OSAxNTMuMTIgMTA4LjAyOCAxNTMuMzgyIDEwOC4wMjggMTUzLjY3MUMxMDguMDI4IDE1My45NDQgMTA3Ljk5MSAxNTQuMTk1IDEwNy45MTUgMTU0LjQyNUMxMDcuODQyIDE1NC42NTQgMTA3LjczMiAxNTQuODU0IDEwNy41ODMgMTU1LjAyNkMxMDcuNDM1IDE1NS4xOTUgMTA3LjI0NyAxNTUuMzI3IDEwNy4wMjEgMTU1LjQyMUMxMDYuNzk3IDE1NS41MTQgMTA2LjUzMiAxNTUuNTYxIDEwNi4yMjggMTU1LjU2MUMxMDUuOTk5IDE1NS41NjEgMTA1Ljc4MSAxNTUuNTMgMTA1LjU3NSAxNTUuNDY4QzEwNS4zNzIgMTU1LjQwMiAxMDUuMTkgMTU1LjMwNSAxMDUuMDI4IDE1NS4xNzVDMTA0Ljg3IDE1NS4wNDIgMTA0LjczOSAxNTQuODc4IDEwNC42MzggMTU0LjY4MkMxMDQuNTM5IDE1NC40ODQgMTA0LjQ3NiAxNTQuMjUzIDEwNC40NSAxNTMuOTg3SDEwNS4xMzhDMTA1LjE2OSAxNTQuMjAxIDEwNS4yMzIgMTU0LjM4IDEwNS4zMjUgMTU0LjUyNkMxMDUuNDE5IDE1NC42NzIgMTA1LjU0MSAxNTQuNzgzIDEwNS42OTMgMTU0Ljg1OEMxMDUuODQ2IDE1NC45MzEgMTA2LjAyNSAxNTQuOTY4IDEwNi4yMjggMTU0Ljk2OEMxMDYuNCAxNTQuOTY4IDEwNi41NTIgMTU0LjkzOCAxMDYuNjg1IDE1NC44NzhDMTA2LjgxOCAxNTQuODE4IDEwNi45MjkgMTU0LjczMiAxMDcuMDIxIDE1NC42MkMxMDcuMTEyIDE1NC41MDggMTA3LjE4MSAxNTQuMzcyIDEwNy4yMjggMTU0LjIxNEMxMDcuMjc3IDE1NC4wNTUgMTA3LjMwMiAxNTMuODc2IDEwNy4zMDIgMTUzLjY3OEMxMDcuMzAyIDE1My40OTkgMTA3LjI3NyAxNTMuMzMyIDEwNy4yMjggMTUzLjE3OEMxMDcuMTc4IDE1My4wMjUgMTA3LjEwNCAxNTIuODkxIDEwNy4wMDUgMTUyLjc3NkMxMDYuOTA5IDE1Mi42NjIgMTA2Ljc5IDE1Mi41NzMgMTA2LjY1IDE1Mi41MUMxMDYuNTA5IDE1Mi40NDUgMTA2LjM0NyAxNTIuNDEzIDEwNi4xNjUgMTUyLjQxM0MxMDUuOTIzIDE1Mi40MTMgMTA1LjczOSAxNTIuNDQ1IDEwNS42MTQgMTUyLjUxQzEwNS40OTIgMTUyLjU3NiAxMDUuMzY2IDE1Mi42NjQgMTA1LjIzNSAxNTIuNzc2Wk0xMTEuOTEyIDE0OS43OTZWMTU1LjQ4M0gxMTEuMTU5VjE0OS43OTZIMTExLjkxMlpNMTE0LjI5NSAxNTIuMzU0VjE1Mi45NzFIMTExLjc0OFYxNTIuMzU0SDExNC4yOTVaTTExNC42ODIgMTQ5Ljc5NlYxNTAuNDEzSDExMS43NDhWMTQ5Ljc5NkgxMTQuNjgyWk0xMTcuMjIyIDE1NS41NjFDMTE2LjkyNyAxNTUuNTYxIDExNi42NiAxNTUuNTEyIDExNi40MjEgMTU1LjQxM0MxMTYuMTg0IDE1NS4zMTEgMTE1Ljk3OSAxNTUuMTY5IDExNS44MDggMTU0Ljk4N0MxMTUuNjM4IDE1NC44MDUgMTE1LjUwOCAxNTQuNTg5IDExNS40MTcgMTU0LjMzOUMxMTUuMzI2IDE1NC4wODkgMTE1LjI4IDE1My44MTUgMTE1LjI4IDE1My41MThWMTUzLjM1NEMxMTUuMjggMTUzLjAxIDExNS4zMzEgMTUyLjcwNSAxMTUuNDMzIDE1Mi40MzZDMTE1LjUzNCAxNTIuMTY1IDExNS42NzIgMTUxLjkzNiAxMTUuODQ3IDE1MS43NDlDMTE2LjAyMSAxNTEuNTYxIDExNi4yMTkgMTUxLjQxOSAxMTYuNDQgMTUxLjMyM0MxMTYuNjYyIDE1MS4yMjcgMTE2Ljg5MSAxNTEuMTc4IDExNy4xMjggMTUxLjE3OEMxMTcuNDMgMTUxLjE3OCAxMTcuNjkgMTUxLjIzMSAxMTcuOTA5IDE1MS4zMzVDMTE4LjEzIDE1MS40MzkgMTE4LjMxMSAxNTEuNTg1IDExOC40NTIgMTUxLjc3MkMxMTguNTkzIDE1MS45NTcgMTE4LjY5NyAxNTIuMTc2IDExOC43NjUgMTUyLjQyOEMxMTguODMyIDE1Mi42NzggMTE4Ljg2NiAxNTIuOTUyIDExOC44NjYgMTUzLjI0OVYxNTMuNTczSDExNS43MVYxNTIuOTgzSDExOC4xNDRWMTUyLjkyOEMxMTguMTMzIDE1Mi43NDEgMTE4LjA5NCAxNTIuNTU5IDExOC4wMjYgMTUyLjM4MkMxMTcuOTYxIDE1Mi4yMDUgMTE3Ljg1NyAxNTIuMDU5IDExNy43MTQgMTUxLjk0NEMxMTcuNTcxIDE1MS44MyAxMTcuMzc1IDE1MS43NzIgMTE3LjEyOCAxNTEuNzcyQzExNi45NjQgMTUxLjc3MiAxMTYuODEzIDE1MS44MDcgMTE2LjY3NSAxNTEuODc4QzExNi41MzcgMTUxLjk0NSAxMTYuNDE4IDE1Mi4wNDcgMTE2LjMxOSAxNTIuMTgyQzExNi4yMiAxNTIuMzE4IDExNi4xNDQgMTUyLjQ4MyAxMTYuMDg5IDE1Mi42NzhDMTE2LjAzNCAxNTIuODc0IDExNi4wMDcgMTUzLjA5OSAxMTYuMDA3IDE1My4zNTRWMTUzLjUxOEMxMTYuMDA3IDE1My43MTkgMTE2LjAzNCAxNTMuOTA4IDExNi4wODkgMTU0LjA4NUMxMTYuMTQ2IDE1NC4yNTkgMTE2LjIyOCAxNTQuNDEzIDExNi4zMzUgMTU0LjU0NkMxMTYuNDQ0IDE1NC42NzggMTE2LjU3NiAxNTQuNzgzIDExNi43MjkgMTU0Ljg1OEMxMTYuODg2IDE1NC45MzQgMTE3LjA2MyAxNTQuOTcxIDExNy4yNjEgMTU0Ljk3MUMxMTcuNTE2IDE1NC45NzEgMTE3LjczMiAxNTQuOTE5IDExNy45MDkgMTU0LjgxNUMxMTguMDg2IDE1NC43MTEgMTE4LjI0MSAxNTQuNTcyIDExOC4zNzQgMTU0LjM5N0wxMTguODExIDE1NC43NDVDMTE4LjcyIDE1NC44ODMgMTE4LjYwNCAxNTUuMDE0IDExOC40NjQgMTU1LjEzOUMxMTguMzIzIDE1NS4yNjQgMTE4LjE1IDE1NS4zNjYgMTE3Ljk0NCAxNTUuNDQ0QzExNy43NDEgMTU1LjUyMiAxMTcuNSAxNTUuNTYxIDExNy4yMjIgMTU1LjU2MVpNMTE5Ljc4OSAxNDkuNDgzSDEyMC41MTVWMTU0LjY2M0wxMjAuNDUzIDE1NS40ODNIMTE5Ljc4OVYxNDkuNDgzWk0xMjMuMzcxIDE1My4zMzVWMTUzLjQxN0MxMjMuMzcxIDE1My43MjQgMTIzLjMzNCAxNTQuMDA5IDEyMy4yNjEgMTU0LjI3MkMxMjMuMTg4IDE1NC41MzMgMTIzLjA4MiAxNTQuNzU5IDEyMi45NDEgMTU0Ljk1MkMxMjIuOCAxNTUuMTQ1IDEyMi42MjggMTU1LjI5NCAxMjIuNDI1IDE1NS40MDFDMTIyLjIyMiAxNTUuNTA4IDEyMS45ODkgMTU1LjU2MSAxMjEuNzI2IDE1NS41NjFDMTIxLjQ1OCAxNTUuNTYxIDEyMS4yMjIgMTU1LjUxNiAxMjEuMDE5IDE1NS40MjVDMTIwLjgxOSAxNTUuMzMxIDEyMC42NDkgMTU1LjE5NyAxMjAuNTExIDE1NS4wMjJDMTIwLjM3MyAxNTQuODQ4IDEyMC4yNjMgMTU0LjYzNyAxMjAuMTc5IDE1NC4zODlDMTIwLjA5OSAxNTQuMTQyIDEyMC4wNDMgMTUzLjg2MyAxMjAuMDExIDE1My41NTNWMTUzLjE5NEMxMjAuMDQzIDE1Mi44ODIgMTIwLjA5OSAxNTIuNjAyIDEyMC4xNzkgMTUyLjM1NEMxMjAuMjYzIDE1Mi4xMDcgMTIwLjM3MyAxNTEuODk2IDEyMC41MTEgMTUxLjcyMUMxMjAuNjQ5IDE1MS41NDQgMTIwLjgxOSAxNTEuNDEgMTIxLjAxOSAxNTEuMzE5QzEyMS4yMiAxNTEuMjI1IDEyMS40NTMgMTUxLjE3OCAxMjEuNzE4IDE1MS4xNzhDMTIxLjk4NCAxNTEuMTc4IDEyMi4yMiAxNTEuMjMxIDEyMi40MjUgMTUxLjMzNUMxMjIuNjMxIDE1MS40MzYgMTIyLjgwMyAxNTEuNTgyIDEyMi45NDEgMTUxLjc3MkMxMjMuMDgyIDE1MS45NjIgMTIzLjE4OCAxNTIuMTkgMTIzLjI2MSAxNTIuNDU2QzEyMy4zMzQgMTUyLjcxOSAxMjMuMzcxIDE1My4wMTIgMTIzLjM3MSAxNTMuMzM1Wk0xMjIuNjQ0IDE1My40MTdWMTUzLjMzNUMxMjIuNjQ0IDE1My4xMjQgMTIyLjYyNSAxNTIuOTI2IDEyMi41ODYgMTUyLjc0MUMxMjIuNTQ2IDE1Mi41NTMgMTIyLjQ4NCAxNTIuMzg5IDEyMi4zOTggMTUyLjI0OUMxMjIuMzEyIDE1Mi4xMDYgMTIyLjE5OSAxNTEuOTk0IDEyMi4wNTggMTUxLjkxM0MxMjEuOTE4IDE1MS44MyAxMjEuNzQ0IDE1MS43ODggMTIxLjUzOSAxNTEuNzg4QzEyMS4zNTYgMTUxLjc4OCAxMjEuMTk4IDE1MS44MTkgMTIxLjA2MiAxNTEuODgyQzEyMC45MjkgMTUxLjk0NCAxMjAuODE2IDE1Mi4wMjkgMTIwLjcyMiAxNTIuMTM1QzEyMC42MjggMTUyLjI0IDEyMC41NTIgMTUyLjM1OSAxMjAuNDkyIDE1Mi40OTVDMTIwLjQzNCAxNTIuNjI4IDEyMC4zOTIgMTUyLjc2NiAxMjAuMzYzIDE1Mi45MDlWMTUzLjg1QzEyMC40MDUgMTU0LjAzMyAxMjAuNDcyIDE1NC4yMDggMTIwLjU2NiAxNTQuMzc4QzEyMC42NjIgMTU0LjU0NCAxMjAuNzkgMTU0LjY4MSAxMjAuOTQ5IDE1NC43ODhDMTIxLjExIDE1NC44OTUgMTIxLjMwOSAxNTQuOTQ4IDEyMS41NDYgMTU0Ljk0OEMxMjEuNzQyIDE1NC45NDggMTIxLjkwOCAxNTQuOTA5IDEyMi4wNDYgMTU0LjgzMUMxMjIuMTg3IDE1NC43NSAxMjIuMyAxNTQuNjM5IDEyMi4zODYgMTU0LjQ5OUMxMjIuNDc1IDE1NC4zNTggMTIyLjU0IDE1NC4xOTUgMTIyLjU4MiAxNTQuMDFDMTIyLjYyMyAxNTMuODI2IDEyMi42NDQgMTUzLjYyOCAxMjIuNjQ0IDE1My40MTdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjwvZz4KPGxpbmUgeDE9IjEyOC45NSIgeTE9IjE0Ny4yMzMiIHgyPSIxMjguOTUiIHkyPSIxNDYuNDExIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDRfNDE4Ml8xMTE5MykiPgo8bGluZSB4MT0iMTQ2LjY1IiB5MT0iMTQ3LjIzMyIgeDI9IjE0Ni42NSIgeTI9IjE0Ni40MTEiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjUiIHN0cm9rZS13aWR0aD0iMC41IiBzdHJva2UtbGluZWNhcD0ic3F1YXJlIi8+CjxwYXRoIGQ9Ik0xMzguNzA5IDE1Mi4xODZWMTUzLjA1M0MxMzguNzA5IDE1My41MiAxMzguNjY3IDE1My45MTMgMTM4LjU4NCAxNTQuMjMzQzEzOC41MDEgMTU0LjU1MyAxMzguMzgxIDE1NC44MTEgMTM4LjIyNSAxNTUuMDA3QzEzOC4wNjggMTU1LjIwMiAxMzcuODggMTU1LjM0NCAxMzcuNjU4IDE1NS40MzJDMTM3LjQzOSAxNTUuNTE4IDEzNy4xOTIgMTU1LjU2MSAxMzYuOTE2IDE1NS41NjFDMTM2LjY5NyAxNTUuNTYxIDEzNi40OTUgMTU1LjUzNCAxMzYuMzExIDE1NS40NzlDMTM2LjEyNiAxNTUuNDI1IDEzNS45NTkgMTU1LjMzNyAxMzUuODExIDE1NS4yMThDMTM1LjY2NSAxNTUuMDk1IDEzNS41NCAxNTQuOTM2IDEzNS40MzYgMTU0Ljc0MUMxMzUuMzMxIDE1NC41NDYgMTM1LjI1MiAxNTQuMzA5IDEzNS4xOTcgMTU0LjAzQzEzNS4xNDMgMTUzLjc1MSAxMzUuMTE1IDE1My40MjYgMTM1LjExNSAxNTMuMDUzVjE1Mi4xODZDMTM1LjExNSAxNTEuNzIgMTM1LjE1NyAxNTEuMzMgMTM1LjI0IDE1MS4wMTRDMTM1LjMyNiAxNTAuNjk5IDEzNS40NDcgMTUwLjQ0NyAxMzUuNjA0IDE1MC4yNTdDMTM1Ljc2IDE1MC4wNjQgMTM1Ljk0NyAxNDkuOTI2IDEzNi4xNjYgMTQ5Ljg0M0MxMzYuMzg3IDE0OS43NTkgMTM2LjYzNSAxNDkuNzE4IDEzNi45MDggMTQ5LjcxOEMxMzcuMTMgMTQ5LjcxOCAxMzcuMzMzIDE0OS43NDUgMTM3LjUxOCAxNDkuOEMxMzcuNzA1IDE0OS44NTIgMTM3Ljg3MiAxNDkuOTM2IDEzOC4wMTggMTUwLjA1M0MxMzguMTYzIDE1MC4xNjggMTM4LjI4NyAxNTAuMzIyIDEzOC4zODkgMTUwLjUxNEMxMzguNDkzIDE1MC43MDUgMTM4LjU3MiAxNTAuOTM4IDEzOC42MjcgMTUxLjIxNEMxMzguNjgyIDE1MS40OSAxMzguNzA5IDE1MS44MTQgMTM4LjcwOSAxNTIuMTg2Wk0xMzcuOTgyIDE1My4xNzFWMTUyLjA2NUMxMzcuOTgyIDE1MS44MSAxMzcuOTY3IDE1MS41ODYgMTM3LjkzNiAxNTEuMzkzQzEzNy45MDcgMTUxLjE5OCAxMzcuODY0IDE1MS4wMzEgMTM3LjgwNyAxNTAuODkzQzEzNy43NDkgMTUwLjc1NSAxMzcuNjc2IDE1MC42NDMgMTM3LjU4OCAxNTAuNTU3QzEzNy41MDIgMTUwLjQ3MSAxMzcuNDAyIDE1MC40MDkgMTM3LjI4NyAxNTAuMzdDMTM3LjE3NSAxNTAuMzI4IDEzNy4wNDkgMTUwLjMwNyAxMzYuOTA4IDE1MC4zMDdDMTM2LjczNiAxNTAuMzA3IDEzNi41ODQgMTUwLjM0IDEzNi40NTEgMTUwLjQwNUMxMzYuMzE4IDE1MC40NjggMTM2LjIwNiAxNTAuNTY4IDEzNi4xMTUgMTUwLjcwNkMxMzYuMDI3IDE1MC44NDQgMTM1Ljk1OSAxNTEuMDI1IDEzNS45MTIgMTUxLjI0OUMxMzUuODY1IDE1MS40NzMgMTM1Ljg0MiAxNTEuNzQ1IDEzNS44NDIgMTUyLjA2NVYxNTMuMTcxQzEzNS44NDIgMTUzLjQyNiAxMzUuODU2IDE1My42NTEgMTM1Ljg4NSAxNTMuODQ2QzEzNS45MTYgMTU0LjA0MiAxMzUuOTYyIDE1NC4yMTEgMTM2LjAyMiAxNTQuMzU0QzEzNi4wODEgMTU0LjQ5NSAxMzYuMTU0IDE1NC42MTEgMTM2LjI0IDE1NC43MDJDMTM2LjMyNiAxNTQuNzkzIDEzNi40MjUgMTU0Ljg2MSAxMzYuNTM3IDE1NC45MDVDMTM2LjY1MiAxNTQuOTQ3IDEzNi43NzggMTU0Ljk2OCAxMzYuOTE2IDE1NC45NjhDMTM3LjA5MyAxNTQuOTY4IDEzNy4yNDggMTU0LjkzNCAxMzcuMzgxIDE1NC44NjZDMTM3LjUxNCAxNTQuNzk4IDEzNy42MjQgMTU0LjY5MyAxMzcuNzEzIDE1NC41NUMxMzcuODA0IDE1NC40MDQgMTM3Ljg3MiAxNTQuMjE4IDEzNy45MTYgMTUzLjk5MUMxMzcuOTYgMTUzLjc2MiAxMzcuOTgyIDE1My40ODggMTM3Ljk4MiAxNTMuMTcxWk0xNDMuMzk3IDE0OS43OTZWMTUwLjIwMkwxNDEuMDQyIDE1NS40ODNIMTQwLjI4TDE0Mi42MzEgMTUwLjM4OUgxMzkuNTUzVjE0OS43OTZIMTQzLjM5N1pNMTQ3LjMxMiAxNDkuNzk2VjE1NS40ODNIMTQ2LjU1OFYxNDkuNzk2SDE0Ny4zMTJaTTE0OS42OTUgMTUyLjM1NFYxNTIuOTcxSDE0Ny4xNDhWMTUyLjM1NEgxNDkuNjk1Wk0xNTAuMDgyIDE0OS43OTZWMTUwLjQxM0gxNDcuMTQ4VjE0OS43OTZIMTUwLjA4MlpNMTUyLjYyMiAxNTUuNTYxQzE1Mi4zMjcgMTU1LjU2MSAxNTIuMDYgMTU1LjUxMiAxNTEuODIxIDE1NS40MTNDMTUxLjU4NCAxNTUuMzExIDE1MS4zNzkgMTU1LjE2OSAxNTEuMjA3IDE1NC45ODdDMTUxLjAzOCAxNTQuODA1IDE1MC45MDggMTU0LjU4OSAxNTAuODE3IDE1NC4zMzlDMTUwLjcyNiAxNTQuMDg5IDE1MC42OCAxNTMuODE1IDE1MC42OCAxNTMuNTE4VjE1My4zNTRDMTUwLjY4IDE1My4wMSAxNTAuNzMxIDE1Mi43MDUgMTUwLjgzMiAxNTIuNDM2QzE1MC45MzQgMTUyLjE2NSAxNTEuMDcyIDE1MS45MzYgMTUxLjI0NyAxNTEuNzQ5QzE1MS40MjEgMTUxLjU2MSAxNTEuNjE5IDE1MS40MTkgMTUxLjg0IDE1MS4zMjNDMTUyLjA2MiAxNTEuMjI3IDE1Mi4yOTEgMTUxLjE3OCAxNTIuNTI4IDE1MS4xNzhDMTUyLjgzIDE1MS4xNzggMTUzLjA5IDE1MS4yMzEgMTUzLjMwOSAxNTEuMzM1QzE1My41MyAxNTEuNDM5IDE1My43MTEgMTUxLjU4NSAxNTMuODUyIDE1MS43NzJDMTUzLjk5MyAxNTEuOTU3IDE1NC4wOTcgMTUyLjE3NiAxNTQuMTY1IDE1Mi40MjhDMTU0LjIzMiAxNTIuNjc4IDE1NC4yNjYgMTUyLjk1MiAxNTQuMjY2IDE1My4yNDlWMTUzLjU3M0gxNTEuMTFWMTUyLjk4M0gxNTMuNTQzVjE1Mi45MjhDMTUzLjUzMyAxNTIuNzQxIDE1My40OTQgMTUyLjU1OSAxNTMuNDI2IDE1Mi4zODJDMTUzLjM2MSAxNTIuMjA1IDE1My4yNTcgMTUyLjA1OSAxNTMuMTE0IDE1MS45NDRDMTUyLjk3MSAxNTEuODMgMTUyLjc3NSAxNTEuNzcyIDE1Mi41MjggMTUxLjc3MkMxNTIuMzY0IDE1MS43NzIgMTUyLjIxMyAxNTEuODA3IDE1Mi4wNzUgMTUxLjg3OEMxNTEuOTM3IDE1MS45NDUgMTUxLjgxOCAxNTIuMDQ3IDE1MS43MTkgMTUyLjE4MkMxNTEuNjIgMTUyLjMxOCAxNTEuNTQzIDE1Mi40ODMgMTUxLjQ4OSAxNTIuNjc4QzE1MS40MzQgMTUyLjg3NCAxNTEuNDA3IDE1My4wOTkgMTUxLjQwNyAxNTMuMzU0VjE1My41MThDMTUxLjQwNyAxNTMuNzE5IDE1MS40MzQgMTUzLjkwOCAxNTEuNDg5IDE1NC4wODVDMTUxLjU0NiAxNTQuMjU5IDE1MS42MjggMTU0LjQxMyAxNTEuNzM1IDE1NC41NDZDMTUxLjg0NCAxNTQuNjc4IDE1MS45NzYgMTU0Ljc4MyAxNTIuMTI5IDE1NC44NThDMTUyLjI4NiAxNTQuOTM0IDE1Mi40NjMgMTU0Ljk3MSAxNTIuNjYxIDE1NC45NzFDMTUyLjkxNiAxNTQuOTcxIDE1My4xMzIgMTU0LjkxOSAxNTMuMzA5IDE1NC44MTVDMTUzLjQ4NiAxNTQuNzExIDE1My42NDEgMTU0LjU3MiAxNTMuNzc0IDE1NC4zOTdMMTU0LjIxMSAxNTQuNzQ1QzE1NC4xMiAxNTQuODgzIDE1NC4wMDQgMTU1LjAxNCAxNTMuODY0IDE1NS4xMzlDMTUzLjcyMyAxNTUuMjY0IDE1My41NSAxNTUuMzY2IDE1My4zNDQgMTU1LjQ0NEMxNTMuMTQxIDE1NS41MjIgMTUyLjkgMTU1LjU2MSAxNTIuNjIyIDE1NS41NjFaTTE1NS4xODkgMTQ5LjQ4M0gxNTUuOTE1VjE1NC42NjNMMTU1Ljg1MyAxNTUuNDgzSDE1NS4xODlWMTQ5LjQ4M1pNMTU4Ljc3MSAxNTMuMzM1VjE1My40MTdDMTU4Ljc3MSAxNTMuNzI0IDE1OC43MzQgMTU0LjAwOSAxNTguNjYxIDE1NC4yNzJDMTU4LjU4OCAxNTQuNTMzIDE1OC40ODIgMTU0Ljc1OSAxNTguMzQxIDE1NC45NTJDMTU4LjIgMTU1LjE0NSAxNTguMDI4IDE1NS4yOTQgMTU3LjgyNSAxNTUuNDAxQzE1Ny42MjIgMTU1LjUwOCAxNTcuMzg5IDE1NS41NjEgMTU3LjEyNiAxNTUuNTYxQzE1Ni44NTggMTU1LjU2MSAxNTYuNjIyIDE1NS41MTYgMTU2LjQxOSAxNTUuNDI1QzE1Ni4yMTggMTU1LjMzMSAxNTYuMDQ5IDE1NS4xOTcgMTU1LjkxMSAxNTUuMDIyQzE1NS43NzMgMTU0Ljg0OCAxNTUuNjYzIDE1NC42MzcgMTU1LjU3OSAxNTQuMzg5QzE1NS40OTggMTU0LjE0MiAxNTUuNDQyIDE1My44NjMgMTU1LjQxMSAxNTMuNTUzVjE1My4xOTRDMTU1LjQ0MiAxNTIuODgyIDE1NS40OTggMTUyLjYwMiAxNTUuNTc5IDE1Mi4zNTRDMTU1LjY2MyAxNTIuMTA3IDE1NS43NzMgMTUxLjg5NiAxNTUuOTExIDE1MS43MjFDMTU2LjA0OSAxNTEuNTQ0IDE1Ni4yMTggMTUxLjQxIDE1Ni40MTkgMTUxLjMxOUMxNTYuNjIgMTUxLjIyNSAxNTYuODUzIDE1MS4xNzggMTU3LjExOCAxNTEuMTc4QzE1Ny4zODQgMTUxLjE3OCAxNTcuNjIgMTUxLjIzMSAxNTcuODI1IDE1MS4zMzVDMTU4LjAzMSAxNTEuNDM2IDE1OC4yMDMgMTUxLjU4MiAxNTguMzQxIDE1MS43NzJDMTU4LjQ4MiAxNTEuOTYyIDE1OC41ODggMTUyLjE5IDE1OC42NjEgMTUyLjQ1NkMxNTguNzM0IDE1Mi43MTkgMTU4Ljc3MSAxNTMuMDEyIDE1OC43NzEgMTUzLjMzNVpNMTU4LjA0NCAxNTMuNDE3VjE1My4zMzVDMTU4LjA0NCAxNTMuMTI0IDE1OC4wMjQgMTUyLjkyNiAxNTcuOTg1IDE1Mi43NDFDMTU3Ljk0NiAxNTIuNTUzIDE1Ny44ODQgMTUyLjM4OSAxNTcuNzk4IDE1Mi4yNDlDMTU3LjcxMiAxNTIuMTA2IDE1Ny41OTkgMTUxLjk5NCAxNTcuNDU4IDE1MS45MTNDMTU3LjMxNyAxNTEuODMgMTU3LjE0NCAxNTEuNzg4IDE1Ni45MzkgMTUxLjc4OEMxNTYuNzU2IDE1MS43ODggMTU2LjU5NyAxNTEuODE5IDE1Ni40NjIgMTUxLjg4MkMxNTYuMzI5IDE1MS45NDQgMTU2LjIxNiAxNTIuMDI5IDE1Ni4xMjIgMTUyLjEzNUMxNTYuMDI4IDE1Mi4yNCAxNTUuOTUyIDE1Mi4zNTkgMTU1Ljg5MiAxNTIuNDk1QzE1NS44MzQgMTUyLjYyOCAxNTUuNzkxIDE1Mi43NjYgMTU1Ljc2MyAxNTIuOTA5VjE1My44NUMxNTUuODA0IDE1NC4wMzMgMTU1Ljg3MiAxNTQuMjA4IDE1NS45NjYgMTU0LjM3OEMxNTYuMDYyIDE1NC41NDQgMTU2LjE5IDE1NC42ODEgMTU2LjM0OSAxNTQuNzg4QzE1Ni41MSAxNTQuODk1IDE1Ni43MDkgMTU0Ljk0OCAxNTYuOTQ2IDE1NC45NDhDMTU3LjE0MiAxNTQuOTQ4IDE1Ny4zMDggMTU0LjkwOSAxNTcuNDQ2IDE1NC44MzFDMTU3LjU4NyAxNTQuNzUgMTU3LjcgMTU0LjYzOSAxNTcuNzg2IDE1NC40OTlDMTU3Ljg3NSAxNTQuMzU4IDE1Ny45NCAxNTQuMTk1IDE1Ny45ODIgMTU0LjAxQzE1OC4wMjMgMTUzLjgyNiAxNTguMDQ0IDE1My42MjggMTU4LjA0NCAxNTMuNDE3WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8L2c+CjxsaW5lIHgxPSIxNjQuMzUiIHkxPSIxNDcuMjMzIiB4Mj0iMTY0LjM1IiB5Mj0iMTQ2LjQxMSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNSIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXA1XzQxODJfMTExOTMpIj4KPGxpbmUgeDE9IjE4Mi4wNSIgeTE9IjE0Ny4yMzMiIHgyPSIxODIuMDUiIHkyPSIxNDYuNDExIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNMTc0LjEwOSAxNTIuMTg2VjE1My4wNTNDMTc0LjEwOSAxNTMuNTIgMTc0LjA2NyAxNTMuOTEzIDE3My45ODQgMTU0LjIzM0MxNzMuOTAxIDE1NC41NTMgMTczLjc4MSAxNTQuODExIDE3My42MjUgMTU1LjAwN0MxNzMuNDY5IDE1NS4yMDIgMTczLjI4IDE1NS4zNDQgMTczLjA1OCAxNTUuNDMyQzE3Mi44NCAxNTUuNTE4IDE3Mi41OTIgMTU1LjU2MSAxNzIuMzE2IDE1NS41NjFDMTcyLjA5NyAxNTUuNTYxIDE3MS44OTYgMTU1LjUzNCAxNzEuNzExIDE1NS40NzlDMTcxLjUyNiAxNTUuNDI1IDE3MS4zNTkgMTU1LjMzNyAxNzEuMjExIDE1NS4yMThDMTcxLjA2NSAxNTUuMDk1IDE3MC45NCAxNTQuOTM2IDE3MC44MzYgMTU0Ljc0MUMxNzAuNzMyIDE1NC41NDYgMTcwLjY1MiAxNTQuMzA5IDE3MC41OTcgMTU0LjAzQzE3MC41NDMgMTUzLjc1MSAxNzAuNTE1IDE1My40MjYgMTcwLjUxNSAxNTMuMDUzVjE1Mi4xODZDMTcwLjUxNSAxNTEuNzIgMTcwLjU1NyAxNTEuMzMgMTcwLjY0IDE1MS4wMTRDMTcwLjcyNiAxNTAuNjk5IDE3MC44NDcgMTUwLjQ0NyAxNzEuMDA0IDE1MC4yNTdDMTcxLjE2IDE1MC4wNjQgMTcxLjM0NyAxNDkuOTI2IDE3MS41NjYgMTQ5Ljg0M0MxNzEuNzg4IDE0OS43NTkgMTcyLjAzNSAxNDkuNzE4IDE3Mi4zMDggMTQ5LjcxOEMxNzIuNTMgMTQ5LjcxOCAxNzIuNzMzIDE0OS43NDUgMTcyLjkxOCAxNDkuOEMxNzMuMTA1IDE0OS44NTIgMTczLjI3MiAxNDkuOTM2IDE3My40MTggMTUwLjA1M0MxNzMuNTY0IDE1MC4xNjggMTczLjY4NyAxNTAuMzIyIDE3My43ODkgMTUwLjUxNEMxNzMuODkzIDE1MC43MDUgMTczLjk3MiAxNTAuOTM4IDE3NC4wMjcgMTUxLjIxNEMxNzQuMDgyIDE1MS40OSAxNzQuMTA5IDE1MS44MTQgMTc0LjEwOSAxNTIuMTg2Wk0xNzMuMzgzIDE1My4xNzFWMTUyLjA2NUMxNzMuMzgzIDE1MS44MSAxNzMuMzY3IDE1MS41ODYgMTczLjMzNiAxNTEuMzkzQzE3My4zMDcgMTUxLjE5OCAxNzMuMjY0IDE1MS4wMzEgMTczLjIwNyAxNTAuODkzQzE3My4xNSAxNTAuNzU1IDE3My4wNzcgMTUwLjY0MyAxNzIuOTg4IDE1MC41NTdDMTcyLjkwMiAxNTAuNDcxIDE3Mi44MDIgMTUwLjQwOSAxNzIuNjg3IDE1MC4zN0MxNzIuNTc1IDE1MC4zMjggMTcyLjQ0OSAxNTAuMzA3IDE3Mi4zMDggMTUwLjMwN0MxNzIuMTM2IDE1MC4zMDcgMTcxLjk4NCAxNTAuMzQgMTcxLjg1MSAxNTAuNDA1QzE3MS43MTkgMTUwLjQ2OCAxNzEuNjA3IDE1MC41NjggMTcxLjUxNSAxNTAuNzA2QzE3MS40MjcgMTUwLjg0NCAxNzEuMzU5IDE1MS4wMjUgMTcxLjMxMiAxNTEuMjQ5QzE3MS4yNjUgMTUxLjQ3MyAxNzEuMjQyIDE1MS43NDUgMTcxLjI0MiAxNTIuMDY1VjE1My4xNzFDMTcxLjI0MiAxNTMuNDI2IDE3MS4yNTYgMTUzLjY1MSAxNzEuMjg1IDE1My44NDZDMTcxLjMxNiAxNTQuMDQyIDE3MS4zNjIgMTU0LjIxMSAxNzEuNDIyIDE1NC4zNTRDMTcxLjQ4MiAxNTQuNDk1IDE3MS41NTQgMTU0LjYxMSAxNzEuNjQgMTU0LjcwMkMxNzEuNzI2IDE1NC43OTMgMTcxLjgyNSAxNTQuODYxIDE3MS45MzcgMTU0LjkwNUMxNzIuMDUyIDE1NC45NDcgMTcyLjE3OCAxNTQuOTY4IDE3Mi4zMTYgMTU0Ljk2OEMxNzIuNDkzIDE1NC45NjggMTcyLjY0OCAxNTQuOTM0IDE3Mi43ODEgMTU0Ljg2NkMxNzIuOTE0IDE1NC43OTggMTczLjAyNSAxNTQuNjkzIDE3My4xMTMgMTU0LjU1QzE3My4yMDQgMTU0LjQwNCAxNzMuMjcyIDE1NC4yMTggMTczLjMxNiAxNTMuOTkxQzE3My4zNiAxNTMuNzYyIDE3My4zODMgMTUzLjQ4OCAxNzMuMzgzIDE1My4xNzFaTTE3NS44NCAxNTQuODc4SDE3NS45MTRDMTc2LjMzMSAxNTQuODc4IDE3Ni42NyAxNTQuODE5IDE3Ni45MyAxNTQuNzAyQzE3Ny4xOSAxNTQuNTg1IDE3Ny4zOTEgMTU0LjQyNyAxNzcuNTMyIDE1NC4yMjlDMTc3LjY3MiAxNTQuMDMxIDE3Ny43NjkgMTUzLjgwOSAxNzcuODIxIDE1My41NjFDMTc3Ljg3MyAxNTMuMzExIDE3Ny44OTkgMTUzLjA1NSAxNzcuODk5IDE1Mi43OTJWMTUxLjkyMUMxNzcuODk5IDE1MS42NjMgMTc3Ljg2OSAxNTEuNDM0IDE3Ny44MDkgMTUxLjIzM0MxNzcuNzUyIDE1MS4wMzMgMTc3LjY3MSAxNTAuODY1IDE3Ny41NjcgMTUwLjcyOUMxNzcuNDY1IDE1MC41OTQgMTc3LjM0OSAxNTAuNDkxIDE3Ny4yMTkgMTUwLjQyMUMxNzcuMDg5IDE1MC4zNSAxNzYuOTUxIDE1MC4zMTUgMTc2LjgwNSAxNTAuMzE1QzE3Ni42MzggMTUwLjMxNSAxNzYuNDg5IDE1MC4zNDkgMTc2LjM1NiAxNTAuNDE3QzE3Ni4yMjYgMTUwLjQ4MiAxNzYuMTE1IDE1MC41NzQgMTc2LjAyNCAxNTAuNjk0QzE3NS45MzUgMTUwLjgxNCAxNzUuODY4IDE1MC45NTUgMTc1LjgyMSAxNTEuMTE2QzE3NS43NzQgMTUxLjI3NyAxNzUuNzUgMTUxLjQ1MyAxNzUuNzUgMTUxLjY0M0MxNzUuNzUgMTUxLjgxMyAxNzUuNzcxIDE1MS45NzcgMTc1LjgxMyAxNTIuMTM1QzE3NS44NTUgMTUyLjI5NCAxNzUuOTE4IDE1Mi40MzggMTc2LjAwNCAxNTIuNTY1QzE3Ni4wOSAxNTIuNjkzIDE3Ni4xOTcgMTUyLjc5NCAxNzYuMzI1IDE1Mi44N0MxNzYuNDU1IDE1Mi45NDMgMTc2LjYwNyAxNTIuOTc5IDE3Ni43ODIgMTUyLjk3OUMxNzYuOTQzIDE1Mi45NzkgMTc3LjA5NCAxNTIuOTQ4IDE3Ny4yMzUgMTUyLjg4NUMxNzcuMzc4IDE1Mi44MiAxNzcuNTA0IDE1Mi43MzMgMTc3LjYxNCAxNTIuNjI0QzE3Ny43MjYgMTUyLjUxMiAxNzcuODE0IDE1Mi4zODUgMTc3Ljg3OSAxNTIuMjQ1QzE3Ny45NDcgMTUyLjEwNCAxNzcuOTg2IDE1MS45NTcgMTc3Ljk5NiAxNTEuODAzSDE3OC4zNEMxNzguMzQgMTUyLjAyIDE3OC4yOTcgMTUyLjIzMyAxNzguMjExIDE1Mi40NDRDMTc4LjEyOCAxNTIuNjUyIDE3OC4wMTEgMTUyLjg0MyAxNzcuODYgMTUzLjAxNEMxNzcuNzA5IDE1My4xODYgMTc3LjUzMiAxNTMuMzI0IDE3Ny4zMjkgMTUzLjQyOEMxNzcuMTI1IDE1My41MyAxNzYuOTA0IDE1My41ODEgMTc2LjY2NCAxNTMuNTgxQzE3Ni4zODMgMTUzLjU4MSAxNzYuMTQgMTUzLjUyNiAxNzUuOTM0IDE1My40MTdDMTc1LjcyOCAxNTMuMzA3IDE3NS41NTkgMTUzLjE2MiAxNzUuNDI2IDE1Mi45NzlDMTc1LjI5NiAxNTIuNzk3IDE3NS4xOTggMTUyLjU5NCAxNzUuMTMzIDE1Mi4zN0MxNzUuMDcxIDE1Mi4xNDMgMTc1LjAzOSAxNTEuOTE0IDE3NS4wMzkgMTUxLjY4MkMxNzUuMDM5IDE1MS40MTIgMTc1LjA3NyAxNTEuMTU4IDE3NS4xNTMgMTUwLjkyMUMxNzUuMjI4IDE1MC42ODQgMTc1LjM0IDE1MC40NzUgMTc1LjQ4OSAxNTAuMjk2QzE3NS42MzcgMTUwLjExMyAxNzUuODIxIDE0OS45NzEgMTc2LjAzOSAxNDkuODdDMTc2LjI2MSAxNDkuNzY4IDE3Ni41MTYgMTQ5LjcxOCAxNzYuODA1IDE0OS43MThDMTc3LjEzMSAxNDkuNzE4IDE3Ny40MDggMTQ5Ljc4MyAxNzcuNjM3IDE0OS45MTNDMTc3Ljg2NiAxNTAuMDQzIDE3OC4wNTIgMTUwLjIxOCAxNzguMTk2IDE1MC40MzZDMTc4LjM0MiAxNTAuNjU1IDE3OC40NDggMTUwLjkwMSAxNzguNTE2IDE1MS4xNzVDMTc4LjU4NCAxNTEuNDQ4IDE3OC42MTggMTUxLjcyOSAxNzguNjE4IDE1Mi4wMThWMTUyLjI4QzE3OC42MTggMTUyLjU3NCAxNzguNTk4IDE1Mi44NzQgMTc4LjU1OSAxNTMuMTc4QzE3OC41MjMgMTUzLjQ4MSAxNzguNDUxIDE1My43NyAxNzguMzQ0IDE1NC4wNDZDMTc4LjI0IDE1NC4zMjIgMTc4LjA4OCAxNTQuNTY5IDE3Ny44ODcgMTU0Ljc4OEMxNzcuNjg3IDE1NS4wMDQgMTc3LjQyNSAxNTUuMTc2IDE3Ny4xMDIgMTU1LjMwM0MxNzYuNzgyIDE1NS40MjggMTc2LjM4NiAxNTUuNDkxIDE3NS45MTQgMTU1LjQ5MUgxNzUuODRWMTU0Ljg3OFpNMTgyLjcxMyAxNDkuNzk2VjE1NS40ODNIMTgxLjk1OVYxNDkuNzk2SDE4Mi43MTNaTTE4NS4wOTUgMTUyLjM1NFYxNTIuOTcxSDE4Mi41NDhWMTUyLjM1NEgxODUuMDk1Wk0xODUuNDgyIDE0OS43OTZWMTUwLjQxM0gxODIuNTQ4VjE0OS43OTZIMTg1LjQ4MlpNMTg4LjAyMiAxNTUuNTYxQzE4Ny43MjcgMTU1LjU2MSAxODcuNDYgMTU1LjUxMiAxODcuMjIxIDE1NS40MTNDMTg2Ljk4NCAxNTUuMzExIDE4Ni43OCAxNTUuMTY5IDE4Ni42MDggMTU0Ljk4N0MxODYuNDM4IDE1NC44MDUgMTg2LjMwOCAxNTQuNTg5IDE4Ni4yMTcgMTU0LjMzOUMxODYuMTI2IDE1NC4wODkgMTg2LjA4IDE1My44MTUgMTg2LjA4IDE1My41MThWMTUzLjM1NEMxODYuMDggMTUzLjAxIDE4Ni4xMzEgMTUyLjcwNSAxODYuMjMzIDE1Mi40MzZDMTg2LjMzNCAxNTIuMTY1IDE4Ni40NzIgMTUxLjkzNiAxODYuNjQ3IDE1MS43NDlDMTg2LjgyMSAxNTEuNTYxIDE4Ny4wMTkgMTUxLjQxOSAxODcuMjQgMTUxLjMyM0MxODcuNDYyIDE1MS4yMjcgMTg3LjY5MSAxNTEuMTc4IDE4Ny45MjggMTUxLjE3OEMxODguMjMgMTUxLjE3OCAxODguNDkgMTUxLjIzMSAxODguNzA5IDE1MS4zMzVDMTg4LjkzMSAxNTEuNDM5IDE4OS4xMTIgMTUxLjU4NSAxODkuMjUyIDE1MS43NzJDMTg5LjM5MyAxNTEuOTU3IDE4OS40OTcgMTUyLjE3NiAxODkuNTY1IDE1Mi40MjhDMTg5LjYzMiAxNTIuNjc4IDE4OS42NjYgMTUyLjk1MiAxODkuNjY2IDE1My4yNDlWMTUzLjU3M0gxODYuNTFWMTUyLjk4M0gxODguOTQ0VjE1Mi45MjhDMTg4LjkzMyAxNTIuNzQxIDE4OC44OTQgMTUyLjU1OSAxODguODI2IDE1Mi4zODJDMTg4Ljc2MSAxNTIuMjA1IDE4OC42NTcgMTUyLjA1OSAxODguNTE0IDE1MS45NDRDMTg4LjM3MSAxNTEuODMgMTg4LjE3NSAxNTEuNzcyIDE4Ny45MjggMTUxLjc3MkMxODcuNzY0IDE1MS43NzIgMTg3LjYxMyAxNTEuODA3IDE4Ny40NzUgMTUxLjg3OEMxODcuMzM3IDE1MS45NDUgMTg3LjIxOCAxNTIuMDQ3IDE4Ny4xMTkgMTUyLjE4MkMxODcuMDIgMTUyLjMxOCAxODYuOTQ0IDE1Mi40ODMgMTg2Ljg4OSAxNTIuNjc4QzE4Ni44MzQgMTUyLjg3NCAxODYuODA3IDE1My4wOTkgMTg2LjgwNyAxNTMuMzU0VjE1My41MThDMTg2LjgwNyAxNTMuNzE5IDE4Ni44MzQgMTUzLjkwOCAxODYuODg5IDE1NC4wODVDMTg2Ljk0NiAxNTQuMjU5IDE4Ny4wMjggMTU0LjQxMyAxODcuMTM1IDE1NC41NDZDMTg3LjI0NCAxNTQuNjc4IDE4Ny4zNzYgMTU0Ljc4MyAxODcuNTMgMTU0Ljg1OEMxODcuNjg2IDE1NC45MzQgMTg3Ljg2MyAxNTQuOTcxIDE4OC4wNjEgMTU0Ljk3MUMxODguMzE2IDE1NC45NzEgMTg4LjUzMiAxNTQuOTE5IDE4OC43MDkgMTU0LjgxNUMxODguODg2IDE1NC43MTEgMTg5LjA0MSAxNTQuNTcyIDE4OS4xNzQgMTU0LjM5N0wxODkuNjEyIDE1NC43NDVDMTg5LjUyIDE1NC44ODMgMTg5LjQwNSAxNTUuMDE0IDE4OS4yNjQgMTU1LjEzOUMxODkuMTIzIDE1NS4yNjQgMTg4Ljk1IDE1NS4zNjYgMTg4Ljc0NCAxNTUuNDQ0QzE4OC41NDEgMTU1LjUyMiAxODguMyAxNTUuNTYxIDE4OC4wMjIgMTU1LjU2MVpNMTkwLjU4OSAxNDkuNDgzSDE5MS4zMTVWMTU0LjY2M0wxOTEuMjUzIDE1NS40ODNIMTkwLjU4OVYxNDkuNDgzWk0xOTQuMTcxIDE1My4zMzVWMTUzLjQxN0MxOTQuMTcxIDE1My43MjQgMTk0LjEzNCAxNTQuMDA5IDE5NC4wNjEgMTU0LjI3MkMxOTMuOTg4IDE1NC41MzMgMTkzLjg4MiAxNTQuNzU5IDE5My43NDEgMTU0Ljk1MkMxOTMuNiAxNTUuMTQ1IDE5My40MjkgMTU1LjI5NCAxOTMuMjI1IDE1NS40MDFDMTkzLjAyMiAxNTUuNTA4IDE5Mi43ODkgMTU1LjU2MSAxOTIuNTI2IDE1NS41NjFDMTkyLjI1OCAxNTUuNTYxIDE5Mi4wMjIgMTU1LjUxNiAxOTEuODE5IDE1NS40MjVDMTkxLjYxOSAxNTUuMzMxIDE5MS40NDkgMTU1LjE5NyAxOTEuMzExIDE1NS4wMjJDMTkxLjE3MyAxNTQuODQ4IDE5MS4wNjMgMTU0LjYzNyAxOTAuOTc5IDE1NC4zODlDMTkwLjg5OSAxNTQuMTQyIDE5MC44NDMgMTUzLjg2MyAxOTAuODExIDE1My41NTNWMTUzLjE5NEMxOTAuODQzIDE1Mi44ODIgMTkwLjg5OSAxNTIuNjAyIDE5MC45NzkgMTUyLjM1NEMxOTEuMDYzIDE1Mi4xMDcgMTkxLjE3MyAxNTEuODk2IDE5MS4zMTEgMTUxLjcyMUMxOTEuNDQ5IDE1MS41NDQgMTkxLjYxOSAxNTEuNDEgMTkxLjgxOSAxNTEuMzE5QzE5Mi4wMiAxNTEuMjI1IDE5Mi4yNTMgMTUxLjE3OCAxOTIuNTE4IDE1MS4xNzhDMTkyLjc4NCAxNTEuMTc4IDE5My4wMiAxNTEuMjMxIDE5My4yMjUgMTUxLjMzNUMxOTMuNDMxIDE1MS40MzYgMTkzLjYwMyAxNTEuNTgyIDE5My43NDEgMTUxLjc3MkMxOTMuODgyIDE1MS45NjIgMTkzLjk4OCAxNTIuMTkgMTk0LjA2MSAxNTIuNDU2QzE5NC4xMzQgMTUyLjcxOSAxOTQuMTcxIDE1My4wMTIgMTk0LjE3MSAxNTMuMzM1Wk0xOTMuNDQ0IDE1My40MTdWMTUzLjMzNUMxOTMuNDQ0IDE1My4xMjQgMTkzLjQyNSAxNTIuOTI2IDE5My4zODYgMTUyLjc0MUMxOTMuMzQ3IDE1Mi41NTMgMTkzLjI4NCAxNTIuMzg5IDE5My4xOTggMTUyLjI0OUMxOTMuMTEyIDE1Mi4xMDYgMTkyLjk5OSAxNTEuOTk0IDE5Mi44NTggMTUxLjkxM0MxOTIuNzE4IDE1MS44MyAxOTIuNTQ0IDE1MS43ODggMTkyLjMzOSAxNTEuNzg4QzE5Mi4xNTYgMTUxLjc4OCAxOTEuOTk4IDE1MS44MTkgMTkxLjg2MiAxNTEuODgyQzE5MS43MjkgMTUxLjk0NCAxOTEuNjE2IDE1Mi4wMjkgMTkxLjUyMiAxNTIuMTM1QzE5MS40MjkgMTUyLjI0IDE5MS4zNTIgMTUyLjM1OSAxOTEuMjkyIDE1Mi40OTVDMTkxLjIzNSAxNTIuNjI4IDE5MS4xOTIgMTUyLjc2NiAxOTEuMTYzIDE1Mi45MDlWMTUzLjg1QzE5MS4yMDUgMTU0LjAzMyAxOTEuMjcyIDE1NC4yMDggMTkxLjM2NiAxNTQuMzc4QzE5MS40NjIgMTU0LjU0NCAxOTEuNTkgMTU0LjY4MSAxOTEuNzQ5IDE1NC43ODhDMTkxLjkxIDE1NC44OTUgMTkyLjExIDE1NC45NDggMTkyLjM0NyAxNTQuOTQ4QzE5Mi41NDIgMTU0Ljk0OCAxOTIuNzA4IDE1NC45MDkgMTkyLjg0NyAxNTQuODMxQzE5Mi45ODcgMTU0Ljc1IDE5My4xIDE1NC42MzkgMTkzLjE4NiAxNTQuNDk5QzE5My4yNzUgMTU0LjM1OCAxOTMuMzQgMTU0LjE5NSAxOTMuMzgyIDE1NC4wMUMxOTMuNDIzIDE1My44MjYgMTkzLjQ0NCAxNTMuNjI4IDE5My40NDQgMTUzLjQxN1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPC9nPgo8Y2lyY2xlIGN4PSI0MSIgY3k9IjczLjE2MTEiIHI9IjMiIGZpbGw9IiMyMTk2RjMiLz4KPGNpcmNsZSBjeD0iNTkiIGN5PSI1MyIgcj0iMyIgZmlsbD0iIzIxOTZGMyIvPgo8Y2lyY2xlIGN4PSI3NyIgY3k9IjgxLjE2MTEiIHI9IjMiIGZpbGw9IiM0Q0FGNTAiLz4KPGNpcmNsZSBjeD0iOTUiIGN5PSI4NyIgcj0iMyIgZmlsbD0iIzRDQUY1MCIvPgo8Y2lyY2xlIGN4PSI0MSIgY3k9IjExMy4xNjEiIHI9IjMiIGZpbGw9IiM0Q0FGNTAiLz4KPGNpcmNsZSBjeD0iNTkiIGN5PSI5MyIgcj0iMyIgZmlsbD0iIzRDQUY1MCIvPgo8Y2lyY2xlIGN4PSIxMTIiIGN5PSI5MC4xNjExIiByPSIzIiBmaWxsPSIjNENBRjUwIi8+CjxjaXJjbGUgY3g9IjE0OCIgY3k9IjgxLjE2MTEiIHI9IjMiIGZpbGw9IiM0Q0FGNTAiLz4KPGNpcmNsZSBjeD0iMTMwIiBjeT0iMTAzIiByPSIzIiBmaWxsPSIjNENBRjUwIi8+CjxjaXJjbGUgY3g9IjE4MyIgY3k9IjEyMS4xNjEiIHI9IjMiIGZpbGw9IiM0Q0FGNTAiLz4KPGNpcmNsZSBjeD0iMTY2IiBjeT0iMTA5IiByPSIzIiBmaWxsPSIjNENBRjUwIi8+CjxjaXJjbGUgY3g9Ijc3IiBjeT0iMzciIHI9IjMiIGZpbGw9IiMyMTk2RjMiLz4KPGNpcmNsZSBjeD0iOTUiIGN5PSIzOSIgcj0iMyIgZmlsbD0iIzIxOTZGMyIvPgo8Y2lyY2xlIGN4PSIxMTIiIGN5PSIxOS4xNjExIiByPSIzIiBmaWxsPSIjMjE5NkYzIi8+CjxjaXJjbGUgY3g9IjE0OCIgY3k9IjM3IiByPSIzIiBmaWxsPSIjMjE5NkYzIi8+CjxjaXJjbGUgY3g9IjEzMCIgY3k9IjQ0IiByPSIzIiBmaWxsPSIjMjE5NkYzIi8+CjxjaXJjbGUgY3g9IjE4MyIgY3k9IjIzIiByPSIzIiBmaWxsPSIjMjE5NkYzIi8+CjxjaXJjbGUgY3g9IjE2NiIgY3k9IjQ0IiByPSIzIiBmaWxsPSIjMjE5NkYzIi8+CjxjaXJjbGUgY3g9Ijc3IiBjeT0iNzIiIHI9IjMiIGZpbGw9IiNGRkMxMDciLz4KPGNpcmNsZSBjeD0iOTUiIGN5PSI1NSIgcj0iMyIgZmlsbD0iI0ZGQzEwNyIvPgo8Y2lyY2xlIGN4PSIxMTIiIGN5PSI2Ni4xNjExIiByPSIzIiBmaWxsPSIjRkZDMTA3Ii8+CjxjaXJjbGUgY3g9IjE0OCIgY3k9IjUxLjE2MTEiIHI9IjMiIGZpbGw9IiNGRkMxMDciLz4KPGNpcmNsZSBjeD0iMTMwIiBjeT0iNzIiIHI9IjMiIGZpbGw9IiNGRkMxMDciLz4KPGNpcmNsZSBjeD0iMTgzIiBjeT0iODEiIHI9IjMiIGZpbGw9IiNGRkMxMDciLz4KPGNpcmNsZSBjeD0iMTY2IiBjeT0iNTkiIHI9IjMiIGZpbGw9IiNGRkMxMDciLz4KPGNpcmNsZSBjeD0iMjgiIGN5PSI4OSIgcj0iMyIgZmlsbD0iI0ZGQzEwNyIvPgo8Y2lyY2xlIGN4PSI0MSIgY3k9Ijk3IiByPSIzIiBmaWxsPSIjRkZDMTA3Ii8+CjxjaXJjbGUgY3g9IjU5IiBjeT0iNjguNTc4MSIgcj0iMyIgZmlsbD0iI0ZGQzEwNyIvPgo8Y2lyY2xlIGN4PSIyOCIgY3k9IjExOC4xNjEiIHI9IjMiIGZpbGw9IiMyMTk2RjMiLz4KPGNpcmNsZSBjeD0iMjgiIGN5PSIxMjkuNDg5IiByPSIzIiBmaWxsPSIjNENBRjUwIi8+CjwvZz4KPGRlZnM+CjxjbGlwUGF0aCBpZD0iY2xpcDBfNDE4Ml8xMTE5MyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8Y2xpcFBhdGggaWQ9ImNsaXAxXzQxODJfMTExOTMiPgo8cmVjdCB3aWR0aD0iMzUuNCIgaGVpZ2h0PSIxMCIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIzIDE0Ni4xNjEpIi8+CjwvY2xpcFBhdGg+CjxjbGlwUGF0aCBpZD0iY2xpcDJfNDE4Ml8xMTE5MyI+CjxyZWN0IHdpZHRoPSIzNS40IiBoZWlnaHQ9IjEwLjMyMiIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDU4LjM5OTkgMTQ2LjE2MSkiLz4KPC9jbGlwUGF0aD4KPGNsaXBQYXRoIGlkPSJjbGlwM180MTgyXzExMTkzIj4KPHJlY3Qgd2lkdGg9IjM1LjQiIGhlaWdodD0iMTAuMzIyIiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOTMuOCAxNDYuMTYxKSIvPgo8L2NsaXBQYXRoPgo8Y2xpcFBhdGggaWQ9ImNsaXA0XzQxODJfMTExOTMiPgo8cmVjdCB3aWR0aD0iMzUuNCIgaGVpZ2h0PSIxMC4zMjIiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMjkuMiAxNDYuMTYxKSIvPgo8L2NsaXBQYXRoPgo8Y2xpcFBhdGggaWQ9ImNsaXA1XzQxODJfMTExOTMiPgo8cmVjdCB3aWR0aD0iMzUuNCIgaGVpZ2h0PSIxMC4zMjIiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNjQuNiAxNDYuMTYxKSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo=",
+ "description": "Displays changes to time-series data over time—for example, temperature or humidity readings.",
+ "descriptor": {
+ "type": "timeseries",
+ "sizeX": 8,
+ "sizeY": 5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n self.ctx.$scope.timeSeriesChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeSeriesChartWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.timeSeriesChartWidget.onLatestDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n chartType: 'point',\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n dataKeySettingsFunction: TbTimeSeriesChart.dataKeySettings('point'),\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries', units: '°C', decimals: 0 }];\n }\n };\n}\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}",
+ "latestDataKeySettingsSchema": "{}",
+ "settingsDirective": "tb-time-series-chart-widget-settings",
+ "dataKeySettingsDirective": "tb-time-series-chart-key-settings",
+ "latestDataKeySettingsDirective": "",
+ "hasBasicMode": true,
+ "basicModeDirective": "tb-time-series-chart-basic-config",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":false,\"step\":false,\"stepType\":\"start\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":true,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"pointShape\":\"circle\",\"pointSize\":8,\"fillAreaSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}}},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#FFC107\",\"settings\":{\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":false,\"step\":false,\"stepType\":\"start\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":true,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"pointShape\":\"circle\",\"pointSize\":8,\"fillAreaSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}}},\"_hash\":0.5534217244004682,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":null}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":0,\"realtime\":{\"realtimeType\":0,\"timewindowMs\":60000,\"quickInterval\":\"CURRENT_DAY\",\"interval\":1000},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"top\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"yAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"xAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"bottom\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":10,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormat\":{},\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipDateInterval\":true,\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"yAxes\":{\"default\":{\"units\":null,\"decimals\":0,\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormatter\":null,\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\",\"id\":\"default\",\"order\":0}},\"noAggregationBarWidthSettings\":{\"strategy\":\"group\",\"groupWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000},\"barWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000}},\"animation\":{\"animation\":true,\"animationThreshold\":2000,\"animationDuration\":500,\"animationEasing\":\"cubicOut\",\"animationDelay\":0,\"animationDurationUpdate\":300,\"animationEasingUpdate\":\"cubicOut\",\"animationDelayUpdate\":0},\"padding\":\"12px\"},\"title\":\"Point chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"\",\"decimals\":null,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}"
+ },
+ "tags": [
+ "chart",
+ "time series",
+ "time-series",
+ "point",
+ "point chart"
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_types/range_chart.json b/application/src/main/data/json/system/widget_types/range_chart.json
index 981e3582c5..72cbeeacbb 100644
--- a/application/src/main/data/json/system/widget_types/range_chart.json
+++ b/application/src/main/data/json/system/widget_types/range_chart.json
@@ -2,7 +2,7 @@
"fqn": "range_chart",
"name": "Range chart",
"deprecated": false,
- "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC/VBMVEX////////8/PwAAAD///////+bsfP+wEzu7u73lkzz8/P1eWTT09PN1emRpeOcsvSxsbGQpeLc3NyXl5epqanjXHPLy8tti+OgoKD/wU26urr29vbts0fnkUf39/fIrYP4l02oqKjJroPl5eWUrPPDw8PCwsLJroSQqfKLpfKDn/HU1NSnu/X/tzD/pwNyku//vD//sB3m5ub2jj98mfB2lfD2hTD/rRPu8v3/3qD1fCD3+f7K1vnnjEf1eh30awSWrvOBnfFkhOLld1n1dRX0cxH/9N/1cVvld1jmjEf2izn/qQry9f2Yr/P/+u/+9u+RpuP82r//yGDdM1D3kEPzWD32gSixwfBWeN//2JDzY0v+vkf/siT+5+RBaNr7x6DbYWXxTC96mPD+7N+VqN35tID2emX3oWD3k0fm6/zc5PvV3vq2xveWrfL+8/Hd3d09ZNr/6b/6vZCPj4/2h3TfQl3bKkjaIUD/ujnxQCL0bwrC0PiWqN5Kbt3/79D6zMqGhob/0n/iUWn0aVH+vUPZGjr/tSuwwvZpiOLR0dH+2I97e3v1dWDkcV3fPVnxSCr/qw+Jo/F/m/GLoum3xOj64uZef+H41NoyW9hdfNbJycnxqLT7x5/4no/nb4P/zXDiVm3hS2TlcV3zXEPyUzj+uTTwRCbkbRB+mOaareN5k+FyjuBHbNw4YNn7xr3/47D70LD5t6zumqjngJH/2I+Ojo7lYnf4oWC+mF7utEj2iDTmfSrxRyrunw/kZwLL1vWWrfS/zPORqfP98fKjt/GGofHv7+/+7erK0umHnuKDm+Lf39/t5d7829bm2dT949Dzt8HqwLFse6r4raDsjJz3lIPopHzGpnT4q3DZVmXSNkvnj0LnjD7mgjbjWzHiSzHwRyrunQXv8/3+9POquOWot+Tu4s3u3b3rzrfUxrHu166boa6coK76uKzTvJvoppv6vpDuyYfdboDmkXH1fGffR2H3oV/kbVnnjkHtrTbOIDTiUiLhUCD/rhTuoROM0RHwAAAABXRSTlMg77MAvxFwlo8AAA5CSURBVHja1JtJaBNRGMfj8scXk8MwSYZxoiFhaC9FEQoRA7YHE6K9iMZ4KIoWcTkpqK3oRVH05HISwX1Db66guF48eHHFBfcFQUUQ933BL6PxZZyZRE2+of4aMhPyWvKb73vzf3nQQL9A/z743+lLGoE+Qlea0JLShVA0mFHFwP9Hn0CgL7QwdERgKnGlmY7QBYgNGwY2lleHBjLSNwAIpdlAFPRIQERBsIjcvDGQEUEimqLq0FFysUR4WLKiG5yQiKGiJGIqIJYCKQEGDhV6wAmJCD3VBDWqCxCmrjeBg+7tK06DkQD8Yd3C2ML1YMM/kY2FWM9GsOGfyLLuWOEg2PBPZMW22JotYMM3kYs9sdjbw2DDN5Eta2KxbQWw4ZtIYRmJrAAbfomsXxgjFq4DF36JXCmQB2eQ+CWybHuMKPAFiU8i61bESqy5Ai58EtnYY4l08wWJTyKHuy2R7d3gwieRnmWkwRok/ogsoc6y4FzI84tYsU4wBwm/iBXrP+i5CC74RcqxTnAu5C0RMypgppQ4lCaIlN6CxnKwUBZZcwhsWN/ZE4CeUA3TiCIiEImjoVCs/6T7MNggkXAk1YIo4lGoCaEDiQQIRRGN4fynaWVu3BRskIjRBJ0sbPtaDRR5fm/1jtWrV+/YsXrH+3uCDRJpDpNI5Me+VlwnMYFG8qYz9IvdZ8AFicQjioKmlC4A0JFeNJSuvBRZuxdcBEAIlGcGkUBDWdIVkuy6DCb4c+TqzpmhED0sipvBBL9IMU8WZToXgAd+kSknQhUs7wQP/CKXd1WK5E88BAv8IvnllSKruO6/7CJzFq0KVbJ2ClhgF9nbFbLRdQ4ssIss6LSLFJ+BBXaRYinWZ9LPT3YyBQmviIx19iCpS2QOanK1GCJ4g6R+kQVLUIvO5SE7+SLqgkWkmEcNptwP/c4i1AeDyIGu3VNqDSn+DyL5zp3VZ66M9Zm9WeTcfVpwVJvvtliXInNQBwwiFHU1UkHGOvcaRYqYKoSiAc0mEs2KwB8wZRFF3apdVa8vuRIzfRRZaiAiVMPUDOiq+KN9rQO7an/jo1h3cIJTxCgJIB5FWMR1wBSoTdGax/ku75LIWJ/pU0WEoRr4y32tS1/GjB4zevSYu9eEJ6/vjh5DY8pPNJ6ePl8SPARIIBqZr0ZKFQHiEUATtUXGHh87dtSosaPu7DkrvDh+ZxQNGVt6EHQsne15IngIgKCKGIaigaCDjppMXxz8SW6G55j2oAu58eDBEkGCZExYmCZqc6Qt+JOOHDzYRGOcZPeDgX/OkczEYLJ8iafCnWQ6SNA4G21T8Y9wiGzKyk/WkYE77bN/OPRmkcyx2s2yn2R7u8h4muqybTpa4casScEKkr1SZFbaPn/Hw4VMa9CNzAz8Exwi09sn2q71MbeSXKCquTGpF4lMbfutXxa7lORFeZAcm+xtInTvtZN2KUlrR4VE76zI1Jw9G4h2R0lkrCftKpNmwZ2qic8i0pp2XOtJrQ7brDSoHJ5MH/H6s+NQBQYRucySzHaUJFlhaxPyFMnMgycsIu9ss9irYXJyHtmF0h6tNa69FZ5wiIyjj+hWkun2hs86FX5WJAlXxufavXuLQ2Rqm3s8XIdExrq9raqsAzAvk/XuLQ6RTIcVCcnfS5IdB4kj1muLJI+lPXuLQ4SWWbUTm2I9+bci6dbZXr3FIkLLLHdm58ZV9Elb0FMk7T71KHfq6i1LZG6LQEJrAehovZA4cm62vffleZssiYx15xJlYsZjrlsLhDoo/9dbSjU0s8mAoqlV9rWut5UFHCoTqSQy1r1wisgaUm/VJ6LGof/Y12pW4zoQ9hJxLLNsRtlfJdmU/WsRus3V2VsBEIZm29fyZFMu6E0HlUTG+l+KpDvKi896RDQDKO9r6YDqua91+/iQKuwpb9btue096NYp4capD9Z7Z+va1zJ1VY0rYSoLoWtzI/AQefRgsIOj8vTjSWHx9KRzkDx7IFx4/ONXvr2sT0TTNBEPt8AirCXgwYSVA4hBA0rIM8mIkSixdfKAKgyDC/u+Wu8NHQ4XGp4j32kvYxWFgSiKVsOkVlnyVrARbJYJM1XID6QJWKQT/Qm7VAFBLLbKH1hZ2/iBq6s4WW/MDle8oCHeIeT47ry85MlW96pKyvy8zlntWcNA9rtfb5nMwXoDSOZ0W4Ik211hs7zoxhS5guQKZatbTRdgvQHEVfp/mSZp8yJ60QXSLC82kS0GZF30Acj9u7K9pF0gdaE1kS0SRIyWW0AQA+Mm8OtzkGOqNZEtDgSiT6qoFWhz63OQrXeAlM5v2HBh1dK1AtmDENkiQdwWk0SoE6SxmsgWB5KlQAAnLMg80ZrIFgdiDlw9EOSI/TD1NpmtOg8EqQstDAaudhlcfLHytnDZsqdAkNIE3St6ISBy8Gu5bM2TDEBCxiwuZfIMxNiz+VK21imAhIxZLBOAwF5n+9Z+FQiysp0DCfMkWW169jqbLVN9B4FkTeBtC5yBjyAL11oqTLbyRIeBWMO2KRSClDv9YrYy1wKZDM6f6HKcdIxZ0Hx7qiD35Pn/ua8iprpafLZK40GieBaf39PH0fBjOIUxC6YsXrtSPShZepfrW27rQT4HaqhmIxVHavoF67oKIFz/NY8gdeNn/tBs4evM6Q4SqdHlOAzovcKOKQACl6eytXB/QeLLMR7/kGoFK27DQDQXixzKTgVljxswKo6NwVBsCDgH5wMM/QbfNt18QU495Zz8RdkfyLEsLXT30OveSy+99Bs6ltWVlfHKMnoklhTNyPM0T5ITEtx8uNh7tYbt841vJyL0h4ep2qLfAj7/J/Lm7Y1Mxnv869a18bvWz3O4CBFRtJCIojAKQyyxjkVb7fqlTYTG2IVX7A/bZgc0wzq2n6O5icVza412yhvvc/4zn4Izuv+dBQrX7+aqlDw0kS9PV/xK4yO+OOftZ5xjo6tiwVWdKztdtl0vHY98buK+HUXZ8M7h6XEKjx/36PZ79BxZZRWTAEZBesaxFIGB24KOU2dlYMHWbB4SdLkbJXK3kfewRu1D5NNpYNR8H1ggSrO5dCKSVO7Bu1iK5GKivjOKWFg1su43y4y5EFnn03IBY927JCDzSbzq3KKtQ3YwUtpGCKNEkmU/CphIhHrAJZFGSDtpqCv52hJSbAyRHhkinY0mhAJ8clKZRH5lQwOARVvbhmWrPi/hQiSJjbmSF2dIB5KRJiDC0IBxbaUxO/XytSqYA5FtQe7hvXtlZO8kQ5naosfBMe1pZuNCRMSvSgd0050hECLVctiRaEsHbsoTYkLEkhAfgNG4JHISxMGureSBsf4iaXZAiVA5GsGAKvxOeOPUvs3IGFRbdG43697SH8/IqvCJWGvQbBcC8VVd4vw131hY5lYvkn3SOXybWRNCFzjVip0INVkqPMjrbsAEtLbo3NZyD2/656nEzJaQyvvJBEhiRmHVVir3KL1IyqymRKgTjdV/HwbHzwe1VSa7rnOzV0skZ5QI3bAnhEfU502tLsrBvRcky1QdQ0dKxJ4Qd4CfF32Wp09+epH8q+56WqSG4Wj98yCHlQ1JiCGBQhk6KONpQBD04pxH8DZ7moN4WfbgwF5d8Kh4c0XWb+Dfo2cRVARvfgdP+iH8pdlprKGtioudRztN8vq68zYv/ZPD9OKF1Ej+S4dUvbj9j68jsd4zqmhNs7V3uT7C5SshNVSMRgouHTOqBOe05ZKFB6qOR0KiThDbjWylNxpxkFx7VGeZjLAJrFjPaxUMogwPVPGovbeNrcZI8IeR206ylV7XfHdtN67Y62hJKAZZleKw+pP4n8BASrN16UPkbtwK/+1ohCuAjxpG8nCX34u/vW1Mm1IyZqvlNHp5dz1xEXvEiurXwiRCeaxpe/3LlzA/4xcqERqzNjdpJaZiq6Jv99TxZ2B9MWkKCgJRtTBUaLlZ0YF/+Zj9hMcviKlnmF48Di3rb+DHiFRqyykuQHBcWRD2vn8799/x/Ond27dp8bh79+mnJkctX2M9w4AxP7h/vsbOrMkdnT9/NI/1QRvBbCcaWS2a3MGTJ9Ha0I0slrWPJwdo4nDn4LCuDN3IfFln69UCTbxbrt7WlaEbidmarXZ/NXm0ipXBG6mz9epZwv08+gdvpM7W692EO3wfy4M3ss7WzrOUercXy8M3sliFDnmbUnNEDN8IZSs996YYvpGQreUC3dgAIz5bs94O2QAjPluvDtGDDTBC2Zod7aIHm2BksaJzbx82wch8+XoPfdgEI5jN0IuNMLJYoB/BCJNghpcYG+RGFQ6Dwhz9ODZSSIwFJLh1xTTMa93Z+m1o3cWWJyR98KBZ90a0Xs9r5fTxx/j4uYt9cELSq3mznnHFbDJBlx5y8MgAYe29CUXLgMAFhE72yjF4ZPBQcMcvdGSqMHmgcm/ojqAPR5RgDV1OznNrJlTUI6BEA3csMOGFI6kvpVJq1O1SoZRulzI+SqRdp1/B3wC2sN7dwwmsQM6Q534hWK6hRZVEte/8dsowBVglHVN9jK3SS8tKGtRuLUUuVYdUAO1SmSORdhkJ40VURsy+s4IZWzizfqWo1utdlOa0LTTXtI+K0ikXJFWGWTEqtGxKuVDtUlsY1ioV+7xMpL9rRAluBZ+CT41TiEYs84dkXPi/5YzjWkTpFh95KTPHUh6lYJapdinLmWmVagvjEunvGgGXgjNYIZSORuwEBMWclE6CGZgiSicY8Upa3hNcoJgKE6Vjy/dHHVLIVqkoyVkqzU6hFYrW0dgb4XBvxMgKRVtWH3LrnlJUo1VIioNkGPP4bbhvAFNUEUxyYh66WloRrdKy4JN2qeEGqTQ7i3+EPGnoZLvJvItlSMnTWbb57wJH9RLtM9lpbDpOnc3O/ACb/i1NA1ABvwAAAABJRU5ErkJggg==",
+ "image": "tb-image:cmFuZ2VfY2hhcnRfc3lzdGVtX3dpZGdldF9pbWFnZS5wbmc=:IlJhbmdlIGNoYXJ0IiBzeXN0ZW0gd2lkZ2V0IGltYWdl;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC/VBMVEX////////8/PwAAAD///////+bsfP+wEzu7u73lkzz8/P1eWTT09PN1emRpeOcsvSxsbGQpeLc3NyXl5epqanjXHPLy8tti+OgoKD/wU26urr29vbts0fnkUf39/fIrYP4l02oqKjJroPl5eWUrPPDw8PCwsLJroSQqfKLpfKDn/HU1NSnu/X/tzD/pwNyku//vD//sB3m5ub2jj98mfB2lfD2hTD/rRPu8v3/3qD1fCD3+f7K1vnnjEf1eh30awSWrvOBnfFkhOLld1n1dRX0cxH/9N/1cVvld1jmjEf2izn/qQry9f2Yr/P/+u/+9u+RpuP82r//yGDdM1D3kEPzWD32gSixwfBWeN//2JDzY0v+vkf/siT+5+RBaNr7x6DbYWXxTC96mPD+7N+VqN35tID2emX3oWD3k0fm6/zc5PvV3vq2xveWrfL+8/Hd3d09ZNr/6b/6vZCPj4/2h3TfQl3bKkjaIUD/ujnxQCL0bwrC0PiWqN5Kbt3/79D6zMqGhob/0n/iUWn0aVH+vUPZGjr/tSuwwvZpiOLR0dH+2I97e3v1dWDkcV3fPVnxSCr/qw+Jo/F/m/GLoum3xOj64uZef+H41NoyW9hdfNbJycnxqLT7x5/4no/nb4P/zXDiVm3hS2TlcV3zXEPyUzj+uTTwRCbkbRB+mOaareN5k+FyjuBHbNw4YNn7xr3/47D70LD5t6zumqjngJH/2I+Ojo7lYnf4oWC+mF7utEj2iDTmfSrxRyrunw/kZwLL1vWWrfS/zPORqfP98fKjt/GGofHv7+/+7erK0umHnuKDm+Lf39/t5d7829bm2dT949Dzt8HqwLFse6r4raDsjJz3lIPopHzGpnT4q3DZVmXSNkvnj0LnjD7mgjbjWzHiSzHwRyrunQXv8/3+9POquOWot+Tu4s3u3b3rzrfUxrHu166boa6coK76uKzTvJvoppv6vpDuyYfdboDmkXH1fGffR2H3oV/kbVnnjkHtrTbOIDTiUiLhUCD/rhTuoROM0RHwAAAABXRSTlMg77MAvxFwlo8AAA5CSURBVHja1JtJaBNRGMfj8scXk8MwSYZxoiFhaC9FEQoRA7YHE6K9iMZ4KIoWcTkpqK3oRVH05HISwX1Db66guF48eHHFBfcFQUUQ933BL6PxZZyZRE2+of4aMhPyWvKb73vzf3nQQL9A/z743+lLGoE+Qlea0JLShVA0mFHFwP9Hn0CgL7QwdERgKnGlmY7QBYgNGwY2lleHBjLSNwAIpdlAFPRIQERBsIjcvDGQEUEimqLq0FFysUR4WLKiG5yQiKGiJGIqIJYCKQEGDhV6wAmJCD3VBDWqCxCmrjeBg+7tK06DkQD8Yd3C2ML1YMM/kY2FWM9GsOGfyLLuWOEg2PBPZMW22JotYMM3kYs9sdjbw2DDN5Eta2KxbQWw4ZtIYRmJrAAbfomsXxgjFq4DF36JXCmQB2eQ+CWybHuMKPAFiU8i61bESqy5Ai58EtnYY4l08wWJTyKHuy2R7d3gwieRnmWkwRok/ogsoc6y4FzI84tYsU4wBwm/iBXrP+i5CC74RcqxTnAu5C0RMypgppQ4lCaIlN6CxnKwUBZZcwhsWN/ZE4CeUA3TiCIiEImjoVCs/6T7MNggkXAk1YIo4lGoCaEDiQQIRRGN4fynaWVu3BRskIjRBJ0sbPtaDRR5fm/1jtWrV+/YsXrH+3uCDRJpDpNI5Me+VlwnMYFG8qYz9IvdZ8AFicQjioKmlC4A0JFeNJSuvBRZuxdcBEAIlGcGkUBDWdIVkuy6DCb4c+TqzpmhED0sipvBBL9IMU8WZToXgAd+kSknQhUs7wQP/CKXd1WK5E88BAv8IvnllSKruO6/7CJzFq0KVbJ2ClhgF9nbFbLRdQ4ssIss6LSLFJ+BBXaRYinWZ9LPT3YyBQmviIx19iCpS2QOanK1GCJ4g6R+kQVLUIvO5SE7+SLqgkWkmEcNptwP/c4i1AeDyIGu3VNqDSn+DyL5zp3VZ66M9Zm9WeTcfVpwVJvvtliXInNQBwwiFHU1UkHGOvcaRYqYKoSiAc0mEs2KwB8wZRFF3apdVa8vuRIzfRRZaiAiVMPUDOiq+KN9rQO7an/jo1h3cIJTxCgJIB5FWMR1wBSoTdGax/ku75LIWJ/pU0WEoRr4y32tS1/GjB4zevSYu9eEJ6/vjh5DY8pPNJ6ePl8SPARIIBqZr0ZKFQHiEUATtUXGHh87dtSosaPu7DkrvDh+ZxQNGVt6EHQsne15IngIgKCKGIaigaCDjppMXxz8SW6G55j2oAu58eDBEkGCZExYmCZqc6Qt+JOOHDzYRGOcZPeDgX/OkczEYLJ8iafCnWQ6SNA4G21T8Y9wiGzKyk/WkYE77bN/OPRmkcyx2s2yn2R7u8h4muqybTpa4casScEKkr1SZFbaPn/Hw4VMa9CNzAz8Exwi09sn2q71MbeSXKCquTGpF4lMbfutXxa7lORFeZAcm+xtInTvtZN2KUlrR4VE76zI1Jw9G4h2R0lkrCftKpNmwZ2qic8i0pp2XOtJrQ7brDSoHJ5MH/H6s+NQBQYRucySzHaUJFlhaxPyFMnMgycsIu9ss9irYXJyHtmF0h6tNa69FZ5wiIyjj+hWkun2hs86FX5WJAlXxufavXuLQ2Rqm3s8XIdExrq9raqsAzAvk/XuLQ6RTIcVCcnfS5IdB4kj1muLJI+lPXuLQ4SWWbUTm2I9+bci6dbZXr3FIkLLLHdm58ZV9Elb0FMk7T71KHfq6i1LZG6LQEJrAehovZA4cm62vffleZssiYx15xJlYsZjrlsLhDoo/9dbSjU0s8mAoqlV9rWut5UFHCoTqSQy1r1wisgaUm/VJ6LGof/Y12pW4zoQ9hJxLLNsRtlfJdmU/WsRus3V2VsBEIZm29fyZFMu6E0HlUTG+l+KpDvKi896RDQDKO9r6YDqua91+/iQKuwpb9btue096NYp4capD9Z7Z+va1zJ1VY0rYSoLoWtzI/AQefRgsIOj8vTjSWHx9KRzkDx7IFx4/ONXvr2sT0TTNBEPt8AirCXgwYSVA4hBA0rIM8mIkSixdfKAKgyDC/u+Wu8NHQ4XGp4j32kvYxWFgSiKVsOkVlnyVrARbJYJM1XID6QJWKQT/Qm7VAFBLLbKH1hZ2/iBq6s4WW/MDle8oCHeIeT47ry85MlW96pKyvy8zlntWcNA9rtfb5nMwXoDSOZ0W4Ik211hs7zoxhS5guQKZatbTRdgvQHEVfp/mSZp8yJ60QXSLC82kS0GZF30Acj9u7K9pF0gdaE1kS0SRIyWW0AQA+Mm8OtzkGOqNZEtDgSiT6qoFWhz63OQrXeAlM5v2HBh1dK1AtmDENkiQdwWk0SoE6SxmsgWB5KlQAAnLMg80ZrIFgdiDlw9EOSI/TD1NpmtOg8EqQstDAaudhlcfLHytnDZsqdAkNIE3St6ISBy8Gu5bM2TDEBCxiwuZfIMxNiz+VK21imAhIxZLBOAwF5n+9Z+FQiysp0DCfMkWW169jqbLVN9B4FkTeBtC5yBjyAL11oqTLbyRIeBWMO2KRSClDv9YrYy1wKZDM6f6HKcdIxZ0Hx7qiD35Pn/ua8iprpafLZK40GieBaf39PH0fBjOIUxC6YsXrtSPShZepfrW27rQT4HaqhmIxVHavoF67oKIFz/NY8gdeNn/tBs4evM6Q4SqdHlOAzovcKOKQACl6eytXB/QeLLMR7/kGoFK27DQDQXixzKTgVljxswKo6NwVBsCDgH5wMM/QbfNt18QU495Zz8RdkfyLEsLXT30OveSy+99Bs6ltWVlfHKMnoklhTNyPM0T5ITEtx8uNh7tYbt841vJyL0h4ep2qLfAj7/J/Lm7Y1Mxnv869a18bvWz3O4CBFRtJCIojAKQyyxjkVb7fqlTYTG2IVX7A/bZgc0wzq2n6O5icVza412yhvvc/4zn4Izuv+dBQrX7+aqlDw0kS9PV/xK4yO+OOftZ5xjo6tiwVWdKztdtl0vHY98buK+HUXZ8M7h6XEKjx/36PZ79BxZZRWTAEZBesaxFIGB24KOU2dlYMHWbB4SdLkbJXK3kfewRu1D5NNpYNR8H1ggSrO5dCKSVO7Bu1iK5GKivjOKWFg1su43y4y5EFnn03IBY927JCDzSbzq3KKtQ3YwUtpGCKNEkmU/CphIhHrAJZFGSDtpqCv52hJSbAyRHhkinY0mhAJ8clKZRH5lQwOARVvbhmWrPi/hQiSJjbmSF2dIB5KRJiDC0IBxbaUxO/XytSqYA5FtQe7hvXtlZO8kQ5naosfBMe1pZuNCRMSvSgd0050hECLVctiRaEsHbsoTYkLEkhAfgNG4JHISxMGureSBsf4iaXZAiVA5GsGAKvxOeOPUvs3IGFRbdG43697SH8/IqvCJWGvQbBcC8VVd4vw131hY5lYvkn3SOXybWRNCFzjVip0INVkqPMjrbsAEtLbo3NZyD2/656nEzJaQyvvJBEhiRmHVVir3KL1IyqymRKgTjdV/HwbHzwe1VSa7rnOzV0skZ5QI3bAnhEfU502tLsrBvRcky1QdQ0dKxJ4Qd4CfF32Wp09+epH8q+56WqSG4Wj98yCHlQ1JiCGBQhk6KONpQBD04pxH8DZ7moN4WfbgwF5d8Kh4c0XWb+Dfo2cRVARvfgdP+iH8pdlprKGtioudRztN8vq68zYv/ZPD9OKF1Ej+S4dUvbj9j68jsd4zqmhNs7V3uT7C5SshNVSMRgouHTOqBOe05ZKFB6qOR0KiThDbjWylNxpxkFx7VGeZjLAJrFjPaxUMogwPVPGovbeNrcZI8IeR206ylV7XfHdtN67Y62hJKAZZleKw+pP4n8BASrN16UPkbtwK/+1ohCuAjxpG8nCX34u/vW1Mm1IyZqvlNHp5dz1xEXvEiurXwiRCeaxpe/3LlzA/4xcqERqzNjdpJaZiq6Jv99TxZ2B9MWkKCgJRtTBUaLlZ0YF/+Zj9hMcviKlnmF48Di3rb+DHiFRqyykuQHBcWRD2vn8799/x/Ond27dp8bh79+mnJkctX2M9w4AxP7h/vsbOrMkdnT9/NI/1QRvBbCcaWS2a3MGTJ9Ha0I0slrWPJwdo4nDn4LCuDN3IfFln69UCTbxbrt7WlaEbidmarXZ/NXm0ipXBG6mz9epZwv08+gdvpM7W692EO3wfy4M3ss7WzrOUercXy8M3sliFDnmbUnNEDN8IZSs996YYvpGQreUC3dgAIz5bs94O2QAjPluvDtGDDTBC2Zod7aIHm2BksaJzbx82wch8+XoPfdgEI5jN0IuNMLJYoB/BCJNghpcYG+RGFQ6Dwhz9ODZSSIwFJLh1xTTMa93Z+m1o3cWWJyR98KBZ90a0Xs9r5fTxx/j4uYt9cELSq3mznnHFbDJBlx5y8MgAYe29CUXLgMAFhE72yjF4ZPBQcMcvdGSqMHmgcm/ojqAPR5RgDV1OznNrJlTUI6BEA3csMOGFI6kvpVJq1O1SoZRulzI+SqRdp1/B3wC2sN7dwwmsQM6Q534hWK6hRZVEte/8dsowBVglHVN9jK3SS8tKGtRuLUUuVYdUAO1SmSORdhkJ40VURsy+s4IZWzizfqWo1utdlOa0LTTXtI+K0ikXJFWGWTEqtGxKuVDtUlsY1ioV+7xMpL9rRAluBZ+CT41TiEYs84dkXPi/5YzjWkTpFh95KTPHUh6lYJapdinLmWmVagvjEunvGgGXgjNYIZSORuwEBMWclE6CGZgiSicY8Upa3hNcoJgKE6Vjy/dHHVLIVqkoyVkqzU6hFYrW0dgb4XBvxMgKRVtWH3LrnlJUo1VIioNkGPP4bbhvAFNUEUxyYh66WloRrdKy4JN2qeEGqTQ7i3+EPGnoZLvJvItlSMnTWbb57wJH9RLtM9lpbDpOnc3O/ACb/i1NA1ABvwAAAABJRU5ErkJggg==",
"description": "Displays changes to time-series data over time visualized with color ranges — for example, temperature or humidity readings.",
"descriptor": {
"type": "timeseries",
@@ -20,9 +20,8 @@
"latestDataKeySettingsDirective": "",
"hasBasicMode": true,
"basicModeDirective": "tb-range-chart-basic-config",
- "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":0,\"realtime\":{\"realtimeType\":0,\"timewindowMs\":60000,\"quickInterval\":\"CURRENT_DAY\",\"interval\":1000},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"color\":\"#D81838\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm\",\"lastUpdateAgo\":false,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Range chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"°C\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}"
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}]}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":0,\"realtime\":{\"realtimeType\":0,\"timewindowMs\":60000,\"quickInterval\":\"CURRENT_DAY\",\"interval\":1000},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"dataZoom\":true,\"rangeColors\":[{\"to\":-20,\"color\":\"#234CC7\"},{\"from\":-20,\"to\":0,\"color\":\"#305AD7\"},{\"from\":0,\"to\":10,\"color\":\"#7191EF\"},{\"from\":10,\"to\":20,\"color\":\"#FFA600\"},{\"from\":20,\"to\":30,\"color\":\"#F36900\"},{\"from\":30,\"to\":40,\"color\":\"#F04022\"},{\"from\":40,\"color\":\"#D81838\"}],\"outOfRangeColor\":\"#ccc\",\"fillArea\":true,\"showLegend\":true,\"legendPosition\":\"top\",\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"tooltipDateInterval\":true},\"title\":\"Range chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"°C\",\"decimals\":0,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}"
},
- "externalId": null,
"tags": [
"range",
"color range",
diff --git a/application/src/main/data/json/system/widget_types/time_series_chart.json b/application/src/main/data/json/system/widget_types/time_series_chart.json
new file mode 100644
index 0000000000..9b2069ca6b
--- /dev/null
+++ b/application/src/main/data/json/system/widget_types/time_series_chart.json
@@ -0,0 +1,36 @@
+{
+ "fqn": "time_series_chart",
+ "name": "Time series chart",
+ "deprecated": false,
+ "image": "tb-image:Y2hhcnQuc3Zn:IlRpbWUgc2VyaWVzIGNoYXJ0IiBzeXN0ZW0gd2lkZ2V0IGltYWdl;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF80MDUzXzE4NTExMykiPgo8cGF0aCBkPSJNMi44NDc2NiAxLjI4MTI1VjdIMi4xMjVWMi4xODM1OUwwLjY2Nzk2OSAyLjcxNDg0VjIuMDYyNUwyLjczNDM4IDEuMjgxMjVIMi44NDc2NlpNOC42NzUxNyAzLjcwMzEyVjQuNTcwMzFDOC42NzUxNyA1LjAzNjQ2IDguNjMzNTEgNS40Mjk2OSA4LjU1MDE3IDUuNzVDOC40NjY4NCA2LjA3MDMxIDguMzQ3MDUgNi4zMjgxMiA4LjE5MDggNi41MjM0NEM4LjAzNDU1IDYuNzE4NzUgNy44NDU3NSA2Ljg2MDY4IDcuNjI0MzkgNi45NDkyMkM3LjQwNTY0IDcuMDM1MTYgNy4xNTgyNSA3LjA3ODEyIDYuODgyMiA3LjA3ODEyQzYuNjYzNDUgNy4wNzgxMiA2LjQ2MTYzIDcuMDUwNzggNi4yNzY3MyA2Ljk5NjA5QzYuMDkxODQgNi45NDE0MSA1LjkyNTE3IDYuODU0MTcgNS43NzY3MyA2LjczNDM4QzUuNjMwOSA2LjYxMTk4IDUuNTA1OSA2LjQ1MzEyIDUuNDAxNzMgNi4yNTc4MUM1LjI5NzU3IDYuMDYyNSA1LjIxODE0IDUuODI1NTIgNS4xNjM0NSA1LjU0Njg4QzUuMTA4NzcgNS4yNjgyMyA1LjA4MTQyIDQuOTQyNzEgNS4wODE0MiA0LjU3MDMxVjMuNzAzMTJDNS4wODE0MiAzLjIzNjk4IDUuMTIzMDkgMi44NDYzNSA1LjIwNjQyIDIuNTMxMjVDNS4yOTIzNiAyLjIxNjE1IDUuNDEzNDUgMS45NjM1NCA1LjU2OTcgMS43NzM0NEM1LjcyNTk1IDEuNTgwNzMgNS45MTM0NSAxLjQ0MjcxIDYuMTMyMiAxLjM1OTM4QzYuMzUzNTYgMS4yNzYwNCA2LjYwMDk1IDEuMjM0MzggNi44NzQzOSAxLjIzNDM4QzcuMDk1NzUgMS4yMzQzOCA3LjI5ODg3IDEuMjYxNzIgNy40ODM3NyAxLjMxNjQxQzcuNjcxMjcgMS4zNjg0OSA3LjgzNzkzIDEuNDUzMTIgNy45ODM3NyAxLjU3MDMxQzguMTI5NiAxLjY4NDkgOC4yNTMzIDEuODM4NTQgOC4zNTQ4NiAyLjAzMTI1QzguNDU5MDMgMi4yMjEzNSA4LjUzODQ1IDIuNDU0NDMgOC41OTMxNCAyLjczMDQ3QzguNjQ3ODMgMy4wMDY1MSA4LjY3NTE3IDMuMzMwNzMgOC42NzUxNyAzLjcwMzEyWk03Ljk0ODYxIDQuNjg3NVYzLjU4MjAzQzcuOTQ4NjEgMy4zMjY4MiA3LjkzMjk4IDMuMTAyODYgNy45MDE3MyAyLjkxMDE2QzcuODczMDkgMi43MTQ4NCA3LjgzMDEyIDIuNTQ4MTggNy43NzI4MyAyLjQxMDE2QzcuNzE1NTQgMi4yNzIxNCA3LjY0MjYyIDIuMTYwMTYgNy41NTQwOCAyLjA3NDIyQzcuNDY4MTQgMS45ODgyOCA3LjM2Nzg4IDEuOTI1NzggNy4yNTMzIDEuODg2NzJDNy4xNDEzMiAxLjg0NTA1IDcuMDE1MDIgMS44MjQyMiA2Ljg3NDM5IDEuODI0MjJDNi43MDI1MiAxLjgyNDIyIDYuNTUwMTcgMS44NTY3NyA2LjQxNzM2IDEuOTIxODhDNi4yODQ1NSAxLjk4NDM4IDYuMTcyNTcgMi4wODQ2NCA2LjA4MTQyIDIuMjIyNjZDNS45OTI4OCAyLjM2MDY4IDUuOTI1MTcgMi41NDE2NyA1Ljg3ODMgMi43NjU2MkM1LjgzMTQyIDIuOTg5NTggNS44MDc5OCAzLjI2MTcyIDUuODA3OTggMy41ODIwM1Y0LjY4NzVDNS44MDc5OCA0Ljk0MjcxIDUuODIyMzEgNS4xNjc5NyA1Ljg1MDk1IDUuMzYzMjhDNS44ODIyIDUuNTU4NTkgNS45Mjc3OCA1LjcyNzg2IDUuOTg3NjcgNS44NzEwOUM2LjA0NzU3IDYuMDExNzIgNi4xMjA0OCA2LjEyNzYgNi4yMDY0MiA2LjIxODc1QzYuMjkyMzYgNi4zMDk5IDYuMzkxMzIgNi4zNzc2IDYuNTAzMyA2LjQyMTg4QzYuNjE3ODggNi40NjM1NCA2Ljc0NDE4IDYuNDg0MzggNi44ODIyIDYuNDg0MzhDNy4wNTkyOSA2LjQ4NDM4IDcuMjE0MjMgNi40NTA1MiA3LjM0NzA1IDYuMzgyODFDNy40Nzk4NiA2LjMxNTEgNy41OTA1NCA2LjIwOTY0IDcuNjc5MDggNi4wNjY0MUM3Ljc3MDIyIDUuOTIwNTcgNy44Mzc5MyA1LjczNDM4IDcuODgyMiA1LjUwNzgxQzcuOTI2NDcgNS4yNzg2NSA3Ljk0ODYxIDUuMDA1MjEgNy45NDg2MSA0LjY4NzVaTTEzLjMwNzQgMy43MDMxMlY0LjU3MDMxQzEzLjMwNzQgNS4wMzY0NiAxMy4yNjU3IDUuNDI5NjkgMTMuMTgyNCA1Ljc1QzEzLjA5OSA2LjA3MDMxIDEyLjk3OTMgNi4zMjgxMiAxMi44MjMgNi41MjM0NEMxMi42NjY4IDYuNzE4NzUgMTIuNDc3OSA2Ljg2MDY4IDEyLjI1NjYgNi45NDkyMkMxMi4wMzc4IDcuMDM1MTYgMTEuNzkwNCA3LjA3ODEyIDExLjUxNDQgNy4wNzgxMkMxMS4yOTU3IDcuMDc4MTIgMTEuMDkzOCA3LjA1MDc4IDEwLjkwODkgNi45OTYwOUMxMC43MjQgNi45NDE0MSAxMC41NTc0IDYuODU0MTcgMTAuNDA4OSA2LjczNDM4QzEwLjI2MzEgNi42MTE5OCAxMC4xMzgxIDYuNDUzMTIgMTAuMDMzOSA2LjI1NzgxQzkuOTI5NzcgNi4wNjI1IDkuODUwMzQgNS44MjU1MiA5Ljc5NTY2IDUuNTQ2ODhDOS43NDA5NyA1LjI2ODIzIDkuNzEzNjMgNC45NDI3MSA5LjcxMzYzIDQuNTcwMzFWMy43MDMxMkM5LjcxMzYzIDMuMjM2OTggOS43NTUyOSAyLjg0NjM1IDkuODM4NjMgMi41MzEyNUM5LjkyNDU2IDIuMjE2MTUgMTAuMDQ1NyAxLjk2MzU0IDEwLjIwMTkgMS43NzM0NEMxMC4zNTgyIDEuNTgwNzMgMTAuNTQ1NyAxLjQ0MjcxIDEwLjc2NDQgMS4zNTkzOEMxMC45ODU4IDEuMjc2MDQgMTEuMjMzMiAxLjIzNDM4IDExLjUwNjYgMS4yMzQzOEMxMS43Mjc5IDEuMjM0MzggMTEuOTMxMSAxLjI2MTcyIDEyLjExNiAxLjMxNjQxQzEyLjMwMzUgMS4zNjg0OSAxMi40NzAxIDEuNDUzMTIgMTIuNjE2IDEuNTcwMzFDMTIuNzYxOCAxLjY4NDkgMTIuODg1NSAxLjgzODU0IDEyLjk4NzEgMi4wMzEyNUMxMy4wOTEyIDIuMjIxMzUgMTMuMTcwNyAyLjQ1NDQzIDEzLjIyNTMgMi43MzA0N0MxMy4yOCAzLjAwNjUxIDEzLjMwNzQgMy4zMzA3MyAxMy4zMDc0IDMuNzAzMTJaTTEyLjU4MDggNC42ODc1VjMuNTgyMDNDMTIuNTgwOCAzLjMyNjgyIDEyLjU2NTIgMy4xMDI4NiAxMi41MzM5IDIuOTEwMTZDMTIuNTA1MyAyLjcxNDg0IDEyLjQ2MjMgMi41NDgxOCAxMi40MDUgMi40MTAxNkMxMi4zNDc3IDIuMjcyMTQgMTIuMjc0OCAyLjE2MDE2IDEyLjE4NjMgMi4wNzQyMkMxMi4xMDAzIDEuOTg4MjggMTIuMDAwMSAxLjkyNTc4IDExLjg4NTUgMS44ODY3MkMxMS43NzM1IDEuODQ1MDUgMTEuNjQ3MiAxLjgyNDIyIDExLjUwNjYgMS44MjQyMkMxMS4zMzQ3IDEuODI0MjIgMTEuMTgyNCAxLjg1Njc3IDExLjA0OTYgMS45MjE4OEMxMC45MTY4IDEuOTg0MzggMTAuODA0OCAyLjA4NDY0IDEwLjcxMzYgMi4yMjI2NkMxMC42MjUxIDIuMzYwNjggMTAuNTU3NCAyLjU0MTY3IDEwLjUxMDUgMi43NjU2MkMxMC40NjM2IDIuOTg5NTggMTAuNDQwMiAzLjI2MTcyIDEwLjQ0MDIgMy41ODIwM1Y0LjY4NzVDMTAuNDQwMiA0Ljk0MjcxIDEwLjQ1NDUgNS4xNjc5NyAxMC40ODMyIDUuMzYzMjhDMTAuNTE0NCA1LjU1ODU5IDEwLjU2IDUuNzI3ODYgMTAuNjE5OSA1Ljg3MTA5QzEwLjY3OTggNi4wMTE3MiAxMC43NTI3IDYuMTI3NiAxMC44Mzg2IDYuMjE4NzVDMTAuOTI0NiA2LjMwOTkgMTEuMDIzNSA2LjM3NzYgMTEuMTM1NSA2LjQyMTg4QzExLjI1MDEgNi40NjM1NCAxMS4zNzY0IDYuNDg0MzggMTEuNTE0NCA2LjQ4NDM4QzExLjY5MTUgNi40ODQzOCAxMS44NDY0IDYuNDUwNTIgMTEuOTc5MyA2LjM4MjgxQzEyLjExMjEgNi4zMTUxIDEyLjIyMjcgNi4yMDk2NCAxMi4zMTEzIDYuMDY2NDFDMTIuNDAyNCA1LjkyMDU3IDEyLjQ3MDEgNS43MzQzOCAxMi41MTQ0IDUuNTA3ODFDMTIuNTU4NyA1LjI3ODY1IDEyLjU4MDggNS4wMDUyMSAxMi41ODA4IDQuNjg3NVpNMTQuMzA2OCAyLjcwNzAzVjIuNDA2MjVDMTQuMzA2OCAyLjE5MDEgMTQuMzUzNiAxLjk5MzQ5IDE0LjQ0NzQgMS44MTY0MUMxNC41NDExIDEuNjM5MzIgMTQuNjc1MyAxLjQ5NzQgMTQuODQ5NyAxLjM5MDYyQzE1LjAyNDIgMS4yODM4NSAxNS4yMzEyIDEuMjMwNDcgMTUuNDcwOCAxLjIzMDQ3QzE1LjcxNTYgMS4yMzA0NyAxNS45MjQgMS4yODM4NSAxNi4wOTU4IDEuMzkwNjJDMTYuMjcwMyAxLjQ5NzQgMTYuNDA0NCAxLjYzOTMyIDE2LjQ5ODIgMS44MTY0MUMxNi41OTE5IDEuOTkzNDkgMTYuNjM4OCAyLjE5MDEgMTYuNjM4OCAyLjQwNjI1VjIuNzA3MDNDMTYuNjM4OCAyLjkxNzk3IDE2LjU5MTkgMy4xMTE5OCAxNi40OTgyIDMuMjg5MDZDMTYuNDA3IDMuNDY2MTUgMTYuMjc0MiAzLjYwODA3IDE2LjA5OTcgMy43MTQ4NEMxNS45Mjc5IDMuODIxNjEgMTUuNzIwOCAzLjg3NSAxNS40Nzg2IDMuODc1QzE1LjIzNjUgMy44NzUgMTUuMDI2OCAzLjgyMTYxIDE0Ljg0OTcgMy43MTQ4NEMxNC42NzUzIDMuNjA4MDcgMTQuNTQxMSAzLjQ2NjE1IDE0LjQ0NzQgMy4yODkwNkMxNC4zNTM2IDMuMTExOTggMTQuMzA2OCAyLjkxNzk3IDE0LjMwNjggMi43MDcwM1pNMTQuODQ5NyAyLjQwNjI1VjIuNzA3MDNDMTQuODQ5NyAyLjgyNjgyIDE0Ljg3MTkgMi45NDAxIDE0LjkxNjEgMy4wNDY4OEMxNC45NjMgMy4xNTM2NSAxNS4wMzMzIDMuMjQwODkgMTUuMTI3MSAzLjMwODU5QzE1LjIyMDggMy4zNzM3IDE1LjMzOCAzLjQwNjI1IDE1LjQ3ODYgMy40MDYyNUMxNS42MTkzIDMuNDA2MjUgMTUuNzM1MiAzLjM3MzcgMTUuODI2MyAzLjMwODU5QzE1LjkxNzQgMy4yNDA4OSAxNS45ODUyIDMuMTUzNjUgMTYuMDI5NCAzLjA0Njg4QzE2LjA3MzcgMi45NDAxIDE2LjA5NTggMi44MjY4MiAxNi4wOTU4IDIuNzA3MDNWMi40MDYyNUMxNi4wOTU4IDIuMjgzODUgMTYuMDcyNCAyLjE2OTI3IDE2LjAyNTUgMi4wNjI1QzE1Ljk4MTIgMS45NTMxMiAxNS45MTIyIDEuODY1ODkgMTUuODE4NSAxLjgwMDc4QzE1LjcyNzMgMS43MzMwNyAxNS42MTE1IDEuNjk5MjIgMTUuNDcwOCAxLjY5OTIyQzE1LjMzMjggMS42OTkyMiAxNS4yMTY5IDEuNzMzMDcgMTUuMTIzMiAxLjgwMDc4QzE1LjAzMiAxLjg2NTg5IDE0Ljk2MyAxLjk1MzEyIDE0LjkxNjEgMi4wNjI1QzE0Ljg3MTkgMi4xNjkyNyAxNC44NDk3IDIuMjgzODUgMTQuODQ5NyAyLjQwNjI1Wk0xNy4wNzYzIDUuOTEwMTZWNS42MDU0N0MxNy4wNzYzIDUuMzkxOTMgMTcuMTIzMiA1LjE5NjYxIDE3LjIxNjkgNS4wMTk1M0MxNy4zMTA3IDQuODQyNDUgMTcuNDQ0OCA0LjcwMDUyIDE3LjYxOTMgNC41OTM3NUMxNy43OTM3IDQuNDg2OTggMTguMDAwOCA0LjQzMzU5IDE4LjI0MDQgNC40MzM1OUMxOC40ODUyIDQuNDMzNTkgMTguNjkzNSA0LjQ4Njk4IDE4Ljg2NTQgNC41OTM3NUMxOS4wMzk4IDQuNzAwNTIgMTkuMTc0IDQuODQyNDUgMTkuMjY3NyA1LjAxOTUzQzE5LjM2MTUgNS4xOTY2MSAxOS40MDgzIDUuMzkxOTMgMTkuNDA4MyA1LjYwNTQ3VjUuOTEwMTZDMTkuNDA4MyA2LjEyMzcgMTkuMzYxNSA2LjMxOTAxIDE5LjI2NzcgNi40OTYwOUMxOS4xNzY2IDYuNjczMTggMTkuMDQzNyA2LjgxNTEgMTguODY5MyA2LjkyMTg4QzE4LjY5NzQgNy4wMjg2NSAxOC40OTA0IDcuMDgyMDMgMTguMjQ4MiA3LjA4MjAzQzE4LjAwNiA3LjA4MjAzIDE3Ljc5NzcgNy4wMjg2NSAxNy42MjMyIDYuOTIxODhDMTcuNDQ4NyA2LjgxNTEgMTcuMzEzMyA2LjY3MzE4IDE3LjIxNjkgNi40OTYwOUMxNy4xMjMyIDYuMzE5MDEgMTcuMDc2MyA2LjEyMzcgMTcuMDc2MyA1LjkxMDE2Wk0xNy42MTkzIDUuNjA1NDdWNS45MTAxNkMxNy42MTkzIDYuMDI5OTUgMTcuNjQxNCA2LjE0NDUzIDE3LjY4NTcgNi4yNTM5MUMxNy43MzI1IDYuMzYwNjggMTcuODAyOSA2LjQ0NzkyIDE3Ljg5NjYgNi41MTU2MkMxNy45OTA0IDYuNTgwNzMgMTguMTA3NSA2LjYxMzI4IDE4LjI0ODIgNi42MTMyOEMxOC4zODg4IDYuNjEzMjggMTguNTA0NyA2LjU4MDczIDE4LjU5NTggNi41MTU2MkMxOC42ODk2IDYuNDQ3OTIgMTguNzU4NiA2LjM2MDY4IDE4LjgwMjkgNi4yNTM5MUMxOC44NDcxIDYuMTQ3MTQgMTguODY5MyA2LjAzMjU1IDE4Ljg2OTMgNS45MTAxNlY1LjYwNTQ3QzE4Ljg2OTMgNS40ODMwNyAxOC44NDU4IDUuMzY4NDkgMTguNzk5IDUuMjYxNzJDMTguNzU0NyA1LjE1NDk1IDE4LjY4NTcgNS4wNjkwMSAxOC41OTE5IDUuMDAzOTFDMTguNTAwOCA0LjkzNjIgMTguMzgzNiA0LjkwMjM0IDE4LjI0MDQgNC45MDIzNEMxOC4xMDIzIDQuOTAyMzQgMTcuOTg2NSA0LjkzNjIgMTcuODkyNyA1LjAwMzkxQzE3LjgwMTYgNS4wNjkwMSAxNy43MzI1IDUuMTU0OTUgMTcuNjg1NyA1LjI2MTcyQzE3LjY0MTQgNS4zNjg0OSAxNy42MTkzIDUuNDgzMDcgMTcuNjE5MyA1LjYwNTQ3Wk0xOC40MiAyLjEyMTA5TDE1LjY0MjcgNi41NjY0MUwxNS4yMzY1IDYuMzA4NTlMMTguMDEzOCAxLjg2MzI4TDE4LjQyIDIuMTIxMDlaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjxwYXRoIGQ9Ik04LjA1ODU5IDMzLjY2OEM4LjA1ODU5IDM0LjAxNDMgNy45Nzc4NiAzNC4zMDg2IDcuODE2NDEgMzQuNTUwOEM3LjY1NzU1IDM0Ljc5MDQgNy40NDE0MSAzNC45NzI3IDcuMTY3OTcgMzUuMDk3N0M2Ljg5NzE0IDM1LjIyMjcgNi41OTExNSAzNS4yODUyIDYuMjUgMzUuMjg1MkM1LjkwODg1IDM1LjI4NTIgNS42MDE1NiAzNS4yMjI3IDUuMzI4MTIgMzUuMDk3N0M1LjA1NDY5IDM0Ljk3MjcgNC44Mzg1NCAzNC43OTA0IDQuNjc5NjkgMzQuNTUwOEM0LjUyMDgzIDM0LjMwODYgNC40NDE0MSAzNC4wMTQzIDQuNDQxNDEgMzMuNjY4QzQuNDQxNDEgMzMuNDQxNCA0LjQ4NDM4IDMzLjIzNDQgNC41NzAzMSAzMy4wNDY5QzQuNjU4ODUgMzIuODU2OCA0Ljc4MjU1IDMyLjY5MTQgNC45NDE0MSAzMi41NTA4QzUuMTAyODYgMzIuNDEwMiA1LjI5Mjk3IDMyLjMwMjEgNS41MTE3MiAzMi4yMjY2QzUuNzMzMDcgMzIuMTQ4NCA1Ljk3NjU2IDMyLjEwOTQgNi4yNDIxOSAzMi4xMDk0QzYuNTkxMTUgMzIuMTA5NCA2LjkwMjM0IDMyLjE3NzEgNy4xNzU3OCAzMi4zMTI1QzcuNDQ5MjIgMzIuNDQ1MyA3LjY2NDA2IDMyLjYyODkgNy44MjAzMSAzMi44NjMzQzcuOTc5MTcgMzMuMDk3NyA4LjA1ODU5IDMzLjM2NTkgOC4wNTg1OSAzMy42NjhaTTcuMzMyMDMgMzMuNjUyM0M3LjMzMjAzIDMzLjQ0MTQgNy4yODY0NiAzMy4yNTUyIDcuMTk1MzEgMzMuMDkzOEM3LjEwNDE3IDMyLjkyOTcgNi45NzY1NiAzMi44MDIxIDYuODEyNSAzMi43MTA5QzYuNjQ4NDQgMzIuNjE5OCA2LjQ1ODMzIDMyLjU3NDIgNi4yNDIxOSAzMi41NzQyQzYuMDIwODMgMzIuNTc0MiA1LjgyOTQzIDMyLjYxOTggNS42Njc5NyAzMi43MTA5QzUuNTA5MTEgMzIuODAyMSA1LjM4NTQyIDMyLjkyOTcgNS4yOTY4OCAzMy4wOTM4QzUuMjA4MzMgMzMuMjU1MiA1LjE2NDA2IDMzLjQ0MTQgNS4xNjQwNiAzMy42NTIzQzUuMTY0MDYgMzMuODcxMSA1LjIwNzAzIDM0LjA1ODYgNS4yOTI5NyAzNC4yMTQ4QzUuMzgxNTEgMzQuMzY4NSA1LjUwNjUxIDM0LjQ4NyA1LjY2Nzk3IDM0LjU3MDNDNS44MzIwMyAzNC42NTEgNi4wMjYwNCAzNC42OTE0IDYuMjUgMzQuNjkxNEM2LjQ3Mzk2IDM0LjY5MTQgNi42NjY2NyAzNC42NTEgNi44MjgxMiAzNC41NzAzQzYuOTg5NTggMzQuNDg3IDcuMTEzMjggMzQuMzY4NSA3LjE5OTIyIDM0LjIxNDhDNy4yODc3NiAzNC4wNTg2IDcuMzMyMDMgMzMuODcxMSA3LjMzMjAzIDMzLjY1MjNaTTcuOTI1NzggMzFDNy45MjU3OCAzMS4yNzYgNy44NTI4NiAzMS41MjQ3IDcuNzA3MDMgMzEuNzQ2MUM3LjU2MTIgMzEuOTY3NCA3LjM2MTk4IDMyLjE0MTkgNy4xMDkzOCAzMi4yNjk1QzYuODU2NzcgMzIuMzk3MSA2LjU3MDMxIDMyLjQ2MDkgNi4yNSAzMi40NjA5QzUuOTI0NDggMzIuNDYwOSA1LjYzNDExIDMyLjM5NzEgNS4zNzg5MSAzMi4yNjk1QzUuMTI2MyAzMi4xNDE5IDQuOTI4MzkgMzEuOTY3NCA0Ljc4NTE2IDMxLjc0NjFDNC42NDE5MyAzMS41MjQ3IDQuNTcwMzEgMzEuMjc2IDQuNTcwMzEgMzFDNC41NzAzMSAzMC42NjkzIDQuNjQxOTMgMzAuMzg4IDQuNzg1MTYgMzAuMTU2MkM0LjkzMDk5IDI5LjkyNDUgNS4xMzAyMSAyOS43NDc0IDUuMzgyODEgMjkuNjI1QzUuNjM1NDIgMjkuNTAyNiA1LjkyMzE4IDI5LjQ0MTQgNi4yNDYwOSAyOS40NDE0QzYuNTcxNjEgMjkuNDQxNCA2Ljg2MDY4IDI5LjUwMjYgNy4xMTMyOCAyOS42MjVDNy4zNjU4OSAyOS43NDc0IDcuNTYzOCAyOS45MjQ1IDcuNzA3MDMgMzAuMTU2MkM3Ljg1Mjg2IDMwLjM4OCA3LjkyNTc4IDMwLjY2OTMgNy45MjU3OCAzMVpNNy4yMDMxMiAzMS4wMTE3QzcuMjAzMTIgMzAuODIxNiA3LjE2Mjc2IDMwLjY1MzYgNy4wODIwMyAzMC41MDc4QzcuMDAxMyAzMC4zNjIgNi44ODkzMiAzMC4yNDc0IDYuNzQ2MDkgMzAuMTY0MUM2LjYwMjg2IDMwLjA3ODEgNi40MzYyIDMwLjAzNTIgNi4yNDYwOSAzMC4wMzUyQzYuMDU1OTkgMzAuMDM1MiA1Ljg4OTMyIDMwLjA3NTUgNS43NDYwOSAzMC4xNTYyQzUuNjA1NDcgMzAuMjM0NCA1LjQ5NDc5IDMwLjM0NjQgNS40MTQwNiAzMC40OTIyQzUuMzM1OTQgMzAuNjM4IDUuMjk2ODggMzAuODExMiA1LjI5Njg4IDMxLjAxMTdDNS4yOTY4OCAzMS4yMDcgNS4zMzU5NCAzMS4zNzc2IDUuNDE0MDYgMzEuNTIzNEM1LjQ5NDc5IDMxLjY2OTMgNS42MDY3NyAzMS43ODI2IDUuNzUgMzEuODYzM0M1Ljg5MzIzIDMxLjk0NCA2LjA1OTkgMzEuOTg0NCA2LjI1IDMxLjk4NDRDNi40NDAxIDMxLjk4NDQgNi42MDU0NyAzMS45NDQgNi43NDYwOSAzMS44NjMzQzYuODg5MzIgMzEuNzgyNiA3LjAwMTMgMzEuNjY5MyA3LjA4MjAzIDMxLjUyMzRDNy4xNjI3NiAzMS4zNzc2IDcuMjAzMTIgMzEuMjA3IDcuMjAzMTIgMzEuMDExN1pNMTIuNjc1MiAzMS45MTAyVjMyLjc3NzNDMTIuNjc1MiAzMy4yNDM1IDEyLjYzMzUgMzMuNjM2NyAxMi41NTAyIDMzLjk1N0MxMi40NjY4IDM0LjI3NzMgMTIuMzQ3IDM0LjUzNTIgMTIuMTkwOCAzNC43MzA1QzEyLjAzNDUgMzQuOTI1OCAxMS44NDU3IDM1LjA2NzcgMTEuNjI0NCAzNS4xNTYyQzExLjQwNTYgMzUuMjQyMiAxMS4xNTgyIDM1LjI4NTIgMTAuODgyMiAzNS4yODUyQzEwLjY2MzUgMzUuMjg1MiAxMC40NjE2IDM1LjI1NzggMTAuMjc2NyAzNS4yMDMxQzEwLjA5MTggMzUuMTQ4NCA5LjkyNTE3IDM1LjA2MTIgOS43NzY3MyAzNC45NDE0QzkuNjMwOSAzNC44MTkgOS41MDU5IDM0LjY2MDIgOS40MDE3MyAzNC40NjQ4QzkuMjk3NTcgMzQuMjY5NSA5LjIxODE0IDM0LjAzMjYgOS4xNjM0NSAzMy43NTM5QzkuMTA4NzcgMzMuNDc1MyA5LjA4MTQyIDMzLjE0OTcgOS4wODE0MiAzMi43NzczVjMxLjkxMDJDOS4wODE0MiAzMS40NDQgOS4xMjMwOSAzMS4wNTM0IDkuMjA2NDIgMzAuNzM4M0M5LjI5MjM2IDMwLjQyMzIgOS40MTM0NSAzMC4xNzA2IDkuNTY5NyAyOS45ODA1QzkuNzI1OTUgMjkuNzg3OCA5LjkxMzQ1IDI5LjY0OTcgMTAuMTMyMiAyOS41NjY0QzEwLjM1MzYgMjkuNDgzMSAxMC42MDEgMjkuNDQxNCAxMC44NzQ0IDI5LjQ0MTRDMTEuMDk1NyAyOS40NDE0IDExLjI5ODkgMjkuNDY4OCAxMS40ODM4IDI5LjUyMzRDMTEuNjcxMyAyOS41NzU1IDExLjgzNzkgMjkuNjYwMiAxMS45ODM4IDI5Ljc3NzNDMTIuMTI5NiAyOS44OTE5IDEyLjI1MzMgMzAuMDQ1NiAxMi4zNTQ5IDMwLjIzODNDMTIuNDU5IDMwLjQyODQgMTIuNTM4NSAzMC42NjE1IDEyLjU5MzEgMzAuOTM3NUMxMi42NDc4IDMxLjIxMzUgMTIuNjc1MiAzMS41Mzc4IDEyLjY3NTIgMzEuOTEwMlpNMTEuOTQ4NiAzMi44OTQ1VjMxLjc4OTFDMTEuOTQ4NiAzMS41MzM5IDExLjkzMyAzMS4zMDk5IDExLjkwMTcgMzEuMTE3MkMxMS44NzMxIDMwLjkyMTkgMTEuODMwMSAzMC43NTUyIDExLjc3MjggMzAuNjE3MkMxMS43MTU1IDMwLjQ3OTIgMTEuNjQyNiAzMC4zNjcyIDExLjU1NDEgMzAuMjgxMkMxMS40NjgxIDMwLjE5NTMgMTEuMzY3OSAzMC4xMzI4IDExLjI1MzMgMzAuMDkzOEMxMS4xNDEzIDMwLjA1MjEgMTEuMDE1IDMwLjAzMTIgMTAuODc0NCAzMC4wMzEyQzEwLjcwMjUgMzAuMDMxMiAxMC41NTAyIDMwLjA2MzggMTAuNDE3NCAzMC4xMjg5QzEwLjI4NDUgMzAuMTkxNCAxMC4xNzI2IDMwLjI5MTcgMTAuMDgxNCAzMC40Mjk3QzkuOTkyODggMzAuNTY3NyA5LjkyNTE3IDMwLjc0ODcgOS44NzgzIDMwLjk3MjdDOS44MzE0MiAzMS4xOTY2IDkuODA3OTggMzEuNDY4OCA5LjgwNzk4IDMxLjc4OTFWMzIuODk0NUM5LjgwNzk4IDMzLjE0OTcgOS44MjIzMSAzMy4zNzUgOS44NTA5NSAzMy41NzAzQzkuODgyMiAzMy43NjU2IDkuOTI3NzggMzMuOTM0OSA5Ljk4NzY3IDM0LjA3ODFDMTAuMDQ3NiAzNC4yMTg4IDEwLjEyMDUgMzQuMzM0NiAxMC4yMDY0IDM0LjQyNThDMTAuMjkyNCAzNC41MTY5IDEwLjM5MTMgMzQuNTg0NiAxMC41MDMzIDM0LjYyODlDMTAuNjE3OSAzNC42NzA2IDEwLjc0NDIgMzQuNjkxNCAxMC44ODIyIDM0LjY5MTRDMTEuMDU5MyAzNC42OTE0IDExLjIxNDIgMzQuNjU3NiAxMS4zNDcgMzQuNTg5OEMxMS40Nzk5IDM0LjUyMjEgMTEuNTkwNSAzNC40MTY3IDExLjY3OTEgMzQuMjczNEMxMS43NzAyIDM0LjEyNzYgMTEuODM3OSAzMy45NDE0IDExLjg4MjIgMzMuNzE0OEMxMS45MjY1IDMzLjQ4NTcgMTEuOTQ4NiAzMy4yMTIyIDExLjk0ODYgMzIuODk0NVpNMTMuNjc0NiAzMC45MTQxVjMwLjYxMzNDMTMuNjc0NiAzMC4zOTcxIDEzLjcyMTQgMzAuMjAwNSAxMy44MTUyIDMwLjAyMzRDMTMuOTA4OSAyOS44NDY0IDE0LjA0MzEgMjkuNzA0NCAxNC4yMTc1IDI5LjU5NzdDMTQuMzkyIDI5LjQ5MDkgMTQuNTk5IDI5LjQzNzUgMTQuODM4NiAyOS40Mzc1QzE1LjA4MzQgMjkuNDM3NSAxNS4yOTE4IDI5LjQ5MDkgMTUuNDYzNiAyOS41OTc3QzE1LjYzODEgMjkuNzA0NCAxNS43NzIyIDI5Ljg0NjQgMTUuODY2IDMwLjAyMzRDMTUuOTU5NyAzMC4yMDA1IDE2LjAwNjYgMzAuMzk3MSAxNi4wMDY2IDMwLjYxMzNWMzAuOTE0MUMxNi4wMDY2IDMxLjEyNSAxNS45NTk3IDMxLjMxOSAxNS44NjYgMzEuNDk2MUMxNS43NzQ4IDMxLjY3MzIgMTUuNjQyIDMxLjgxNTEgMTUuNDY3NSAzMS45MjE5QzE1LjI5NTcgMzIuMDI4NiAxNS4wODg2IDMyLjA4MiAxNC44NDY0IDMyLjA4MkMxNC42MDQzIDMyLjA4MiAxNC4zOTQ2IDMyLjAyODYgMTQuMjE3NSAzMS45MjE5QzE0LjA0MzEgMzEuODE1MSAxMy45MDg5IDMxLjY3MzIgMTMuODE1MiAzMS40OTYxQzEzLjcyMTQgMzEuMzE5IDEzLjY3NDYgMzEuMTI1IDEzLjY3NDYgMzAuOTE0MVpNMTQuMjE3NSAzMC42MTMzVjMwLjkxNDFDMTQuMjE3NSAzMS4wMzM5IDE0LjIzOTcgMzEuMTQ3MSAxNC4yODM5IDMxLjI1MzlDMTQuMzMwOCAzMS4zNjA3IDE0LjQwMTEgMzEuNDQ3OSAxNC40OTQ5IDMxLjUxNTZDMTQuNTg4NiAzMS41ODA3IDE0LjcwNTggMzEuNjEzMyAxNC44NDY0IDMxLjYxMzNDMTQuOTg3MSAzMS42MTMzIDE1LjEwMjkgMzEuNTgwNyAxNS4xOTQxIDMxLjUxNTZDMTUuMjg1MiAzMS40NDc5IDE1LjM1MjkgMzEuMzYwNyAxNS4zOTcyIDMxLjI1MzlDMTUuNDQxNSAzMS4xNDcxIDE1LjQ2MzYgMzEuMDMzOSAxNS40NjM2IDMwLjkxNDFWMzAuNjEzM0MxNS40NjM2IDMwLjQ5MDkgMTUuNDQwMiAzMC4zNzYzIDE1LjM5MzMgMzAuMjY5NUMxNS4zNDkgMzAuMTYwMiAxNS4yOCAzMC4wNzI5IDE1LjE4NjMgMzAuMDA3OEMxNS4wOTUxIDI5Ljk0MDEgMTQuOTc5MyAyOS45MDYyIDE0LjgzODYgMjkuOTA2MkMxNC43MDA2IDI5LjkwNjIgMTQuNTg0NyAyOS45NDAxIDE0LjQ5MSAzMC4wMDc4QzE0LjM5OTggMzAuMDcyOSAxNC4zMzA4IDMwLjE2MDIgMTQuMjgzOSAzMC4yNjk1QzE0LjIzOTcgMzAuMzc2MyAxNC4yMTc1IDMwLjQ5MDkgMTQuMjE3NSAzMC42MTMzWk0xNi40NDQxIDM0LjExNzJWMzMuODEyNUMxNi40NDQxIDMzLjU5OSAxNi40OTEgMzMuNDAzNiAxNi41ODQ3IDMzLjIyNjZDMTYuNjc4NSAzMy4wNDk1IDE2LjgxMjYgMzIuOTA3NiAxNi45ODcxIDMyLjgwMDhDMTcuMTYxNSAzMi42OTQgMTcuMzY4NiAzMi42NDA2IDE3LjYwODIgMzIuNjQwNkMxNy44NTI5IDMyLjY0MDYgMTguMDYxMyAzMi42OTQgMTguMjMzMiAzMi44MDA4QzE4LjQwNzYgMzIuOTA3NiAxOC41NDE4IDMzLjA0OTUgMTguNjM1NSAzMy4yMjY2QzE4LjcyOTMgMzMuNDAzNiAxOC43NzYxIDMzLjU5OSAxOC43NzYxIDMzLjgxMjVWMzQuMTE3MkMxOC43NzYxIDM0LjMzMDcgMTguNzI5MyAzNC41MjYgMTguNjM1NSAzNC43MDMxQzE4LjU0NDQgMzQuODgwMiAxOC40MTE1IDM1LjAyMjEgMTguMjM3MSAzNS4xMjg5QzE4LjA2NTIgMzUuMjM1NyAxNy44NTgyIDM1LjI4OTEgMTcuNjE2IDM1LjI4OTFDMTcuMzczOCAzNS4yODkxIDE3LjE2NTQgMzUuMjM1NyAxNi45OTEgMzUuMTI4OUMxNi44MTY1IDM1LjAyMjEgMTYuNjgxMSAzNC44ODAyIDE2LjU4NDcgMzQuNzAzMUMxNi40OTEgMzQuNTI2IDE2LjQ0NDEgMzQuMzMwNyAxNi40NDQxIDM0LjExNzJaTTE2Ljk4NzEgMzMuODEyNVYzNC4xMTcyQzE2Ljk4NzEgMzQuMjM3IDE3LjAwOTIgMzQuMzUxNiAxNy4wNTM1IDM0LjQ2MDlDMTcuMTAwMyAzNC41Njc3IDE3LjE3MDcgMzQuNjU0OSAxNy4yNjQ0IDM0LjcyMjdDMTcuMzU4MiAzNC43ODc4IDE3LjQ3NTMgMzQuODIwMyAxNy42MTYgMzQuODIwM0MxNy43NTY2IDM0LjgyMDMgMTcuODcyNSAzNC43ODc4IDE3Ljk2MzYgMzQuNzIyN0MxOC4wNTc0IDM0LjY1NDkgMTguMTI2NCAzNC41Njc3IDE4LjE3MDcgMzQuNDYwOUMxOC4yMTQ5IDM0LjM1NDIgMTguMjM3MSAzNC4yMzk2IDE4LjIzNzEgMzQuMTE3MlYzMy44MTI1QzE4LjIzNzEgMzMuNjkwMSAxOC4yMTM2IDMzLjU3NTUgMTguMTY2OCAzMy40Njg4QzE4LjEyMjUgMzMuMzYyIDE4LjA1MzUgMzMuMjc2IDE3Ljk1OTcgMzMuMjEwOUMxNy44Njg2IDMzLjE0MzIgMTcuNzUxNCAzMy4xMDk0IDE3LjYwODIgMzMuMTA5NEMxNy40NzAxIDMzLjEwOTQgMTcuMzU0MyAzMy4xNDMyIDE3LjI2MDUgMzMuMjEwOUMxNy4xNjk0IDMzLjI3NiAxNy4xMDAzIDMzLjM2MiAxNy4wNTM1IDMzLjQ2ODhDMTcuMDA5MiAzMy41NzU1IDE2Ljk4NzEgMzMuNjkwMSAxNi45ODcxIDMzLjgxMjVaTTE3Ljc4NzggMzAuMzI4MUwxNS4wMTA1IDM0Ljc3MzRMMTQuNjA0MyAzNC41MTU2TDE3LjM4MTYgMzAuMDcwM0wxNy43ODc4IDMwLjMyODFaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjxwYXRoIGQ9Ik03LjI0NjA5IDU3LjcxNzhINy4zMDg1OVY1OC4zMzExSDcuMjQ2MDlDNi44NjMyOCA1OC4zMzExIDYuNTQyOTcgNTguMzkzNiA2LjI4NTE2IDU4LjUxODZDNi4wMjczNCA1OC42NDEgNS44MjI5MiA1OC44MDYzIDUuNjcxODggNTkuMDE0NkM1LjUyMDgzIDU5LjIyMDQgNS40MTE0NiA1OS40NTIxIDUuMzQzNzUgNTkuNzFDNS4yNzg2NSA1OS45Njc4IDUuMjQ2MDkgNjAuMjI5NSA1LjI0NjA5IDYwLjQ5NTFWNjEuMzMxMUM1LjI0NjA5IDYxLjU4MzcgNS4yNzYwNCA2MS44MDc2IDUuMzM1OTQgNjIuMDAyOUM1LjM5NTgzIDYyLjE5NTYgNS40Nzc4NiA2Mi4zNTg0IDUuNTgyMDMgNjIuNDkxMkM1LjY4NjIgNjIuNjI0IDUuODAzMzkgNjIuNzI0MyA1LjkzMzU5IDYyLjc5MkM2LjA2NjQxIDYyLjg1OTcgNi4yMDQ0MyA2Mi44OTM2IDYuMzQ3NjYgNjIuODkzNkM2LjUxNDMyIDYyLjg5MzYgNi42NjI3NiA2Mi44NjIzIDYuNzkyOTcgNjIuNzk5OEM2LjkyMzE4IDYyLjczNDcgNy4wMzI1NSA2Mi42NDQ5IDcuMTIxMDkgNjIuNTMwM0M3LjIxMjI0IDYyLjQxMzEgNy4yODEyNSA2Mi4yNzUxIDcuMzI4MTIgNjIuMTE2MkM3LjM3NSA2MS45NTc0IDcuMzk4NDQgNjEuNzgyOSA3LjM5ODQ0IDYxLjU5MjhDNy4zOTg0NCA2MS40MjM1IDcuMzc3NiA2MS4yNjA3IDcuMzM1OTQgNjEuMTA0NUM3LjI5NDI3IDYwLjk0NTYgNy4yMzA0NyA2MC44MDUgNy4xNDQ1MyA2MC42ODI2QzcuMDU4NTkgNjAuNTU3NiA2Ljk1MDUyIDYwLjQ2IDYuODIwMzEgNjAuMzg5NkM2LjY5MjcxIDYwLjMxNjcgNi41NDAzNiA2MC4yODAzIDYuMzYzMjggNjAuMjgwM0M2LjE2Mjc2IDYwLjI4MDMgNS45NzUyNiA2MC4zMjk4IDUuODAwNzggNjAuNDI4N0M1LjYyODkxIDYwLjUyNTEgNS40ODY5OCA2MC42NTI3IDUuMzc1IDYwLjgxMTVDNS4yNjU2MiA2MC45Njc4IDUuMjAzMTIgNjEuMTM4MyA1LjE4NzUgNjEuMzIzMkw0LjgwNDY5IDYxLjMxOTNDNC44NDExNSA2MS4wMjc3IDQuOTA4ODUgNjAuNzc5IDUuMDA3ODEgNjAuNTczMkM1LjEwOTM4IDYwLjM2NDkgNS4yMzQzOCA2MC4xOTU2IDUuMzgyODEgNjAuMDY1NEM1LjUzMzg1IDU5LjkzMjYgNS43MDE4MiA1OS44MzYzIDUuODg2NzIgNTkuNzc2NEM2LjA3NDIyIDU5LjcxMzkgNi4yNzIxNCA1OS42ODI2IDYuNDgwNDcgNTkuNjgyNkM2Ljc2NDMyIDU5LjY4MjYgNy4wMDkxMSA1OS43MzYgNy4yMTQ4NCA1OS44NDI4QzcuNDIwNTcgNTkuOTQ5NSA3LjU4OTg0IDYwLjA5MjggNy43MjI2NiA2MC4yNzI1QzcuODU1NDcgNjAuNDQ5NSA3Ljk1MzEyIDYwLjY1MDEgOC4wMTU2MiA2MC44NzRDOC4wODA3MyA2MS4wOTU0IDguMTEzMjggNjEuMzIzMiA4LjExMzI4IDYxLjU1NzZDOC4xMTMyOCA2MS44MjU4IDguMDc1NTIgNjIuMDc3MSA4IDYyLjMxMTVDNy45MjQ0OCA2Mi41NDU5IDcuODExMiA2Mi43NTE2IDcuNjYwMTYgNjIuOTI4N0M3LjUxMTcyIDYzLjEwNTggNy4zMjgxMiA2My4yNDM4IDcuMTA5MzggNjMuMzQyOEM2Ljg5MDYyIDYzLjQ0MTcgNi42MzY3MiA2My40OTEyIDYuMzQ3NjYgNjMuNDkxMkM2LjA0MDM2IDYzLjQ5MTIgNS43NzIxNCA2My40Mjg3IDUuNTQyOTcgNjMuMzAzN0M1LjMxMzggNjMuMTc2MSA1LjEyMzcgNjMuMDA2OCA0Ljk3MjY2IDYyLjc5NTlDNC44MjE2MSA2Mi41ODUgNC43MDgzMyA2Mi4zNTA2IDQuNjMyODEgNjIuMDkyOEM0LjU1NzI5IDYxLjgzNSA0LjUxOTUzIDYxLjU3MzIgNC41MTk1MyA2MS4zMDc2VjYwLjk2NzhDNC41MTk1MyA2MC41NjY3IDQuNTU5OSA2MC4xNzM1IDQuNjQwNjIgNTkuNzg4MUM0LjcyMTM1IDU5LjQwMjcgNC44NjA2OCA1OS4wNTM3IDUuMDU4NTkgNTguNzQxMkM1LjI1OTExIDU4LjQyODcgNS41MzY0NiA1OC4xOCA1Ljg5MDYyIDU3Ljk5NTFDNi4yNDQ3OSA1Ny44MTAyIDYuNjk2NjEgNTcuNzE3OCA3LjI0NjA5IDU3LjcxNzhaTTEyLjY3NTIgNjAuMTE2MlY2MC45ODM0QzEyLjY3NTIgNjEuNDQ5NSAxMi42MzM1IDYxLjg0MjggMTIuNTUwMiA2Mi4xNjMxQzEyLjQ2NjggNjIuNDgzNCAxMi4zNDcgNjIuNzQxMiAxMi4xOTA4IDYyLjkzNjVDMTIuMDM0NSA2My4xMzE4IDExLjg0NTcgNjMuMjczOCAxMS42MjQ0IDYzLjM2MjNDMTEuNDA1NiA2My40NDgyIDExLjE1ODIgNjMuNDkxMiAxMC44ODIyIDYzLjQ5MTJDMTAuNjYzNSA2My40OTEyIDEwLjQ2MTYgNjMuNDYzOSAxMC4yNzY3IDYzLjQwOTJDMTAuMDkxOCA2My4zNTQ1IDkuOTI1MTcgNjMuMjY3MyA5Ljc3NjczIDYzLjE0NzVDOS42MzA5IDYzLjAyNTEgOS41MDU5IDYyLjg2NjIgOS40MDE3MyA2Mi42NzA5QzkuMjk3NTcgNjIuNDc1NiA5LjIxODE0IDYyLjIzODYgOS4xNjM0NSA2MS45NkM5LjEwODc3IDYxLjY4MTMgOS4wODE0MiA2MS4zNTU4IDkuMDgxNDIgNjAuOTgzNFY2MC4xMTYyQzkuMDgxNDIgNTkuNjUwMSA5LjEyMzA5IDU5LjI1OTQgOS4yMDY0MiA1OC45NDQzQzkuMjkyMzYgNTguNjI5MiA5LjQxMzQ1IDU4LjM3NjYgOS41Njk3IDU4LjE4NjVDOS43MjU5NSA1Ny45OTM4IDkuOTEzNDUgNTcuODU1OCAxMC4xMzIyIDU3Ljc3MjVDMTAuMzUzNiA1Ny42ODkxIDEwLjYwMSA1Ny42NDc1IDEwLjg3NDQgNTcuNjQ3NUMxMS4wOTU3IDU3LjY0NzUgMTEuMjk4OSA1Ny42NzQ4IDExLjQ4MzggNTcuNzI5NUMxMS42NzEzIDU3Ljc4MTYgMTEuODM3OSA1Ny44NjYyIDExLjk4MzggNTcuOTgzNEMxMi4xMjk2IDU4LjA5OCAxMi4yNTMzIDU4LjI1MTYgMTIuMzU0OSA1OC40NDQzQzEyLjQ1OSA1OC42MzQ0IDEyLjUzODUgNTguODY3NSAxMi41OTMxIDU5LjE0MzZDMTIuNjQ3OCA1OS40MTk2IDEyLjY3NTIgNTkuNzQzOCAxMi42NzUyIDYwLjExNjJaTTExLjk0ODYgNjEuMTAwNlY1OS45OTUxQzExLjk0ODYgNTkuNzM5OSAxMS45MzMgNTkuNTE2IDExLjkwMTcgNTkuMzIzMkMxMS44NzMxIDU5LjEyNzkgMTEuODMwMSA1OC45NjEzIDExLjc3MjggNTguODIzMkMxMS43MTU1IDU4LjY4NTIgMTEuNjQyNiA1OC41NzMyIDExLjU1NDEgNTguNDg3M0MxMS40NjgxIDU4LjQwMTQgMTEuMzY3OSA1OC4zMzg5IDExLjI1MzMgNTguMjk5OEMxMS4xNDEzIDU4LjI1ODEgMTEuMDE1IDU4LjIzNzMgMTAuODc0NCA1OC4yMzczQzEwLjcwMjUgNTguMjM3MyAxMC41NTAyIDU4LjI2OTkgMTAuNDE3NCA1OC4zMzVDMTAuMjg0NSA1OC4zOTc1IDEwLjE3MjYgNTguNDk3NyAxMC4wODE0IDU4LjYzNTdDOS45OTI4OCA1OC43NzM4IDkuOTI1MTcgNTguOTU0OCA5Ljg3ODMgNTkuMTc4N0M5LjgzMTQyIDU5LjQwMjcgOS44MDc5OCA1OS42NzQ4IDkuODA3OTggNTkuOTk1MVY2MS4xMDA2QzkuODA3OTggNjEuMzU1OCA5LjgyMjMxIDYxLjU4MTEgOS44NTA5NSA2MS43NzY0QzkuODgyMiA2MS45NzE3IDkuOTI3NzggNjIuMTQxIDkuOTg3NjcgNjIuMjg0MkMxMC4wNDc2IDYyLjQyNDggMTAuMTIwNSA2Mi41NDA3IDEwLjIwNjQgNjIuNjMxOEMxMC4yOTI0IDYyLjcyMyAxMC4zOTEzIDYyLjc5MDcgMTAuNTAzMyA2Mi44MzVDMTAuNjE3OSA2Mi44NzY2IDEwLjc0NDIgNjIuODk3NSAxMC44ODIyIDYyLjg5NzVDMTEuMDU5MyA2Mi44OTc1IDExLjIxNDIgNjIuODYzNiAxMS4zNDcgNjIuNzk1OUMxMS40Nzk5IDYyLjcyODIgMTEuNTkwNSA2Mi42MjI3IDExLjY3OTEgNjIuNDc5NUMxMS43NzAyIDYyLjMzMzcgMTEuODM3OSA2Mi4xNDc1IDExLjg4MjIgNjEuOTIwOUMxMS45MjY1IDYxLjY5MTcgMTEuOTQ4NiA2MS40MTgzIDExLjk0ODYgNjEuMTAwNlpNMTMuNjc0NiA1OS4xMjAxVjU4LjgxOTNDMTMuNjc0NiA1OC42MDMyIDEzLjcyMTQgNTguNDA2NiAxMy44MTUyIDU4LjIyOTVDMTMuOTA4OSA1OC4wNTI0IDE0LjA0MzEgNTcuOTEwNSAxNC4yMTc1IDU3LjgwMzdDMTQuMzkyIDU3LjY5NjkgMTQuNTk5IDU3LjY0MzYgMTQuODM4NiA1Ny42NDM2QzE1LjA4MzQgNTcuNjQzNiAxNS4yOTE4IDU3LjY5NjkgMTUuNDYzNiA1Ny44MDM3QzE1LjYzODEgNTcuOTEwNSAxNS43NzIyIDU4LjA1MjQgMTUuODY2IDU4LjIyOTVDMTUuOTU5NyA1OC40MDY2IDE2LjAwNjYgNTguNjAzMiAxNi4wMDY2IDU4LjgxOTNWNTkuMTIwMUMxNi4wMDY2IDU5LjMzMTEgMTUuOTU5NyA1OS41MjUxIDE1Ljg2NiA1OS43MDIxQzE1Ljc3NDggNTkuODc5MiAxNS42NDIgNjAuMDIxMiAxNS40Njc1IDYwLjEyNzlDMTUuMjk1NyA2MC4yMzQ3IDE1LjA4ODYgNjAuMjg4MSAxNC44NDY0IDYwLjI4ODFDMTQuNjA0MyA2MC4yODgxIDE0LjM5NDYgNjAuMjM0NyAxNC4yMTc1IDYwLjEyNzlDMTQuMDQzMSA2MC4wMjEyIDEzLjkwODkgNTkuODc5MiAxMy44MTUyIDU5LjcwMjFDMTMuNzIxNCA1OS41MjUxIDEzLjY3NDYgNTkuMzMxMSAxMy42NzQ2IDU5LjEyMDFaTTE0LjIxNzUgNTguODE5M1Y1OS4xMjAxQzE0LjIxNzUgNTkuMjM5OSAxNC4yMzk3IDU5LjM1MzIgMTQuMjgzOSA1OS40NkMxNC4zMzA4IDU5LjU2NjcgMTQuNDAxMSA1OS42NTQgMTQuNDk0OSA1OS43MjE3QzE0LjU4ODYgNTkuNzg2OCAxNC43MDU4IDU5LjgxOTMgMTQuODQ2NCA1OS44MTkzQzE0Ljk4NzEgNTkuODE5MyAxNS4xMDI5IDU5Ljc4NjggMTUuMTk0MSA1OS43MjE3QzE1LjI4NTIgNTkuNjU0IDE1LjM1MjkgNTkuNTY2NyAxNS4zOTcyIDU5LjQ2QzE1LjQ0MTUgNTkuMzUzMiAxNS40NjM2IDU5LjIzOTkgMTUuNDYzNiA1OS4xMjAxVjU4LjgxOTNDMTUuNDYzNiA1OC42OTY5IDE1LjQ0MDIgNTguNTgyNCAxNS4zOTMzIDU4LjQ3NTZDMTUuMzQ5IDU4LjM2NjIgMTUuMjggNTguMjc5IDE1LjE4NjMgNTguMjEzOUMxNS4wOTUxIDU4LjE0NjIgMTQuOTc5MyA1OC4xMTIzIDE0LjgzODYgNTguMTEyM0MxNC43MDA2IDU4LjExMjMgMTQuNTg0NyA1OC4xNDYyIDE0LjQ5MSA1OC4yMTM5QzE0LjM5OTggNTguMjc5IDE0LjMzMDggNTguMzY2MiAxNC4yODM5IDU4LjQ3NTZDMTQuMjM5NyA1OC41ODI0IDE0LjIxNzUgNTguNjk2OSAxNC4yMTc1IDU4LjgxOTNaTTE2LjQ0NDEgNjIuMzIzMlY2Mi4wMTg2QzE2LjQ0NDEgNjEuODA1IDE2LjQ5MSA2MS42MDk3IDE2LjU4NDcgNjEuNDMyNkMxNi42Nzg1IDYxLjI1NTUgMTYuODEyNiA2MS4xMTM2IDE2Ljk4NzEgNjEuMDA2OEMxNy4xNjE1IDYwLjkwMDEgMTcuMzY4NiA2MC44NDY3IDE3LjYwODIgNjAuODQ2N0MxNy44NTI5IDYwLjg0NjcgMTguMDYxMyA2MC45MDAxIDE4LjIzMzIgNjEuMDA2OEMxOC40MDc2IDYxLjExMzYgMTguNTQxOCA2MS4yNTU1IDE4LjYzNTUgNjEuNDMyNkMxOC43MjkzIDYxLjYwOTcgMTguNzc2MSA2MS44MDUgMTguNzc2MSA2Mi4wMTg2VjYyLjMyMzJDMTguNzc2MSA2Mi41MzY4IDE4LjcyOTMgNjIuNzMyMSAxOC42MzU1IDYyLjkwOTJDMTguNTQ0NCA2My4wODYzIDE4LjQxMTUgNjMuMjI4MiAxOC4yMzcxIDYzLjMzNUMxOC4wNjUyIDYzLjQ0MTcgMTcuODU4MiA2My40OTUxIDE3LjYxNiA2My40OTUxQzE3LjM3MzggNjMuNDk1MSAxNy4xNjU0IDYzLjQ0MTcgMTYuOTkxIDYzLjMzNUMxNi44MTY1IDYzLjIyODIgMTYuNjgxMSA2My4wODYzIDE2LjU4NDcgNjIuOTA5MkMxNi40OTEgNjIuNzMyMSAxNi40NDQxIDYyLjUzNjggMTYuNDQ0MSA2Mi4zMjMyWk0xNi45ODcxIDYyLjAxODZWNjIuMzIzMkMxNi45ODcxIDYyLjQ0MyAxNy4wMDkyIDYyLjU1NzYgMTcuMDUzNSA2Mi42NjdDMTcuMTAwMyA2Mi43NzM4IDE3LjE3MDcgNjIuODYxIDE3LjI2NDQgNjIuOTI4N0MxNy4zNTgyIDYyLjk5MzggMTcuNDc1MyA2My4wMjY0IDE3LjYxNiA2My4wMjY0QzE3Ljc1NjYgNjMuMDI2NCAxNy44NzI1IDYyLjk5MzggMTcuOTYzNiA2Mi45Mjg3QzE4LjA1NzQgNjIuODYxIDE4LjEyNjQgNjIuNzczOCAxOC4xNzA3IDYyLjY2N0MxOC4yMTQ5IDYyLjU2MDIgMTguMjM3MSA2Mi40NDU2IDE4LjIzNzEgNjIuMzIzMlY2Mi4wMTg2QzE4LjIzNzEgNjEuODk2MiAxOC4yMTM2IDYxLjc4MTYgMTguMTY2OCA2MS42NzQ4QzE4LjEyMjUgNjEuNTY4IDE4LjA1MzUgNjEuNDgyMSAxNy45NTk3IDYxLjQxN0MxNy44Njg2IDYxLjM0OTMgMTcuNzUxNCA2MS4zMTU0IDE3LjYwODIgNjEuMzE1NEMxNy40NzAxIDYxLjMxNTQgMTcuMzU0MyA2MS4zNDkzIDE3LjI2MDUgNjEuNDE3QzE3LjE2OTQgNjEuNDgyMSAxNy4xMDAzIDYxLjU2OCAxNy4wNTM1IDYxLjY3NDhDMTcuMDA5MiA2MS43ODE2IDE2Ljk4NzEgNjEuODk2MiAxNi45ODcxIDYyLjAxODZaTTE3Ljc4NzggNTguNTM0MkwxNS4wMTA1IDYyLjk3OTVMMTQuNjA0MyA2Mi43MjE3TDE3LjM4MTYgNTguMjc2NEwxNy43ODc4IDU4LjUzNDJaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjxwYXRoIGQ9Ik04LjMxNjQxIDg5LjcwNjFWOTAuMjk5OEg0LjIwNzAzVjg5Ljg3NEw2Ljc1MzkxIDg1LjkzMjZINy4zNDM3NUw2LjcxMDk0IDg3LjA3MzJMNS4wMjczNCA4OS43MDYxSDguMzE2NDFaTTcuNTIzNDQgODUuOTMyNlY5MS42MjAxSDYuODAwNzhWODUuOTMyNkg3LjUyMzQ0Wk0xMi42NzUyIDg4LjMyMzJWODkuMTkwNEMxMi42NzUyIDg5LjY1NjYgMTIuNjMzNSA5MC4wNDk4IDEyLjU1MDIgOTAuMzcwMUMxMi40NjY4IDkwLjY5MDQgMTIuMzQ3IDkwLjk0ODIgMTIuMTkwOCA5MS4xNDM2QzEyLjAzNDUgOTEuMzM4OSAxMS44NDU3IDkxLjQ4MDggMTEuNjI0NCA5MS41NjkzQzExLjQwNTYgOTEuNjU1MyAxMS4xNTgyIDkxLjY5ODIgMTAuODgyMiA5MS42OTgyQzEwLjY2MzUgOTEuNjk4MiAxMC40NjE2IDkxLjY3MDkgMTAuMjc2NyA5MS42MTYyQzEwLjA5MTggOTEuNTYxNSA5LjkyNTE3IDkxLjQ3NDMgOS43NzY3MyA5MS4zNTQ1QzkuNjMwOSA5MS4yMzIxIDkuNTA1OSA5MS4wNzMyIDkuNDAxNzMgOTAuODc3OUM5LjI5NzU3IDkwLjY4MjYgOS4yMTgxNCA5MC40NDU2IDkuMTYzNDUgOTAuMTY3QzkuMTA4NzcgODkuODg4MyA5LjA4MTQyIDg5LjU2MjggOS4wODE0MiA4OS4xOTA0Vjg4LjMyMzJDOS4wODE0MiA4Ny44NTcxIDkuMTIzMDkgODcuNDY2NSA5LjIwNjQyIDg3LjE1MTRDOS4yOTIzNiA4Ni44MzYzIDkuNDEzNDUgODYuNTgzNyA5LjU2OTcgODYuMzkzNkM5LjcyNTk1IDg2LjIwMDggOS45MTM0NSA4Ni4wNjI4IDEwLjEzMjIgODUuOTc5NUMxMC4zNTM2IDg1Ljg5NjIgMTAuNjAxIDg1Ljg1NDUgMTAuODc0NCA4NS44NTQ1QzExLjA5NTcgODUuODU0NSAxMS4yOTg5IDg1Ljg4MTggMTEuNDgzOCA4NS45MzY1QzExLjY3MTMgODUuOTg4NiAxMS44Mzc5IDg2LjA3MzIgMTEuOTgzOCA4Ni4xOTA0QzEyLjEyOTYgODYuMzA1IDEyLjI1MzMgODYuNDU4NyAxMi4zNTQ5IDg2LjY1MTRDMTIuNDU5IDg2Ljg0MTUgMTIuNTM4NSA4Ny4wNzQ1IDEyLjU5MzEgODcuMzUwNkMxMi42NDc4IDg3LjYyNjYgMTIuNjc1MiA4Ny45NTA4IDEyLjY3NTIgODguMzIzMlpNMTEuOTQ4NiA4OS4zMDc2Vjg4LjIwMjFDMTEuOTQ4NiA4Ny45NDY5IDExLjkzMyA4Ny43MjMgMTEuOTAxNyA4Ny41MzAzQzExLjg3MzEgODcuMzM1IDExLjgzMDEgODcuMTY4MyAxMS43NzI4IDg3LjAzMDNDMTEuNzE1NSA4Ni44OTIzIDExLjY0MjYgODYuNzgwMyAxMS41NTQxIDg2LjY5NDNDMTEuNDY4MSA4Ni42MDg0IDExLjM2NzkgODYuNTQ1OSAxMS4yNTMzIDg2LjUwNjhDMTEuMTQxMyA4Ni40NjUyIDExLjAxNSA4Ni40NDQzIDEwLjg3NDQgODYuNDQ0M0MxMC43MDI1IDg2LjQ0NDMgMTAuNTUwMiA4Ni40NzY5IDEwLjQxNzQgODYuNTQyQzEwLjI4NDUgODYuNjA0NSAxMC4xNzI2IDg2LjcwNDggMTAuMDgxNCA4Ni44NDI4QzkuOTkyODggODYuOTgwOCA5LjkyNTE3IDg3LjE2MTggOS44NzgzIDg3LjM4NTdDOS44MzE0MiA4Ny42MDk3IDkuODA3OTggODcuODgxOCA5LjgwNzk4IDg4LjIwMjFWODkuMzA3NkM5LjgwNzk4IDg5LjU2MjggOS44MjIzMSA4OS43ODgxIDkuODUwOTUgODkuOTgzNEM5Ljg4MjIgOTAuMTc4NyA5LjkyNzc4IDkwLjM0OCA5Ljk4NzY3IDkwLjQ5MTJDMTAuMDQ3NiA5MC42MzE4IDEwLjEyMDUgOTAuNzQ3NyAxMC4yMDY0IDkwLjgzODlDMTAuMjkyNCA5MC45MyAxMC4zOTEzIDkwLjk5NzcgMTAuNTAzMyA5MS4wNDJDMTAuNjE3OSA5MS4wODM3IDEwLjc0NDIgOTEuMTA0NSAxMC44ODIyIDkxLjEwNDVDMTEuMDU5MyA5MS4xMDQ1IDExLjIxNDIgOTEuMDcwNiAxMS4zNDcgOTEuMDAyOUMxMS40Nzk5IDkwLjkzNTIgMTEuNTkwNSA5MC44Mjk4IDExLjY3OTEgOTAuNjg2NUMxMS43NzAyIDkwLjU0MDcgMTEuODM3OSA5MC4zNTQ1IDExLjg4MjIgOTAuMTI3OUMxMS45MjY1IDg5Ljg5ODggMTEuOTQ4NiA4OS42MjUzIDExLjk0ODYgODkuMzA3NlpNMTMuNjc0NiA4Ny4zMjcxVjg3LjAyNjRDMTMuNjc0NiA4Ni44MTAyIDEzLjcyMTQgODYuNjEzNiAxMy44MTUyIDg2LjQzNjVDMTMuOTA4OSA4Ni4yNTk0IDE0LjA0MzEgODYuMTE3NSAxNC4yMTc1IDg2LjAxMDdDMTQuMzkyIDg1LjkwNCAxNC41OTkgODUuODUwNiAxNC44Mzg2IDg1Ljg1MDZDMTUuMDgzNCA4NS44NTA2IDE1LjI5MTggODUuOTA0IDE1LjQ2MzYgODYuMDEwN0MxNS42MzgxIDg2LjExNzUgMTUuNzcyMiA4Ni4yNTk0IDE1Ljg2NiA4Ni40MzY1QzE1Ljk1OTcgODYuNjEzNiAxNi4wMDY2IDg2LjgxMDIgMTYuMDA2NiA4Ny4wMjY0Vjg3LjMyNzFDMTYuMDA2NiA4Ny41MzgxIDE1Ljk1OTcgODcuNzMyMSAxNS44NjYgODcuOTA5MkMxNS43NzQ4IDg4LjA4NjMgMTUuNjQyIDg4LjIyODIgMTUuNDY3NSA4OC4zMzVDMTUuMjk1NyA4OC40NDE3IDE1LjA4ODYgODguNDk1MSAxNC44NDY0IDg4LjQ5NTFDMTQuNjA0MyA4OC40OTUxIDE0LjM5NDYgODguNDQxNyAxNC4yMTc1IDg4LjMzNUMxNC4wNDMxIDg4LjIyODIgMTMuOTA4OSA4OC4wODYzIDEzLjgxNTIgODcuOTA5MkMxMy43MjE0IDg3LjczMjEgMTMuNjc0NiA4Ny41MzgxIDEzLjY3NDYgODcuMzI3MVpNMTQuMjE3NSA4Ny4wMjY0Vjg3LjMyNzFDMTQuMjE3NSA4Ny40NDY5IDE0LjIzOTcgODcuNTYwMiAxNC4yODM5IDg3LjY2N0MxNC4zMzA4IDg3Ljc3MzggMTQuNDAxMSA4Ny44NjEgMTQuNDk0OSA4Ny45Mjg3QzE0LjU4ODYgODcuOTkzOCAxNC43MDU4IDg4LjAyNjQgMTQuODQ2NCA4OC4wMjY0QzE0Ljk4NzEgODguMDI2NCAxNS4xMDI5IDg3Ljk5MzggMTUuMTk0MSA4Ny45Mjg3QzE1LjI4NTIgODcuODYxIDE1LjM1MjkgODcuNzczOCAxNS4zOTcyIDg3LjY2N0MxNS40NDE1IDg3LjU2MDIgMTUuNDYzNiA4Ny40NDY5IDE1LjQ2MzYgODcuMzI3MVY4Ny4wMjY0QzE1LjQ2MzYgODYuOTA0IDE1LjQ0MDIgODYuNzg5NCAxNS4zOTMzIDg2LjY4MjZDMTUuMzQ5IDg2LjU3MzIgMTUuMjggODYuNDg2IDE1LjE4NjMgODYuNDIwOUMxNS4wOTUxIDg2LjM1MzIgMTQuOTc5MyA4Ni4zMTkzIDE0LjgzODYgODYuMzE5M0MxNC43MDA2IDg2LjMxOTMgMTQuNTg0NyA4Ni4zNTMyIDE0LjQ5MSA4Ni40MjA5QzE0LjM5OTggODYuNDg2IDE0LjMzMDggODYuNTczMiAxNC4yODM5IDg2LjY4MjZDMTQuMjM5NyA4Ni43ODk0IDE0LjIxNzUgODYuOTA0IDE0LjIxNzUgODcuMDI2NFpNMTYuNDQ0MSA5MC41MzAzVjkwLjIyNTZDMTYuNDQ0MSA5MC4wMTIgMTYuNDkxIDg5LjgxNjcgMTYuNTg0NyA4OS42Mzk2QzE2LjY3ODUgODkuNDYyNiAxNi44MTI2IDg5LjMyMDYgMTYuOTg3MSA4OS4yMTM5QzE3LjE2MTUgODkuMTA3MSAxNy4zNjg2IDg5LjA1MzcgMTcuNjA4MiA4OS4wNTM3QzE3Ljg1MjkgODkuMDUzNyAxOC4wNjEzIDg5LjEwNzEgMTguMjMzMiA4OS4yMTM5QzE4LjQwNzYgODkuMzIwNiAxOC41NDE4IDg5LjQ2MjYgMTguNjM1NSA4OS42Mzk2QzE4LjcyOTMgODkuODE2NyAxOC43NzYxIDkwLjAxMiAxOC43NzYxIDkwLjIyNTZWOTAuNTMwM0MxOC43NzYxIDkwLjc0MzggMTguNzI5MyA5MC45MzkxIDE4LjYzNTUgOTEuMTE2MkMxOC41NDQ0IDkxLjI5MzMgMTguNDExNSA5MS40MzUyIDE4LjIzNzEgOTEuNTQyQzE4LjA2NTIgOTEuNjQ4OCAxNy44NTgyIDkxLjcwMjEgMTcuNjE2IDkxLjcwMjFDMTcuMzczOCA5MS43MDIxIDE3LjE2NTQgOTEuNjQ4OCAxNi45OTEgOTEuNTQyQzE2LjgxNjUgOTEuNDM1MiAxNi42ODExIDkxLjI5MzMgMTYuNTg0NyA5MS4xMTYyQzE2LjQ5MSA5MC45MzkxIDE2LjQ0NDEgOTAuNzQzOCAxNi40NDQxIDkwLjUzMDNaTTE2Ljk4NzEgOTAuMjI1NlY5MC41MzAzQzE2Ljk4NzEgOTAuNjUwMSAxNy4wMDkyIDkwLjc2NDYgMTcuMDUzNSA5MC44NzRDMTcuMTAwMyA5MC45ODA4IDE3LjE3MDcgOTEuMDY4IDE3LjI2NDQgOTEuMTM1N0MxNy4zNTgyIDkxLjIwMDggMTcuNDc1MyA5MS4yMzM0IDE3LjYxNiA5MS4yMzM0QzE3Ljc1NjYgOTEuMjMzNCAxNy44NzI1IDkxLjIwMDggMTcuOTYzNiA5MS4xMzU3QzE4LjA1NzQgOTEuMDY4IDE4LjEyNjQgOTAuOTgwOCAxOC4xNzA3IDkwLjg3NEMxOC4yMTQ5IDkwLjc2NzMgMTguMjM3MSA5MC42NTI3IDE4LjIzNzEgOTAuNTMwM1Y5MC4yMjU2QzE4LjIzNzEgOTAuMTAzMiAxOC4yMTM2IDg5Ljk4ODYgMTguMTY2OCA4OS44ODE4QzE4LjEyMjUgODkuNzc1MSAxOC4wNTM1IDg5LjY4OTEgMTcuOTU5NyA4OS42MjRDMTcuODY4NiA4OS41NTYzIDE3Ljc1MTQgODkuNTIyNSAxNy42MDgyIDg5LjUyMjVDMTcuNDcwMSA4OS41MjI1IDE3LjM1NDMgODkuNTU2MyAxNy4yNjA1IDg5LjYyNEMxNy4xNjk0IDg5LjY4OTEgMTcuMTAwMyA4OS43NzUxIDE3LjA1MzUgODkuODgxOEMxNy4wMDkyIDg5Ljk4ODYgMTYuOTg3MSA5MC4xMDMyIDE2Ljk4NzEgOTAuMjI1NlpNMTcuNzg3OCA4Ni43NDEyTDE1LjAxMDUgOTEuMTg2NUwxNC42MDQzIDkwLjkyODdMMTcuMzgxNiA4Ni40ODM0TDE3Ljc4NzggODYuNzQxMloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTguMTk5MjIgMTE5LjIzM1YxMTkuODI3SDQuNDc2NTZWMTE5LjMwOEw2LjMzOTg0IDExNy4yMzNDNi41NjkwMSAxMTYuOTc4IDYuNzQ2MDkgMTE2Ljc2MiA2Ljg3MTA5IDExNi41ODVDNi45OTg3IDExNi40MDUgNy4wODcyNCAxMTYuMjQ1IDcuMTM2NzIgMTE2LjEwNEM3LjE4ODggMTE1Ljk2MSA3LjIxNDg0IDExNS44MTUgNy4yMTQ4NCAxMTUuNjY3QzcuMjE0ODQgMTE1LjQ3OSA3LjE3NTc4IDExNS4zMSA3LjA5NzY2IDExNS4xNTlDNy4wMjIxNCAxMTUuMDA2IDYuOTEwMTYgMTE0Ljg4MyA2Ljc2MTcyIDExNC43OTJDNi42MTMyOCAxMTQuNzAxIDYuNDMzNTkgMTE0LjY1NSA2LjIyMjY2IDExNC42NTVDNS45NzAwNSAxMTQuNjU1IDUuNzU5MTEgMTE0LjcwNSA1LjU4OTg0IDExNC44MDRDNS40MjMxOCAxMTQuOSA1LjI5ODE4IDExNS4wMzUgNS4yMTQ4NCAxMTUuMjFDNS4xMzE1MSAxMTUuMzg0IDUuMDg5ODQgMTE1LjU4NSA1LjA4OTg0IDExNS44MTJINC4zNjcxOUM0LjM2NzE5IDExNS40OTEgNC40Mzc1IDExNS4xOTggNC41NzgxMiAxMTQuOTMzQzQuNzE4NzUgMTE0LjY2NyA0LjkyNzA4IDExNC40NTYgNS4yMDMxMiAxMTQuM0M1LjQ3OTE3IDExNC4xNDEgNS44MTkwMSAxMTQuMDYyIDYuMjIyNjYgMTE0LjA2MkM2LjU4MjAzIDExNC4wNjIgNi44ODkzMiAxMTQuMTI1IDcuMTQ0NTMgMTE0LjI1M0M3LjM5OTc0IDExNC4zNzggNy41OTUwNSAxMTQuNTU1IDcuNzMwNDcgMTE0Ljc4NEM3Ljg2ODQ5IDExNS4wMTEgNy45Mzc1IDExNS4yNzYgNy45Mzc1IDExNS41ODFDNy45Mzc1IDExNS43NDggNy45MDg4NSAxMTUuOTE3IDcuODUxNTYgMTE2LjA4OUM3Ljc5Njg4IDExNi4yNTggNy43MjAwNSAxMTYuNDI3IDcuNjIxMDkgMTE2LjU5N0M3LjUyNDc0IDExNi43NjYgNy40MTE0NiAxMTYuOTMzIDcuMjgxMjUgMTE3LjA5N0M3LjE1MzY1IDExNy4yNjEgNy4wMTY5MyAxMTcuNDIyIDYuODcxMDkgMTE3LjU4MUw1LjM0NzY2IDExOS4yMzNIOC4xOTkyMlpNMTIuNjc1MiAxMTYuNTNWMTE3LjM5N0MxMi42NzUyIDExNy44NjQgMTIuNjMzNSAxMTguMjU3IDEyLjU1MDIgMTE4LjU3N0MxMi40NjY4IDExOC44OTcgMTIuMzQ3IDExOS4xNTUgMTIuMTkwOCAxMTkuMzUxQzEyLjAzNDUgMTE5LjU0NiAxMS44NDU3IDExOS42ODggMTEuNjI0NCAxMTkuNzc2QzExLjQwNTYgMTE5Ljg2MiAxMS4xNTgyIDExOS45MDUgMTAuODgyMiAxMTkuOTA1QzEwLjY2MzUgMTE5LjkwNSAxMC40NjE2IDExOS44NzggMTAuMjc2NyAxMTkuODIzQzEwLjA5MTggMTE5Ljc2OSA5LjkyNTE3IDExOS42ODEgOS43NzY3MyAxMTkuNTYyQzkuNjMwOSAxMTkuNDM5IDkuNTA1OSAxMTkuMjggOS40MDE3MyAxMTkuMDg1QzkuMjk3NTcgMTE4Ljg5IDkuMjE4MTQgMTE4LjY1MyA5LjE2MzQ1IDExOC4zNzRDOS4xMDg3NyAxMTguMDk1IDkuMDgxNDIgMTE3Ljc3IDkuMDgxNDIgMTE3LjM5N1YxMTYuNTNDOS4wODE0MiAxMTYuMDY0IDkuMTIzMDkgMTE1LjY3NCA5LjIwNjQyIDExNS4zNThDOS4yOTIzNiAxMTUuMDQzIDkuNDEzNDUgMTE0Ljc5MSA5LjU2OTcgMTE0LjYwMUM5LjcyNTk1IDExNC40MDggOS45MTM0NSAxMTQuMjcgMTAuMTMyMiAxMTQuMTg3QzEwLjM1MzYgMTE0LjEwMyAxMC42MDEgMTE0LjA2MiAxMC44NzQ0IDExNC4wNjJDMTEuMDk1NyAxMTQuMDYyIDExLjI5ODkgMTE0LjA4OSAxMS40ODM4IDExNC4xNDRDMTEuNjcxMyAxMTQuMTk2IDExLjgzNzkgMTE0LjI4IDExLjk4MzggMTE0LjM5N0MxMi4xMjk2IDExNC41MTIgMTIuMjUzMyAxMTQuNjY2IDEyLjM1NDkgMTE0Ljg1OEMxMi40NTkgMTE1LjA0OSAxMi41Mzg1IDExNS4yODIgMTIuNTkzMSAxMTUuNTU4QzEyLjY0NzggMTE1LjgzNCAxMi42NzUyIDExNi4xNTggMTIuNjc1MiAxMTYuNTNaTTExLjk0ODYgMTE3LjUxNVYxMTYuNDA5QzExLjk0ODYgMTE2LjE1NCAxMS45MzMgMTE1LjkzIDExLjkwMTcgMTE1LjczN0MxMS44NzMxIDExNS41NDIgMTEuODMwMSAxMTUuMzc1IDExLjc3MjggMTE1LjIzN0MxMS43MTU1IDExNS4wOTkgMTEuNjQyNiAxMTQuOTg3IDExLjU1NDEgMTE0LjkwMUMxMS40NjgxIDExNC44MTUgMTEuMzY3OSAxMTQuNzUzIDExLjI1MzMgMTE0LjcxNEMxMS4xNDEzIDExNC42NzIgMTEuMDE1IDExNC42NTEgMTAuODc0NCAxMTQuNjUxQzEwLjcwMjUgMTE0LjY1MSAxMC41NTAyIDExNC42ODQgMTAuNDE3NCAxMTQuNzQ5QzEwLjI4NDUgMTE0LjgxMiAxMC4xNzI2IDExNC45MTIgMTAuMDgxNCAxMTUuMDVDOS45OTI4OCAxMTUuMTg4IDkuOTI1MTcgMTE1LjM2OSA5Ljg3ODMgMTE1LjU5M0M5LjgzMTQyIDExNS44MTcgOS44MDc5OCAxMTYuMDg5IDkuODA3OTggMTE2LjQwOVYxMTcuNTE1QzkuODA3OTggMTE3Ljc3IDkuODIyMzEgMTE3Ljk5NSA5Ljg1MDk1IDExOC4xOUM5Ljg4MjIgMTE4LjM4NiA5LjkyNzc4IDExOC41NTUgOS45ODc2NyAxMTguNjk4QzEwLjA0NzYgMTE4LjgzOSAxMC4xMjA1IDExOC45NTUgMTAuMjA2NCAxMTkuMDQ2QzEwLjI5MjQgMTE5LjEzNyAxMC4zOTEzIDExOS4yMDUgMTAuNTAzMyAxMTkuMjQ5QzEwLjYxNzkgMTE5LjI5MSAxMC43NDQyIDExOS4zMTIgMTAuODgyMiAxMTkuMzEyQzExLjA1OTMgMTE5LjMxMiAxMS4yMTQyIDExOS4yNzggMTEuMzQ3IDExOS4yMUMxMS40Nzk5IDExOS4xNDIgMTEuNTkwNSAxMTkuMDM3IDExLjY3OTEgMTE4Ljg5NEMxMS43NzAyIDExOC43NDggMTEuODM3OSAxMTguNTYyIDExLjg4MjIgMTE4LjMzNUMxMS45MjY1IDExOC4xMDYgMTEuOTQ4NiAxMTcuODMyIDExLjk0ODYgMTE3LjUxNVpNMTMuNjc0NiAxMTUuNTM0VjExNS4yMzNDMTMuNjc0NiAxMTUuMDE3IDEzLjcyMTQgMTE0LjgyMSAxMy44MTUyIDExNC42NDRDMTMuOTA4OSAxMTQuNDY2IDE0LjA0MzEgMTE0LjMyNSAxNC4yMTc1IDExNC4yMThDMTQuMzkyIDExNC4xMTEgMTQuNTk5IDExNC4wNTggMTQuODM4NiAxMTQuMDU4QzE1LjA4MzQgMTE0LjA1OCAxNS4yOTE4IDExNC4xMTEgMTUuNDYzNiAxMTQuMjE4QzE1LjYzODEgMTE0LjMyNSAxNS43NzIyIDExNC40NjYgMTUuODY2IDExNC42NDRDMTUuOTU5NyAxMTQuODIxIDE2LjAwNjYgMTE1LjAxNyAxNi4wMDY2IDExNS4yMzNWMTE1LjUzNEMxNi4wMDY2IDExNS43NDUgMTUuOTU5NyAxMTUuOTM5IDE1Ljg2NiAxMTYuMTE2QzE1Ljc3NDggMTE2LjI5MyAxNS42NDIgMTE2LjQzNSAxNS40Njc1IDExNi41NDJDMTUuMjk1NyAxMTYuNjQ5IDE1LjA4ODYgMTE2LjcwMiAxNC44NDY0IDExNi43MDJDMTQuNjA0MyAxMTYuNzAyIDE0LjM5NDYgMTE2LjY0OSAxNC4yMTc1IDExNi41NDJDMTQuMDQzMSAxMTYuNDM1IDEzLjkwODkgMTE2LjI5MyAxMy44MTUyIDExNi4xMTZDMTMuNzIxNCAxMTUuOTM5IDEzLjY3NDYgMTE1Ljc0NSAxMy42NzQ2IDExNS41MzRaTTE0LjIxNzUgMTE1LjIzM1YxMTUuNTM0QzE0LjIxNzUgMTE1LjY1NCAxNC4yMzk3IDExNS43NjcgMTQuMjgzOSAxMTUuODc0QzE0LjMzMDggMTE1Ljk4MSAxNC40MDExIDExNi4wNjggMTQuNDk0OSAxMTYuMTM2QzE0LjU4ODYgMTE2LjIwMSAxNC43MDU4IDExNi4yMzMgMTQuODQ2NCAxMTYuMjMzQzE0Ljk4NzEgMTE2LjIzMyAxNS4xMDI5IDExNi4yMDEgMTUuMTk0MSAxMTYuMTM2QzE1LjI4NTIgMTE2LjA2OCAxNS4zNTI5IDExNS45ODEgMTUuMzk3MiAxMTUuODc0QzE1LjQ0MTUgMTE1Ljc2NyAxNS40NjM2IDExNS42NTQgMTUuNDYzNiAxMTUuNTM0VjExNS4yMzNDMTUuNDYzNiAxMTUuMTExIDE1LjQ0MDIgMTE0Ljk5NiAxNS4zOTMzIDExNC44OUMxNS4zNDkgMTE0Ljc4IDE1LjI4IDExNC42OTMgMTUuMTg2MyAxMTQuNjI4QzE1LjA5NTEgMTE0LjU2IDE0Ljk3OTMgMTE0LjUyNiAxNC44Mzg2IDExNC41MjZDMTQuNzAwNiAxMTQuNTI2IDE0LjU4NDcgMTE0LjU2IDE0LjQ5MSAxMTQuNjI4QzE0LjM5OTggMTE0LjY5MyAxNC4zMzA4IDExNC43OCAxNC4yODM5IDExNC44OUMxNC4yMzk3IDExNC45OTYgMTQuMjE3NSAxMTUuMTExIDE0LjIxNzUgMTE1LjIzM1pNMTYuNDQ0MSAxMTguNzM3VjExOC40MzNDMTYuNDQ0MSAxMTguMjE5IDE2LjQ5MSAxMTguMDI0IDE2LjU4NDcgMTE3Ljg0N0MxNi42Nzg1IDExNy42NyAxNi44MTI2IDExNy41MjggMTYuOTg3MSAxMTcuNDIxQzE3LjE2MTUgMTE3LjMxNCAxNy4zNjg2IDExNy4yNjEgMTcuNjA4MiAxMTcuMjYxQzE3Ljg1MjkgMTE3LjI2MSAxOC4wNjEzIDExNy4zMTQgMTguMjMzMiAxMTcuNDIxQzE4LjQwNzYgMTE3LjUyOCAxOC41NDE4IDExNy42NyAxOC42MzU1IDExNy44NDdDMTguNzI5MyAxMTguMDI0IDE4Ljc3NjEgMTE4LjIxOSAxOC43NzYxIDExOC40MzNWMTE4LjczN0MxOC43NzYxIDExOC45NTEgMTguNzI5MyAxMTkuMTQ2IDE4LjYzNTUgMTE5LjMyM0MxOC41NDQ0IDExOS41IDE4LjQxMTUgMTE5LjY0MiAxOC4yMzcxIDExOS43NDlDMTguMDY1MiAxMTkuODU2IDE3Ljg1ODIgMTE5LjkwOSAxNy42MTYgMTE5LjkwOUMxNy4zNzM4IDExOS45MDkgMTcuMTY1NCAxMTkuODU2IDE2Ljk5MSAxMTkuNzQ5QzE2LjgxNjUgMTE5LjY0MiAxNi42ODExIDExOS41IDE2LjU4NDcgMTE5LjMyM0MxNi40OTEgMTE5LjE0NiAxNi40NDQxIDExOC45NTEgMTYuNDQ0MSAxMTguNzM3Wk0xNi45ODcxIDExOC40MzNWMTE4LjczN0MxNi45ODcxIDExOC44NTcgMTcuMDA5MiAxMTguOTcyIDE3LjA1MzUgMTE5LjA4MUMxNy4xMDAzIDExOS4xODggMTcuMTcwNyAxMTkuMjc1IDE3LjI2NDQgMTE5LjM0M0MxNy4zNTgyIDExOS40MDggMTcuNDc1MyAxMTkuNDQgMTcuNjE2IDExOS40NEMxNy43NTY2IDExOS40NCAxNy44NzI1IDExOS40MDggMTcuOTYzNiAxMTkuMzQzQzE4LjA1NzQgMTE5LjI3NSAxOC4xMjY0IDExOS4xODggMTguMTcwNyAxMTkuMDgxQzE4LjIxNDkgMTE4Ljk3NCAxOC4yMzcxIDExOC44NiAxOC4yMzcxIDExOC43MzdWMTE4LjQzM0MxOC4yMzcxIDExOC4zMSAxOC4yMTM2IDExOC4xOTYgMTguMTY2OCAxMTguMDg5QzE4LjEyMjUgMTE3Ljk4MiAxOC4wNTM1IDExNy44OTYgMTcuOTU5NyAxMTcuODMxQzE3Ljg2ODYgMTE3Ljc2MyAxNy43NTE0IDExNy43MjkgMTcuNjA4MiAxMTcuNzI5QzE3LjQ3MDEgMTE3LjcyOSAxNy4zNTQzIDExNy43NjMgMTcuMjYwNSAxMTcuODMxQzE3LjE2OTQgMTE3Ljg5NiAxNy4xMDAzIDExNy45ODIgMTcuMDUzNSAxMTguMDg5QzE3LjAwOTIgMTE4LjE5NiAxNi45ODcxIDExOC4zMSAxNi45ODcxIDExOC40MzNaTTE3Ljc4NzggMTE0Ljk0OEwxNS4wMTA1IDExOS4zOTRMMTQuNjA0MyAxMTkuMTM2TDE3LjM4MTYgMTE0LjY5TDE3Ljc4NzggMTE0Ljk0OFoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTEzLjA0MyAxNDQuNzM3VjE0NS42MDRDMTMuMDQzIDE0Ni4wNzEgMTMuMDAxMyAxNDYuNDY0IDEyLjkxOCAxNDYuNzg0QzEyLjgzNDYgMTQ3LjEwNCAxMi43MTQ4IDE0Ny4zNjIgMTIuNTU4NiAxNDcuNTU4QzEyLjQwMjMgMTQ3Ljc1MyAxMi4yMTM1IDE0Ny44OTUgMTEuOTkyMiAxNDcuOTgzQzExLjc3MzQgMTQ4LjA2OSAxMS41MjYgMTQ4LjExMiAxMS4yNSAxNDguMTEyQzExLjAzMTIgMTQ4LjExMiAxMC44Mjk0IDE0OC4wODUgMTAuNjQ0NSAxNDguMDNDMTAuNDU5NiAxNDcuOTc2IDEwLjI5MyAxNDcuODg4IDEwLjE0NDUgMTQ3Ljc2OUM5Ljk5ODcgMTQ3LjY0NiA5Ljg3MzcgMTQ3LjQ4NyA5Ljc2OTUzIDE0Ny4yOTJDOS42NjUzNiAxNDcuMDk3IDkuNTg1OTQgMTQ2Ljg2IDkuNTMxMjUgMTQ2LjU4MUM5LjQ3NjU2IDE0Ni4zMDIgOS40NDkyMiAxNDUuOTc3IDkuNDQ5MjIgMTQ1LjYwNFYxNDQuNzM3QzkuNDQ5MjIgMTQ0LjI3MSA5LjQ5MDg5IDE0My44ODEgOS41NzQyMiAxNDMuNTY1QzkuNjYwMTYgMTQzLjI1IDkuNzgxMjUgMTQyLjk5OCA5LjkzNzUgMTQyLjgwOEMxMC4wOTM4IDE0Mi42MTUgMTAuMjgxMiAxNDIuNDc3IDEwLjUgMTQyLjM5NEMxMC43MjE0IDE0Mi4zMSAxMC45Njg4IDE0Mi4yNjkgMTEuMjQyMiAxNDIuMjY5QzExLjQ2MzUgMTQyLjI2OSAxMS42NjY3IDE0Mi4yOTYgMTEuODUxNiAxNDIuMzUxQzEyLjAzOTEgMTQyLjQwMyAxMi4yMDU3IDE0Mi40ODcgMTIuMzUxNiAxNDIuNjA0QzEyLjQ5NzQgMTQyLjcxOSAxMi42MjExIDE0Mi44NzMgMTIuNzIyNyAxNDMuMDY1QzEyLjgyNjggMTQzLjI1NiAxMi45MDYyIDE0My40ODkgMTIuOTYwOSAxNDMuNzY1QzEzLjAxNTYgMTQ0LjA0MSAxMy4wNDMgMTQ0LjM2NSAxMy4wNDMgMTQ0LjczN1pNMTIuMzE2NCAxNDUuNzIyVjE0NC42MTZDMTIuMzE2NCAxNDQuMzYxIDEyLjMwMDggMTQ0LjEzNyAxMi4yNjk1IDE0My45NDRDMTIuMjQwOSAxNDMuNzQ5IDEyLjE5NzkgMTQzLjU4MiAxMi4xNDA2IDE0My40NDRDMTIuMDgzMyAxNDMuMzA2IDEyLjAxMDQgMTQzLjE5NCAxMS45MjE5IDE0My4xMDhDMTEuODM1OSAxNDMuMDIyIDExLjczNTcgMTQyLjk2IDExLjYyMTEgMTQyLjkyMUMxMS41MDkxIDE0Mi44NzkgMTEuMzgyOCAxNDIuODU4IDExLjI0MjIgMTQyLjg1OEMxMS4wNzAzIDE0Mi44NTggMTAuOTE4IDE0Mi44OTEgMTAuNzg1MiAxNDIuOTU2QzEwLjY1MjMgMTQzLjAxOSAxMC41NDA0IDE0My4xMTkgMTAuNDQ5MiAxNDMuMjU3QzEwLjM2MDcgMTQzLjM5NSAxMC4yOTMgMTQzLjU3NiAxMC4yNDYxIDE0My44QzEwLjE5OTIgMTQ0LjAyNCAxMC4xNzU4IDE0NC4yOTYgMTAuMTc1OCAxNDQuNjE2VjE0NS43MjJDMTAuMTc1OCAxNDUuOTc3IDEwLjE5MDEgMTQ2LjIwMiAxMC4yMTg4IDE0Ni4zOTdDMTAuMjUgMTQ2LjU5MyAxMC4yOTU2IDE0Ni43NjIgMTAuMzU1NSAxNDYuOTA1QzEwLjQxNTQgMTQ3LjA0NiAxMC40ODgzIDE0Ny4xNjIgMTAuNTc0MiAxNDcuMjUzQzEwLjY2MDIgMTQ3LjM0NCAxMC43NTkxIDE0Ny40MTIgMTAuODcxMSAxNDcuNDU2QzEwLjk4NTcgMTQ3LjQ5OCAxMS4xMTIgMTQ3LjUxOSAxMS4yNSAxNDcuNTE5QzExLjQyNzEgMTQ3LjUxOSAxMS41ODIgMTQ3LjQ4NSAxMS43MTQ4IDE0Ny40MTdDMTEuODQ3NyAxNDcuMzQ5IDExLjk1ODMgMTQ3LjI0NCAxMi4wNDY5IDE0Ny4xMDFDMTIuMTM4IDE0Ni45NTUgMTIuMjA1NyAxNDYuNzY5IDEyLjI1IDE0Ni41NDJDMTIuMjk0MyAxNDYuMzEzIDEyLjMxNjQgMTQ2LjAzOSAxMi4zMTY0IDE0NS43MjJaTTE0LjA0MjQgMTQzLjc0MVYxNDMuNDRDMTQuMDQyNCAxNDMuMjI0IDE0LjA4OTIgMTQzLjAyOCAxNC4xODMgMTQyLjg1MUMxNC4yNzY3IDE0Mi42NzQgMTQuNDEwOCAxNDIuNTMyIDE0LjU4NTMgMTQyLjQyNUMxNC43NTk4IDE0Mi4zMTggMTQuOTY2OCAxNDIuMjY1IDE1LjIwNjQgMTQyLjI2NUMxNS40NTEyIDE0Mi4yNjUgMTUuNjU5NSAxNDIuMzE4IDE1LjgzMTQgMTQyLjQyNUMxNi4wMDU5IDE0Mi41MzIgMTYuMTQgMTQyLjY3NCAxNi4yMzM4IDE0Mi44NTFDMTYuMzI3NSAxNDMuMDI4IDE2LjM3NDQgMTQzLjIyNCAxNi4zNzQ0IDE0My40NFYxNDMuNzQxQzE2LjM3NDQgMTQzLjk1MiAxNi4zMjc1IDE0NC4xNDYgMTYuMjMzOCAxNDQuMzIzQzE2LjE0MjYgMTQ0LjUgMTYuMDA5OCAxNDQuNjQyIDE1LjgzNTMgMTQ0Ljc0OUMxNS42NjM1IDE0NC44NTYgMTUuNDU2NCAxNDQuOTA5IDE1LjIxNDIgMTQ0LjkwOUMxNC45NzIgMTQ0LjkwOSAxNC43NjI0IDE0NC44NTYgMTQuNTg1MyAxNDQuNzQ5QzE0LjQxMDggMTQ0LjY0MiAxNC4yNzY3IDE0NC41IDE0LjE4MyAxNDQuMzIzQzE0LjA4OTIgMTQ0LjE0NiAxNC4wNDI0IDE0My45NTIgMTQuMDQyNCAxNDMuNzQxWk0xNC41ODUzIDE0My40NFYxNDMuNzQxQzE0LjU4NTMgMTQzLjg2MSAxNC42MDc1IDE0My45NzQgMTQuNjUxNyAxNDQuMDgxQzE0LjY5ODYgMTQ0LjE4OCAxNC43Njg5IDE0NC4yNzUgMTQuODYyNyAxNDQuMzQzQzE0Ljk1NjQgMTQ0LjQwOCAxNS4wNzM2IDE0NC40NCAxNS4yMTQyIDE0NC40NEMxNS4zNTQ5IDE0NC40NCAxNS40NzA3IDE0NC40MDggMTUuNTYxOSAxNDQuMzQzQzE1LjY1MyAxNDQuMjc1IDE1LjcyMDcgMTQ0LjE4OCAxNS43NjUgMTQ0LjA4MUMxNS44MDkzIDE0My45NzQgMTUuODMxNCAxNDMuODYxIDE1LjgzMTQgMTQzLjc0MVYxNDMuNDRDMTUuODMxNCAxNDMuMzE4IDE1LjgwOCAxNDMuMjAzIDE1Ljc2MTEgMTQzLjA5N0MxNS43MTY4IDE0Mi45ODcgMTUuNjQ3OCAxNDIuOSAxNS41NTQxIDE0Mi44MzVDMTUuNDYyOSAxNDIuNzY3IDE1LjM0NyAxNDIuNzMzIDE1LjIwNjQgMTQyLjczM0MxNS4wNjg0IDE0Mi43MzMgMTQuOTUyNSAxNDIuNzY3IDE0Ljg1ODggMTQyLjgzNUMxNC43Njc2IDE0Mi45IDE0LjY5ODYgMTQyLjk4NyAxNC42NTE3IDE0My4wOTdDMTQuNjA3NSAxNDMuMjAzIDE0LjU4NTMgMTQzLjMxOCAxNC41ODUzIDE0My40NFpNMTYuODExOSAxNDYuOTQ0VjE0Ni42NEMxNi44MTE5IDE0Ni40MjYgMTYuODU4OCAxNDYuMjMxIDE2Ljk1MjUgMTQ2LjA1NEMxNy4wNDYzIDE0NS44NzcgMTcuMTgwNCAxNDUuNzM1IDE3LjM1NDkgMTQ1LjYyOEMxNy41MjkzIDE0NS41MjEgMTcuNzM2NCAxNDUuNDY4IDE3Ljk3NiAxNDUuNDY4QzE4LjIyMDcgMTQ1LjQ2OCAxOC40MjkxIDE0NS41MjEgMTguNjAxIDE0NS42MjhDMTguNzc1NCAxNDUuNzM1IDE4LjkwOTUgMTQ1Ljg3NyAxOS4wMDMzIDE0Ni4wNTRDMTkuMDk3IDE0Ni4yMzEgMTkuMTQzOSAxNDYuNDI2IDE5LjE0MzkgMTQ2LjY0VjE0Ni45NDRDMTkuMTQzOSAxNDcuMTU4IDE5LjA5NyAxNDcuMzUzIDE5LjAwMzMgMTQ3LjUzQzE4LjkxMjIgMTQ3LjcwNyAxOC43NzkzIDE0Ny44NDkgMTguNjA0OSAxNDcuOTU2QzE4LjQzMyAxNDguMDYzIDE4LjIyNiAxNDguMTE2IDE3Ljk4MzggMTQ4LjExNkMxNy43NDE2IDE0OC4xMTYgMTcuNTMzMiAxNDguMDYzIDE3LjM1ODggMTQ3Ljk1NkMxNy4xODQzIDE0Ny44NDkgMTcuMDQ4OSAxNDcuNzA3IDE2Ljk1MjUgMTQ3LjUzQzE2Ljg1ODggMTQ3LjM1MyAxNi44MTE5IDE0Ny4xNTggMTYuODExOSAxNDYuOTQ0Wk0xNy4zNTQ5IDE0Ni42NFYxNDYuOTQ0QzE3LjM1NDkgMTQ3LjA2NCAxNy4zNzcgMTQ3LjE3OSAxNy40MjEzIDE0Ny4yODhDMTcuNDY4MSAxNDcuMzk1IDE3LjUzODUgMTQ3LjQ4MiAxNy42MzIyIDE0Ny41NUMxNy43MjYgMTQ3LjYxNSAxNy44NDMxIDE0Ny42NDcgMTcuOTgzOCAxNDcuNjQ3QzE4LjEyNDQgMTQ3LjY0NyAxOC4yNDAzIDE0Ny42MTUgMTguMzMxNCAxNDcuNTVDMTguNDI1MiAxNDcuNDgyIDE4LjQ5NDIgMTQ3LjM5NSAxOC41Mzg1IDE0Ny4yODhDMTguNTgyNyAxNDcuMTgxIDE4LjYwNDkgMTQ3LjA2NyAxOC42MDQ5IDE0Ni45NDRWMTQ2LjY0QzE4LjYwNDkgMTQ2LjUxNyAxOC41ODE0IDE0Ni40MDMgMTguNTM0NSAxNDYuMjk2QzE4LjQ5MDMgMTQ2LjE4OSAxOC40MjEzIDE0Ni4xMDMgMTguMzI3NSAxNDYuMDM4QzE4LjIzNjQgMTQ1Ljk3IDE4LjExOTIgMTQ1LjkzNyAxNy45NzYgMTQ1LjkzN0MxNy44Mzc5IDE0NS45MzcgMTcuNzIyIDE0NS45NyAxNy42MjgzIDE0Ni4wMzhDMTcuNTM3MiAxNDYuMTAzIDE3LjQ2ODEgMTQ2LjE4OSAxNy40MjEzIDE0Ni4yOTZDMTcuMzc3IDE0Ni40MDMgMTcuMzU0OSAxNDYuNTE3IDE3LjM1NDkgMTQ2LjY0Wk0xOC4xNTU2IDE0My4xNTVMMTUuMzc4MyAxNDcuNjAxTDE0Ljk3MiAxNDcuMzQzTDE3Ljc0OTQgMTQyLjg5N0wxOC4xNTU2IDE0My4xNTVaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjxwYXRoIGQ9Ik0xMzggNThDMTM4IDU2Ljg5NTQgMTM4Ljg5NSA1NiAxNDAgNTZIMTU0QzE1NS4xMDUgNTYgMTU2IDU2Ljg5NTQgMTU2IDU4VjE0NkgxMzhWNThaIiBmaWxsPSIjM0ZBNzFBIi8+CjxwYXRoIGQ9Ik0yNSA0LjE2MTEzTDIwMCA0LjE2MTE2IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMjUgMzMuMTYxMUwyMDAgMzMuMTYxMiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTI1IDYxLjE2MTFMMjAwIDYxLjE2MTIiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIi8+CjxwYXRoIGQ9Ik0yNSA4OS4xNjExTDIwMCA4OS4xNjEyIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMjUgMTE4LjE2MUwyMDAgMTE4LjE2MSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPGxpbmUgeDE9IjIzLjIiIHkxPSIxNDUuOTYxIiB4Mj0iMjAyLjgiIHkyPSIxNDUuOTYxIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC43IiBzdHJva2Utd2lkdGg9IjAuNCIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8bGluZSB4MT0iNDAuNDUwMiIgeTE9IjE0OC4wNzIiIHgyPSI0MC40NTAyIiB5Mj0iMTQ3LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNMzIuNTA5MSAxNTIuMDI1VjE1Mi44OTNDMzIuNTA5MSAxNTMuMzU5IDMyLjQ2NzQgMTUzLjc1MiAzMi4zODQxIDE1NC4wNzJDMzIuMzAwNyAxNTQuMzkzIDMyLjE4MDkgMTU0LjY1IDMyLjAyNDcgMTU0Ljg0NkMzMS44Njg0IDE1NS4wNDEgMzEuNjc5NiAxNTUuMTgzIDMxLjQ1ODMgMTU1LjI3MUMzMS4yMzk1IDE1NS4zNTcgMzAuOTkyMSAxNTUuNCAzMC43MTYxIDE1NS40QzMwLjQ5NzMgMTU1LjQgMzAuMjk1NSAxNTUuMzczIDMwLjExMDYgMTU1LjMxOEMyOS45MjU3IDE1NS4yNjQgMjkuNzU5MSAxNTUuMTc2IDI5LjYxMDYgMTU1LjA1N0MyOS40NjQ4IDE1NC45MzQgMjkuMzM5OCAxNTQuNzc1IDI5LjIzNTYgMTU0LjU4QzI5LjEzMTUgMTU0LjM4NSAyOS4wNTIgMTU0LjE0OCAyOC45OTczIDE1My44NjlDMjguOTQyNyAxNTMuNTkgMjguOTE1MyAxNTMuMjY1IDI4LjkxNTMgMTUyLjg5M1YxNTIuMDI1QzI4LjkxNTMgMTUxLjU1OSAyOC45NTcgMTUxLjE2OSAyOS4wNDAzIDE1MC44NTRDMjkuMTI2MiAxNTAuNTM4IDI5LjI0NzMgMTUwLjI4NiAyOS40MDM2IDE1MC4wOTZDMjkuNTU5OCAxNDkuOTAzIDI5Ljc0NzMgMTQ5Ljc2NSAyOS45NjYxIDE0OS42ODJDMzAuMTg3NCAxNDkuNTk4IDMwLjQzNDggMTQ5LjU1NyAzMC43MDgzIDE0OS41NTdDMzAuOTI5NiAxNDkuNTU3IDMxLjEzMjggMTQ5LjU4NCAzMS4zMTc3IDE0OS42MzlDMzEuNTA1MiAxNDkuNjkxIDMxLjY3MTggMTQ5Ljc3NSAzMS44MTc3IDE0OS44OTNDMzEuOTYzNSAxNTAuMDA3IDMyLjA4NzIgMTUwLjE2MSAzMi4xODg3IDE1MC4zNTRDMzIuMjkyOSAxNTAuNTQ0IDMyLjM3MjMgMTUwLjc3NyAzMi40MjcgMTUxLjA1M0MzMi40ODE3IDE1MS4zMjkgMzIuNTA5MSAxNTEuNjUzIDMyLjUwOTEgMTUyLjAyNVpNMzEuNzgyNSAxNTMuMDFWMTUxLjkwNEMzMS43ODI1IDE1MS42NDkgMzEuNzY2OSAxNTEuNDI1IDMxLjczNTYgMTUxLjIzMkMzMS43MDcgMTUxLjAzNyAzMS42NjQgMTUwLjg3IDMxLjYwNjcgMTUwLjczMkMzMS41NDk0IDE1MC41OTQgMzEuNDc2NSAxNTAuNDgyIDMxLjM4OCAxNTAuMzk2QzMxLjMwMiAxNTAuMzExIDMxLjIwMTggMTUwLjI0OCAzMS4wODcyIDE1MC4yMDlDMzAuOTc1MiAxNTAuMTY3IDMwLjg0ODkgMTUwLjE0NiAzMC43MDgzIDE1MC4xNDZDMzAuNTM2NCAxNTAuMTQ2IDMwLjM4NDEgMTUwLjE3OSAzMC4yNTEyIDE1MC4yNDRDMzAuMTE4NCAxNTAuMzA3IDMwLjAwNjUgMTUwLjQwNyAyOS45MTUzIDE1MC41NDVDMjkuODI2OCAxNTAuNjgzIDI5Ljc1OTEgMTUwLjg2NCAyOS43MTIyIDE1MS4wODhDMjkuNjY1MyAxNTEuMzEyIDI5LjY0MTkgMTUxLjU4NCAyOS42NDE5IDE1MS45MDRWMTUzLjAxQzI5LjY0MTkgMTUzLjI2NSAyOS42NTYyIDE1My40OSAyOS42ODQ4IDE1My42ODZDMjkuNzE2MSAxNTMuODgxIDI5Ljc2MTcgMTU0LjA1IDI5LjgyMTYgMTU0LjE5M0MyOS44ODE1IDE1NC4zMzQgMjkuOTU0NCAxNTQuNDUgMzAuMDQwMyAxNTQuNTQxQzMwLjEyNjIgMTU0LjYzMiAzMC4yMjUyIDE1NC43IDMwLjMzNzIgMTU0Ljc0NEMzMC40NTE4IDE1NC43ODYgMzAuNTc4MSAxNTQuODA3IDMwLjcxNjEgMTU0LjgwN0MzMC44OTMyIDE1NC44MDcgMzEuMDQ4MSAxNTQuNzczIDMxLjE4MDkgMTU0LjcwNUMzMS4zMTM3IDE1NC42MzcgMzEuNDI0NCAxNTQuNTMyIDMxLjUxMyAxNTQuMzg5QzMxLjYwNDEgMTU0LjI0MyAzMS42NzE4IDE1NC4wNTcgMzEuNzE2MSAxNTMuODNDMzEuNzYwNCAxNTMuNjAxIDMxLjc4MjUgMTUzLjMyNyAzMS43ODI1IDE1My4wMVpNMzUuODk2NCAxNDkuNjA0VjE1NS4zMjJIMzUuMTczN1YxNTAuNTA2TDMzLjcxNjcgMTUxLjAzN1YxNTAuMzg1TDM1Ljc4MzEgMTQ5LjYwNEgzNS44OTY0Wk00MS4xMTI0IDE0OS42MzVWMTU1LjMyMkg0MC4zNTg1VjE0OS42MzVINDEuMTEyNFpNNDMuNDk1MiAxNTIuMTkzVjE1Mi44MTFINDAuOTQ4M1YxNTIuMTkzSDQzLjQ5NTJaTTQzLjg4MTkgMTQ5LjYzNVYxNTAuMjUySDQwLjk0ODNWMTQ5LjYzNUg0My44ODE5Wk00Ni40MjE2IDE1NS40QzQ2LjEyNzMgMTU1LjQgNDUuODYwNCAxNTUuMzUxIDQ1LjYyMDggMTU1LjI1MkM0NS4zODM4IDE1NS4xNSA0NS4xNzk0IDE1NS4wMDggNDUuMDA3NSAxNTQuODI2QzQ0LjgzODMgMTU0LjY0NCA0NC43MDgxIDE1NC40MjggNDQuNjE2OSAxNTQuMTc4QzQ0LjUyNTggMTUzLjkyOCA0NC40ODAyIDE1My42NTQgNDQuNDgwMiAxNTMuMzU3VjE1My4xOTNDNDQuNDgwMiAxNTIuODUgNDQuNTMxIDE1Mi41NDQgNDQuNjMyNSAxNTIuMjc1QzQ0LjczNDEgMTUyLjAwNSA0NC44NzIxIDE1MS43NzUgNDUuMDQ2NiAxNTEuNTg4QzQ1LjIyMTEgMTUxLjQgNDUuNDE5IDE1MS4yNTggNDUuNjQwMyAxNTEuMTYyQzQ1Ljg2MTcgMTUxLjA2NiA0Ni4wOTA5IDE1MS4wMTggNDYuMzI3OCAxNTEuMDE4QzQ2LjYyOTkgMTUxLjAxOCA0Ni44OTAzIDE1MS4wNyA0Ny4xMDkxIDE1MS4xNzRDNDcuMzMwNSAxNTEuMjc4IDQ3LjUxMTQgMTUxLjQyNCA0Ny42NTIxIDE1MS42MTFDNDcuNzkyNyAxNTEuNzk2IDQ3Ljg5NjkgMTUyLjAxNSA0Ny45NjQ2IDE1Mi4yNjhDNDguMDMyMyAxNTIuNTE4IDQ4LjA2NjEgMTUyLjc5MSA0OC4wNjYxIDE1My4wODhWMTUzLjQxMkg0NC45MDk5VjE1Mi44MjJINDcuMzQzNVYxNTIuNzY4QzQ3LjMzMzEgMTUyLjU4IDQ3LjI5NCAxNTIuMzk4IDQ3LjIyNjMgMTUyLjIyMUM0Ny4xNjEyIDE1Mi4wNDQgNDcuMDU3IDE1MS44OTggNDYuOTEzOCAxNTEuNzgzQzQ2Ljc3MDYgMTUxLjY2OSA0Ni41NzUyIDE1MS42MTEgNDYuMzI3OCAxNTEuNjExQzQ2LjE2MzggMTUxLjYxMSA0Ni4wMTI3IDE1MS42NDYgNDUuODc0NyAxNTEuNzE3QzQ1LjczNjcgMTUxLjc4NSA0NS42MTgyIDE1MS44ODYgNDUuNTE5MyAxNTIuMDIxQzQ1LjQyMDMgMTUyLjE1NyA0NS4zNDM1IDE1Mi4zMjIgNDUuMjg4OCAxNTIuNTE4QzQ1LjIzNDEgMTUyLjcxMyA0NS4yMDY4IDE1Mi45MzggNDUuMjA2OCAxNTMuMTkzVjE1My4zNTdDNDUuMjA2OCAxNTMuNTU4IDQ1LjIzNDEgMTUzLjc0NyA0NS4yODg4IDE1My45MjRDNDUuMzQ2MSAxNTQuMDk4IDQ1LjQyODEgMTU0LjI1MiA0NS41MzQ5IDE1NC4zODVDNDUuNjQ0MyAxNTQuNTE4IDQ1Ljc3NTggMTU0LjYyMiA0NS45Mjk0IDE1NC42OTdDNDYuMDg1NyAxNTQuNzczIDQ2LjI2MjcgMTU0LjgxMSA0Ni40NjA3IDE1NC44MTFDNDYuNzE1OSAxNTQuODExIDQ2LjkzMiAxNTQuNzU4IDQ3LjEwOTEgMTU0LjY1NEM0Ny4yODYyIDE1NC41NSA0Ny40NDExIDE1NC40MTEgNDcuNTczOSAxNTQuMjM2TDQ4LjAxMTQgMTU0LjU4NEM0Ny45MjAzIDE1NC43MjIgNDcuODA0NCAxNTQuODU0IDQ3LjY2MzggMTU0Ljk3OUM0Ny41MjMyIDE1NS4xMDQgNDcuMzUgMTU1LjIwNSA0Ny4xNDQzIDE1NS4yODNDNDYuOTQxMSAxNTUuMzYxIDQ2LjcwMDIgMTU1LjQgNDYuNDIxNiAxNTUuNFpNNDguOTg4NiAxNDkuMzIySDQ5LjcxNTJWMTU0LjUwMkw0OS42NTI3IDE1NS4zMjJINDguOTg4NlYxNDkuMzIyWk01Mi41NzA2IDE1My4xNzRWMTUzLjI1NkM1Mi41NzA2IDE1My41NjMgNTIuNTM0MiAxNTMuODQ4IDUyLjQ2MTMgMTU0LjExMUM1Mi4zODgzIDE1NC4zNzIgNTIuMjgxNiAxNTQuNTk4IDUyLjE0MDkgMTU0Ljc5MUM1Mi4wMDAzIDE1NC45ODQgNTEuODI4NCAxNTUuMTMzIDUxLjYyNTMgMTU1LjI0QzUxLjQyMjIgMTU1LjM0NyA1MS4xODkxIDE1NS40IDUwLjkyNjEgMTU1LjRDNTAuNjU3OSAxNTUuNCA1MC40MjIyIDE1NS4zNTUgNTAuMjE5MSAxNTUuMjY0QzUwLjAxODUgMTU1LjE3IDQ5Ljg0OTMgMTU1LjAzNiA0OS43MTEzIDE1NC44NjFDNDkuNTczMiAxNTQuNjg3IDQ5LjQ2MjYgMTU0LjQ3NiA0OS4zNzkyIDE1NC4yMjlDNDkuMjk4NSAxNTMuOTgxIDQ5LjI0MjUgMTUzLjcwMiA0OS4yMTEzIDE1My4zOTNWMTUzLjAzM0M0OS4yNDI1IDE1Mi43MjEgNDkuMjk4NSAxNTIuNDQxIDQ5LjM3OTIgMTUyLjE5M0M0OS40NjI2IDE1MS45NDYgNDkuNTczMiAxNTEuNzM1IDQ5LjcxMTMgMTUxLjU2MUM0OS44NDkzIDE1MS4zODMgNTAuMDE4NSAxNTEuMjQ5IDUwLjIxOTEgMTUxLjE1OEM1MC40MTk2IDE1MS4wNjQgNTAuNjUyNyAxNTEuMDE4IDUwLjkxODMgMTUxLjAxOEM1MS4xODM5IDE1MS4wMTggNTEuNDE5NiAxNTEuMDcgNTEuNjI1MyAxNTEuMTc0QzUxLjgzMSAxNTEuMjc1IDUyLjAwMjkgMTUxLjQyMSA1Mi4xNDA5IDE1MS42MTFDNTIuMjgxNiAxNTEuODAxIDUyLjM4ODMgMTUyLjAyOSA1Mi40NjEzIDE1Mi4yOTVDNTIuNTM0MiAxNTIuNTU4IDUyLjU3MDYgMTUyLjg1MSA1Mi41NzA2IDE1My4xNzRaTTUxLjg0NDEgMTUzLjI1NlYxNTMuMTc0QzUxLjg0NDEgMTUyLjk2MyA1MS44MjQ1IDE1Mi43NjUgNTEuNzg1NSAxNTIuNThDNTEuNzQ2NCAxNTIuMzkzIDUxLjY4MzkgMTUyLjIyOSA1MS41OTggMTUyLjA4OEM1MS41MTIgMTUxLjk0NSA1MS4zOTg4IDE1MS44MzMgNTEuMjU4MSAxNTEuNzUyQzUxLjExNzUgMTUxLjY2OSA1MC45NDQzIDE1MS42MjcgNTAuNzM4NiAxNTEuNjI3QzUwLjU1NjMgMTUxLjYyNyA1MC4zOTc1IDE1MS42NTggNTAuMjYyIDE1MS43MjFDNTAuMTI5MiAxNTEuNzgzIDUwLjAxNTkgMTUxLjg2OCA0OS45MjIyIDE1MS45NzVDNDkuODI4NCAxNTIuMDc5IDQ5Ljc1MTYgMTUyLjE5OSA0OS42OTE3IDE1Mi4zMzRDNDkuNjM0NCAxNTIuNDY3IDQ5LjU5MTUgMTUyLjYwNSA0OS41NjI4IDE1Mi43NDhWMTUzLjY4OUM0OS42MDQ1IDE1My44NzIgNDkuNjcyMiAxNTQuMDQ4IDQ5Ljc2NTkgMTU0LjIxN0M0OS44NjIzIDE1NC4zODMgNDkuOTg5OSAxNTQuNTIgNTAuMTQ4OCAxNTQuNjI3QzUwLjMxMDIgMTU0LjczNCA1MC41MDk0IDE1NC43ODcgNTAuNzQ2NCAxNTQuNzg3QzUwLjk0MTcgMTU0Ljc4NyA1MS4xMDg0IDE1NC43NDggNTEuMjQ2NCAxNTQuNjdDNTEuMzg3IDE1NC41ODkgNTEuNTAwMyAxNTQuNDc5IDUxLjU4NjMgMTU0LjMzOEM1MS42NzQ4IDE1NC4xOTcgNTEuNzM5OSAxNTQuMDM1IDUxLjc4MTYgMTUzLjg1QzUxLjgyMzIgMTUzLjY2NSA1MS44NDQxIDE1My40NjcgNTEuODQ0MSAxNTMuMjU2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDFfNDA1M18xODUxMTMpIj4KPGxpbmUgeDE9Ijc1Ljg1MDEiIHkxPSIxNDcuMDcyIiB4Mj0iNzUuODUwMSIgeTI9IjE0Ni4yNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNSIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPHBhdGggZD0iTTY3LjkwOSAxNTIuMDI1VjE1Mi44OTNDNjcuOTA5IDE1My4zNTkgNjcuODY3MyAxNTMuNzUyIDY3Ljc4NCAxNTQuMDcyQzY3LjcwMDYgMTU0LjM5MyA2Ny41ODA4IDE1NC42NSA2Ny40MjQ2IDE1NC44NDZDNjcuMjY4MyAxNTUuMDQxIDY3LjA3OTUgMTU1LjE4MyA2Ni44NTgyIDE1NS4yNzFDNjYuNjM5NCAxNTUuMzU3IDY2LjM5MiAxNTUuNCA2Ni4xMTYgMTU1LjRDNjUuODk3MiAxNTUuNCA2NS42OTU0IDE1NS4zNzMgNjUuNTEwNSAxNTUuMzE4QzY1LjMyNTYgMTU1LjI2NCA2NS4xNTkgMTU1LjE3NiA2NS4wMTA1IDE1NS4wNTdDNjQuODY0NyAxNTQuOTM0IDY0LjczOTcgMTU0Ljc3NSA2NC42MzU1IDE1NC41OEM2NC41MzE0IDE1NC4zODUgNjQuNDUxOSAxNTQuMTQ4IDY0LjM5NzIgMTUzLjg2OUM2NC4zNDI2IDE1My41OSA2NC4zMTUyIDE1My4yNjUgNjQuMzE1MiAxNTIuODkzVjE1Mi4wMjVDNjQuMzE1MiAxNTEuNTU5IDY0LjM1NjkgMTUxLjE2OSA2NC40NDAyIDE1MC44NTRDNjQuNTI2MSAxNTAuNTM4IDY0LjY0NzIgMTUwLjI4NiA2NC44MDM1IDE1MC4wOTZDNjQuOTU5NyAxNDkuOTAzIDY1LjE0NzIgMTQ5Ljc2NSA2NS4zNjYgMTQ5LjY4MkM2NS41ODczIDE0OS41OTggNjUuODM0NyAxNDkuNTU3IDY2LjEwODIgMTQ5LjU1N0M2Ni4zMjk1IDE0OS41NTcgNjYuNTMyNyAxNDkuNTg0IDY2LjcxNzYgMTQ5LjYzOUM2Ni45MDUxIDE0OS42OTEgNjcuMDcxNyAxNDkuNzc1IDY3LjIxNzYgMTQ5Ljg5M0M2Ny4zNjM0IDE1MC4wMDcgNjcuNDg3MSAxNTAuMTYxIDY3LjU4ODYgMTUwLjM1NEM2Ny42OTI4IDE1MC41NDQgNjcuNzcyMiAxNTAuNzc3IDY3LjgyNjkgMTUxLjA1M0M2Ny44ODE2IDE1MS4zMjkgNjcuOTA5IDE1MS42NTMgNjcuOTA5IDE1Mi4wMjVaTTY3LjE4MjQgMTUzLjAxVjE1MS45MDRDNjcuMTgyNCAxNTEuNjQ5IDY3LjE2NjggMTUxLjQyNSA2Ny4xMzU1IDE1MS4yMzJDNjcuMTA2OSAxNTEuMDM3IDY3LjA2MzkgMTUwLjg3IDY3LjAwNjYgMTUwLjczMkM2Ni45NDkzIDE1MC41OTQgNjYuODc2NCAxNTAuNDgyIDY2Ljc4NzkgMTUwLjM5NkM2Ni43MDE5IDE1MC4zMTEgNjYuNjAxNyAxNTAuMjQ4IDY2LjQ4NzEgMTUwLjIwOUM2Ni4zNzUxIDE1MC4xNjcgNjYuMjQ4OCAxNTAuMTQ2IDY2LjEwODIgMTUwLjE0NkM2NS45MzYzIDE1MC4xNDYgNjUuNzg0IDE1MC4xNzkgNjUuNjUxMSAxNTAuMjQ0QzY1LjUxODMgMTUwLjMwNyA2NS40MDY0IDE1MC40MDcgNjUuMzE1MiAxNTAuNTQ1QzY1LjIyNjcgMTUwLjY4MyA2NS4xNTkgMTUwLjg2NCA2NS4xMTIxIDE1MS4wODhDNjUuMDY1MiAxNTEuMzEyIDY1LjA0MTggMTUxLjU4NCA2NS4wNDE4IDE1MS45MDRWMTUzLjAxQzY1LjA0MTggMTUzLjI2NSA2NS4wNTYxIDE1My40OSA2NS4wODQ3IDE1My42ODZDNjUuMTE2IDE1My44ODEgNjUuMTYxNiAxNTQuMDUgNjUuMjIxNSAxNTQuMTkzQzY1LjI4MTQgMTU0LjMzNCA2NS4zNTQzIDE1NC40NSA2NS40NDAyIDE1NC41NDFDNjUuNTI2MSAxNTQuNjMyIDY1LjYyNTEgMTU0LjcgNjUuNzM3MSAxNTQuNzQ0QzY1Ljg1MTcgMTU0Ljc4NiA2NS45NzggMTU0LjgwNyA2Ni4xMTYgMTU0LjgwN0M2Ni4yOTMxIDE1NC44MDcgNjYuNDQ4IDE1NC43NzMgNjYuNTgwOCAxNTQuNzA1QzY2LjcxMzYgMTU0LjYzNyA2Ni44MjQzIDE1NC41MzIgNjYuOTEyOSAxNTQuMzg5QzY3LjAwNCAxNTQuMjQzIDY3LjA3MTcgMTU0LjA1NyA2Ny4xMTYgMTUzLjgzQzY3LjE2MDMgMTUzLjYwMSA2Ny4xODI0IDE1My4zMjcgNjcuMTgyNCAxNTMuMDFaTTcyLjY0NzggMTU0LjcyOVYxNTUuMzIySDY4LjkyNTJWMTU0LjgwM0w3MC43ODg1IDE1Mi43MjlDNzEuMDE3NiAxNTIuNDczIDcxLjE5NDcgMTUyLjI1NyA3MS4zMTk3IDE1Mi4wOEM3MS40NDczIDE1MS45IDcxLjUzNTkgMTUxLjc0IDcxLjU4NTMgMTUxLjZDNzEuNjM3NCAxNTEuNDU2IDcxLjY2MzUgMTUxLjMxMSA3MS42NjM1IDE1MS4xNjJDNzEuNjYzNSAxNTAuOTc1IDcxLjYyNDQgMTUwLjgwNSA3MS41NDYzIDE1MC42NTRDNzEuNDcwOCAxNTAuNTAxIDcxLjM1ODggMTUwLjM3OCA3MS4yMTAzIDE1MC4yODdDNzEuMDYxOSAxNTAuMTk2IDcwLjg4MjIgMTUwLjE1IDcwLjY3MTMgMTUwLjE1QzcwLjQxODcgMTUwLjE1IDcwLjIwNzcgMTUwLjIgNzAuMDM4NSAxNTAuMjk5QzY5Ljg3MTggMTUwLjM5NSA2OS43NDY4IDE1MC41MzEgNjkuNjYzNSAxNTAuNzA1QzY5LjU4MDEgMTUwLjg4IDY5LjUzODUgMTUxLjA4IDY5LjUzODUgMTUxLjMwN0g2OC44MTU4QzY4LjgxNTggMTUwLjk4NiA2OC44ODYxIDE1MC42OTMgNjkuMDI2NyAxNTAuNDI4QzY5LjE2NzQgMTUwLjE2MiA2OS4zNzU3IDE0OS45NTEgNjkuNjUxNyAxNDkuNzk1QzY5LjkyNzggMTQ5LjYzNiA3MC4yNjc2IDE0OS41NTcgNzAuNjcxMyAxNDkuNTU3QzcxLjAzMDcgMTQ5LjU1NyA3MS4zMzc5IDE0OS42MiA3MS41OTMyIDE0OS43NDhDNzEuODQ4NCAxNDkuODczIDcyLjA0MzcgMTUwLjA1IDcyLjE3OTEgMTUwLjI3OUM3Mi4zMTcxIDE1MC41MDYgNzIuMzg2MSAxNTAuNzcxIDcyLjM4NjEgMTUxLjA3NkM3Mi4zODYxIDE1MS4yNDMgNzIuMzU3NSAxNTEuNDEyIDcyLjMwMDIgMTUxLjU4NEM3Mi4yNDU1IDE1MS43NTMgNzIuMTY4NyAxNTEuOTIzIDcyLjA2OTcgMTUyLjA5MkM3MS45NzM0IDE1Mi4yNjEgNzEuODYwMSAxNTIuNDI4IDcxLjcyOTkgMTUyLjU5MkM3MS42MDIzIDE1Mi43NTYgNzEuNDY1NSAxNTIuOTE3IDcxLjMxOTcgMTUzLjA3Nkw2OS43OTYzIDE1NC43MjlINzIuNjQ3OFpNNzYuNTEyMyAxNDkuNjM1VjE1NS4zMjJINzUuNzU4NFYxNDkuNjM1SDc2LjUxMjNaTTc4Ljg5NTEgMTUyLjE5M1YxNTIuODExSDc2LjM0ODJWMTUyLjE5M0g3OC44OTUxWk03OS4yODE4IDE0OS42MzVWMTUwLjI1Mkg3Ni4zNDgyVjE0OS42MzVINzkuMjgxOFpNODEuODIxNSAxNTUuNEM4MS41MjcyIDE1NS40IDgxLjI2MDMgMTU1LjM1MSA4MS4wMjA3IDE1NS4yNTJDODAuNzgzNyAxNTUuMTUgODAuNTc5MyAxNTUuMDA4IDgwLjQwNzQgMTU0LjgyNkM4MC4yMzgyIDE1NC42NDQgODAuMTA4IDE1NC40MjggODAuMDE2OCAxNTQuMTc4Qzc5LjkyNTcgMTUzLjkyOCA3OS44ODAxIDE1My42NTQgNzkuODgwMSAxNTMuMzU3VjE1My4xOTNDNzkuODgwMSAxNTIuODUgNzkuOTMwOSAxNTIuNTQ0IDgwLjAzMjQgMTUyLjI3NUM4MC4xMzQgMTUyLjAwNSA4MC4yNzIgMTUxLjc3NSA4MC40NDY1IDE1MS41ODhDODAuNjIxIDE1MS40IDgwLjgxODkgMTUxLjI1OCA4MS4wNDAyIDE1MS4xNjJDODEuMjYxNiAxNTEuMDY2IDgxLjQ5MDggMTUxLjAxOCA4MS43Mjc3IDE1MS4wMThDODIuMDI5OCAxNTEuMDE4IDgyLjI5MDIgMTUxLjA3IDgyLjUwOSAxNTEuMTc0QzgyLjczMDQgMTUxLjI3OCA4Mi45MTEzIDE1MS40MjQgODMuMDUyIDE1MS42MTFDODMuMTkyNiAxNTEuNzk2IDgzLjI5NjggMTUyLjAxNSA4My4zNjQ1IDE1Mi4yNjhDODMuNDMyMiAxNTIuNTE4IDgzLjQ2NiAxNTIuNzkxIDgzLjQ2NiAxNTMuMDg4VjE1My40MTJIODAuMzA5OFYxNTIuODIySDgyLjc0MzRWMTUyLjc2OEM4Mi43MzMgMTUyLjU4IDgyLjY5MzkgMTUyLjM5OCA4Mi42MjYyIDE1Mi4yMjFDODIuNTYxMSAxNTIuMDQ0IDgyLjQ1NjkgMTUxLjg5OCA4Mi4zMTM3IDE1MS43ODNDODIuMTcwNSAxNTEuNjY5IDgxLjk3NTEgMTUxLjYxMSA4MS43Mjc3IDE1MS42MTFDODEuNTYzNyAxNTEuNjExIDgxLjQxMjYgMTUxLjY0NiA4MS4yNzQ2IDE1MS43MTdDODEuMTM2NiAxNTEuNzg1IDgxLjAxODEgMTUxLjg4NiA4MC45MTkyIDE1Mi4wMjFDODAuODIwMiAxNTIuMTU3IDgwLjc0MzQgMTUyLjMyMiA4MC42ODg3IDE1Mi41MThDODAuNjM0IDE1Mi43MTMgODAuNjA2NyAxNTIuOTM4IDgwLjYwNjcgMTUzLjE5M1YxNTMuMzU3QzgwLjYwNjcgMTUzLjU1OCA4MC42MzQgMTUzLjc0NyA4MC42ODg3IDE1My45MjRDODAuNzQ2IDE1NC4wOTggODAuODI4IDE1NC4yNTIgODAuOTM0OCAxNTQuMzg1QzgxLjA0NDIgMTU0LjUxOCA4MS4xNzU3IDE1NC42MjIgODEuMzI5MyAxNTQuNjk3QzgxLjQ4NTYgMTU0Ljc3MyA4MS42NjI2IDE1NC44MTEgODEuODYwNiAxNTQuODExQzgyLjExNTggMTU0LjgxMSA4Mi4zMzE5IDE1NC43NTggODIuNTA5IDE1NC42NTRDODIuNjg2MSAxNTQuNTUgODIuODQxIDE1NC40MTEgODIuOTczOCAxNTQuMjM2TDgzLjQxMTMgMTU0LjU4NEM4My4zMjAyIDE1NC43MjIgODMuMjA0MyAxNTQuODU0IDgzLjA2MzcgMTU0Ljk3OUM4Mi45MjMxIDE1NS4xMDQgODIuNzQ5OSAxNTUuMjA1IDgyLjU0NDIgMTU1LjI4M0M4Mi4zNDEgMTU1LjM2MSA4Mi4xMDAxIDE1NS40IDgxLjgyMTUgMTU1LjRaTTg0LjM4ODUgMTQ5LjMyMkg4NS4xMTUxVjE1NC41MDJMODUuMDUyNiAxNTUuMzIySDg0LjM4ODVWMTQ5LjMyMlpNODcuOTcwNSAxNTMuMTc0VjE1My4yNTZDODcuOTcwNSAxNTMuNTYzIDg3LjkzNDEgMTUzLjg0OCA4Ny44NjEyIDE1NC4xMTFDODcuNzg4MiAxNTQuMzcyIDg3LjY4MTUgMTU0LjU5OCA4Ny41NDA4IDE1NC43OTFDODcuNDAwMiAxNTQuOTg0IDg3LjIyODMgMTU1LjEzMyA4Ny4wMjUyIDE1NS4yNEM4Ni44MjIxIDE1NS4zNDcgODYuNTg5IDE1NS40IDg2LjMyNiAxNTUuNEM4Ni4wNTc4IDE1NS40IDg1LjgyMjEgMTU1LjM1NSA4NS42MTkgMTU1LjI2NEM4NS40MTg1IDE1NS4xNyA4NS4yNDkyIDE1NS4wMzYgODUuMTExMiAxNTQuODYxQzg0Ljk3MzEgMTU0LjY4NyA4NC44NjI1IDE1NC40NzYgODQuNzc5MSAxNTQuMjI5Qzg0LjY5ODQgMTUzLjk4MSA4NC42NDI0IDE1My43MDIgODQuNjExMiAxNTMuMzkzVjE1My4wMzNDODQuNjQyNCAxNTIuNzIxIDg0LjY5ODQgMTUyLjQ0MSA4NC43NzkxIDE1Mi4xOTNDODQuODYyNSAxNTEuOTQ2IDg0Ljk3MzEgMTUxLjczNSA4NS4xMTEyIDE1MS41NjFDODUuMjQ5MiAxNTEuMzgzIDg1LjQxODUgMTUxLjI0OSA4NS42MTkgMTUxLjE1OEM4NS44MTk1IDE1MS4wNjQgODYuMDUyNiAxNTEuMDE4IDg2LjMxODIgMTUxLjAxOEM4Ni41ODM4IDE1MS4wMTggODYuODE5NSAxNTEuMDcgODcuMDI1MiAxNTEuMTc0Qzg3LjIzMSAxNTEuMjc1IDg3LjQwMjggMTUxLjQyMSA4Ny41NDA4IDE1MS42MTFDODcuNjgxNSAxNTEuODAxIDg3Ljc4ODIgMTUyLjAyOSA4Ny44NjEyIDE1Mi4yOTVDODcuOTM0MSAxNTIuNTU4IDg3Ljk3MDUgMTUyLjg1MSA4Ny45NzA1IDE1My4xNzRaTTg3LjI0NCAxNTMuMjU2VjE1My4xNzRDODcuMjQ0IDE1Mi45NjMgODcuMjI0NCAxNTIuNzY1IDg3LjE4NTQgMTUyLjU4Qzg3LjE0NjMgMTUyLjM5MyA4Ny4wODM4IDE1Mi4yMjkgODYuOTk3OSAxNTIuMDg4Qzg2LjkxMTkgMTUxLjk0NSA4Ni43OTg3IDE1MS44MzMgODYuNjU4IDE1MS43NTJDODYuNTE3NCAxNTEuNjY5IDg2LjM0NDIgMTUxLjYyNyA4Ni4xMzg1IDE1MS42MjdDODUuOTU2MiAxNTEuNjI3IDg1Ljc5NzQgMTUxLjY1OCA4NS42NjE5IDE1MS43MjFDODUuNTI5MSAxNTEuNzgzIDg1LjQxNTggMTUxLjg2OCA4NS4zMjIxIDE1MS45NzVDODUuMjI4MyAxNTIuMDc5IDg1LjE1MTUgMTUyLjE5OSA4NS4wOTE2IDE1Mi4zMzRDODUuMDM0MyAxNTIuNDY3IDg0Ljk5MTQgMTUyLjYwNSA4NC45NjI3IDE1Mi43NDhWMTUzLjY4OUM4NS4wMDQ0IDE1My44NzIgODUuMDcyMSAxNTQuMDQ4IDg1LjE2NTggMTU0LjIxN0M4NS4yNjIyIDE1NC4zODMgODUuMzg5OCAxNTQuNTIgODUuNTQ4NyAxNTQuNjI3Qzg1LjcxMDEgMTU0LjczNCA4NS45MDkzIDE1NC43ODcgODYuMTQ2MyAxNTQuNzg3Qzg2LjM0MTYgMTU0Ljc4NyA4Ni41MDgzIDE1NC43NDggODYuNjQ2MyAxNTQuNjdDODYuNzg2OSAxNTQuNTg5IDg2LjkwMDIgMTU0LjQ3OSA4Ni45ODYyIDE1NC4zMzhDODcuMDc0NyAxNTQuMTk3IDg3LjEzOTggMTU0LjAzNSA4Ny4xODE1IDE1My44NUM4Ny4yMjMxIDE1My42NjUgODcuMjQ0IDE1My40NjcgODcuMjQ0IDE1My4yNTZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjwvZz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAyXzQwNTNfMTg1MTEzKSI+CjxsaW5lIHgxPSIxMTEuMjUiIHkxPSIxNDcuMDcyIiB4Mj0iMTExLjI1IiB5Mj0iMTQ2LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNMTAzLjMwOSAxNTIuMDI1VjE1Mi44OTNDMTAzLjMwOSAxNTMuMzU5IDEwMy4yNjcgMTUzLjc1MiAxMDMuMTg0IDE1NC4wNzJDMTAzLjEwMSAxNTQuMzkzIDEwMi45ODEgMTU0LjY1IDEwMi44MjQgMTU0Ljg0NkMxMDIuNjY4IDE1NS4wNDEgMTAyLjQ3OSAxNTUuMTgzIDEwMi4yNTggMTU1LjI3MUMxMDIuMDM5IDE1NS4zNTcgMTAxLjc5MiAxNTUuNCAxMDEuNTE2IDE1NS40QzEwMS4yOTcgMTU1LjQgMTAxLjA5NSAxNTUuMzczIDEwMC45MSAxNTUuMzE4QzEwMC43MjYgMTU1LjI2NCAxMDAuNTU5IDE1NS4xNzYgMTAwLjQxIDE1NS4wNTdDMTAwLjI2NSAxNTQuOTM0IDEwMC4xNCAxNTQuNzc1IDEwMC4wMzUgMTU0LjU4Qzk5LjkzMTMgMTU0LjM4NSA5OS44NTE4IDE1NC4xNDggOTkuNzk3MSAxNTMuODY5Qzk5Ljc0MjUgMTUzLjU5IDk5LjcxNTEgMTUzLjI2NSA5OS43MTUxIDE1Mi44OTNWMTUyLjAyNUM5OS43MTUxIDE1MS41NTkgOTkuNzU2OCAxNTEuMTY5IDk5Ljg0MDEgMTUwLjg1NEM5OS45MjYxIDE1MC41MzggMTAwLjA0NyAxNTAuMjg2IDEwMC4yMDMgMTUwLjA5NkMxMDAuMzYgMTQ5LjkwMyAxMDAuNTQ3IDE0OS43NjUgMTAwLjc2NiAxNDkuNjgyQzEwMC45ODcgMTQ5LjU5OCAxMDEuMjM1IDE0OS41NTcgMTAxLjUwOCAxNDkuNTU3QzEwMS43MjkgMTQ5LjU1NyAxMDEuOTMzIDE0OS41ODQgMTAyLjExNyAxNDkuNjM5QzEwMi4zMDUgMTQ5LjY5MSAxMDIuNDcyIDE0OS43NzUgMTAyLjYxNyAxNDkuODkzQzEwMi43NjMgMTUwLjAwNyAxMDIuODg3IDE1MC4xNjEgMTAyLjk4OSAxNTAuMzU0QzEwMy4wOTMgMTUwLjU0NCAxMDMuMTcyIDE1MC43NzcgMTAzLjIyNyAxNTEuMDUzQzEwMy4yODIgMTUxLjMyOSAxMDMuMzA5IDE1MS42NTMgMTAzLjMwOSAxNTIuMDI1Wk0xMDIuNTgyIDE1My4wMVYxNTEuOTA0QzEwMi41ODIgMTUxLjY0OSAxMDIuNTY3IDE1MS40MjUgMTAyLjUzNSAxNTEuMjMyQzEwMi41MDcgMTUxLjAzNyAxMDIuNDY0IDE1MC44NyAxMDIuNDA3IDE1MC43MzJDMTAyLjM0OSAxNTAuNTk0IDEwMi4yNzYgMTUwLjQ4MiAxMDIuMTg4IDE1MC4zOTZDMTAyLjEwMiAxNTAuMzExIDEwMi4wMDIgMTUwLjI0OCAxMDEuODg3IDE1MC4yMDlDMTAxLjc3NSAxNTAuMTY3IDEwMS42NDkgMTUwLjE0NiAxMDEuNTA4IDE1MC4xNDZDMTAxLjMzNiAxNTAuMTQ2IDEwMS4xODQgMTUwLjE3OSAxMDEuMDUxIDE1MC4yNDRDMTAwLjkxOCAxNTAuMzA3IDEwMC44MDYgMTUwLjQwNyAxMDAuNzE1IDE1MC41NDVDMTAwLjYyNyAxNTAuNjgzIDEwMC41NTkgMTUwLjg2NCAxMDAuNTEyIDE1MS4wODhDMTAwLjQ2NSAxNTEuMzEyIDEwMC40NDIgMTUxLjU4NCAxMDAuNDQyIDE1MS45MDRWMTUzLjAxQzEwMC40NDIgMTUzLjI2NSAxMDAuNDU2IDE1My40OSAxMDAuNDg1IDE1My42ODZDMTAwLjUxNiAxNTMuODgxIDEwMC41NjEgMTU0LjA1IDEwMC42MjEgMTU0LjE5M0MxMDAuNjgxIDE1NC4zMzQgMTAwLjc1NCAxNTQuNDUgMTAwLjg0IDE1NC41NDFDMTAwLjkyNiAxNTQuNjMyIDEwMS4wMjUgMTU0LjcgMTAxLjEzNyAxNTQuNzQ0QzEwMS4yNTIgMTU0Ljc4NiAxMDEuMzc4IDE1NC44MDcgMTAxLjUxNiAxNTQuODA3QzEwMS42OTMgMTU0LjgwNyAxMDEuODQ4IDE1NC43NzMgMTAxLjk4MSAxNTQuNzA1QzEwMi4xMTQgMTU0LjYzNyAxMDIuMjI0IDE1NC41MzIgMTAyLjMxMyAxNTQuMzg5QzEwMi40MDQgMTU0LjI0MyAxMDIuNDcyIDE1NC4wNTcgMTAyLjUxNiAxNTMuODNDMTAyLjU2IDE1My42MDEgMTAyLjU4MiAxNTMuMzI3IDEwMi41ODIgMTUzLjAxWk0xMDUuMzc2IDE1Mi4xMjNIMTA1Ljg5MUMxMDYuMTQ0IDE1Mi4xMjMgMTA2LjM1MiAxNTIuMDgxIDEwNi41MTYgMTUxLjk5OEMxMDYuNjgzIDE1MS45MTIgMTA2LjgwNyAxNTEuNzk2IDEwNi44ODggMTUxLjY1QzEwNi45NzEgMTUxLjUwMiAxMDcuMDEzIDE1MS4zMzUgMTA3LjAxMyAxNTEuMTVDMTA3LjAxMyAxNTAuOTMyIDEwNi45NzYgMTUwLjc0OCAxMDYuOTAzIDE1MC42QzEwNi44MyAxNTAuNDUxIDEwNi43MjEgMTUwLjMzOSAxMDYuNTc1IDE1MC4yNjRDMTA2LjQyOSAxNTAuMTg4IDEwNi4yNDQgMTUwLjE1IDEwNi4wMiAxNTAuMTVDMTA1LjgxNyAxNTAuMTUgMTA1LjYzOCAxNTAuMTkxIDEwNS40ODEgMTUwLjI3MUMxMDUuMzI4IDE1MC4zNSAxMDUuMjA3IDE1MC40NjIgMTA1LjExOCAxNTAuNjA3QzEwNS4wMzIgMTUwLjc1MyAxMDQuOTg5IDE1MC45MjUgMTA0Ljk4OSAxNTEuMTIzSDEwNC4yNjZDMTA0LjI2NiAxNTAuODM0IDEwNC4zMzkgMTUwLjU3MSAxMDQuNDg1IDE1MC4zMzRDMTA0LjYzMSAxNTAuMDk3IDEwNC44MzYgMTQ5LjkwOCAxMDUuMDk5IDE0OS43NjhDMTA1LjM2NCAxNDkuNjI3IDEwNS42NzEgMTQ5LjU1NyAxMDYuMDIgMTQ5LjU1N0MxMDYuMzY0IDE0OS41NTcgMTA2LjY2NSAxNDkuNjE4IDEwNi45MjMgMTQ5Ljc0QzEwNy4xODEgMTQ5Ljg2IDEwNy4zODEgMTUwLjA0IDEwNy41MjQgMTUwLjI3OUMxMDcuNjY4IDE1MC41MTYgMTA3LjczOSAxNTAuODEyIDEwNy43MzkgMTUxLjE2NkMxMDcuNzM5IDE1MS4zMDkgMTA3LjcwNSAxNTEuNDYzIDEwNy42MzggMTUxLjYyN0MxMDcuNTcyIDE1MS43ODggMTA3LjQ3IDE1MS45MzkgMTA3LjMyOSAxNTIuMDhDMTA3LjE5MSAxNTIuMjIxIDEwNy4wMTEgMTUyLjMzNyAxMDYuNzkgMTUyLjQyOEMxMDYuNTY5IDE1Mi41MTYgMTA2LjMwMyAxNTIuNTYxIDEwNS45OTMgMTUyLjU2MUgxMDUuMzc2VjE1Mi4xMjNaTTEwNS4zNzYgMTUyLjcxN1YxNTIuMjgzSDEwNS45OTNDMTA2LjM1NSAxNTIuMjgzIDEwNi42NTUgMTUyLjMyNiAxMDYuODkxIDE1Mi40MTJDMTA3LjEyOCAxNTIuNDk4IDEwNy4zMTUgMTUyLjYxMyAxMDcuNDUgMTUyLjc1NkMxMDcuNTg4IDE1Mi44OTkgMTA3LjY4NCAxNTMuMDU3IDEwNy43MzkgMTUzLjIyOUMxMDcuNzk2IDE1My4zOTggMTA3LjgyNSAxNTMuNTY3IDEwNy44MjUgMTUzLjczNkMxMDcuODI1IDE1NC4wMDIgMTA3Ljc4IDE1NC4yMzggMTA3LjY4OCAxNTQuNDQzQzEwNy42IDE1NC42NDkgMTA3LjQ3NCAxNTQuODI0IDEwNy4zMDkgMTU0Ljk2N0MxMDcuMTQ4IDE1NS4xMSAxMDYuOTU4IDE1NS4yMTggMTA2LjczOSAxNTUuMjkxQzEwNi41MiAxNTUuMzY0IDEwNi4yODIgMTU1LjQgMTA2LjAyNCAxNTUuNEMxMDUuNzc3IDE1NS40IDEwNS41NDQgMTU1LjM2NSAxMDUuMzI1IDE1NS4yOTVDMTA1LjEwOSAxNTUuMjI1IDEwNC45MTggMTU1LjEyMyAxMDQuNzUxIDE1NC45OUMxMDQuNTg0IDE1NC44NTUgMTA0LjQ1NCAxNTQuNjg5IDEwNC4zNiAxNTQuNDk0QzEwNC4yNjYgMTU0LjI5NiAxMDQuMjIgMTU0LjA3MSAxMDQuMjIgMTUzLjgxOEgxMDQuOTQyQzEwNC45NDIgMTU0LjAxNiAxMDQuOTg1IDE1NC4xODkgMTA1LjA3MSAxNTQuMzM4QzEwNS4xNiAxNTQuNDg2IDEwNS4yODUgMTU0LjYwMiAxMDUuNDQ2IDE1NC42ODZDMTA1LjYxIDE1NC43NjYgMTA1LjgwMyAxNTQuODA3IDEwNi4wMjQgMTU0LjgwN0MxMDYuMjQ2IDE1NC44MDcgMTA2LjQzNiAxNTQuNzY5IDEwNi41OTUgMTU0LjY5M0MxMDYuNzU2IDE1NC42MTUgMTA2Ljg4IDE1NC40OTggMTA2Ljk2NiAxNTQuMzQyQzEwNy4wNTQgMTU0LjE4NiAxMDcuMDk5IDE1My45ODkgMTA3LjA5OSAxNTMuNzUyQzEwNy4wOTkgMTUzLjUxNSAxMDcuMDQ5IDE1My4zMjEgMTA2Ljk1IDE1My4xN0MxMDYuODUxIDE1My4wMTYgMTA2LjcxMSAxNTIuOTAzIDEwNi41MjggMTUyLjgzQzEwNi4zNDkgMTUyLjc1NSAxMDYuMTM2IDE1Mi43MTcgMTA1Ljg5MSAxNTIuNzE3SDEwNS4zNzZaTTExMS45MTIgMTQ5LjYzNVYxNTUuMzIySDExMS4xNThWMTQ5LjYzNUgxMTEuOTEyWk0xMTQuMjk1IDE1Mi4xOTNWMTUyLjgxMUgxMTEuNzQ4VjE1Mi4xOTNIMTE0LjI5NVpNMTE0LjY4MiAxNDkuNjM1VjE1MC4yNTJIMTExLjc0OFYxNDkuNjM1SDExNC42ODJaTTExNy4yMjEgMTU1LjRDMTE2LjkyNyAxNTUuNCAxMTYuNjYgMTU1LjM1MSAxMTYuNDIxIDE1NS4yNTJDMTE2LjE4NCAxNTUuMTUgMTE1Ljk3OSAxNTUuMDA4IDExNS44MDcgMTU0LjgyNkMxMTUuNjM4IDE1NC42NDQgMTE1LjUwOCAxNTQuNDI4IDExNS40MTcgMTU0LjE3OEMxMTUuMzI2IDE1My45MjggMTE1LjI4IDE1My42NTQgMTE1LjI4IDE1My4zNTdWMTUzLjE5M0MxMTUuMjggMTUyLjg1IDExNS4zMzEgMTUyLjU0NCAxMTUuNDMyIDE1Mi4yNzVDMTE1LjUzNCAxNTIuMDA1IDExNS42NzIgMTUxLjc3NSAxMTUuODQ2IDE1MS41ODhDMTE2LjAyMSAxNTEuNCAxMTYuMjE5IDE1MS4yNTggMTE2LjQ0IDE1MS4xNjJDMTE2LjY2MiAxNTEuMDY2IDExNi44OTEgMTUxLjAxOCAxMTcuMTI4IDE1MS4wMThDMTE3LjQzIDE1MS4wMTggMTE3LjY5IDE1MS4wNyAxMTcuOTA5IDE1MS4xNzRDMTE4LjEzIDE1MS4yNzggMTE4LjMxMSAxNTEuNDI0IDExOC40NTIgMTUxLjYxMUMxMTguNTkyIDE1MS43OTYgMTE4LjY5NyAxNTIuMDE1IDExOC43NjQgMTUyLjI2OEMxMTguODMyIDE1Mi41MTggMTE4Ljg2NiAxNTIuNzkxIDExOC44NjYgMTUzLjA4OFYxNTMuNDEySDExNS43MVYxNTIuODIySDExOC4xNDNWMTUyLjc2OEMxMTguMTMzIDE1Mi41OCAxMTguMDk0IDE1Mi4zOTggMTE4LjAyNiAxNTIuMjIxQzExNy45NjEgMTUyLjA0NCAxMTcuODU3IDE1MS44OTggMTE3LjcxNCAxNTEuNzgzQzExNy41NyAxNTEuNjY5IDExNy4zNzUgMTUxLjYxMSAxMTcuMTI4IDE1MS42MTFDMTE2Ljk2NCAxNTEuNjExIDExNi44MTMgMTUxLjY0NiAxMTYuNjc1IDE1MS43MTdDMTE2LjUzNyAxNTEuNzg1IDExNi40MTggMTUxLjg4NiAxMTYuMzE5IDE1Mi4wMjFDMTE2LjIyIDE1Mi4xNTcgMTE2LjE0MyAxNTIuMzIyIDExNi4wODkgMTUyLjUxOEMxMTYuMDM0IDE1Mi43MTMgMTE2LjAwNyAxNTIuOTM4IDExNi4wMDcgMTUzLjE5M1YxNTMuMzU3QzExNi4wMDcgMTUzLjU1OCAxMTYuMDM0IDE1My43NDcgMTE2LjA4OSAxNTMuOTI0QzExNi4xNDYgMTU0LjA5OCAxMTYuMjI4IDE1NC4yNTIgMTE2LjMzNSAxNTQuMzg1QzExNi40NDQgMTU0LjUxOCAxMTYuNTc2IDE1NC42MjIgMTE2LjcyOSAxNTQuNjk3QzExNi44ODUgMTU0Ljc3MyAxMTcuMDYzIDE1NC44MTEgMTE3LjI2IDE1NC44MTFDMTE3LjUxNiAxNTQuODExIDExNy43MzIgMTU0Ljc1OCAxMTcuOTA5IDE1NC42NTRDMTE4LjA4NiAxNTQuNTUgMTE4LjI0MSAxNTQuNDExIDExOC4zNzQgMTU0LjIzNkwxMTguODExIDE1NC41ODRDMTE4LjcyIDE1NC43MjIgMTE4LjYwNCAxNTQuODU0IDExOC40NjQgMTU0Ljk3OUMxMTguMzIzIDE1NS4xMDQgMTE4LjE1IDE1NS4yMDUgMTE3Ljk0NCAxNTUuMjgzQzExNy43NDEgMTU1LjM2MSAxMTcuNSAxNTUuNCAxMTcuMjIxIDE1NS40Wk0xMTkuNzg4IDE0OS4zMjJIMTIwLjUxNVYxNTQuNTAyTDEyMC40NTIgMTU1LjMyMkgxMTkuNzg4VjE0OS4zMjJaTTEyMy4zNyAxNTMuMTc0VjE1My4yNTZDMTIzLjM3IDE1My41NjMgMTIzLjMzNCAxNTMuODQ4IDEyMy4yNjEgMTU0LjExMUMxMjMuMTg4IDE1NC4zNzIgMTIzLjA4MSAxNTQuNTk4IDEyMi45NDEgMTU0Ljc5MUMxMjIuOCAxNTQuOTg0IDEyMi42MjggMTU1LjEzMyAxMjIuNDI1IDE1NS4yNEMxMjIuMjIyIDE1NS4zNDcgMTIxLjk4OSAxNTUuNCAxMjEuNzI2IDE1NS40QzEyMS40NTggMTU1LjQgMTIxLjIyMiAxNTUuMzU1IDEyMS4wMTkgMTU1LjI2NEMxMjAuODE4IDE1NS4xNyAxMjAuNjQ5IDE1NS4wMzYgMTIwLjUxMSAxNTQuODYxQzEyMC4zNzMgMTU0LjY4NyAxMjAuMjYyIDE1NC40NzYgMTIwLjE3OSAxNTQuMjI5QzEyMC4wOTggMTUzLjk4MSAxMjAuMDQyIDE1My43MDIgMTIwLjAxMSAxNTMuMzkzVjE1My4wMzNDMTIwLjA0MiAxNTIuNzIxIDEyMC4wOTggMTUyLjQ0MSAxMjAuMTc5IDE1Mi4xOTNDMTIwLjI2MiAxNTEuOTQ2IDEyMC4zNzMgMTUxLjczNSAxMjAuNTExIDE1MS41NjFDMTIwLjY0OSAxNTEuMzgzIDEyMC44MTggMTUxLjI0OSAxMjEuMDE5IDE1MS4xNThDMTIxLjIxOSAxNTEuMDY0IDEyMS40NTIgMTUxLjAxOCAxMjEuNzE4IDE1MS4wMThDMTIxLjk4NCAxNTEuMDE4IDEyMi4yMTkgMTUxLjA3IDEyMi40MjUgMTUxLjE3NEMxMjIuNjMxIDE1MS4yNzUgMTIyLjgwMyAxNTEuNDIxIDEyMi45NDEgMTUxLjYxMUMxMjMuMDgxIDE1MS44MDEgMTIzLjE4OCAxNTIuMDI5IDEyMy4yNjEgMTUyLjI5NUMxMjMuMzM0IDE1Mi41NTggMTIzLjM3IDE1Mi44NTEgMTIzLjM3IDE1My4xNzRaTTEyMi42NDQgMTUzLjI1NlYxNTMuMTc0QzEyMi42NDQgMTUyLjk2MyAxMjIuNjI0IDE1Mi43NjUgMTIyLjU4NSAxNTIuNThDMTIyLjU0NiAxNTIuMzkzIDEyMi40ODQgMTUyLjIyOSAxMjIuMzk4IDE1Mi4wODhDMTIyLjMxMiAxNTEuOTQ1IDEyMi4xOTkgMTUxLjgzMyAxMjIuMDU4IDE1MS43NTJDMTIxLjkxNyAxNTEuNjY5IDEyMS43NDQgMTUxLjYyNyAxMjEuNTM4IDE1MS42MjdDMTIxLjM1NiAxNTEuNjI3IDEyMS4xOTcgMTUxLjY1OCAxMjEuMDYyIDE1MS43MjFDMTIwLjkyOSAxNTEuNzgzIDEyMC44MTYgMTUxLjg2OCAxMjAuNzIyIDE1MS45NzVDMTIwLjYyOCAxNTIuMDc5IDEyMC41NTEgMTUyLjE5OSAxMjAuNDkyIDE1Mi4zMzRDMTIwLjQzNCAxNTIuNDY3IDEyMC4zOTEgMTUyLjYwNSAxMjAuMzYzIDE1Mi43NDhWMTUzLjY4OUMxMjAuNDA0IDE1My44NzIgMTIwLjQ3MiAxNTQuMDQ4IDEyMC41NjYgMTU0LjIxN0MxMjAuNjYyIDE1NC4zODMgMTIwLjc5IDE1NC41MiAxMjAuOTQ5IDE1NC42MjdDMTIxLjExIDE1NC43MzQgMTIxLjMwOSAxNTQuNzg3IDEyMS41NDYgMTU0Ljc4N0MxMjEuNzQyIDE1NC43ODcgMTIxLjkwOCAxNTQuNzQ4IDEyMi4wNDYgMTU0LjY3QzEyMi4xODcgMTU0LjU4OSAxMjIuMyAxNTQuNDc5IDEyMi4zODYgMTU0LjMzOEMxMjIuNDc1IDE1NC4xOTcgMTIyLjU0IDE1NC4wMzUgMTIyLjU4MSAxNTMuODVDMTIyLjYyMyAxNTMuNjY1IDEyMi42NDQgMTUzLjQ2NyAxMjIuNjQ0IDE1My4yNTZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjwvZz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAzXzQwNTNfMTg1MTEzKSI+CjxsaW5lIHgxPSIxNDYuNjUiIHkxPSIxNDcuMDcyIiB4Mj0iMTQ2LjY1IiB5Mj0iMTQ2LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNMTM4LjcwOSAxNTIuMDI1VjE1Mi44OTNDMTM4LjcwOSAxNTMuMzU5IDEzOC42NjggMTUzLjc1MiAxMzguNTg0IDE1NC4wNzJDMTM4LjUwMSAxNTQuMzkzIDEzOC4zODEgMTU0LjY1IDEzOC4yMjUgMTU0Ljg0NkMxMzguMDY5IDE1NS4wNDEgMTM3Ljg4IDE1NS4xODMgMTM3LjY1OCAxNTUuMjcxQzEzNy40NCAxNTUuMzU3IDEzNy4xOTIgMTU1LjQgMTM2LjkxNiAxNTUuNEMxMzYuNjk4IDE1NS40IDEzNi40OTYgMTU1LjM3MyAxMzYuMzExIDE1NS4zMThDMTM2LjEyNiAxNTUuMjY0IDEzNS45NTkgMTU1LjE3NiAxMzUuODExIDE1NS4wNTdDMTM1LjY2NSAxNTQuOTM0IDEzNS41NCAxNTQuNzc1IDEzNS40MzYgMTU0LjU4QzEzNS4zMzIgMTU0LjM4NSAxMzUuMjUyIDE1NC4xNDggMTM1LjE5OCAxNTMuODY5QzEzNS4xNDMgMTUzLjU5IDEzNS4xMTYgMTUzLjI2NSAxMzUuMTE2IDE1Mi44OTNWMTUyLjAyNUMxMzUuMTE2IDE1MS41NTkgMTM1LjE1NyAxNTEuMTY5IDEzNS4yNDEgMTUwLjg1NEMxMzUuMzI2IDE1MC41MzggMTM1LjQ0OCAxNTAuMjg2IDEzNS42MDQgMTUwLjA5NkMxMzUuNzYgMTQ5LjkwMyAxMzUuOTQ4IDE0OS43NjUgMTM2LjE2NiAxNDkuNjgyQzEzNi4zODggMTQ5LjU5OCAxMzYuNjM1IDE0OS41NTcgMTM2LjkwOCAxNDkuNTU3QzEzNy4xMyAxNDkuNTU3IDEzNy4zMzMgMTQ5LjU4NCAxMzcuNTE4IDE0OS42MzlDMTM3LjcwNSAxNDkuNjkxIDEzNy44NzIgMTQ5Ljc3NSAxMzguMDE4IDE0OS44OTNDMTM4LjE2NCAxNTAuMDA3IDEzOC4yODcgMTUwLjE2MSAxMzguMzg5IDE1MC4zNTRDMTM4LjQ5MyAxNTAuNTQ0IDEzOC41NzMgMTUwLjc3NyAxMzguNjI3IDE1MS4wNTNDMTM4LjY4MiAxNTEuMzI5IDEzOC43MDkgMTUxLjY1MyAxMzguNzA5IDE1Mi4wMjVaTTEzNy45ODMgMTUzLjAxVjE1MS45MDRDMTM3Ljk4MyAxNTEuNjQ5IDEzNy45NjcgMTUxLjQyNSAxMzcuOTM2IDE1MS4yMzJDMTM3LjkwNyAxNTEuMDM3IDEzNy44NjQgMTUwLjg3IDEzNy44MDcgMTUwLjczMkMxMzcuNzUgMTUwLjU5NCAxMzcuNjc3IDE1MC40ODIgMTM3LjU4OCAxNTAuMzk2QzEzNy41MDIgMTUwLjMxMSAxMzcuNDAyIDE1MC4yNDggMTM3LjI4NyAxNTAuMjA5QzEzNy4xNzUgMTUwLjE2NyAxMzcuMDQ5IDE1MC4xNDYgMTM2LjkwOCAxNTAuMTQ2QzEzNi43MzcgMTUwLjE0NiAxMzYuNTg0IDE1MC4xNzkgMTM2LjQ1MSAxNTAuMjQ0QzEzNi4zMTkgMTUwLjMwNyAxMzYuMjA3IDE1MC40MDcgMTM2LjExNiAxNTAuNTQ1QzEzNi4wMjcgMTUwLjY4MyAxMzUuOTU5IDE1MC44NjQgMTM1LjkxMiAxNTEuMDg4QzEzNS44NjYgMTUxLjMxMiAxMzUuODQyIDE1MS41ODQgMTM1Ljg0MiAxNTEuOTA0VjE1My4wMUMxMzUuODQyIDE1My4yNjUgMTM1Ljg1NiAxNTMuNDkgMTM1Ljg4NSAxNTMuNjg2QzEzNS45MTYgMTUzLjg4MSAxMzUuOTYyIDE1NC4wNSAxMzYuMDIyIDE1NC4xOTNDMTM2LjA4MiAxNTQuMzM0IDEzNi4xNTUgMTU0LjQ1IDEzNi4yNDEgMTU0LjU0MUMxMzYuMzI2IDE1NC42MzIgMTM2LjQyNSAxNTQuNyAxMzYuNTM3IDE1NC43NDRDMTM2LjY1MiAxNTQuNzg2IDEzNi43NzggMTU0LjgwNyAxMzYuOTE2IDE1NC44MDdDMTM3LjA5MyAxNTQuODA3IDEzNy4yNDggMTU0Ljc3MyAxMzcuMzgxIDE1NC43MDVDMTM3LjUxNCAxNTQuNjM3IDEzNy42MjUgMTU0LjUzMiAxMzcuNzEzIDE1NC4zODlDMTM3LjgwNCAxNTQuMjQzIDEzNy44NzIgMTU0LjA1NyAxMzcuOTE2IDE1My44M0MxMzcuOTYxIDE1My42MDEgMTM3Ljk4MyAxNTMuMzI3IDEzNy45ODMgMTUzLjAxWk0xNDMuNTY1IDE1My40MDhWMTU0LjAwMkgxMzkuNDU2VjE1My41NzZMMTQyLjAwMyAxNDkuNjM1SDE0Mi41OTNMMTQxLjk2IDE1MC43NzVMMTQwLjI3NiAxNTMuNDA4SDE0My41NjVaTTE0Mi43NzIgMTQ5LjYzNVYxNTUuMzIySDE0Mi4wNVYxNDkuNjM1SDE0Mi43NzJaTTE0Ny4zMTMgMTQ5LjYzNVYxNTUuMzIySDE0Ni41NTlWMTQ5LjYzNUgxNDcuMzEzWk0xNDkuNjk1IDE1Mi4xOTNWMTUyLjgxMUgxNDcuMTQ5VjE1Mi4xOTNIMTQ5LjY5NVpNMTUwLjA4MiAxNDkuNjM1VjE1MC4yNTJIMTQ3LjE0OVYxNDkuNjM1SDE1MC4wODJaTTE1Mi42MjIgMTU1LjRDMTUyLjMyOCAxNTUuNCAxNTIuMDYxIDE1NS4zNTEgMTUxLjgyMSAxNTUuMjUyQzE1MS41ODQgMTU1LjE1IDE1MS4zOCAxNTUuMDA4IDE1MS4yMDggMTU0LjgyNkMxNTEuMDM4IDE1NC42NDQgMTUwLjkwOCAxNTQuNDI4IDE1MC44MTcgMTU0LjE3OEMxNTAuNzI2IDE1My45MjggMTUwLjY4IDE1My42NTQgMTUwLjY4IDE1My4zNTdWMTUzLjE5M0MxNTAuNjggMTUyLjg1IDE1MC43MzEgMTUyLjU0NCAxNTAuODMzIDE1Mi4yNzVDMTUwLjkzNCAxNTIuMDA1IDE1MS4wNzIgMTUxLjc3NSAxNTEuMjQ3IDE1MS41ODhDMTUxLjQyMSAxNTEuNCAxNTEuNjE5IDE1MS4yNTggMTUxLjg0MSAxNTEuMTYyQzE1Mi4wNjIgMTUxLjA2NiAxNTIuMjkxIDE1MS4wMTggMTUyLjUyOCAxNTEuMDE4QzE1Mi44MyAxNTEuMDE4IDE1My4wOTEgMTUxLjA3IDE1My4zMDkgMTUxLjE3NEMxNTMuNTMxIDE1MS4yNzggMTUzLjcxMiAxNTEuNDI0IDE1My44NTIgMTUxLjYxMUMxNTMuOTkzIDE1MS43OTYgMTU0LjA5NyAxNTIuMDE1IDE1NC4xNjUgMTUyLjI2OEMxNTQuMjMyIDE1Mi41MTggMTU0LjI2NiAxNTIuNzkxIDE1NC4yNjYgMTUzLjA4OFYxNTMuNDEySDE1MS4xMVYxNTIuODIySDE1My41NDRWMTUyLjc2OEMxNTMuNTMzIDE1Mi41OCAxNTMuNDk0IDE1Mi4zOTggMTUzLjQyNiAxNTIuMjIxQzE1My4zNjEgMTUyLjA0NCAxNTMuMjU3IDE1MS44OTggMTUzLjExNCAxNTEuNzgzQzE1Mi45NzEgMTUxLjY2OSAxNTIuNzc1IDE1MS42MTEgMTUyLjUyOCAxNTEuNjExQzE1Mi4zNjQgMTUxLjYxMSAxNTIuMjEzIDE1MS42NDYgMTUyLjA3NSAxNTEuNzE3QzE1MS45MzcgMTUxLjc4NSAxNTEuODE4IDE1MS44ODYgMTUxLjcxOSAxNTIuMDIxQzE1MS42MiAxNTIuMTU3IDE1MS41NDQgMTUyLjMyMiAxNTEuNDg5IDE1Mi41MThDMTUxLjQzNCAxNTIuNzEzIDE1MS40MDcgMTUyLjkzOCAxNTEuNDA3IDE1My4xOTNWMTUzLjM1N0MxNTEuNDA3IDE1My41NTggMTUxLjQzNCAxNTMuNzQ3IDE1MS40ODkgMTUzLjkyNEMxNTEuNTQ2IDE1NC4wOTggMTUxLjYyOCAxNTQuMjUyIDE1MS43MzUgMTU0LjM4NUMxNTEuODQ0IDE1NC41MTggMTUxLjk3NiAxNTQuNjIyIDE1Mi4xMyAxNTQuNjk3QzE1Mi4yODYgMTU0Ljc3MyAxNTIuNDYzIDE1NC44MTEgMTUyLjY2MSAxNTQuODExQzE1Mi45MTYgMTU0LjgxMSAxNTMuMTMyIDE1NC43NTggMTUzLjMwOSAxNTQuNjU0QzE1My40ODYgMTU0LjU1IDE1My42NDEgMTU0LjQxMSAxNTMuNzc0IDE1NC4yMzZMMTU0LjIxMiAxNTQuNTg0QzE1NC4xMiAxNTQuNzIyIDE1NC4wMDUgMTU0Ljg1NCAxNTMuODY0IDE1NC45NzlDMTUzLjcyMyAxNTUuMTA0IDE1My41NSAxNTUuMjA1IDE1My4zNDQgMTU1LjI4M0MxNTMuMTQxIDE1NS4zNjEgMTUyLjkgMTU1LjQgMTUyLjYyMiAxNTUuNFpNMTU1LjE4OSAxNDkuMzIySDE1NS45MTVWMTU0LjUwMkwxNTUuODUzIDE1NS4zMjJIMTU1LjE4OVYxNDkuMzIyWk0xNTguNzcxIDE1My4xNzRWMTUzLjI1NkMxNTguNzcxIDE1My41NjMgMTU4LjczNCAxNTMuODQ4IDE1OC42NjEgMTU0LjExMUMxNTguNTg5IDE1NC4zNzIgMTU4LjQ4MiAxNTQuNTk4IDE1OC4zNDEgMTU0Ljc5MUMxNTguMjAxIDE1NC45ODQgMTU4LjAyOSAxNTUuMTMzIDE1Ny44MjYgMTU1LjI0QzE1Ny42MjIgMTU1LjM0NyAxNTcuMzg5IDE1NS40IDE1Ny4xMjYgMTU1LjRDMTU2Ljg1OCAxNTUuNCAxNTYuNjIyIDE1NS4zNTUgMTU2LjQxOSAxNTUuMjY0QzE1Ni4yMTkgMTU1LjE3IDE1Ni4wNDkgMTU1LjAzNiAxNTUuOTExIDE1NC44NjFDMTU1Ljc3MyAxNTQuNjg3IDE1NS42NjMgMTU0LjQ3NiAxNTUuNTc5IDE1NC4yMjlDMTU1LjQ5OSAxNTMuOTgxIDE1NS40NDMgMTUzLjcwMiAxNTUuNDExIDE1My4zOTNWMTUzLjAzM0MxNTUuNDQzIDE1Mi43MjEgMTU1LjQ5OSAxNTIuNDQxIDE1NS41NzkgMTUyLjE5M0MxNTUuNjYzIDE1MS45NDYgMTU1Ljc3MyAxNTEuNzM1IDE1NS45MTEgMTUxLjU2MUMxNTYuMDQ5IDE1MS4zODMgMTU2LjIxOSAxNTEuMjQ5IDE1Ni40MTkgMTUxLjE1OEMxNTYuNjIgMTUxLjA2NCAxNTYuODUzIDE1MS4wMTggMTU3LjExOCAxNTEuMDE4QzE1Ny4zODQgMTUxLjAxOCAxNTcuNjIgMTUxLjA3IDE1Ny44MjYgMTUxLjE3NEMxNTguMDMxIDE1MS4yNzUgMTU4LjIwMyAxNTEuNDIxIDE1OC4zNDEgMTUxLjYxMUMxNTguNDgyIDE1MS44MDEgMTU4LjU4OSAxNTIuMDI5IDE1OC42NjEgMTUyLjI5NUMxNTguNzM0IDE1Mi41NTggMTU4Ljc3MSAxNTIuODUxIDE1OC43NzEgMTUzLjE3NFpNMTU4LjA0NCAxNTMuMjU2VjE1My4xNzRDMTU4LjA0NCAxNTIuOTYzIDE1OC4wMjUgMTUyLjc2NSAxNTcuOTg2IDE1Mi41OEMxNTcuOTQ3IDE1Mi4zOTMgMTU3Ljg4NCAxNTIuMjI5IDE1Ny43OTggMTUyLjA4OEMxNTcuNzEyIDE1MS45NDUgMTU3LjU5OSAxNTEuODMzIDE1Ny40NTggMTUxLjc1MkMxNTcuMzE4IDE1MS42NjkgMTU3LjE0NSAxNTEuNjI3IDE1Ni45MzkgMTUxLjYyN0MxNTYuNzU3IDE1MS42MjcgMTU2LjU5OCAxNTEuNjU4IDE1Ni40NjIgMTUxLjcyMUMxNTYuMzI5IDE1MS43ODMgMTU2LjIxNiAxNTEuODY4IDE1Ni4xMjIgMTUxLjk3NUMxNTYuMDI5IDE1Mi4wNzkgMTU1Ljk1MiAxNTIuMTk5IDE1NS44OTIgMTUyLjMzNEMxNTUuODM1IDE1Mi40NjcgMTU1Ljc5MiAxNTIuNjA1IDE1NS43NjMgMTUyLjc0OFYxNTMuNjg5QzE1NS44MDUgMTUzLjg3MiAxNTUuODcyIDE1NC4wNDggMTU1Ljk2NiAxNTQuMjE3QzE1Ni4wNjIgMTU0LjM4MyAxNTYuMTkgMTU0LjUyIDE1Ni4zNDkgMTU0LjYyN0MxNTYuNTEgMTU0LjczNCAxNTYuNzEgMTU0Ljc4NyAxNTYuOTQ3IDE1NC43ODdDMTU3LjE0MiAxNTQuNzg3IDE1Ny4zMDkgMTU0Ljc0OCAxNTcuNDQ3IDE1NC42N0MxNTcuNTg3IDE1NC41ODkgMTU3LjcwMSAxNTQuNDc5IDE1Ny43ODYgMTU0LjMzOEMxNTcuODc1IDE1NC4xOTcgMTU3Ljk0IDE1NC4wMzUgMTU3Ljk4MiAxNTMuODVDMTU4LjAyMyAxNTMuNjY1IDE1OC4wNDQgMTUzLjQ2NyAxNTguMDQ0IDE1My4yNTZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjwvZz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXA0XzQwNTNfMTg1MTEzKSI+CjxsaW5lIHgxPSIxODIuMDUiIHkxPSIxNDcuMDcyIiB4Mj0iMTgyLjA1IiB5Mj0iMTQ2LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNMTc0LjEwOSAxNTIuMDI1VjE1Mi44OTNDMTc0LjEwOSAxNTMuMzU5IDE3NC4wNjcgMTUzLjc1MiAxNzMuOTg0IDE1NC4wNzJDMTczLjkwMSAxNTQuMzkzIDE3My43ODEgMTU0LjY1IDE3My42MjUgMTU0Ljg0NkMxNzMuNDY5IDE1NS4wNDEgMTczLjI4IDE1NS4xODMgMTczLjA1OCAxNTUuMjcxQzE3Mi44NCAxNTUuMzU3IDE3Mi41OTIgMTU1LjQgMTcyLjMxNiAxNTUuNEMxNzIuMDk3IDE1NS40IDE3MS44OTYgMTU1LjM3MyAxNzEuNzExIDE1NS4zMThDMTcxLjUyNiAxNTUuMjY0IDE3MS4zNTkgMTU1LjE3NiAxNzEuMjExIDE1NS4wNTdDMTcxLjA2NSAxNTQuOTM0IDE3MC45NCAxNTQuNzc1IDE3MC44MzYgMTU0LjU4QzE3MC43MzIgMTU0LjM4NSAxNzAuNjUyIDE1NC4xNDggMTcwLjU5NyAxNTMuODY5QzE3MC41NDMgMTUzLjU5IDE3MC41MTUgMTUzLjI2NSAxNzAuNTE1IDE1Mi44OTNWMTUyLjAyNUMxNzAuNTE1IDE1MS41NTkgMTcwLjU1NyAxNTEuMTY5IDE3MC42NCAxNTAuODU0QzE3MC43MjYgMTUwLjUzOCAxNzAuODQ3IDE1MC4yODYgMTcxLjAwNCAxNTAuMDk2QzE3MS4xNiAxNDkuOTAzIDE3MS4zNDcgMTQ5Ljc2NSAxNzEuNTY2IDE0OS42ODJDMTcxLjc4OCAxNDkuNTk4IDE3Mi4wMzUgMTQ5LjU1NyAxNzIuMzA4IDE0OS41NTdDMTcyLjUzIDE0OS41NTcgMTcyLjczMyAxNDkuNTg0IDE3Mi45MTggMTQ5LjYzOUMxNzMuMTA1IDE0OS42OTEgMTczLjI3MiAxNDkuNzc1IDE3My40MTggMTQ5Ljg5M0MxNzMuNTY0IDE1MC4wMDcgMTczLjY4NyAxNTAuMTYxIDE3My43ODkgMTUwLjM1NEMxNzMuODkzIDE1MC41NDQgMTczLjk3MiAxNTAuNzc3IDE3NC4wMjcgMTUxLjA1M0MxNzQuMDgyIDE1MS4zMjkgMTc0LjEwOSAxNTEuNjUzIDE3NC4xMDkgMTUyLjAyNVpNMTczLjM4MyAxNTMuMDFWMTUxLjkwNEMxNzMuMzgzIDE1MS42NDkgMTczLjM2NyAxNTEuNDI1IDE3My4zMzYgMTUxLjIzMkMxNzMuMzA3IDE1MS4wMzcgMTczLjI2NCAxNTAuODcgMTczLjIwNyAxNTAuNzMyQzE3My4xNSAxNTAuNTk0IDE3My4wNzcgMTUwLjQ4MiAxNzIuOTg4IDE1MC4zOTZDMTcyLjkwMiAxNTAuMzExIDE3Mi44MDIgMTUwLjI0OCAxNzIuNjg3IDE1MC4yMDlDMTcyLjU3NSAxNTAuMTY3IDE3Mi40NDkgMTUwLjE0NiAxNzIuMzA4IDE1MC4xNDZDMTcyLjEzNiAxNTAuMTQ2IDE3MS45ODQgMTUwLjE3OSAxNzEuODUxIDE1MC4yNDRDMTcxLjcxOSAxNTAuMzA3IDE3MS42MDcgMTUwLjQwNyAxNzEuNTE1IDE1MC41NDVDMTcxLjQyNyAxNTAuNjgzIDE3MS4zNTkgMTUwLjg2NCAxNzEuMzEyIDE1MS4wODhDMTcxLjI2NSAxNTEuMzEyIDE3MS4yNDIgMTUxLjU4NCAxNzEuMjQyIDE1MS45MDRWMTUzLjAxQzE3MS4yNDIgMTUzLjI2NSAxNzEuMjU2IDE1My40OSAxNzEuMjg1IDE1My42ODZDMTcxLjMxNiAxNTMuODgxIDE3MS4zNjIgMTU0LjA1IDE3MS40MjIgMTU0LjE5M0MxNzEuNDgyIDE1NC4zMzQgMTcxLjU1NCAxNTQuNDUgMTcxLjY0IDE1NC41NDFDMTcxLjcyNiAxNTQuNjMyIDE3MS44MjUgMTU0LjcgMTcxLjkzNyAxNTQuNzQ0QzE3Mi4wNTIgMTU0Ljc4NiAxNzIuMTc4IDE1NC44MDcgMTcyLjMxNiAxNTQuODA3QzE3Mi40OTMgMTU0LjgwNyAxNzIuNjQ4IDE1NC43NzMgMTcyLjc4MSAxNTQuNzA1QzE3Mi45MTQgMTU0LjYzNyAxNzMuMDI1IDE1NC41MzIgMTczLjExMyAxNTQuMzg5QzE3My4yMDQgMTU0LjI0MyAxNzMuMjcyIDE1NC4wNTcgMTczLjMxNiAxNTMuODNDMTczLjM2IDE1My42MDEgMTczLjM4MyAxNTMuMzI3IDE3My4zODMgMTUzLjAxWk0xNzYuMDM2IDE1Mi42MTVMMTc1LjQ1NyAxNTIuNDY3TDE3NS43NDMgMTQ5LjYzNUgxNzguNjYxVjE1MC4zMDNIMTc2LjM1NkwxNzYuMTg0IDE1MS44NUMxNzYuMjg4IDE1MS43OSAxNzYuNDIgMTUxLjczNCAxNzYuNTc5IDE1MS42ODJDMTc2Ljc0IDE1MS42MyAxNzYuOTI1IDE1MS42MDQgMTc3LjEzMyAxNTEuNjA0QzE3Ny4zOTYgMTUxLjYwNCAxNzcuNjMyIDE1MS42NDkgMTc3Ljg0IDE1MS43NEMxNzguMDQ5IDE1MS44MjkgMTc4LjIyNiAxNTEuOTU2IDE3OC4zNzEgMTUyLjEyM0MxNzguNTIgMTUyLjI5IDE3OC42MzMgMTUyLjQ5IDE3OC43MTEgMTUyLjcyNUMxNzguNzg5IDE1Mi45NTkgMTc4LjgyOSAxNTMuMjIxIDE3OC44MjkgMTUzLjUxQzE3OC44MjkgMTUzLjc4MyAxNzguNzkxIDE1NC4wMzUgMTc4LjcxNSAxNTQuMjY0QzE3OC42NDIgMTU0LjQ5MyAxNzguNTMyIDE1NC42OTMgMTc4LjM4MyAxNTQuODY1QzE3OC4yMzUgMTU1LjAzNSAxNzguMDQ3IDE1NS4xNjYgMTc3LjgyMSAxNTUuMjZDMTc3LjU5NyAxNTUuMzU0IDE3Ny4zMzIgMTU1LjQgMTc3LjAyOCAxNTUuNEMxNzYuNzk5IDE1NS40IDE3Ni41ODEgMTU1LjM2OSAxNzYuMzc1IDE1NS4zMDdDMTc2LjE3MiAxNTUuMjQyIDE3NS45OSAxNTUuMTQ0IDE3NS44MjkgMTU1LjAxNEMxNzUuNjcgMTU0Ljg4MSAxNzUuNTM5IDE1NC43MTcgMTc1LjQzOCAxNTQuNTIxQzE3NS4zMzkgMTU0LjMyNCAxNzUuMjc2IDE1NC4wOTIgMTc1LjI1IDE1My44MjZIMTc1LjkzOEMxNzUuOTY5IDE1NC4wNCAxNzYuMDMyIDE1NC4yMTkgMTc2LjEyNSAxNTQuMzY1QzE3Ni4yMTkgMTU0LjUxMSAxNzYuMzQyIDE1NC42MjIgMTc2LjQ5MyAxNTQuNjk3QzE3Ni42NDYgMTU0Ljc3IDE3Ni44MjUgMTU0LjgwNyAxNzcuMDI4IDE1NC44MDdDMTc3LjIgMTU0LjgwNyAxNzcuMzUyIDE1NC43NzcgMTc3LjQ4NSAxNTQuNzE3QzE3Ny42MTggMTU0LjY1NyAxNzcuNzMgMTU0LjU3MSAxNzcuODIxIDE1NC40NTlDMTc3LjkxMiAxNTQuMzQ3IDE3Ny45ODEgMTU0LjIxMiAxNzguMDI4IDE1NC4wNTNDMTc4LjA3NyAxNTMuODk0IDE3OC4xMDIgMTUzLjcxNSAxNzguMTAyIDE1My41MThDMTc4LjEwMiAxNTMuMzM4IDE3OC4wNzcgMTUzLjE3MSAxNzguMDI4IDE1My4wMThDMTc3Ljk3OCAxNTIuODY0IDE3Ny45MDQgMTUyLjczIDE3Ny44MDUgMTUyLjYxNUMxNzcuNzA5IDE1Mi41MDEgMTc3LjU5IDE1Mi40MTIgMTc3LjQ1IDE1Mi4zNUMxNzcuMzA5IDE1Mi4yODUgMTc3LjE0OCAxNTIuMjUyIDE3Ni45NjUgMTUyLjI1MkMxNzYuNzIzIDE1Mi4yNTIgMTc2LjUzOSAxNTIuMjg1IDE3Ni40MTQgMTUyLjM1QzE3Ni4yOTIgMTUyLjQxNSAxNzYuMTY2IDE1Mi41MDMgMTc2LjAzNiAxNTIuNjE1Wk0xODIuNzEzIDE0OS42MzVWMTU1LjMyMkgxODEuOTU5VjE0OS42MzVIMTgyLjcxM1pNMTg1LjA5NSAxNTIuMTkzVjE1Mi44MTFIMTgyLjU0OFYxNTIuMTkzSDE4NS4wOTVaTTE4NS40ODIgMTQ5LjYzNVYxNTAuMjUySDE4Mi41NDhWMTQ5LjYzNUgxODUuNDgyWk0xODguMDIyIDE1NS40QzE4Ny43MjcgMTU1LjQgMTg3LjQ2IDE1NS4zNTEgMTg3LjIyMSAxNTUuMjUyQzE4Ni45ODQgMTU1LjE1IDE4Ni43OCAxNTUuMDA4IDE4Ni42MDggMTU0LjgyNkMxODYuNDM4IDE1NC42NDQgMTg2LjMwOCAxNTQuNDI4IDE4Ni4yMTcgMTU0LjE3OEMxODYuMTI2IDE1My45MjggMTg2LjA4IDE1My42NTQgMTg2LjA4IDE1My4zNTdWMTUzLjE5M0MxODYuMDggMTUyLjg1IDE4Ni4xMzEgMTUyLjU0NCAxODYuMjMzIDE1Mi4yNzVDMTg2LjMzNCAxNTIuMDA1IDE4Ni40NzIgMTUxLjc3NSAxODYuNjQ3IDE1MS41ODhDMTg2LjgyMSAxNTEuNCAxODcuMDE5IDE1MS4yNTggMTg3LjI0IDE1MS4xNjJDMTg3LjQ2MiAxNTEuMDY2IDE4Ny42OTEgMTUxLjAxOCAxODcuOTI4IDE1MS4wMThDMTg4LjIzIDE1MS4wMTggMTg4LjQ5IDE1MS4wNyAxODguNzA5IDE1MS4xNzRDMTg4LjkzMSAxNTEuMjc4IDE4OS4xMTIgMTUxLjQyNCAxODkuMjUyIDE1MS42MTFDMTg5LjM5MyAxNTEuNzk2IDE4OS40OTcgMTUyLjAxNSAxODkuNTY1IDE1Mi4yNjhDMTg5LjYzMiAxNTIuNTE4IDE4OS42NjYgMTUyLjc5MSAxODkuNjY2IDE1My4wODhWMTUzLjQxMkgxODYuNTFWMTUyLjgyMkgxODguOTQ0VjE1Mi43NjhDMTg4LjkzMyAxNTIuNTggMTg4Ljg5NCAxNTIuMzk4IDE4OC44MjYgMTUyLjIyMUMxODguNzYxIDE1Mi4wNDQgMTg4LjY1NyAxNTEuODk4IDE4OC41MTQgMTUxLjc4M0MxODguMzcxIDE1MS42NjkgMTg4LjE3NSAxNTEuNjExIDE4Ny45MjggMTUxLjYxMUMxODcuNzY0IDE1MS42MTEgMTg3LjYxMyAxNTEuNjQ2IDE4Ny40NzUgMTUxLjcxN0MxODcuMzM3IDE1MS43ODUgMTg3LjIxOCAxNTEuODg2IDE4Ny4xMTkgMTUyLjAyMUMxODcuMDIgMTUyLjE1NyAxODYuOTQ0IDE1Mi4zMjIgMTg2Ljg4OSAxNTIuNTE4QzE4Ni44MzQgMTUyLjcxMyAxODYuODA3IDE1Mi45MzggMTg2LjgwNyAxNTMuMTkzVjE1My4zNTdDMTg2LjgwNyAxNTMuNTU4IDE4Ni44MzQgMTUzLjc0NyAxODYuODg5IDE1My45MjRDMTg2Ljk0NiAxNTQuMDk4IDE4Ny4wMjggMTU0LjI1MiAxODcuMTM1IDE1NC4zODVDMTg3LjI0NCAxNTQuNTE4IDE4Ny4zNzYgMTU0LjYyMiAxODcuNTMgMTU0LjY5N0MxODcuNjg2IDE1NC43NzMgMTg3Ljg2MyAxNTQuODExIDE4OC4wNjEgMTU0LjgxMUMxODguMzE2IDE1NC44MTEgMTg4LjUzMiAxNTQuNzU4IDE4OC43MDkgMTU0LjY1NEMxODguODg2IDE1NC41NSAxODkuMDQxIDE1NC40MTEgMTg5LjE3NCAxNTQuMjM2TDE4OS42MTIgMTU0LjU4NEMxODkuNTIgMTU0LjcyMiAxODkuNDA1IDE1NC44NTQgMTg5LjI2NCAxNTQuOTc5QzE4OS4xMjMgMTU1LjEwNCAxODguOTUgMTU1LjIwNSAxODguNzQ0IDE1NS4yODNDMTg4LjU0MSAxNTUuMzYxIDE4OC4zIDE1NS40IDE4OC4wMjIgMTU1LjRaTTE5MC41ODkgMTQ5LjMyMkgxOTEuMzE1VjE1NC41MDJMMTkxLjI1MyAxNTUuMzIySDE5MC41ODlWMTQ5LjMyMlpNMTk0LjE3MSAxNTMuMTc0VjE1My4yNTZDMTk0LjE3MSAxNTMuNTYzIDE5NC4xMzQgMTUzLjg0OCAxOTQuMDYxIDE1NC4xMTFDMTkzLjk4OCAxNTQuMzcyIDE5My44ODIgMTU0LjU5OCAxOTMuNzQxIDE1NC43OTFDMTkzLjYgMTU0Ljk4NCAxOTMuNDI5IDE1NS4xMzMgMTkzLjIyNSAxNTUuMjRDMTkzLjAyMiAxNTUuMzQ3IDE5Mi43ODkgMTU1LjQgMTkyLjUyNiAxNTUuNEMxOTIuMjU4IDE1NS40IDE5Mi4wMjIgMTU1LjM1NSAxOTEuODE5IDE1NS4yNjRDMTkxLjYxOSAxNTUuMTcgMTkxLjQ0OSAxNTUuMDM2IDE5MS4zMTEgMTU0Ljg2MUMxOTEuMTczIDE1NC42ODcgMTkxLjA2MyAxNTQuNDc2IDE5MC45NzkgMTU0LjIyOUMxOTAuODk5IDE1My45ODEgMTkwLjg0MyAxNTMuNzAyIDE5MC44MTEgMTUzLjM5M1YxNTMuMDMzQzE5MC44NDMgMTUyLjcyMSAxOTAuODk5IDE1Mi40NDEgMTkwLjk3OSAxNTIuMTkzQzE5MS4wNjMgMTUxLjk0NiAxOTEuMTczIDE1MS43MzUgMTkxLjMxMSAxNTEuNTYxQzE5MS40NDkgMTUxLjM4MyAxOTEuNjE5IDE1MS4yNDkgMTkxLjgxOSAxNTEuMTU4QzE5Mi4wMiAxNTEuMDY0IDE5Mi4yNTMgMTUxLjAxOCAxOTIuNTE4IDE1MS4wMThDMTkyLjc4NCAxNTEuMDE4IDE5My4wMiAxNTEuMDcgMTkzLjIyNSAxNTEuMTc0QzE5My40MzEgMTUxLjI3NSAxOTMuNjAzIDE1MS40MjEgMTkzLjc0MSAxNTEuNjExQzE5My44ODIgMTUxLjgwMSAxOTMuOTg4IDE1Mi4wMjkgMTk0LjA2MSAxNTIuMjk1QzE5NC4xMzQgMTUyLjU1OCAxOTQuMTcxIDE1Mi44NTEgMTk0LjE3MSAxNTMuMTc0Wk0xOTMuNDQ0IDE1My4yNTZWMTUzLjE3NEMxOTMuNDQ0IDE1Mi45NjMgMTkzLjQyNSAxNTIuNzY1IDE5My4zODYgMTUyLjU4QzE5My4zNDcgMTUyLjM5MyAxOTMuMjg0IDE1Mi4yMjkgMTkzLjE5OCAxNTIuMDg4QzE5My4xMTIgMTUxLjk0NSAxOTIuOTk5IDE1MS44MzMgMTkyLjg1OCAxNTEuNzUyQzE5Mi43MTggMTUxLjY2OSAxOTIuNTQ0IDE1MS42MjcgMTkyLjMzOSAxNTEuNjI3QzE5Mi4xNTYgMTUxLjYyNyAxOTEuOTk4IDE1MS42NTggMTkxLjg2MiAxNTEuNzIxQzE5MS43MjkgMTUxLjc4MyAxOTEuNjE2IDE1MS44NjggMTkxLjUyMiAxNTEuOTc1QzE5MS40MjkgMTUyLjA3OSAxOTEuMzUyIDE1Mi4xOTkgMTkxLjI5MiAxNTIuMzM0QzE5MS4yMzUgMTUyLjQ2NyAxOTEuMTkyIDE1Mi42MDUgMTkxLjE2MyAxNTIuNzQ4VjE1My42ODlDMTkxLjIwNSAxNTMuODcyIDE5MS4yNzIgMTU0LjA0OCAxOTEuMzY2IDE1NC4yMTdDMTkxLjQ2MiAxNTQuMzgzIDE5MS41OSAxNTQuNTIgMTkxLjc0OSAxNTQuNjI3QzE5MS45MSAxNTQuNzM0IDE5Mi4xMSAxNTQuNzg3IDE5Mi4zNDcgMTU0Ljc4N0MxOTIuNTQyIDE1NC43ODcgMTkyLjcwOCAxNTQuNzQ4IDE5Mi44NDcgMTU0LjY3QzE5Mi45ODcgMTU0LjU4OSAxOTMuMSAxNTQuNDc5IDE5My4xODYgMTU0LjMzOEMxOTMuMjc1IDE1NC4xOTcgMTkzLjM0IDE1NC4wMzUgMTkzLjM4MiAxNTMuODVDMTkzLjQyMyAxNTMuNjY1IDE5My40NDQgMTUzLjQ2NyAxOTMuNDQ0IDE1My4yNTZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjwvZz4KPHBhdGggZD0iTTI3IDEwNy40NDVMNDAgODguMDk2Mkw3NS44NjUgNDUuMDgwMkM3Ni4yMTY2IDQ0LjY1ODYgNzYuODQyMyA0NC41OTkyIDc3LjI2NjkgNDQuOTQ3MUwxMTEuMTM1IDcyLjcwMDZDMTExLjM2NiA3Mi44OTAyIDExMS42NyA3Mi45NjYyIDExMS45NjMgNzIuOTA4TDE0Ni43OTQgNjUuOTkxQzE0Ni45MyA2NS45NjQgMTQ3LjA1OSA2NS45MDg5IDE0Ny4xNzIgNjUuODI5MkwxODQuMjY3IDM5Ljg0NjhDMTg0LjQxOSAzOS43NCAxODQuNTM5IDM5LjU5MjcgMTg0LjYxMiAzOS40MjE1TDE5OC41IDciIHN0cm9rZT0iI0ZCREIwRiIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPGNpcmNsZSBjeD0iNzYiIGN5PSI0NS4xNjExIiByPSIxLjUiIGZpbGw9IndoaXRlIiBzdHJva2U9IiNGQkRCMEYiLz4KPGNpcmNsZSBjeD0iMTEyIiBjeT0iNzMuMTYxMSIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjRkJEQjBGIi8+CjxjaXJjbGUgY3g9IjE0NyIgY3k9IjY2LjE2MTEiIHI9IjEuNSIgZmlsbD0id2hpdGUiIHN0cm9rZT0iI0ZCREIwRiIvPgo8Y2lyY2xlIGN4PSIxODUiIGN5PSIzOS4xNjExIiByPSIxLjUiIGZpbGw9IndoaXRlIiBzdHJva2U9IiNGQkRCMEYiLz4KPGNpcmNsZSBjeD0iMTk4IiBjeT0iNyIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjRkJEQjBGIi8+CjxwYXRoIGQ9Ik0yOC41IDEwNy4xMTRDMjguNSAxMDcuOTcyIDI3LjgxOTcgMTA4LjY1MSAyNyAxMDguNjUxQzI2LjE4MDMgMTA4LjY1MSAyNS41IDEwNy45NzIgMjUuNSAxMDcuMTE0QzI1LjUgMTA2LjI1NiAyNi4xODAzIDEwNS41NzYgMjcgMTA1LjU3NkMyNy44MTk3IDEwNS41NzYgMjguNSAxMDYuMjU2IDI4LjUgMTA3LjExNFoiIGZpbGw9IndoaXRlIiBzdHJva2U9IiNGQkRCMEYiLz4KPHBhdGggZD0iTTQxLjUgODcuNzc2OEM0MS41IDg4LjYzNDcgNDAuODE5NyA4OS4zMTQzIDQwIDg5LjMxNDNDMzkuMTgwMyA4OS4zMTQzIDM4LjUgODguNjM0NyAzOC41IDg3Ljc3NjhDMzguNSA4Ni45MTg4IDM5LjE4MDMgODYuMjM5MyA0MCA4Ni4yMzkzQzQwLjgxOTcgODYuMjM5MyA0MS41IDg2LjkxODggNDEuNSA4Ny43NzY4WiIgZmlsbD0id2hpdGUiIHN0cm9rZT0iI0ZCREIwRiIvPgo8cGF0aCBkPSJNMzIgMTIyQzMyIDEyMC44OTUgMzIuODk1NCAxMjAgMzQgMTIwSDQ4QzQ5LjEwNDYgMTIwIDUwIDEyMC44OTUgNTAgMTIyVjE0NkgzMlYxMjJaIiBmaWxsPSIjM0ZBNzFBIi8+CjxwYXRoIGQ9Ik02NyA3OEM2NyA3Ni44OTU0IDY3Ljg5NTQgNzYgNjkgNzZIODNDODQuMTA0NiA3NiA4NSA3Ni44OTU0IDg1IDc4VjE0Nkg2N1Y3OFoiIGZpbGw9IiMzRkE3MUEiLz4KPHBhdGggZD0iTTEwMiA5MkMxMDIgOTAuODk1NCAxMDIuODk1IDkwIDEwNCA5MEgxMThDMTE5LjEwNSA5MCAxMjAgOTAuODk1NCAxMjAgOTJWMTQ2SDEwMlY5MloiIGZpbGw9IiMzRkE3MUEiLz4KPHBhdGggZD0iTTE3MyAxMDVDMTczIDEwMy44OTUgMTczLjg5NSAxMDMgMTc1IDEwM0gxODlDMTkwLjEwNSAxMDMgMTkxIDEwMy44OTUgMTkxIDEwNVYxNDZIMTczVjEwNVoiIGZpbGw9IiMzRkE3MUEiLz4KPHBhdGggZD0iTTI3IDExOEw0MCA3MS43NzkyTDc4LjUgODRMMTE5IDMyTDE0OCAzNi41TDE4MiA3MS43NzkyTDE5NS4zMTEgMTAwLjIxNkwxOTcgMTA3IiBzdHJva2U9IiM0QjcwREQiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxjaXJjbGUgY3g9Ijc4IiBjeT0iODQuMTYxMSIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjNEI3MEREIi8+CjxjaXJjbGUgY3g9IjQwIiBjeT0iNzIuMTYxMSIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjNEI3MEREIi8+CjxjaXJjbGUgY3g9IjExOSIgY3k9IjMyIiByPSIxLjUiIGZpbGw9IndoaXRlIiBzdHJva2U9IiM0QjcwREQiLz4KPGNpcmNsZSBjeD0iMTgyIiBjeT0iNzIuMTYxMSIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjNEI3MEREIi8+CjxjaXJjbGUgY3g9IjE5NyIgY3k9IjEwNyIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjNEI3MEREIi8+CjxjaXJjbGUgY3g9IjI3IiBjeT0iMTE3LjE2MSIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjNEI3MEREIi8+CjxjaXJjbGUgY3g9IjE0NyIgY3k9IjM3LjE2MTEiIHI9IjEuNSIgZmlsbD0id2hpdGUiIHN0cm9rZT0iIzRCNzBERCIvPgo8L2c+CjxkZWZzPgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzQwNTNfMTg1MTEzIj4KPHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjxjbGlwUGF0aCBpZD0iY2xpcDFfNDA1M18xODUxMTMiPgo8cmVjdCB3aWR0aD0iMzUuNCIgaGVpZ2h0PSIxMC4zMjIiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1OC4zOTk5IDE0NikiLz4KPC9jbGlwUGF0aD4KPGNsaXBQYXRoIGlkPSJjbGlwMl80MDUzXzE4NTExMyI+CjxyZWN0IHdpZHRoPSIzNS40IiBoZWlnaHQ9IjEwLjMyMiIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDkzLjc5OTggMTQ2KSIvPgo8L2NsaXBQYXRoPgo8Y2xpcFBhdGggaWQ9ImNsaXAzXzQwNTNfMTg1MTEzIj4KPHJlY3Qgd2lkdGg9IjM1LjQiIGhlaWdodD0iMTAuMzIyIiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTI5LjIgMTQ2KSIvPgo8L2NsaXBQYXRoPgo8Y2xpcFBhdGggaWQ9ImNsaXA0XzQwNTNfMTg1MTEzIj4KPHJlY3Qgd2lkdGg9IjM1LjQiIGhlaWdodD0iMTAuMzIyIiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTY0LjYgMTQ2KSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo=",
+ "description": "Displays changes to time-series data over time—for example, temperature or humidity readings.",
+ "descriptor": {
+ "type": "timeseries",
+ "sizeX": 8,
+ "sizeY": 5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n self.ctx.$scope.timeSeriesChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeSeriesChartWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.timeSeriesChartWidget.onLatestDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n dataKeySettingsFunction: TbTimeSeriesChart.dataKeySettings(),\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries', units: '°C', decimals: 0 }];\n }\n };\n}\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}",
+ "latestDataKeySettingsSchema": "{}",
+ "settingsDirective": "tb-time-series-chart-widget-settings",
+ "dataKeySettingsDirective": "tb-time-series-chart-key-settings",
+ "latestDataKeySettingsDirective": "",
+ "hasBasicMode": true,
+ "basicModeDirective": "tb-time-series-chart-basic-config",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#FFC107\",\"settings\":{\"type\":\"bar\"},\"_hash\":0.5534217244004682,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":null}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":0,\"realtime\":{\"realtimeType\":0,\"timewindowMs\":60000,\"quickInterval\":\"CURRENT_DAY\",\"interval\":1000},\"aggregation\":{\"type\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"top\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"yAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"xAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"bottom\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":10,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormat\":{},\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipDateInterval\":true,\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"yAxes\":{\"default\":{\"units\":null,\"decimals\":0,\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormatter\":null,\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\",\"id\":\"default\",\"order\":0}},\"noAggregationBarWidthSettings\":{\"strategy\":\"group\",\"groupWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000},\"barWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000}},\"animation\":{\"animation\":true,\"animationThreshold\":2000,\"animationDuration\":500,\"animationEasing\":\"cubicOut\",\"animationDelay\":0,\"animationDurationUpdate\":300,\"animationEasingUpdate\":\"cubicOut\",\"animationDelayUpdate\":0},\"padding\":\"12px\"},\"title\":\"Time series chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"\",\"decimals\":null,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}"
+ },
+ "tags": [
+ "chart",
+ "time series",
+ "time-series",
+ "line",
+ "line chart",
+ "bar",
+ "bar chart",
+ "point",
+ "point chart"
+ ]
+}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_types/timeseries_bar_chart.json b/application/src/main/data/json/system/widget_types/timeseries_bar_chart.json
index ff9c7971f0..ebb0d2680d 100644
--- a/application/src/main/data/json/system/widget_types/timeseries_bar_chart.json
+++ b/application/src/main/data/json/system/widget_types/timeseries_bar_chart.json
@@ -1,8 +1,8 @@
{
"fqn": "charts.timeseries_bars_flot",
"name": "Timeseries Bar Chart",
- "deprecated": false,
- "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAABOFBMVEUAAAA3oPR3d3d6enp8fHyBgYGDg4OGhoaNjY2RkZGSkpKTk5OUlJSVlZWWlpaXl5eYmJiZmZmampqcnJydnZ2enp6goKChoaGioqKjo6OkpKSlpaWmpqanp6eoqKipqamqqqqrq6usrKytra2urq6wsLCxsbGysrK0tLS1tbW2tra3t7e4uLi5ubm6urq8vLy9vb2+vr6/v7/AwMDBwcHDw8PExMTHx8fIyMjJycnLy8vNzc3Ozs7Pz8/S0tLT09PU1NTV1dXW1tbX19fZ2dna2trb29vc3Nzd3d3e3t7f39/h4eHi4uLj4+Pk5OTl5eXm5ubn5+fo6Ojq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fH09PT19fX29vb39/f4+Pj5+fn6+vr7+/v8/Pz9/f3+/v7/xx////8KXFhiAAAAAWJLR0RnW9PpswAAAvtJREFUeNrt3GtXElEUBuDpZlAqylVHCyMwyi4SpkaWAl4qk8wwUhJlmOn9//+gLzbKbc6AAm5991p82WvPOedZZw6zYM3aGupCQ4tYkZDSsKDrocNVvz8jHQKYY1ZiT/6OAJllRPSpsnzIqIG9SjYmHpKfAzI4CIuHjJeBtyHfZwCatiI1/m+BYV2Dw35NniOEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQUp/6Wx+DgbRbxOAhEb/fXJ98akiH1AIAHtSWlqRDSvdDGcOHnYR0SPV7zVsaQyHeO0jTJapEV5C9bUz9fIjsvHRIefhRxHqpjxzA3ftajXOudHGJOtHV+1rV04/wHenDc2QQkFbLJISQwUDaP90IIYQQQgghpAvIxRO9gFxG4lZ9XBFIx6sihJD+QVRjEEIIIYQQcoMgahkhhBDSe0iLX3ydQdSLIKR9ghCJkIYEIYQQIhPS8d+AhPQboppFw9HMxBrs/lqCIbObpgezP67YjnQ8iwagHDzrryUZUgsUz/prCYZY05vn+msJhiwM6XrR7q/V9OJU4xQuEpcxRsezNPbXEn3Yzz9Hulg3IYT0C3LxxI2CNCQIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQlpA7P5a0iF2fy3hkLP+WsIhVbu/lvRby+6vJR1i99eSG6ffu1XHxlRNcfWq3I0iIK4tJK2nXVyV0lOqEmvjGWAtvnCu+jMzkQWMiOlYdRSd3MAH/UnVPWR/ApFfSkcpiOBvRc3XtAeYW1ZUJbZMD5C8azhWLX4xvZVR692Se0huHm9ySogxkhs3lFVewPM4WlFUlUMoJEYUox1+jAFIbLuHrKaQWlMu8TicDp+4gdw7/qS4t2qBohk4UUF2nieBfLyDM/ItgXhBucT113i14QbixW7M+SRNb6EQ0u8kHavyZQxj2/kgNUCsYDRoqXfEF/Mdu4G8D43uOtakh3R9H1DsyKZvOmneDjt+D/0DTzolrPMHmggAAAAASUVORK5CYII=",
+ "deprecated": true,
+ "image": "tb-image:dGltZXNlcmllc19iYXJfY2hhcnRfc3lzdGVtX3dpZGdldF9pbWFnZS5wbmc=:IlRpbWVzZXJpZXMgQmFyIENoYXJ0IiBzeXN0ZW0gd2lkZ2V0IGltYWdl;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAABOFBMVEUAAAA3oPR3d3d6enp8fHyBgYGDg4OGhoaNjY2RkZGSkpKTk5OUlJSVlZWWlpaXl5eYmJiZmZmampqcnJydnZ2enp6goKChoaGioqKjo6OkpKSlpaWmpqanp6eoqKipqamqqqqrq6usrKytra2urq6wsLCxsbGysrK0tLS1tbW2tra3t7e4uLi5ubm6urq8vLy9vb2+vr6/v7/AwMDBwcHDw8PExMTHx8fIyMjJycnLy8vNzc3Ozs7Pz8/S0tLT09PU1NTV1dXW1tbX19fZ2dna2trb29vc3Nzd3d3e3t7f39/h4eHi4uLj4+Pk5OTl5eXm5ubn5+fo6Ojq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fH09PT19fX29vb39/f4+Pj5+fn6+vr7+/v8/Pz9/f3+/v7/xx////8KXFhiAAAAAWJLR0RnW9PpswAAAvtJREFUeNrt3GtXElEUBuDpZlAqylVHCyMwyi4SpkaWAl4qk8wwUhJlmOn9//+gLzbKbc6AAm5991p82WvPOedZZw6zYM3aGupCQ4tYkZDSsKDrocNVvz8jHQKYY1ZiT/6OAJllRPSpsnzIqIG9SjYmHpKfAzI4CIuHjJeBtyHfZwCatiI1/m+BYV2Dw35NniOEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQUp/6Wx+DgbRbxOAhEb/fXJ98akiH1AIAHtSWlqRDSvdDGcOHnYR0SPV7zVsaQyHeO0jTJapEV5C9bUz9fIjsvHRIefhRxHqpjxzA3ftajXOudHGJOtHV+1rV04/wHenDc2QQkFbLJISQwUDaP90IIYQQQgghpAvIxRO9gFxG4lZ9XBFIx6sihJD+QVRjEEIIIYQQcoMgahkhhBDSe0iLX3ydQdSLIKR9ghCJkIYEIYQQIhPS8d+AhPQboppFw9HMxBrs/lqCIbObpgezP67YjnQ8iwagHDzrryUZUgsUz/prCYZY05vn+msJhiwM6XrR7q/V9OJU4xQuEpcxRsezNPbXEn3Yzz9Hulg3IYT0C3LxxI2CNCQIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQlpA7P5a0iF2fy3hkLP+WsIhVbu/lvRby+6vJR1i99eSG6ffu1XHxlRNcfWq3I0iIK4tJK2nXVyV0lOqEmvjGWAtvnCu+jMzkQWMiOlYdRSd3MAH/UnVPWR/ApFfSkcpiOBvRc3XtAeYW1ZUJbZMD5C8azhWLX4xvZVR692Se0huHm9ySogxkhs3lFVewPM4WlFUlUMoJEYUox1+jAFIbLuHrKaQWlMu8TicDp+4gdw7/qS4t2qBohk4UUF2nieBfLyDM/ItgXhBucT113i14QbixW7M+SRNb6EQ0u8kHavyZQxj2/kgNUCsYDRoqXfEF/Mdu4G8D43uOtakh3R9H1DsyKZvOmneDjt+D/0DTzolrPMHmggAAAAASUVORK5CYII=",
"description": "Displays changes to time-series data over time—for example, daily water consumption for the last month.",
"descriptor": {
"type": "timeseries",
@@ -21,6 +21,5 @@
"basicModeDirective": "tb-flot-basic-config",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"stack\":true,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"defaultBarWidth\":600,\"barAlignment\":\"left\",\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false},\"title\":\"Timeseries Bar Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"configMode\":\"basic\",\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\"}"
},
- "externalId": null,
"tags": null
}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_types/timeseries_line_chart.json b/application/src/main/data/json/system/widget_types/timeseries_line_chart.json
index e83b213ae0..87f7330657 100644
--- a/application/src/main/data/json/system/widget_types/timeseries_line_chart.json
+++ b/application/src/main/data/json/system/widget_types/timeseries_line_chart.json
@@ -1,8 +1,8 @@
{
"fqn": "charts.basic_timeseries",
"name": "Timeseries Line Chart",
- "deprecated": false,
- "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAXk0lEQVR42u2deXwb1bXH+Y+lfY/utK98WkogZSk7LcujBF4pAT6lLaWvlBZeCrTw4AGFvrbQkPYRwlIWfyCr7WA7XrN4wzZx7DjxIu+2vEteZHmTJdmOd8faR9K838y1FUWWZVmaGQM585mPPmNZR/fOvV+de+455849iw/7OOuss/gojpaWFhI8cwQFVlQqVbp4JCUlzc3NaTSaveKhVqsJLBKMHCx2eDyeuLg4r9ebl5c3NjZGGosEpQEL+qm+vh4XKSkp0F4ZGRkWi4XAIsFowdqzZw/HcbiAurLb7Z2dndnZ2QQWCUYFVnd3d1FRkU914XV2djY5OZnAIsGowIqPj/cNfOXl5bDiY2Nj+/v7/anC0UIHHeEdwZUQxkRY8WS8k6AENhb5sUiQwCJBAosancAisEiQwCJBAosancAisEiQwCJBAosancAisEiQwCJBAosancAisEiQwCJBAosECSwCiwQJLBIksEiQwCKwSJDAIkECiwQJLAKLBAksEiSwSJDAIrBIkMAiQQKLBAksAosECSwSJLBIkMAisEiQwCJBAuuzK5jV7SKwCCyJBd+pd35nz7x6xE1gEViSCb7XIFCF84lCO4FFYEkj+IFaoOqSuPn1cfMX75nvnvQQWARWtIKvFg6CqnWx8+83Op8ptuP6hWN2AovAikowsd3FqIppcDaOuIv7uUtj5y+JnR+a9RBYBFaEgvs6BKow9r1V6wBV7PzdYRve3KJyEFgEViSC6VrXxSJVL+YN+6jC+ZHOBQV2Wfz8uNW75lXVTni2q50/zbLWGN0E1qdAEP4q0AOqXq92ZKi6/cHC+auPBKX1dp1zTarq9vBweWytctyaamETVZy3pFim7V6FwMKOX2yPQmwup9Pp0tLScnJy2J5NBFaII7uHA1Xora3Vwgi4FKz9ncIQeXWC5aTTq1hVwU1OD/fMUfv3PjzF0w1JlieL7HcftOIaF0qA5Xa7ExISfG/t2LED79SJB4EV4ijs4+BWQD+9orIzjJaChfMnWUJfxrY45a6qYc6T1O56pMB2ady8j6cfpllfOOZI1bgaxMpgSnGVSNtbRf2ygzU9Pf3mm29iYybs+wUthe2Z8KbRaMSOmATWcgd6iFH1t3K7j6GgYCWIs8Ub91nsnPRVbWpu0Yx7Pmh03i/iy851cfP3HLS+WuU40s8trc/OJsHTtj52ric8H1vkYLlcLrPZDC21fft2QIZ9VvGmyWTKzc0lsIIeRwc4phX+Umb377OgYOH80X5BSaRpXBJW1eXhXy53XL131scTBtxNH9t2qJ2Vw1zQavjOxw8LPrb7DlkdbjnBws6XBoMBF9hVdXx8fOfOnbjGRpilpaUE1tKjwuD+brzQkc+V2AM6bDmwdoju+NvTrW6PZFVl4UicN6dY4YyFF63O5A7Nk+8Eed9PnIHstmqHjGBhp8Jdu3YdOHAAJjx2kysuLsbWvbt378ZemP5U0X6FOJLKdevjTqJLHs0cAUZhnukV3awjPyjRS1KNA6qudXtOrgMZhYPhV8P/fLe4/5LYk5jMJpTp5N2vEANi0GvSWL4DHqDL9wpKAhoiqCZYTmPhfLvOAcG7D1g93miranV578wQLKo/lTpClBj6hOAWlTAg3rDPsqKbjfxYMgo2jbqvFOdTyFloWL63lutIjFPf3yeIlw5xUVZ1c4XA6J0ZllqzOxqw6s3un4omPywzL4G1JoLNY27mE3rssB390bh6sHD+X6UAxC9ybNFUtXLYjfELqRPZ3dyKJYYGC6/HBrlrE4X7golGYCkniN9xy5h7W42DUYXYXwiqVuxmmMyYuOF7GszuyKoKz+dNycI3/KPSEU6J4VR1T4sTpGI60jnhIbBkF+ydEpxDd2Sccg49nG+rN4XbW8udfy1ziGrPFllVnz0qWEX3HrL6+I4eLJxPiRk+Pz5gtXEEljyCjKd/3zd9KhKyzwJT3ee5jhKsMgN3+V5B5cCrudqq5uo4CF7xoQUe//BLDKeqNSb3hnSLtIkYBJZwwAeNJKr/2G/1j6yBpzRtWDytqpuZ1nm+xL6qqo7Me68RjaF36hyrLTGcqsJiWy+6fIsHuGhatc7sZhH3Mxos/bQHmcQYAnw8wZLFuPDGkYHQtlQ0YBUtJgAitBdmVWHq/bZAyJJArkQEJYZZ1deqhWH6+iTLqCWSePngrOepIjtrRrTemQWW082PznvbTgjxsnsOWf2DIU8ese9rdzGeJOytEBGVVyocYd5jkhhtBPTHBzn5wIJu/kWugO9v8m2nnG1el3CGbNVZh/f1GgdTeBjoETxFVFResMb82JcbLLTFpM0LIwnauEDPJXe4/ppvQOfhZ/TLXBuGuasSTuWQsBNx/scL7QltrgCrXG6wPupdSAA8seiZDHGPUKvMK4vpW8QlhikIcK9LspyWi9H7DF/1Rb7jXn74XX6+GaD5V5Xz8Cka1/WiCO4IjYkoaqO46E1esKC64VeUFawdTc6NB63wPbK8qNAnxiAYT3ekWx4rtMe3LhtckxssnL/OE3TDPxcTAJe7R/QcS1tAnylT1Q/bhGxYhNih13nj+7zq7NPO2gtnau/jRxJ4x3DZEHfXolWK1KDM7lPaVF6wgD8zC+QDyzzvZfFgdkIbIyKLjJEHc23wJm/KHEHqyHv1Tnj/cns4mKVhWk4KgHWgU2gcuMfmHN4Q9xgjLlG8KcW6XMKCHFV9tkQYqV/K+YivPI9XncOP7uPHMviex/m6i/wh6z16TVLu83/Oyo1tnAr4BhnBwsDkM2KqZUuy/psY3IBNUAJoTGvAx3KC7QbdaMfLjSOh0ldYOGVXk3O5e2wdcyPlC5oYzg4lfwPwPjyRqZ4v+4oAkOGN0zy009pDha+XHb7fWv4lH2Heys+fbLzTpHmta6BGbXbICxZSY9m46wtiSA4WZlXQ2Ehqy+91rZXiWU5wuvnXaPHxtqcbR5ZlImkxARBuyaX3iDeZ++OPxxwKK9cW45i1ej3qX1DwcIF+wXJH5hYML2anAvfnik826yrN2q3zDbd7Kz/ng8xd9dWZpgd554gsYCEHbUO60Chv1DpYKAqRV8nB+suCF9u+hiNaUMHOgXphBBEberLlUfYjDnqyDHQopKX3+HcxsIjE4hqToqO22mw/qb4LNR+tvPmKuBOYLxtPemDVsA7FeVfKBOwKf5Em01xP37GRjs2W+ptx457K83mPTRawEtuE3yKcubBpYOWwZEUk0UoIFrwm+N1cFj+tbXt/qvm3zaaZTwxYHH7E6Jjp5l+5q74iXjykHrEF/fwuMUv4tjSLurk1aKQ5q4dT2BycaP096uysubh12PSQOMO4cnFFBiY90LKhS2wbNuj6imUZCi0u741ifgimXWzAZn/uOq6XECwsXX8k5bC5/FqmGGz117cZBj4JYOl7s1AfV/WFYL1zoJGr/gb+nG26v8k8v/TzcB1BJ6Fx3i/p890azHm2WmuLyq7wPGO4813U1lP5hc6BBvxZbuBYqg/0FhRE+E4+WcBiE5n7Mq2+Yt6sFZTWbUlTbq80YA2NaisL72VI2WuvstddLfRlzbfRkWsL1gGVxl57JSoz2B3P3tEONnPV38Q7c+q7m0yzS0XeqRca5/bkKV/bPF8SGGlWBqxefQGvOhdjmb43x/cmVnj/6bjDf066NmDBRckyTDK0tq6BamPnWzp9Yb3RCS8A3sQqvGjB4qZ4/YselWAtWisuMHR9AJug2TQ517hR/Kl90b9RlAerSfV3QX3WXeNvV2mGNK5qYaKOIbLZOLVcAiBSo3B/Bb0c81/DalbSM6IZbGcDt0n7RvQlSg9WjKpn84Hddccecldd4JspQJc01Lx8R0IH1L7LEylYXk7wy9UKv35Pxbl5BY+W6Uf8TE7HifbnxOLOMWtfWxOwwLdDJdx1r/7jgH+1G/odtZcJP4b6H2DOFZgAKJqhD+TYEKi4TpzrvF3nUGwCK04Dxx11l6N6Uy0Ph3aRKAuWtZs3x/KdD3lqvuHvQLPVXTve+hSGqgVvh+qc+iN31jan8B7rqsGaOsqrr2Hf01V+z8bExv85GsT+MHa+Iyrzs1EuNJnCYGFahKIxpVrGqh1ko6St/rrWYXNAAuCV8XPChEv0LyAAFV6JLoyz0d+j2mydb9ggKNTG25rMFkkaJwqwnKP8eLYQSKq/xB+mE6XfUpc+NNi9t214yFdM56AaHh1nxaJLrfqrfM9j/HSpGLZf6bD1CjEEJth45XBfFkt3LB4I/sPS9+ZiQBRsmsaNWapGxcDCbEgs95wAO8//bB0esdbdgLo5ar/bbujz/9cT2WZfOLxkgFsRqT5dJgZcfNWM6qqBnlS12RnxPeJHKM42LmobHpaqcVYP1vghvvdpvvHy0+JHdd/muzeN6JM2JHRihlzUH7xdDqmaYvLjmotuOyWovlaIRrnGg5fHTfN9/8tXfl74ZM3XhE96nQiZregzxIwGg6/AlurydoNeGbAmWzYJNrvqgdAfazGesNbfJMznay9tH+o9FaEr07EEQKxADCEO7YJpAbhcdHl/zjc49OvSQzjMlrtHUcef7an6knawRcLGWT1Yi34/vvrL/hFv/PcRMWEoRJejQvEtwoTxgfQ218Brp1QdAlL4Kug/X3rGgjl1ofiBc6dqf8Y7T+DtjnHPxaJhuzR7ZIn+GGb9h9l+d79KbrDQK6gnHIN5qrKVTTHjFHN0uWq+BZPZVyIidI8V2kMitRfuJdZoMInwJzyTtap3HbVXLLxZu97QtT2c4YyVqNMf8YrRwD7dIWkbZ/Vg9T7LG2NEmE4zwrHgjoVUkXobukIbDy4GyEDP5Me89kHeFxCA5ht4hR/P4tXXLbzTvpG3dPhmhYgrC4vpjjvCM6VnzKofiVPF8/t0+2UFC64EwVXd8XKYgvA7zKl/LHL/TaYqIIiEk4pgrddsmsbkmvksBPO/7kaMg75IEQRhTQ707LPXfo99APAZunYEdZv5n4WqfE/Vl/F5fLnkjSON8Q59hRmN/0NXQlQI4Qt8Elm2M45FA8s5xpt28k03nja8Qp+NpfvPCptH3cwLHILdwEcIqbRj7S+yqSIs6xXnO5GBhTmggEjV11tM4+ELQgMJMTUhuHZB10BtUEFM1jDD5aoWpkSYUYpIcctU1QVHlBhUOZshC1lMVJcbkedV68Rp4G/CbxalwTrcx7Ek8SpjWBVij/WBHzXwi+bqeN1TfP13+OH3eE9gdiVSGCCFVS6rbQJD105R4SNstwndKS1YsJph3+DLMQatlkjEeRDtEdn6yjFVeoCZD/3HNAqbaSIYF15VuV59nqXh1gW8qr4OvEB8ANNsLJ5v+GGY4+YagIWnXLBJsv+zN0NXCKlIF4u6ZzK89bcAC4+lY0NtuYGLoAkQvXJXf401ZatxVEKwYOiIxs1lDNnVjqHgcqL1CYEA1fmoJPNKQMuyia1Y4Q09faURVLWrv2qm6efsSwAovrPVaFycBj6JNy2qi4CvTDMbCcDC4zfR5bemWle15u5BMbcaidJhgsXyLf2fRLXaJtAOtTlr1jGzt8PQJQlYMJXY9FMcoSJ2gLngixGneP8K/6RX9S9syjLd/J9w00TZzd39lbNNP1nAq/ILY20vjHS8wlCDjSWfLyZasJAzjwdaosuxNmFVFcJiI5b0jaUNK5abVNbDHDwrPvYpdBMgXG9puIUNEJGpgYATowzTgj4zJVJ3JdetenzRg3DeZMt/aQY7JOxmuNamm355akavOrdXny+r9zhasFjWx10HLBEs5nw4P9xHVW9MnQgz1L9iEzSZT0ITMA8Qpk7RtB0whftHiAQMVEsSYDFrXp1o/QNST2WawAJWIAtw4buSO94VFVhY9MOS+LBqKoIKIe0TyZ9IAfVfXrf0QMY+m0WuVl0t3wQucTgQfr4TrY8HtV7Dabvxtv9mSVefkJzVsCMEQwqUGBVYsJDE0Kk14gptEp+vj0TQEIX+PFv4DMK00jYBxgIk0bI5fASJXJohLXQecr07DD2fLrCUEYwcLCyWxWI3TO4OdkXuHCrsc7E1wVg6F7RE+AwF62rvbLVR+iaA15tlHHDV/xZgcq3YdkjcgyCmWp9tPtYArD+XCjE7hHGirBDWH7OHeQb1u94nrvN5/iOjTE0AB8+s+j5mchk73w5TsKevjDmflnouCKyowOqbEfLNcX4cXjJaiAohSQGpCtB8Sx/OVCj6XZHWnFLRI18TwJMkprucwzyoLBISUtDFvNv+IBJY0oD1e1HNPF1sl6RCbHnkH47Ygy5LRFqzAm3X13uQTfEQuoaLMoQgclRYPA4TTAJLSrCwipKlGBwblCbGhJVhLGMEz87zlZUvZuj+IDnax2yuIkNhqI2loyDKdlyVFtwjarY4a4SkDOSonAl8KArWw3mrjtmtWCEkLDCLbSFM5F0IE2GhgZJth6DvnPpeMdn1vKAjHRIBWH7BcstQCawIwTomPo4BTnCVgZOwQogAsg1eak1CnbLFVdS3pCyEiZRsu0WTS1xr2vo7/+QTZASwgONy8WACK3Kw2DSN7XQlbYVeLrezdG9ucRU126d0TdquWvU+CwMLQethE3vzRNsfhVTgpp+dOXwoB5Zo96yw6DviJwqzRyE+J9ryt6VJ/PzW1Qoi/46ZXEgGR74U0ogRHkY8JHQUj8A6DSybzZaZmZmamqrVavGnRqNhexdiM7AAsGC2v9fglKlC/xCfVsDOD/xSv9eq7bBOiz3FAHkHWGnNFv+cUXxEC1Z+fr5er8cWmDExMezP0dHRoBoL1k9kD+cMp0Jwr98gLt3ckGHxL2UN2w7LE3wmF1JNfMMigbWKoRBbNSUmJvLibqvYpAk7geGdALAiq0r4FXpdfL7qrtOTcNa87fp1GTC5TJqtZxofEoDFNlmdmJjgxV3m7HY7tpXLzs5WGCysN0dOX8Mnr+2Qcxf0yQsEViiwsJXc/v37MRoygJhphT3lkpOTFQaLsUW99RkBS6VSbdu2LV08oLTKy8uxjS828O3v7/enCkdkW+PReaadp+1XeNoiZI6DGltqvCugsUjwTPRjEVgkSGCRIIFFggQWgUWCBBYJElgkSGBR25EggUWCBBYJEljUdiRIYJEggUWCBBY1OgkSWCRIYJEggUWNToIEFgkSWCRIYFGjkyCBRYIEFgkSWNToJEhgkSCBRYIEFjU6CRJYJEhgkSCBRY1OggQWCRJYJEhgUaOTIIFFggQWCRJY1OgkSGCRIIFFggQWNToJElgkSGCRIIFFjU5gEVgkqBRYOp0uLS0tJycH+1MQWCQoGVg7duzAZmB14kFgkaA0YEFLYXsmXBiNxry8PAKLBKUBy+VyxcXF4cJkMuXm5hJYJCjZULhz5068YiPM0tJSAosEJQOruLgYGxfu3r0be2EG7FdIBx3hHkE9CxgQQzgdeGUPKvHTWKLS90PHGXIQWHR8AsCqqKiA+YVXxepXVlaGEvEqd0HYqri3t7egoMD3Z3V19ZEjR+Qr0Wq1ZmZmpqamYp7k8/WkpKR4PB6ZSrTZbIcOHYL3G3eKP5ubm9G2WVlZISwfJcCanJxEK+ACNz81NaUAVdPT04mJibjA68zMjKxlDQ4O4gcTExPD/iwqKqqvr5e1RLgJ+/r6gJGv0MOHD2/ZsiUg4CHhgZ/KwMAASty+fbvdbt+zZw9+P1VVVQGecKXB6urqYg6I48eP41oBsNDEmJyirPj4ePma2/9Ai7MLdPbBgwfx+0YHyFqixWJJSkpijkOghvuV9U7n5uZaW1uzs7P9+TYYDGsJlkajYUMSXrVarQLd7HA4kpOToUjw6nQ6lQRr69atKL2jo0PW0RChs4SEhImJCWgRXOAe5QYLAZXCwkKoRvZnd3d3gBt8DcAaHh5mQR5UBT8vBboZ4fCSkhJcHD16lJkFioHFLkZGRvx/3JJbdfv378doyNQV9BYsns2bN/t6XfIDGEFB4mLXrl14haKSz6Q7a1UNAVsHowNeca2MxkLgEl2LV1wrCVZDQwN6GlYI2JKpLGjibdu2pYsH7Ff2pqwaS6/XoyVBM9gFTy+99BKGAplmY/8Pl7O7ukBGoYYAAAAASUVORK5CYII=",
+ "deprecated": true,
+ "image": "tb-image:Z2F0ZXdheV9nZW5lcmFsX2NoYXJ0X3N0YXRpc3RpY3Nfc3lzdGVtX3dpZGdldF9pbWFnZS5wbmc=:IkdhdGV3YXkgZ2VuZXJhbCBjaGFydCBzdGF0aXN0aWNzIiBzeXN0ZW0gd2lkZ2V0IGltYWdl;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAXk0lEQVR42u2deXwb1bXH+Y+lfY/utK98WkogZSk7LcujBF4pAT6lLaWvlBZeCrTw4AGFvrbQkPYRwlIWfyCr7WA7XrN4wzZx7DjxIu+2vEteZHmTJdmOd8faR9K838y1FUWWZVmaGQM585mPPmNZR/fOvV+de+455849iw/7OOuss/gojpaWFhI8cwQFVlQqVbp4JCUlzc3NaTSaveKhVqsJLBKMHCx2eDyeuLg4r9ebl5c3NjZGGosEpQEL+qm+vh4XKSkp0F4ZGRkWi4XAIsFowdqzZw/HcbiAurLb7Z2dndnZ2QQWCUYFVnd3d1FRkU914XV2djY5OZnAIsGowIqPj/cNfOXl5bDiY2Nj+/v7/anC0UIHHeEdwZUQxkRY8WS8k6AENhb5sUiQwCJBAosancAisEiQwCJBAosancAisEiQwCJBAosancAisEiQwCJBAosancAisEiQwCJBAosECSwCiwQJLBIksEiQwCKwSJDAIkECiwQJLAKLBAksEiSwSJDAIrBIkMAiQQKLBAksAosECSwSJLBIkMAisEiQwCJBAuuzK5jV7SKwCCyJBd+pd35nz7x6xE1gEViSCb7XIFCF84lCO4FFYEkj+IFaoOqSuPn1cfMX75nvnvQQWARWtIKvFg6CqnWx8+83Op8ptuP6hWN2AovAikowsd3FqIppcDaOuIv7uUtj5y+JnR+a9RBYBFaEgvs6BKow9r1V6wBV7PzdYRve3KJyEFgEViSC6VrXxSJVL+YN+6jC+ZHOBQV2Wfz8uNW75lXVTni2q50/zbLWGN0E1qdAEP4q0AOqXq92ZKi6/cHC+auPBKX1dp1zTarq9vBweWytctyaamETVZy3pFim7V6FwMKOX2yPQmwup9Pp0tLScnJy2J5NBFaII7uHA1Xora3Vwgi4FKz9ncIQeXWC5aTTq1hVwU1OD/fMUfv3PjzF0w1JlieL7HcftOIaF0qA5Xa7ExISfG/t2LED79SJB4EV4ijs4+BWQD+9orIzjJaChfMnWUJfxrY45a6qYc6T1O56pMB2ady8j6cfpllfOOZI1bgaxMpgSnGVSNtbRf2ygzU9Pf3mm29iYybs+wUthe2Z8KbRaMSOmATWcgd6iFH1t3K7j6GgYCWIs8Ub91nsnPRVbWpu0Yx7Pmh03i/iy851cfP3HLS+WuU40s8trc/OJsHTtj52ric8H1vkYLlcLrPZDC21fft2QIZ9VvGmyWTKzc0lsIIeRwc4phX+Umb377OgYOH80X5BSaRpXBJW1eXhXy53XL131scTBtxNH9t2qJ2Vw1zQavjOxw8LPrb7DlkdbjnBws6XBoMBF9hVdXx8fOfOnbjGRpilpaUE1tKjwuD+brzQkc+V2AM6bDmwdoju+NvTrW6PZFVl4UicN6dY4YyFF63O5A7Nk+8Eed9PnIHstmqHjGBhp8Jdu3YdOHAAJjx2kysuLsbWvbt378ZemP5U0X6FOJLKdevjTqJLHs0cAUZhnukV3awjPyjRS1KNA6qudXtOrgMZhYPhV8P/fLe4/5LYk5jMJpTp5N2vEANi0GvSWL4DHqDL9wpKAhoiqCZYTmPhfLvOAcG7D1g93miranV578wQLKo/lTpClBj6hOAWlTAg3rDPsqKbjfxYMgo2jbqvFOdTyFloWL63lutIjFPf3yeIlw5xUVZ1c4XA6J0ZllqzOxqw6s3un4omPywzL4G1JoLNY27mE3rssB390bh6sHD+X6UAxC9ybNFUtXLYjfELqRPZ3dyKJYYGC6/HBrlrE4X7golGYCkniN9xy5h7W42DUYXYXwiqVuxmmMyYuOF7GszuyKoKz+dNycI3/KPSEU6J4VR1T4sTpGI60jnhIbBkF+ydEpxDd2Sccg49nG+rN4XbW8udfy1ziGrPFllVnz0qWEX3HrL6+I4eLJxPiRk+Pz5gtXEEljyCjKd/3zd9KhKyzwJT3ee5jhKsMgN3+V5B5cCrudqq5uo4CF7xoQUe//BLDKeqNSb3hnSLtIkYBJZwwAeNJKr/2G/1j6yBpzRtWDytqpuZ1nm+xL6qqo7Me68RjaF36hyrLTGcqsJiWy+6fIsHuGhatc7sZhH3Mxos/bQHmcQYAnw8wZLFuPDGkYHQtlQ0YBUtJgAitBdmVWHq/bZAyJJArkQEJYZZ1deqhWH6+iTLqCWSePngrOepIjtrRrTemQWW082PznvbTgjxsnsOWf2DIU8ese9rdzGeJOytEBGVVyocYd5jkhhtBPTHBzn5wIJu/kWugO9v8m2nnG1el3CGbNVZh/f1GgdTeBjoETxFVFResMb82JcbLLTFpM0LIwnauEDPJXe4/ppvQOfhZ/TLXBuGuasSTuWQsBNx/scL7QltrgCrXG6wPupdSAA8seiZDHGPUKvMK4vpW8QlhikIcK9LspyWi9H7DF/1Rb7jXn74XX6+GaD5V5Xz8Cka1/WiCO4IjYkoaqO46E1esKC64VeUFawdTc6NB63wPbK8qNAnxiAYT3ekWx4rtMe3LhtckxssnL/OE3TDPxcTAJe7R/QcS1tAnylT1Q/bhGxYhNih13nj+7zq7NPO2gtnau/jRxJ4x3DZEHfXolWK1KDM7lPaVF6wgD8zC+QDyzzvZfFgdkIbIyKLjJEHc23wJm/KHEHqyHv1Tnj/cns4mKVhWk4KgHWgU2gcuMfmHN4Q9xgjLlG8KcW6XMKCHFV9tkQYqV/K+YivPI9XncOP7uPHMviex/m6i/wh6z16TVLu83/Oyo1tnAr4BhnBwsDkM2KqZUuy/psY3IBNUAJoTGvAx3KC7QbdaMfLjSOh0ldYOGVXk3O5e2wdcyPlC5oYzg4lfwPwPjyRqZ4v+4oAkOGN0zy009pDha+XHb7fWv4lH2Heys+fbLzTpHmta6BGbXbICxZSY9m46wtiSA4WZlXQ2Ehqy+91rZXiWU5wuvnXaPHxtqcbR5ZlImkxARBuyaX3iDeZ++OPxxwKK9cW45i1ej3qX1DwcIF+wXJH5hYML2anAvfnik826yrN2q3zDbd7Kz/ng8xd9dWZpgd554gsYCEHbUO60Chv1DpYKAqRV8nB+suCF9u+hiNaUMHOgXphBBEberLlUfYjDnqyDHQopKX3+HcxsIjE4hqToqO22mw/qb4LNR+tvPmKuBOYLxtPemDVsA7FeVfKBOwKf5Em01xP37GRjs2W+ptx457K83mPTRawEtuE3yKcubBpYOWwZEUk0UoIFrwm+N1cFj+tbXt/qvm3zaaZTwxYHH7E6Jjp5l+5q74iXjykHrEF/fwuMUv4tjSLurk1aKQ5q4dT2BycaP096uysubh12PSQOMO4cnFFBiY90LKhS2wbNuj6imUZCi0u741ifgimXWzAZn/uOq6XECwsXX8k5bC5/FqmGGz117cZBj4JYOl7s1AfV/WFYL1zoJGr/gb+nG26v8k8v/TzcB1BJ6Fx3i/p890azHm2WmuLyq7wPGO4813U1lP5hc6BBvxZbuBYqg/0FhRE+E4+WcBiE5n7Mq2+Yt6sFZTWbUlTbq80YA2NaisL72VI2WuvstddLfRlzbfRkWsL1gGVxl57JSoz2B3P3tEONnPV38Q7c+q7m0yzS0XeqRca5/bkKV/bPF8SGGlWBqxefQGvOhdjmb43x/cmVnj/6bjDf066NmDBRckyTDK0tq6BamPnWzp9Yb3RCS8A3sQqvGjB4qZ4/YselWAtWisuMHR9AJug2TQ517hR/Kl90b9RlAerSfV3QX3WXeNvV2mGNK5qYaKOIbLZOLVcAiBSo3B/Bb0c81/DalbSM6IZbGcDt0n7RvQlSg9WjKpn84Hddccecldd4JspQJc01Lx8R0IH1L7LEylYXk7wy9UKv35Pxbl5BY+W6Uf8TE7HifbnxOLOMWtfWxOwwLdDJdx1r/7jgH+1G/odtZcJP4b6H2DOFZgAKJqhD+TYEKi4TpzrvF3nUGwCK04Dxx11l6N6Uy0Ph3aRKAuWtZs3x/KdD3lqvuHvQLPVXTve+hSGqgVvh+qc+iN31jan8B7rqsGaOsqrr2Hf01V+z8bExv85GsT+MHa+Iyrzs1EuNJnCYGFahKIxpVrGqh1ko6St/rrWYXNAAuCV8XPChEv0LyAAFV6JLoyz0d+j2mydb9ggKNTG25rMFkkaJwqwnKP8eLYQSKq/xB+mE6XfUpc+NNi9t214yFdM56AaHh1nxaJLrfqrfM9j/HSpGLZf6bD1CjEEJth45XBfFkt3LB4I/sPS9+ZiQBRsmsaNWapGxcDCbEgs95wAO8//bB0esdbdgLo5ar/bbujz/9cT2WZfOLxkgFsRqT5dJgZcfNWM6qqBnlS12RnxPeJHKM42LmobHpaqcVYP1vghvvdpvvHy0+JHdd/muzeN6JM2JHRihlzUH7xdDqmaYvLjmotuOyWovlaIRrnGg5fHTfN9/8tXfl74ZM3XhE96nQiZregzxIwGg6/AlurydoNeGbAmWzYJNrvqgdAfazGesNbfJMznay9tH+o9FaEr07EEQKxADCEO7YJpAbhcdHl/zjc49OvSQzjMlrtHUcef7an6knawRcLGWT1Yi34/vvrL/hFv/PcRMWEoRJejQvEtwoTxgfQ218Brp1QdAlL4Kug/X3rGgjl1ofiBc6dqf8Y7T+DtjnHPxaJhuzR7ZIn+GGb9h9l+d79KbrDQK6gnHIN5qrKVTTHjFHN0uWq+BZPZVyIidI8V2kMitRfuJdZoMInwJzyTtap3HbVXLLxZu97QtT2c4YyVqNMf8YrRwD7dIWkbZ/Vg9T7LG2NEmE4zwrHgjoVUkXobukIbDy4GyEDP5Me89kHeFxCA5ht4hR/P4tXXLbzTvpG3dPhmhYgrC4vpjjvCM6VnzKofiVPF8/t0+2UFC64EwVXd8XKYgvA7zKl/LHL/TaYqIIiEk4pgrddsmsbkmvksBPO/7kaMg75IEQRhTQ707LPXfo99APAZunYEdZv5n4WqfE/Vl/F5fLnkjSON8Q59hRmN/0NXQlQI4Qt8Elm2M45FA8s5xpt28k03nja8Qp+NpfvPCptH3cwLHILdwEcIqbRj7S+yqSIs6xXnO5GBhTmggEjV11tM4+ELQgMJMTUhuHZB10BtUEFM1jDD5aoWpkSYUYpIcctU1QVHlBhUOZshC1lMVJcbkedV68Rp4G/CbxalwTrcx7Ek8SpjWBVij/WBHzXwi+bqeN1TfP13+OH3eE9gdiVSGCCFVS6rbQJD105R4SNstwndKS1YsJph3+DLMQatlkjEeRDtEdn6yjFVeoCZD/3HNAqbaSIYF15VuV59nqXh1gW8qr4OvEB8ANNsLJ5v+GGY4+YagIWnXLBJsv+zN0NXCKlIF4u6ZzK89bcAC4+lY0NtuYGLoAkQvXJXf401ZatxVEKwYOiIxs1lDNnVjqHgcqL1CYEA1fmoJPNKQMuyia1Y4Q09faURVLWrv2qm6efsSwAovrPVaFycBj6JNy2qi4CvTDMbCcDC4zfR5bemWle15u5BMbcaidJhgsXyLf2fRLXaJtAOtTlr1jGzt8PQJQlYMJXY9FMcoSJ2gLngixGneP8K/6RX9S9syjLd/J9w00TZzd39lbNNP1nAq/ILY20vjHS8wlCDjSWfLyZasJAzjwdaosuxNmFVFcJiI5b0jaUNK5abVNbDHDwrPvYpdBMgXG9puIUNEJGpgYATowzTgj4zJVJ3JdetenzRg3DeZMt/aQY7JOxmuNamm355akavOrdXny+r9zhasFjWx10HLBEs5nw4P9xHVW9MnQgz1L9iEzSZT0ITMA8Qpk7RtB0whftHiAQMVEsSYDFrXp1o/QNST2WawAJWIAtw4buSO94VFVhY9MOS+LBqKoIKIe0TyZ9IAfVfXrf0QMY+m0WuVl0t3wQucTgQfr4TrY8HtV7Dabvxtv9mSVefkJzVsCMEQwqUGBVYsJDE0Kk14gptEp+vj0TQEIX+PFv4DMK00jYBxgIk0bI5fASJXJohLXQecr07DD2fLrCUEYwcLCyWxWI3TO4OdkXuHCrsc7E1wVg6F7RE+AwF62rvbLVR+iaA15tlHHDV/xZgcq3YdkjcgyCmWp9tPtYArD+XCjE7hHGirBDWH7OHeQb1u94nrvN5/iOjTE0AB8+s+j5mchk73w5TsKevjDmflnouCKyowOqbEfLNcX4cXjJaiAohSQGpCtB8Sx/OVCj6XZHWnFLRI18TwJMkprucwzyoLBISUtDFvNv+IBJY0oD1e1HNPF1sl6RCbHnkH47Ygy5LRFqzAm3X13uQTfEQuoaLMoQgclRYPA4TTAJLSrCwipKlGBwblCbGhJVhLGMEz87zlZUvZuj+IDnax2yuIkNhqI2loyDKdlyVFtwjarY4a4SkDOSonAl8KArWw3mrjtmtWCEkLDCLbSFM5F0IE2GhgZJth6DvnPpeMdn1vKAjHRIBWH7BcstQCawIwTomPo4BTnCVgZOwQogAsg1eak1CnbLFVdS3pCyEiZRsu0WTS1xr2vo7/+QTZASwgONy8WACK3Kw2DSN7XQlbYVeLrezdG9ucRU126d0TdquWvU+CwMLQethE3vzRNsfhVTgpp+dOXwoB5Zo96yw6DviJwqzRyE+J9ryt6VJ/PzW1Qoi/46ZXEgGR74U0ogRHkY8JHQUj8A6DSybzZaZmZmamqrVavGnRqNhexdiM7AAsGC2v9fglKlC/xCfVsDOD/xSv9eq7bBOiz3FAHkHWGnNFv+cUXxEC1Z+fr5er8cWmDExMezP0dHRoBoL1k9kD+cMp0Jwr98gLt3ckGHxL2UN2w7LE3wmF1JNfMMigbWKoRBbNSUmJvLibqvYpAk7geGdALAiq0r4FXpdfL7qrtOTcNa87fp1GTC5TJqtZxofEoDFNlmdmJjgxV3m7HY7tpXLzs5WGCysN0dOX8Mnr+2Qcxf0yQsEViiwsJXc/v37MRoygJhphT3lkpOTFQaLsUW99RkBS6VSbdu2LV08oLTKy8uxjS828O3v7/enCkdkW+PReaadp+1XeNoiZI6DGltqvCugsUjwTPRjEVgkSGCRIIFFggQWgUWCBBYJElgkSGBR25EggUWCBBYJEljUdiRIYJEggUWCBBY1OgkSWCRIYJEggUWNToIEFgkSWCRIYFGjkyCBRYIEFgkSWNToJEhgkSCBRYIEFjU6CRJYJEhgkSCBRY1OggQWCRJYJEhgUaOTIIFFggQWCRJY1OgkSGCRIIFFggQWNToJElgkSGCRIIFFjU5gEVgkqBRYOp0uLS0tJycH+1MQWCQoGVg7duzAZmB14kFgkaA0YEFLYXsmXBiNxry8PAKLBKUBy+VyxcXF4cJkMuXm5hJYJCjZULhz5068YiPM0tJSAosEJQOruLgYGxfu3r0be2EG7FdIBx3hHkE9CxgQQzgdeGUPKvHTWKLS90PHGXIQWHR8AsCqqKiA+YVXxepXVlaGEvEqd0HYqri3t7egoMD3Z3V19ZEjR+Qr0Wq1ZmZmpqamYp7k8/WkpKR4PB6ZSrTZbIcOHYL3G3eKP5ubm9G2WVlZISwfJcCanJxEK+ACNz81NaUAVdPT04mJibjA68zMjKxlDQ4O4gcTExPD/iwqKqqvr5e1RLgJ+/r6gJGv0MOHD2/ZsiUg4CHhgZ/KwMAASty+fbvdbt+zZw9+P1VVVQGecKXB6urqYg6I48eP41oBsNDEmJyirPj4ePma2/9Ai7MLdPbBgwfx+0YHyFqixWJJSkpijkOghvuV9U7n5uZaW1uzs7P9+TYYDGsJlkajYUMSXrVarQLd7HA4kpOToUjw6nQ6lQRr69atKL2jo0PW0RChs4SEhImJCWgRXOAe5QYLAZXCwkKoRvZnd3d3gBt8DcAaHh5mQR5UBT8vBboZ4fCSkhJcHD16lJkFioHFLkZGRvx/3JJbdfv378doyNQV9BYsns2bN/t6XfIDGEFB4mLXrl14haKSz6Q7a1UNAVsHowNeca2MxkLgEl2LV1wrCVZDQwN6GlYI2JKpLGjibdu2pYsH7Ff2pqwaS6/XoyVBM9gFTy+99BKGAplmY/8Pl7O7ukBGoYYAAAAASUVORK5CYII=",
"description": "Displays changes to time-series data over time—for example, temperature or humidity readings.",
"descriptor": {
"type": "timeseries",
@@ -22,6 +22,5 @@
"basicModeDirective": "tb-flot-basic-config",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false},\"title\":\"Timeseries Line Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\"}"
},
- "externalId": null,
"tags": null
}
\ No newline at end of file
diff --git a/application/src/main/data/upgrade/3.6.3/schema_update.sql b/application/src/main/data/upgrade/3.6.3/schema_update.sql
new file mode 100644
index 0000000000..e2d454353e
--- /dev/null
+++ b/application/src/main/data/upgrade/3.6.3/schema_update.sql
@@ -0,0 +1,108 @@
+--
+-- Copyright © 2016-2024 The Thingsboard Authors
+--
+-- 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.
+--
+
+
+-- create new attribute_kv table schema
+DO
+$$
+ BEGIN
+ -- in case of running the upgrade script a second time:
+ IF EXISTS(SELECT 1 FROM information_schema.columns WHERE table_name = 'attribute_kv' and column_name='entity_type') THEN
+ DROP VIEW IF EXISTS device_info_view;
+ DROP VIEW IF EXISTS device_info_active_attribute_view;
+ ALTER INDEX IF EXISTS idx_attribute_kv_by_key_and_last_update_ts RENAME TO idx_attribute_kv_by_key_and_last_update_ts_old;
+ IF EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'attribute_kv_pkey') THEN
+ ALTER TABLE attribute_kv RENAME CONSTRAINT attribute_kv_pkey TO attribute_kv_pkey_old;
+ END IF;
+ ALTER TABLE attribute_kv RENAME TO attribute_kv_old;
+ CREATE TABLE IF NOT EXISTS attribute_kv
+ (
+ entity_id uuid,
+ attribute_type int,
+ attribute_key int,
+ bool_v boolean,
+ str_v varchar(10000000),
+ long_v bigint,
+ dbl_v double precision,
+ json_v json,
+ last_update_ts bigint,
+ CONSTRAINT attribute_kv_pkey PRIMARY KEY (entity_id, attribute_type, attribute_key)
+ );
+ END IF;
+ END;
+$$;
+
+-- rename ts_kv_dictionary table to key_dictionary or create table if not exists
+DO
+$$
+ BEGIN
+ IF EXISTS(SELECT 1 FROM information_schema.tables WHERE table_name = 'ts_kv_dictionary') THEN
+ ALTER TABLE ts_kv_dictionary RENAME CONSTRAINT ts_key_id_pkey TO key_dictionary_id_pkey;
+ ALTER TABLE ts_kv_dictionary RENAME TO key_dictionary;
+ ELSE CREATE TABLE IF NOT EXISTS key_dictionary(
+ key varchar(255) NOT NULL,
+ key_id serial UNIQUE,
+ CONSTRAINT key_dictionary_id_pkey PRIMARY KEY (key)
+ );
+ END IF;
+ END;
+$$;
+
+-- insert keys into key_dictionary
+DO
+$$
+ BEGIN
+ IF EXISTS(SELECT 1 FROM information_schema.tables WHERE table_name = 'attribute_kv_old') THEN
+ INSERT INTO key_dictionary(key) SELECT DISTINCT attribute_key FROM attribute_kv_old ON CONFLICT DO NOTHING;
+ END IF;
+ END;
+$$;
+
+-- migrate attributes from attribute_kv_old to attribute_kv
+DO
+$$
+DECLARE
+ row_num_old integer;
+ row_num integer;
+BEGIN
+ IF EXISTS(SELECT 1 FROM information_schema.tables WHERE table_name = 'attribute_kv_old') THEN
+ INSERT INTO attribute_kv(entity_id, attribute_type, attribute_key, bool_v, str_v, long_v, dbl_v, json_v, last_update_ts)
+ SELECT a.entity_id, CASE
+ WHEN a.attribute_type = 'CLIENT_SCOPE' THEN 1
+ WHEN a.attribute_type = 'SERVER_SCOPE' THEN 2
+ WHEN a.attribute_type = 'SHARED_SCOPE' THEN 3
+ ELSE 0
+ END,
+ k.key_id, a.bool_v, a.str_v, a.long_v, a.dbl_v, a.json_v, a.last_update_ts
+ FROM attribute_kv_old a INNER JOIN key_dictionary k ON (a.attribute_key = k.key);
+ SELECT COUNT(*) INTO row_num_old FROM attribute_kv_old;
+ SELECT COUNT(*) INTO row_num FROM attribute_kv;
+ RAISE NOTICE 'Migrated % of % rows', row_num, row_num_old;
+
+ IF row_num != 0 THEN
+ DROP TABLE IF EXISTS attribute_kv_old;
+ ELSE
+ RAISE EXCEPTION 'Table attribute_kv is empty';
+ END IF;
+
+ CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc);
+ END IF;
+EXCEPTION
+ WHEN others THEN
+ ROLLBACK;
+ RAISE EXCEPTION 'Error during COPY: %', SQLERRM;
+END
+$$;
\ No newline at end of file
diff --git a/application/src/main/java/org/thingsboard/server/ThingsboardInstallApplication.java b/application/src/main/java/org/thingsboard/server/ThingsboardInstallApplication.java
index bead4d4049..226d4233f9 100644
--- a/application/src/main/java/org/thingsboard/server/ThingsboardInstallApplication.java
+++ b/application/src/main/java/org/thingsboard/server/ThingsboardInstallApplication.java
@@ -33,8 +33,7 @@ import java.util.Arrays;
"org.thingsboard.server.dao",
"org.thingsboard.server.common.stats",
"org.thingsboard.server.common.transport.config.ssl",
- "org.thingsboard.server.cache",
- "org.thingsboard.server.springfox"
+ "org.thingsboard.server.cache"
})
public class ThingsboardInstallApplication {
diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
index ca9576b335..6a10791091 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
@@ -93,7 +93,6 @@ import org.thingsboard.server.dao.widget.WidgetsBundleService;
import org.thingsboard.server.queue.discovery.DiscoveryService;
import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider;
-import org.thingsboard.server.queue.util.DataDecodingEncodingService;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
@@ -116,8 +115,9 @@ import org.thingsboard.server.service.telemetry.AlarmSubscriptionService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import org.thingsboard.server.service.transport.TbCoreToTransportService;
-import javax.annotation.Nullable;
-import javax.annotation.PostConstruct;
+import jakarta.annotation.Nullable;
+import jakarta.annotation.PostConstruct;
+
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.ConcurrentHashMap;
@@ -184,10 +184,6 @@ public class ActorSystemContext {
@Getter
private DiscoveryService discoveryService;
- @Autowired
- @Getter
- private DataDecodingEncodingService encodingService;
-
@Autowired
@Getter
private DeviceService deviceService;
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
index f1c0260124..d6b5da4ddf 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
@@ -28,6 +28,7 @@ import org.thingsboard.common.util.LinkedHashMapRemoveEldest;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.actors.TbActorCtx;
import org.thingsboard.server.actors.shared.AbstractContextAwareMsgProcessor;
+import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EdgeUtils;
@@ -66,6 +67,8 @@ import org.thingsboard.server.common.msg.rule.engine.DeviceCredentialsUpdateNoti
import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg;
import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
+import org.thingsboard.server.common.util.KvProtoUtil;
+import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry;
@@ -94,7 +97,7 @@ import org.thingsboard.server.service.rpc.RpcSubmitStrategy;
import org.thingsboard.server.service.state.DefaultDeviceStateService;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
-import javax.annotation.Nullable;
+import jakarta.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -504,13 +507,13 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
private void handleGetAttributesRequest(SessionInfoProto sessionInfo, GetAttributeRequestMsg request) {
int requestId = request.getRequestId();
if (request.getOnlyShared()) {
- Futures.addCallback(findAllAttributesByScope(DataConstants.SHARED_SCOPE), new FutureCallback<>() {
+ Futures.addCallback(findAllAttributesByScope(AttributeScope.SHARED_SCOPE), new FutureCallback<>() {
@Override
public void onSuccess(@Nullable List result) {
GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
.setRequestId(requestId)
.setSharedStateMsg(true)
- .addAllSharedAttributeList(toTsKvProtos(result))
+ .addAllSharedAttributeList(KvProtoUtil.attrToTsKvProtos(result))
.setIsMultipleAttributesRequest(request.getSharedAttributeNamesCount() > 1)
.build();
sendToTransport(responseMsg, sessionInfo);
@@ -531,8 +534,8 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
public void onSuccess(@Nullable List> result) {
GetAttributeResponseMsg responseMsg = GetAttributeResponseMsg.newBuilder()
.setRequestId(requestId)
- .addAllClientAttributeList(toTsKvProtos(result.get(0)))
- .addAllSharedAttributeList(toTsKvProtos(result.get(1)))
+ .addAllClientAttributeList(KvProtoUtil.attrToTsKvProtos(result.get(0)))
+ .addAllSharedAttributeList(KvProtoUtil.attrToTsKvProtos(result.get(1)))
.setIsMultipleAttributesRequest(
request.getSharedAttributeNamesCount() + request.getClientAttributeNamesCount() > 1)
.build();
@@ -554,26 +557,26 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
ListenableFuture> clientAttributesFuture;
ListenableFuture> sharedAttributesFuture;
if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
- clientAttributesFuture = findAllAttributesByScope(DataConstants.CLIENT_SCOPE);
- sharedAttributesFuture = findAllAttributesByScope(DataConstants.SHARED_SCOPE);
+ clientAttributesFuture = findAllAttributesByScope(AttributeScope.CLIENT_SCOPE);
+ sharedAttributesFuture = findAllAttributesByScope(AttributeScope.SHARED_SCOPE);
} else if (!CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
- clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), DataConstants.CLIENT_SCOPE);
- sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), DataConstants.SHARED_SCOPE);
+ clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), AttributeScope.CLIENT_SCOPE);
+ sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), AttributeScope.SHARED_SCOPE);
} else if (CollectionUtils.isEmpty(request.getClientAttributeNamesList()) && !CollectionUtils.isEmpty(request.getSharedAttributeNamesList())) {
clientAttributesFuture = Futures.immediateFuture(Collections.emptyList());
- sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), DataConstants.SHARED_SCOPE);
+ sharedAttributesFuture = findAttributesByScope(toSet(request.getSharedAttributeNamesList()), AttributeScope.SHARED_SCOPE);
} else {
sharedAttributesFuture = Futures.immediateFuture(Collections.emptyList());
- clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), DataConstants.CLIENT_SCOPE);
+ clientAttributesFuture = findAttributesByScope(toSet(request.getClientAttributeNamesList()), AttributeScope.CLIENT_SCOPE);
}
return Futures.allAsList(Arrays.asList(clientAttributesFuture, sharedAttributesFuture));
}
- private ListenableFuture> findAllAttributesByScope(String scope) {
+ private ListenableFuture> findAllAttributesByScope(AttributeScope scope) {
return systemContext.getAttributesService().findAll(tenantId, deviceId, scope);
}
- private ListenableFuture> findAttributesByScope(Set attributesSet, String scope) {
+ private ListenableFuture> findAttributesByScope(Set attributesSet, AttributeScope scope) {
return systemContext.getAttributesService().find(tenantId, deviceId, scope, attributesSet);
}
@@ -602,7 +605,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
if (DataConstants.SHARED_SCOPE.equals(msg.getScope())) {
List attributes = new ArrayList<>(msg.getValues());
if (attributes.size() > 0) {
- List sharedUpdated = msg.getValues().stream().map(this::toTsKvProto)
+ List sharedUpdated = msg.getValues().stream().map(t -> KvProtoUtil.toTsKvProto(t.getLastUpdateTs(), t))
.collect(Collectors.toList());
if (!sharedUpdated.isEmpty()) {
notification.addAllSharedUpdated(sharedUpdated);
@@ -922,28 +925,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
EdgeEvent edgeEvent = EdgeUtils.constructEdgeEvent(tenantId, edgeId, EdgeEventType.DEVICE, EdgeEventActionType.RPC_CALL, deviceId, body);
- return Futures.transform(systemContext.getEdgeEventService().saveAsync(edgeEvent), unused -> {
- systemContext.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
- return null;
- }, systemContext.getDbCallbackExecutor());
- }
-
- private List toTsKvProtos(@Nullable List result) {
- List clientAttributes;
- if (result == null || result.isEmpty()) {
- clientAttributes = Collections.emptyList();
- } else {
- clientAttributes = new ArrayList<>(result.size());
- for (AttributeKvEntry attrEntry : result) {
- clientAttributes.add(toTsKvProto(attrEntry));
- }
- }
- return clientAttributes;
- }
-
- private TsKvProto toTsKvProto(AttributeKvEntry attrEntry) {
- return TsKvProto.newBuilder().setTs(attrEntry.getLastUpdateTs())
- .setKv(toKeyValueProto(attrEntry)).build();
+ return systemContext.getEdgeEventService().saveAsync(edgeEvent);
}
private KeyValueProto toKeyValueProto(KvEntry kvEntry) {
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
index a5828f8d80..79dddfdbe4 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
@@ -54,7 +54,6 @@ import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
@@ -476,11 +475,6 @@ class DefaultTbContext implements TbContext {
return entityActionMsg(originator, tbMsgMetaData, msgData, actionMsgType, profile);
}
- @Override
- public void onEdgeEventUpdate(TenantId tenantId, EdgeId edgeId) {
- mainCtx.getClusterService().onEdgeEventUpdate(tenantId, edgeId);
- }
-
public TbMsg entityActionMsg(E entity, I id, RuleNodeId ruleNodeId, TbMsgType actionMsgType) {
return entityActionMsg(entity, id, ruleNodeId, actionMsgType, null);
}
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
index dab5d05fc3..e6dda31675 100644
--- a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
+++ b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
@@ -36,8 +36,8 @@ import org.thingsboard.server.queue.discovery.TbApplicationEventListener;
import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent;
import org.thingsboard.server.queue.util.AfterStartUp;
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.PreDestroy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
diff --git a/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java b/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java
index 368f55a154..1784c6fa1b 100644
--- a/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java
+++ b/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java
@@ -15,6 +15,7 @@
*/
package org.thingsboard.server.config;
+import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
@@ -43,7 +44,6 @@ import org.thingsboard.server.service.security.auth.oauth2.TbOAuth2ParameterName
import org.thingsboard.server.service.security.model.token.OAuth2AppTokenFactory;
import org.thingsboard.server.utils.MiscUtils;
-import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -115,7 +115,6 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
return request.getParameter("appToken");
}
- @SuppressWarnings("deprecation")
private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction, String appPackage, String appToken) {
if (registrationId == null) {
return null;
@@ -156,8 +155,6 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
addPkceParameters(attributes, additionalParameters);
}
builder.additionalParameters(additionalParameters);
- } else if (AuthorizationGrantType.IMPLICIT.equals(clientRegistration.getAuthorizationGrantType())) {
- builder = OAuth2AuthorizationRequest.implicit();
} else {
throw new IllegalArgumentException("Invalid Authorization Grant Type (" +
clientRegistration.getAuthorizationGrantType().getValue() +
diff --git a/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java b/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java
index 0b859e7b06..73209c1ed6 100644
--- a/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java
+++ b/application/src/main/java/org/thingsboard/server/config/RateLimitProcessingFilter.java
@@ -30,10 +30,10 @@ import org.thingsboard.server.cache.limits.RateLimitService;
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
import org.thingsboard.server.service.security.model.SecurityUser;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
diff --git a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
index a38512496d..21255bf1f7 100644
--- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
@@ -15,71 +15,49 @@
*/
package org.thingsboard.server.config;
-import com.fasterxml.classmate.TypeResolver;
+import io.swagger.v3.core.converter.AnnotatedType;
+import io.swagger.v3.core.converter.ModelConverters;
+import io.swagger.v3.oas.models.Components;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.Operation;
+import io.swagger.v3.oas.models.PathItem;
+import io.swagger.v3.oas.models.examples.Example;
+import io.swagger.v3.oas.models.info.Contact;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.info.License;
+import io.swagger.v3.oas.models.media.Content;
+import io.swagger.v3.oas.models.media.MediaType;
+import io.swagger.v3.oas.models.media.Schema;
+import io.swagger.v3.oas.models.parameters.RequestBody;
+import io.swagger.v3.oas.models.responses.ApiResponse;
+import io.swagger.v3.oas.models.responses.ApiResponses;
+import io.swagger.v3.oas.models.security.SecurityRequirement;
+import io.swagger.v3.oas.models.security.SecurityScheme;
+import io.swagger.v3.oas.models.servers.Server;
+import io.swagger.v3.oas.models.tags.Tag;
import lombok.extern.slf4j.Slf4j;
+import org.springdoc.core.customizers.OpenApiCustomizer;
+import org.springdoc.core.models.GroupedOpenApi;
+import org.springdoc.core.properties.SwaggerUiConfigProperties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
-import org.springframework.core.annotation.Order;
-import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
-import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.exception.ThingsboardCredentialsExpiredResponse;
import org.thingsboard.server.exception.ThingsboardErrorResponse;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.auth.rest.LoginRequest;
import org.thingsboard.server.service.security.auth.rest.LoginResponse;
-import springfox.documentation.builders.ApiInfoBuilder;
-import springfox.documentation.builders.ExampleBuilder;
-import springfox.documentation.builders.OperationBuilder;
-import springfox.documentation.builders.RepresentationBuilder;
-import springfox.documentation.builders.RequestParameterBuilder;
-import springfox.documentation.builders.ResponseBuilder;
-import springfox.documentation.schema.Example;
-import springfox.documentation.service.ApiDescription;
-import springfox.documentation.service.ApiInfo;
-import springfox.documentation.service.ApiListing;
-import springfox.documentation.service.AuthorizationScope;
-import springfox.documentation.service.Contact;
-import springfox.documentation.service.HttpLoginPasswordScheme;
-import springfox.documentation.service.ParameterType;
-import springfox.documentation.service.Response;
-import springfox.documentation.service.SecurityReference;
-import springfox.documentation.service.SecurityScheme;
-import springfox.documentation.service.Tag;
-import springfox.documentation.spi.DocumentationType;
-import springfox.documentation.spi.service.ApiListingBuilderPlugin;
-import springfox.documentation.spi.service.ApiListingScannerPlugin;
-import springfox.documentation.spi.service.contexts.ApiListingContext;
-import springfox.documentation.spi.service.contexts.DocumentationContext;
-import springfox.documentation.spi.service.contexts.OperationContext;
-import springfox.documentation.spi.service.contexts.SecurityContext;
-import springfox.documentation.spring.web.plugins.Docket;
-import springfox.documentation.spring.web.readers.operation.CachingOperationNameGenerator;
-import springfox.documentation.swagger.common.SwaggerPluginSupport;
-import springfox.documentation.swagger.web.DocExpansion;
-import springfox.documentation.swagger.web.ModelRendering;
-import springfox.documentation.swagger.web.OperationsSorter;
-import springfox.documentation.swagger.web.UiConfiguration;
-import springfox.documentation.swagger.web.UiConfigurationBuilder;
-
-import java.util.ArrayList;
-import java.util.Collection;
+
import java.util.Collections;
import java.util.List;
-import java.util.Set;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
+import java.util.Map;
-import static com.google.common.collect.Lists.newArrayList;
-import static java.util.function.Predicate.not;
-import static springfox.documentation.builders.PathSelectors.any;
-import static springfox.documentation.builders.PathSelectors.regex;
+import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@Slf4j
@Configuration
@@ -87,12 +65,10 @@ import static springfox.documentation.builders.PathSelectors.regex;
@Profile("!test")
public class SwaggerConfiguration {
- @Value("${swagger.enabled:true}")
- private boolean enabled;
- @Value("${swagger.api_path_regex}")
- private String apiPathRegex;
- @Value("${swagger.security_path_regex}")
- private String securityPathRegex;
+ public static final String LOGIN_ENDPOINT = "/api/auth/login";
+
+ @Value("${swagger.api_path:/api/**}")
+ private String apiPath;
@Value("${swagger.non_security_path_regex}")
private String nonSecurityPathRegex;
@Value("${swagger.title}")
@@ -115,288 +91,217 @@ public class SwaggerConfiguration {
private String appVersion;
@Bean
- public Docket thingsboardApi() {
- TypeResolver typeResolver = new TypeResolver();
- return new Docket(DocumentationType.OAS_30)
- .enable(enabled)
- .groupName("thingsboard")
- .apiInfo(apiInfo())
- .additionalModels(
- typeResolver.resolve(ThingsboardErrorResponse.class),
- typeResolver.resolve(ThingsboardCredentialsExpiredResponse.class),
- typeResolver.resolve(LoginRequest.class),
- typeResolver.resolve(LoginResponse.class)
- )
- .select()
- .paths(apiPaths())
- .paths(any())
- .build()
- .globalResponses(HttpMethod.GET,
- defaultErrorResponses(false)
- )
- .globalResponses(HttpMethod.POST,
- defaultErrorResponses(true)
- )
- .globalResponses(HttpMethod.DELETE,
- defaultErrorResponses(false)
- )
- .securitySchemes(newArrayList(httpLogin()))
- .securityContexts(newArrayList(securityContext()))
- .ignoredParameterTypes(AuthenticationPrincipal.class)
- .enableUrlTemplating(true);
- }
+ public OpenAPI thingsboardApi() {
+ Contact contact = new Contact()
+ .name(contactName)
+ .url(contactUrl)
+ .email(contactEmail);
- @Bean
- @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER)
- ApiListingScannerPlugin loginEndpointListingScanner(final CachingOperationNameGenerator operationNames) {
- return new ApiListingScannerPlugin() {
- @Override
- public List apply(DocumentationContext context) {
- return List.of(loginEndpointApiDescription(operationNames));
- }
-
- @Override
- public boolean supports(DocumentationType delimiter) {
- return DocumentationType.SWAGGER_2.equals(delimiter) || DocumentationType.OAS_30.equals(delimiter);
- }
- };
- }
+ License license = new License()
+ .name(licenseTitle)
+ .url(licenseUrl);
- @Bean
- @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER)
- ApiListingBuilderPlugin loginEndpointListingBuilder() {
- return new ApiListingBuilderPlugin() {
- @Override
- public void apply(ApiListingContext apiListingContext) {
- if (apiListingContext.getResourceGroup().getGroupName().equals("default")) {
- ApiListing apiListing = apiListingContext.apiListingBuilder().build();
- if (apiListing.getResourcePath().equals("/api/auth/login")) {
- apiListingContext.apiListingBuilder().tags(Set.of(new Tag("login-endpoint", "Login Endpoint")));
- apiListingContext.apiListingBuilder().description("Login Endpoint");
- }
- }
- }
+ String apiVersion = version;
+ if (StringUtils.isEmpty(apiVersion)) {
+ apiVersion = appVersion;
+ }
- @Override
- public boolean supports(DocumentationType delimiter) {
- return DocumentationType.SWAGGER_2.equals(delimiter) || DocumentationType.OAS_30.equals(delimiter);
- }
- };
+ Info info = new Info()
+ .title(title)
+ .description(description)
+ .contact(contact)
+ .license(license)
+ .version(apiVersion);
+
+ SecurityScheme securityScheme = new SecurityScheme()
+ .type(SecurityScheme.Type.HTTP)
+ .scheme("bearer")
+ .bearerFormat("JWT")
+ .in(SecurityScheme.In.HEADER)
+ .description("Enter Username / Password");
+
+ var openApi = new OpenAPI()
+ .addServersItem(new Server().url("/").description("Default Server URL"))
+ .components(new Components().addSecuritySchemes("HTTP login form", securityScheme))
+ .info(info);
+ addLoginOperation(openApi);
+ return openApi;
}
- @Bean
- UiConfiguration uiConfig() {
- return UiConfigurationBuilder.builder()
- .deepLinking(true)
- .displayOperationId(false)
- .defaultModelsExpandDepth(1)
- .defaultModelExpandDepth(1)
- .defaultModelRendering(ModelRendering.EXAMPLE)
- .displayRequestDuration(false)
- .docExpansion(DocExpansion.LIST)
- .filter(false)
- .maxDisplayedTags(null)
- .operationsSorter(OperationsSorter.ALPHA)
- .showExtensions(false)
- .showCommonExtensions(false)
- .supportedSubmitMethods(UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS)
- .validatorUrl(null)
- .persistAuthorization(true)
- .syntaxHighlightActivate(true)
- .syntaxHighlightTheme("agate")
- .build();
+ public void addLoginOperation(OpenAPI openAPI) {
+ openAPI.getComponents()
+ .addSchemas("LoginRequest", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(LoginRequest.class)).schema)
+ .addSchemas("LoginResponse", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(LoginResponse.class)).schema)
+ .addSchemas("ThingsboardErrorResponse", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(ThingsboardErrorResponse.class)).schema)
+ .addSchemas("ThingsboardCredentialsExpiredResponse", ModelConverters.getInstance().readAllAsResolvedSchema(new AnnotatedType().type(ThingsboardCredentialsExpiredResponse.class)).schema);
+
+ var operation = new Operation();
+ operation.summary("Login method to get user JWT token data");
+ operation.description("Login method used to authenticate user and get JWT token data.\n\nValue of the response **token** " +
+ "field can be used as **X-Authorization** header value:\n\n`X-Authorization: Bearer $JWT_TOKEN_VALUE`.");
+ var requestBody = new RequestBody().content(new Content().addMediaType(APPLICATION_JSON_VALUE,
+ new MediaType().schema(new Schema().$ref("#/components/schemas/LoginRequest"))));
+ operation.requestBody(requestBody);
+ operation.responses(getResponses());
+ operation.addTagsItem("login-endpoint");
+ var pathItem = new PathItem().post(operation);
+ openAPI.path(LOGIN_ENDPOINT, pathItem);
}
- private SecurityScheme httpLogin() {
- return HttpLoginPasswordScheme
- .X_AUTHORIZATION_BUILDER
- .loginEndpoint("/api/auth/login")
- .name("HTTP login form")
- .description("Enter Username / Password")
- .build();
- }
+ private ApiResponses getResponses() {
+ ApiResponses apiResponses = new ApiResponses();
+
+ apiResponses.addApiResponse("200", new ApiResponse().description("OK")
+ .content(new Content().addMediaType(APPLICATION_JSON_VALUE,
+ new MediaType().schema(new Schema().$ref("#/components/schemas/LoginResponse")))));
+
+ ApiResponse unauthorizedResponse = new ApiResponse().description("Unauthorized");
+ Content content = new Content();
+ MediaType mediaType = new MediaType().schema(new Schema().$ref("#/components/schemas/ThingsboardErrorResponse"));
+
+ Map examples = Map.of(
+ "bad-credentials", errorExample("Bad credentials",
+ ThingsboardErrorResponse.of("Invalid username or password", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)),
+ "token-expired", errorExample("JWT token expired",
+ ThingsboardErrorResponse.of("Token has expired", ThingsboardErrorCode.JWT_TOKEN_EXPIRED, HttpStatus.UNAUTHORIZED)),
+ "account-disabled", errorExample("Disabled account",
+ ThingsboardErrorResponse.of("User account is not active", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)),
+ "account-locked", errorExample("Locked account",
+ ThingsboardErrorResponse.of("User account is locked due to security policy", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)),
+ "authentication-failed", errorExample("General authentication error",
+ ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED))
+ );
- private SecurityContext securityContext() {
- return SecurityContext.builder()
- .securityReferences(defaultAuth())
- .operationSelector(securityPathOperationSelector())
- .build();
+ mediaType.setExamples(examples);
+ content.addMediaType(APPLICATION_JSON_VALUE, mediaType);
+ unauthorizedResponse.setContent(content);
+ apiResponses.addApiResponse("401", unauthorizedResponse);
+
+ ApiResponse expiredCredentialsResponse = new ApiResponse().description("Unauthorized (**Expired credentials**)");
+ Content expiredContent = new Content();
+ MediaType expiredMediaType = new MediaType().schema(new Schema().$ref("#/components/schemas/ThingsboardCredentialsExpiredResponse"));
+ expiredMediaType.addExamples("credentials-expired", errorExample("Expired credentials",
+ ThingsboardCredentialsExpiredResponse.of("User password expired!", StringUtils.randomAlphanumeric(30))));
+ expiredContent.addMediaType(APPLICATION_JSON_VALUE, expiredMediaType);
+ expiredCredentialsResponse.setContent(expiredContent);
+ apiResponses.addApiResponse("401 ", expiredCredentialsResponse);
+
+ return apiResponses;
}
- private Predicate apiPaths() {
- return regex(apiPathRegex);
+ private Example errorExample(String summary, ThingsboardErrorResponse example) {
+ return new Example()
+ .summary(summary)
+ .value(example);
}
- private Predicate securityPathOperationSelector() {
- return new SecurityPathOperationSelector(securityPathRegex, nonSecurityPathRegex);
+ @Bean
+ public GroupedOpenApi groupedApi() {
+ return GroupedOpenApi.builder()
+ .group("thingsboard")
+ .pathsToMatch(apiPath)
+ .addOpenApiCustomizer(customOpenApiCustomizer())
+ .build();
}
- List defaultAuth() {
- AuthorizationScope[] authorizationScopes = new AuthorizationScope[3];
- authorizationScopes[0] = new AuthorizationScope(Authority.SYS_ADMIN.name(), "System administrator");
- authorizationScopes[1] = new AuthorizationScope(Authority.TENANT_ADMIN.name(), "Tenant administrator");
- authorizationScopes[2] = new AuthorizationScope(Authority.CUSTOMER_USER.name(), "Customer");
- return newArrayList(
- new SecurityReference("HTTP login form", authorizationScopes));
+ @Bean
+ @Primary
+ public SwaggerUiConfigProperties swaggerUiConfig(SwaggerUiConfigProperties uiProperties) {
+ uiProperties.setDeepLinking(true);
+ uiProperties.setDisplayOperationId(false);
+ uiProperties.setDefaultModelsExpandDepth(1);
+ uiProperties.setDefaultModelExpandDepth(1);
+ uiProperties.setDefaultModelRendering("example");
+ uiProperties.setDisplayRequestDuration(false);
+ uiProperties.setDocExpansion("list");
+ uiProperties.setFilter("false");
+ uiProperties.setMaxDisplayedTags(null);
+ uiProperties.setOperationsSorter("alpha");
+ uiProperties.setTagsSorter("alpha");
+ uiProperties.setShowExtensions(false);
+ uiProperties.setShowCommonExtensions(false);
+ uiProperties.setSupportedSubmitMethods(List.of("get", "put", "post", "delete", "options", "head", "patch", "trace"));
+ uiProperties.setValidatorUrl(null);
+ uiProperties.setPersistAuthorization(true);
+
+ var syntaxHighLight = new SwaggerUiConfigProperties.SyntaxHighlight();
+ syntaxHighLight.setActivated(true);
+ syntaxHighLight.setTheme("agate");
+
+ uiProperties.setSyntaxHighlight(syntaxHighLight);
+ return uiProperties;
}
- private ApiInfo apiInfo() {
- String apiVersion = version;
- if (StringUtils.isEmpty(apiVersion)) {
- apiVersion = appVersion;
- }
- return new ApiInfoBuilder()
- .title(title)
- .description(description)
- .contact(new Contact(contactName, contactUrl, contactEmail))
- .license(licenseTitle)
- .licenseUrl(licenseUrl)
- .version(apiVersion)
- .build();
+ public OpenApiCustomizer customOpenApiCustomizer() {
+ var loginForm = new SecurityRequirement().addList("HTTP login form");
+ return openAPI -> openAPI.getPaths().entrySet().stream().peek(entry -> {
+ securityCustomization(loginForm, entry);
+ if (!entry.getKey().equals(LOGIN_ENDPOINT)) {
+ defaultErrorResponsesCustomization(entry.getValue());
+ }
+ }).map(this::tagsCustomization).forEach(openAPI::addTagsItem);
}
- private ApiDescription loginEndpointApiDescription(final CachingOperationNameGenerator operationNames) {
- return new ApiDescription(null, "/api/auth/login", "Login method to get user JWT token data", "Login endpoint", Collections.singletonList(
- new OperationBuilder(operationNames)
- .summary("Login method to get user JWT token data")
- .tags(Set.of("login-endpoint"))
- .authorizations(new ArrayList<>())
- .position(0)
- .codegenMethodNameStem("loginPost")
- .method(HttpMethod.POST)
- .notes("Login method used to authenticate user and get JWT token data.\n\nValue of the response **token** " +
- "field can be used as **X-Authorization** header value:\n\n`X-Authorization: Bearer $JWT_TOKEN_VALUE`.")
- .requestParameters(
- List.of(
- new RequestParameterBuilder()
- .in(ParameterType.BODY)
- .required(true)
- .description("Login request")
- .content(c ->
- c.requestBody(true)
- .representation(MediaType.APPLICATION_JSON)
- .apply(classRepresentation(LoginRequest.class, false))
- )
- .build()
- )
- )
- .responses(loginResponses())
- .build()
- ), false);
+ private Tag tagsCustomization(Map.Entry entry) {
+ var operations = entry.getValue().readOperationsMap().values();
+ var tagItem = operations.stream().findAny().get().getTags().get(0);
+ return tagFromTagItem(tagItem);
}
- private Collection loginResponses() {
- List responses = new ArrayList<>();
- responses.add(
- new ResponseBuilder()
- .code("200")
- .description("OK")
- .representation(MediaType.APPLICATION_JSON)
- .apply(classRepresentation(LoginResponse.class, true)).
- build()
- );
- responses.addAll(loginErrorResponses());
- return responses;
+ private void defaultErrorResponsesCustomization(PathItem pathItem) {
+ pathItem.readOperationsMap().forEach(((httpMethod, operation) -> {
+ operation.setResponses(getResponses(operation.getResponses(), httpMethod.equals(PathItem.HttpMethod.POST)));
+ }));
}
- /** Helper methods **/
-
- private List defaultErrorResponses(boolean isPost) {
- return List.of(
- errorResponse("400", "Bad Request",
- ThingsboardErrorResponse.of(isPost ? "Invalid request body" : "Invalid UUID string: 123", ThingsboardErrorCode.BAD_REQUEST_PARAMS, HttpStatus.BAD_REQUEST)),
- errorResponse("401", "Unauthorized",
- ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)),
- errorResponse("403", "Forbidden",
- ThingsboardErrorResponse.of("You don't have permission to perform this operation!",
- ThingsboardErrorCode.PERMISSION_DENIED, HttpStatus.FORBIDDEN)),
- errorResponse("404", "Not Found",
- ThingsboardErrorResponse.of("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND, HttpStatus.NOT_FOUND)),
- errorResponse("429", "Too Many Requests",
- ThingsboardErrorResponse.of("Too many requests for current tenant!",
- ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS))
- );
+ private void securityCustomization(SecurityRequirement loginForm, Map.Entry entry) {
+ if (!(entry.getKey().matches(nonSecurityPathRegex) || entry.getKey().equals(LOGIN_ENDPOINT))) {
+ entry.getValue()
+ .readOperationsMap()
+ .values()
+ .forEach(operation -> operation.addSecurityItem(loginForm));
+ }
}
- private List loginErrorResponses() {
- return List.of(
- errorResponse("401", "Unauthorized",
- List.of(
- errorExample("bad-credentials", "Bad credentials",
- ThingsboardErrorResponse.of("Invalid username or password", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)),
- errorExample("token-expired", "JWT token expired",
- ThingsboardErrorResponse.of("Token has expired", ThingsboardErrorCode.JWT_TOKEN_EXPIRED, HttpStatus.UNAUTHORIZED)),
- errorExample("account-disabled", "Disabled account",
- ThingsboardErrorResponse.of("User account is not active", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)),
- errorExample("account-locked", "Locked account",
- ThingsboardErrorResponse.of("User account is locked due to security policy", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED)),
- errorExample("authentication-failed", "General authentication error",
- ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED))
- )
- ),
- errorResponse("401 ", "Unauthorized (**Expired credentials**)",
- List.of(
- errorExample("credentials-expired", "Expired credentials",
- ThingsboardCredentialsExpiredResponse.of("User password expired!", StringUtils.randomAlphanumeric(30)))
- ), ThingsboardCredentialsExpiredResponse.class
- )
- );
- }
+ private Tag tagFromTagItem(String tagItem) {
+ String[] words = tagItem.split("-");
+ StringBuilder sb = new StringBuilder();
- private Response errorResponse(String code, String description, ThingsboardErrorResponse example) {
- return errorResponse(code, description, List.of(errorExample("error-code-" + code, description, example)));
- }
+ for (String word : words) {
+ sb.append(word.substring(0, 1).toUpperCase());
+ sb.append(word.substring(1).toLowerCase());
+ sb.append(" ");
+ }
- private Response errorResponse(String code, String description, List examples) {
- return errorResponse(code, description, examples, ThingsboardErrorResponse.class);
+ return new Tag().name(tagItem).description(sb.toString().trim());
}
- private Response errorResponse(String code, String description, List examples,
- Class extends ThingsboardErrorResponse> errorResponseClass) {
- return new ResponseBuilder()
- .code(code)
- .description(description)
- .examples(examples)
- .representation(MediaType.APPLICATION_JSON)
- .apply(classRepresentation(errorResponseClass, true))
- .build();
- }
+ private ApiResponses getResponses(ApiResponses apiResponses, boolean isPost) {
+ if (apiResponses == null) {
+ apiResponses = new ApiResponses();
+ }
- private Example errorExample(String id, String summary, ThingsboardErrorResponse example) {
- return new ExampleBuilder()
- .mediaType(MediaType.APPLICATION_JSON_VALUE)
- .summary(summary)
- .id(id)
- .value(example).build();
- }
+ apiResponses.addApiResponse("400", new ApiResponse().description("Bad Request")
+ .content(getErrorContent(ThingsboardErrorResponse.of(isPost ? "Invalid request body" : "Invalid UUID string: 123", ThingsboardErrorCode.BAD_REQUEST_PARAMS, HttpStatus.BAD_REQUEST))));
- private Consumer classRepresentation(Class> clazz, boolean isResponse) {
- return r -> r.model(
- m ->
- m.referenceModel(ref ->
- ref.key(k ->
- k.qualifiedModelName(q ->
- q.namespace(clazz.getPackageName())
- .name(clazz.getSimpleName())).isResponse(isResponse)))
- );
- }
+ apiResponses.addApiResponse("401", new ApiResponse().description("Unauthorized")
+ .content(getErrorContent(ThingsboardErrorResponse.of("Authentication failed", ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED))));
- private static class SecurityPathOperationSelector implements Predicate {
+ apiResponses.addApiResponse("403", new ApiResponse().description("Forbidden")
+ .content(getErrorContent(ThingsboardErrorResponse.of("You don't have permission to perform this operation!", ThingsboardErrorCode.PERMISSION_DENIED, HttpStatus.FORBIDDEN))));
- private final Predicate securityPathSelector;
+ apiResponses.addApiResponse("404", new ApiResponse().description("Not Found")
+ .content(getErrorContent(ThingsboardErrorResponse.of("Requested item wasn't found!", ThingsboardErrorCode.ITEM_NOT_FOUND, HttpStatus.NOT_FOUND))));
- SecurityPathOperationSelector(String securityPathRegex, String nonSecurityPathRegex) {
- this.securityPathSelector = regex(securityPathRegex).and(
- not(
- regex(nonSecurityPathRegex)
- ));
- }
+ apiResponses.addApiResponse("429", new ApiResponse().description("Too Many Requests")
+ .content(getErrorContent(ThingsboardErrorResponse.of("Too many requests for current tenant!", ThingsboardErrorCode.TOO_MANY_REQUESTS, HttpStatus.TOO_MANY_REQUESTS))));
- @Override
- public boolean test(OperationContext operationContext) {
- return this.securityPathSelector.test(operationContext.requestMappingPattern());
- }
+ return apiResponses;
}
+ private Content getErrorContent(ThingsboardErrorResponse errorResponse) {
+ return new Content().addMediaType(org.springframework.http.MediaType.APPLICATION_JSON_VALUE,
+ new MediaType().schema(new Schema().example(errorResponse)));
+ }
}
diff --git a/application/src/main/java/org/thingsboard/server/config/TbRuleEngineSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/TbRuleEngineSecurityConfiguration.java
index 6772608311..e1ab755036 100644
--- a/application/src/main/java/org/thingsboard/server/config/TbRuleEngineSecurityConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/TbRuleEngineSecurityConfiguration.java
@@ -20,24 +20,28 @@ import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
-@EnableGlobalMethodSecurity(prePostEnabled = true)
+@EnableMethodSecurity
@Order(SecurityProperties.BASIC_AUTH_ORDER)
@ConditionalOnExpression("'${service.type:null}'=='tb-rule-engine'")
public class TbRuleEngineSecurityConfiguration {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
- http.headers().cacheControl().and().frameOptions().disable()
- .and().cors().and().csrf().disable()
+ http.headers(headers -> headers
+ .cacheControl(config -> {})
+ .frameOptions(config -> {}).disable())
+ .cors(cors -> {})
+ .csrf(AbstractHttpConfigurer::disable)
.authorizeRequests()
- .antMatchers("/actuator/prometheus").permitAll()
+ .requestMatchers("/actuator/prometheus").permitAll()
.anyRequest().authenticated();
return http.build();
}
diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
index 816b5a1c3f..c27ca35313 100644
--- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
+++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
@@ -28,9 +28,11 @@ import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.config.annotation.web.configurers.RequestCacheConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
import org.springframework.security.web.SecurityFilterChain;
@@ -61,7 +63,7 @@ import java.util.List;
@Configuration
@EnableWebSecurity
-@EnableGlobalMethodSecurity(prePostEnabled = true)
+@EnableMethodSecurity
@Order(SecurityProperties.BASIC_AUTH_ORDER)
@TbCoreComponent
public class ThingsboardSecurityConfiguration {
@@ -81,8 +83,8 @@ public class ThingsboardSecurityConfiguration {
public static final String MAIL_OAUTH2_PROCESSING_ENTRY_POINT = "/api/admin/mail/oauth2/code";
public static final String DEVICE_CONNECTIVITY_CERTIFICATE_DOWNLOAD_ENTRY_POINT = "/api/device-connectivity/mqtts/certificate/download";
-
- @Autowired private ThingsboardErrorResponseHandler restAccessDeniedHandler;
+ @Autowired
+ private ThingsboardErrorResponseHandler restAccessDeniedHandler;
@Autowired(required = false)
@Qualifier("oauth2AuthenticationSuccessHandler")
@@ -103,27 +105,33 @@ public class ThingsboardSecurityConfiguration {
@Qualifier("defaultAuthenticationFailureHandler")
private AuthenticationFailureHandler failureHandler;
- @Autowired private RestAuthenticationProvider restAuthenticationProvider;
- @Autowired private JwtAuthenticationProvider jwtAuthenticationProvider;
- @Autowired private RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider;
+ @Autowired
+ private RestAuthenticationProvider restAuthenticationProvider;
+ @Autowired
+ private JwtAuthenticationProvider jwtAuthenticationProvider;
+ @Autowired
+ private RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider;
- @Autowired(required = false) OAuth2Configuration oauth2Configuration;
+ @Autowired(required = false)
+ OAuth2Configuration oauth2Configuration;
@Autowired
@Qualifier("jwtHeaderTokenExtractor")
private TokenExtractor jwtHeaderTokenExtractor;
- @Autowired private AuthenticationManager authenticationManager;
+ @Autowired
+ private AuthenticationManager authenticationManager;
- @Autowired private RateLimitProcessingFilter rateLimitProcessingFilter;
+ @Autowired
+ private RateLimitProcessingFilter rateLimitProcessingFilter;
@Bean
protected FilterRegistrationBean buildEtagFilter() throws Exception {
ShallowEtagHeaderFilter etagFilter = new ShallowEtagHeaderFilter();
etagFilter.setWriteWeakETag(true);
FilterRegistrationBean filterRegistrationBean
- = new FilterRegistrationBean<>( etagFilter);
- filterRegistrationBean.addUrlPatterns("*.js","*.css","*.ico","/assets/*","/static/*");
+ = new FilterRegistrationBean<>(etagFilter);
+ filterRegistrationBean.addUrlPatterns("*.js", "*.css", "*.ico", "/assets/*", "/static/*");
filterRegistrationBean.setName("etagFilter");
return filterRegistrationBean;
}
@@ -180,60 +188,54 @@ public class ThingsboardSecurityConfiguration {
@Order(0)
SecurityFilterChain resources(HttpSecurity http) throws Exception {
http
- .requestMatchers((matchers) -> matchers.antMatchers("/*.js","/*.css","/*.ico","/assets/**","/static/**"))
- .headers().defaultsDisabled()
- .addHeaderWriter(new StaticHeadersWriter(HttpHeaders.CACHE_CONTROL, "max-age=0, public"))
- .and()
+ .securityMatchers(matchers -> matchers
+ .requestMatchers("/*.js", "/*.css", "/*.ico", "/assets/**", "/static/**"))
+ .headers(header -> header
+ .defaultsDisabled()
+ .addHeaderWriter(new StaticHeadersWriter(HttpHeaders.CACHE_CONTROL, "max-age=0, public")))
.authorizeHttpRequests((authorize) -> authorize.anyRequest().permitAll())
- .requestCache().disable()
- .securityContext().disable()
- .sessionManagement().disable();
+ .requestCache(RequestCacheConfigurer::disable)
+ .securityContext(AbstractHttpConfigurer::disable)
+ .sessionManagement(AbstractHttpConfigurer::disable);
return http.build();
}
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
- http.headers().cacheControl().and().frameOptions().disable()
- .and()
- .cors()
- .and()
- .csrf().disable()
- .exceptionHandling()
- .and()
- .sessionManagement()
- .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
- .and()
- .authorizeRequests()
- .antMatchers(WEBJARS_ENTRY_POINT).permitAll() // Webjars
- .antMatchers(DEVICE_API_ENTRY_POINT).permitAll() // Device HTTP Transport API
- .antMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll() // Login end-point
- .antMatchers(PUBLIC_LOGIN_ENTRY_POINT).permitAll() // Public login end-point
- .antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point
- .antMatchers(MAIL_OAUTH2_PROCESSING_ENTRY_POINT).permitAll() // Mail oauth2 code processing url
- .antMatchers(DEVICE_CONNECTIVITY_CERTIFICATE_DOWNLOAD_ENTRY_POINT).permitAll() // Mail oauth2 code processing url
- .antMatchers(NON_TOKEN_BASED_AUTH_ENTRY_POINTS).permitAll() // static resources, user activation and password reset end-points
- .and()
- .authorizeRequests()
- .antMatchers(WS_ENTRY_POINT).permitAll() // WebSocket API End-points
- .antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated() // Protected API End-points
- .and()
- .exceptionHandling().accessDeniedHandler(restAccessDeniedHandler)
- .and()
+ http.headers(headers -> headers
+ .cacheControl(config -> {})
+ .frameOptions(config -> {}).disable())
+ .cors(cors -> {})
+ .csrf(AbstractHttpConfigurer::disable)
+ .exceptionHandling(config -> {})
+ .sessionManagement(config -> config.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .authorizeRequests(config -> config
+ .requestMatchers(WEBJARS_ENTRY_POINT).permitAll() // Webjars
+ .requestMatchers(DEVICE_API_ENTRY_POINT).permitAll() // Device HTTP Transport API
+ .requestMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll() // Login end-point
+ .requestMatchers(PUBLIC_LOGIN_ENTRY_POINT).permitAll() // Public login end-point
+ .requestMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point
+ .requestMatchers(MAIL_OAUTH2_PROCESSING_ENTRY_POINT).permitAll() // Mail oauth2 code processing url
+ .requestMatchers(DEVICE_CONNECTIVITY_CERTIFICATE_DOWNLOAD_ENTRY_POINT).permitAll() // Device connectivity certificate (public)
+ .requestMatchers(NON_TOKEN_BASED_AUTH_ENTRY_POINTS).permitAll()) // static resources, user activation and password reset end-points
+ .authorizeRequests(config -> config
+ .requestMatchers(WS_ENTRY_POINT).permitAll() // Protected WebSocket API End-points
+ .requestMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated()) // Protected API End-points
+ .exceptionHandling(config -> config.accessDeniedHandler(restAccessDeniedHandler))
.addFilterBefore(buildRestLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(buildRestPublicLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(rateLimitProcessingFilter, UsernamePasswordAuthenticationFilter.class);
if (oauth2Configuration != null) {
- http.oauth2Login()
- .authorizationEndpoint()
- .authorizationRequestRepository(httpCookieOAuth2AuthorizationRequestRepository)
- .authorizationRequestResolver(oAuth2AuthorizationRequestResolver)
- .and()
+ http.oauth2Login(login -> login
+ .authorizationEndpoint(config -> config
+ .authorizationRequestRepository(httpCookieOAuth2AuthorizationRequestRepository)
+ .authorizationRequestResolver(oAuth2AuthorizationRequestResolver))
.loginPage("/oauth2Login")
.loginProcessingUrl(oauth2Configuration.getLoginProcessingUrl())
.successHandler(oauth2AuthenticationSuccessHandler)
- .failureHandler(oauth2AuthenticationFailureHandler);
+ .failureHandler(oauth2AuthenticationFailureHandler));
}
return http.build();
}
diff --git a/application/src/main/java/org/thingsboard/server/config/WebConfig.java b/application/src/main/java/org/thingsboard/server/config/WebConfig.java
index 36645e99c4..81597952c0 100644
--- a/application/src/main/java/org/thingsboard/server/config/WebConfig.java
+++ b/application/src/main/java/org/thingsboard/server/config/WebConfig.java
@@ -19,8 +19,8 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.thingsboard.server.utils.MiscUtils;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@Controller
diff --git a/application/src/main/java/org/thingsboard/server/config/annotations/ApiOperation.java b/application/src/main/java/org/thingsboard/server/config/annotations/ApiOperation.java
new file mode 100644
index 0000000000..c54fa44070
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/config/annotations/ApiOperation.java
@@ -0,0 +1,45 @@
+/**
+ * Copyright © 2016-2024 The Thingsboard Authors
+ *
+ * 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.
+ */
+package org.thingsboard.server.config.annotations;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import org.springframework.core.annotation.AliasFor;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Operation
+public @interface ApiOperation {
+
+ @AliasFor(annotation = Operation.class, attribute = "summary")
+ String value();
+
+ @AliasFor(annotation = Operation.class, attribute = "description")
+ String notes() default "";
+
+ boolean hidden() default false;
+
+ RequestBody requestBody() default @RequestBody;
+
+ ApiResponse[] responses() default {};
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java b/application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java
index afd3b33734..93e8c0be90 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.util.concurrent.FutureCallback;
+import jakarta.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -45,7 +46,6 @@ import org.thingsboard.server.service.security.AccessValidator;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
-import javax.annotation.Nullable;
import java.util.Optional;
import java.util.UUID;
diff --git a/application/src/main/java/org/thingsboard/server/controller/AdminController.java b/application/src/main/java/org/thingsboard/server/controller/AdminController.java
index ebb4bd3916..739f386dc9 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java
@@ -28,10 +28,15 @@ import com.google.api.client.json.gson.GsonFactory;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -68,6 +73,7 @@ import org.thingsboard.server.common.data.sync.vc.AutoCommitSettings;
import org.thingsboard.server.common.data.sync.vc.RepositorySettings;
import org.thingsboard.server.common.data.sync.vc.RepositorySettingsInfo;
import org.thingsboard.server.common.data.sync.vc.VcUtils;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.queue.util.TbCoreComponent;
@@ -83,9 +89,6 @@ import org.thingsboard.server.service.sync.vc.autocommit.TbAutoCommitSettingsSer
import org.thingsboard.server.service.system.SystemInfoService;
import org.thingsboard.server.service.update.UpdateService;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
@@ -123,7 +126,7 @@ public class AdminController extends BaseController {
@RequestMapping(value = "/settings/{key}", method = RequestMethod.GET)
@ResponseBody
public AdminSettings getAdminSettings(
- @ApiParam(value = "A string value of the key (e.g. 'general' or 'mail').")
+ @Parameter(description = "A string value of the key (e.g. 'general' or 'mail').")
@PathVariable("key") String key) throws ThingsboardException {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
AdminSettings adminSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, key), "No Administration settings found for key: " + key);
@@ -142,7 +145,7 @@ public class AdminController extends BaseController {
@RequestMapping(value = "/settings", method = RequestMethod.POST)
@ResponseBody
public AdminSettings saveAdminSettings(
- @ApiParam(value = "A JSON value representing the Administration Settings.")
+ @Parameter(description = "A JSON value representing the Administration Settings.")
@RequestBody AdminSettings adminSettings) throws ThingsboardException {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE);
adminSettings.setTenantId(getTenantId());
@@ -173,7 +176,7 @@ public class AdminController extends BaseController {
@RequestMapping(value = "/securitySettings", method = RequestMethod.POST)
@ResponseBody
public SecuritySettings saveSecuritySettings(
- @ApiParam(value = "A JSON value representing the Security Settings.")
+ @Parameter(description = "A JSON value representing the Security Settings.")
@RequestBody SecuritySettings securitySettings) throws ThingsboardException {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE);
securitySettings = checkNotNull(systemSecurityService.saveSecuritySettings(securitySettings));
@@ -182,7 +185,7 @@ public class AdminController extends BaseController {
@ApiOperation(value = "Get the JWT Settings object (getJwtSettings)",
notes = "Get the JWT Settings object that contains JWT token policy, etc. " + SYSTEM_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/jwtSettings", method = RequestMethod.GET)
@ResponseBody
@@ -193,12 +196,12 @@ public class AdminController extends BaseController {
@ApiOperation(value = "Update JWT Settings (saveJwtSettings)",
notes = "Updates the JWT Settings object that contains JWT token policy, etc. The tokenSigningKey field is a Base64 encoded string." + SYSTEM_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/jwtSettings", method = RequestMethod.POST)
@ResponseBody
public JwtPair saveJwtSettings(
- @ApiParam(value = "A JSON value representing the JWT Settings.")
+ @Parameter(description = "A JSON value representing the JWT Settings.")
@RequestBody JwtSettings jwtSettings) throws ThingsboardException {
SecurityUser securityUser = getCurrentUser();
accessControlService.checkPermission(securityUser, Resource.ADMIN_SETTINGS, Operation.WRITE);
@@ -212,12 +215,12 @@ public class AdminController extends BaseController {
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/settings/testMail", method = RequestMethod.POST)
public void sendTestMail(
- @ApiParam(value = "A JSON value representing the Mail Settings.")
+ @Parameter(description = "A JSON value representing the Mail Settings.")
@RequestBody AdminSettings adminSettings) throws ThingsboardException {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
adminSettings = checkNotNull(adminSettings);
if (adminSettings.getKey().equals("mail")) {
- if (adminSettings.getJsonValue().has("enableOauth2") && adminSettings.getJsonValue().get("enableOauth2").asBoolean()){
+ if (adminSettings.getJsonValue().has("enableOauth2") && adminSettings.getJsonValue().get("enableOauth2").asBoolean()) {
AdminSettings mailSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail"));
JsonNode refreshToken = mailSettings.getJsonValue().get("refreshToken");
if (refreshToken == null) {
@@ -225,8 +228,7 @@ public class AdminController extends BaseController {
}
ObjectNode settings = (ObjectNode) adminSettings.getJsonValue();
settings.put("refreshToken", refreshToken.asText());
- }
- else {
+ } else {
if (!adminSettings.getJsonValue().has("password")) {
AdminSettings mailSettings = checkNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail"));
((ObjectNode) adminSettings.getJsonValue()).put("password", mailSettings.getJsonValue().get("password").asText());
@@ -243,7 +245,7 @@ public class AdminController extends BaseController {
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/settings/testSms", method = RequestMethod.POST)
public void sendTestSms(
- @ApiParam(value = "A JSON value representing the Test SMS request.")
+ @Parameter(description = "A JSON value representing the Test SMS request.")
@RequestBody TestSmsRequest testSmsRequest) throws ThingsboardException {
SecurityUser user = getCurrentUser();
accessControlService.checkPermission(user, Resource.ADMIN_SETTINGS, Operation.READ);
@@ -326,7 +328,7 @@ public class AdminController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/repositorySettings/checkAccess", method = RequestMethod.POST)
public DeferredResult checkRepositoryAccess(
- @ApiParam(value = "A JSON value representing the Repository Settings.")
+ @Parameter(description = "A JSON value representing the Repository Settings.")
@RequestBody RepositorySettings settings) throws Exception {
accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ);
settings = checkNotNull(settings);
@@ -409,12 +411,12 @@ public class AdminController extends BaseController {
@RequestMapping(value = "/mail/oauth2/loginProcessingUrl", method = RequestMethod.GET)
@ResponseBody
public String getMailProcessingUrl() throws ThingsboardException {
- accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
- return "\"/api/admin/mail/oauth2/code\"";
+ accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
+ return "\"/api/admin/mail/oauth2/code\"";
}
@ApiOperation(value = "Redirect user to mail provider login page. ", notes = "After user logged in and provided access" +
- "provider sends authorization code to specified redirect uri.)" )
+ "provider sends authorization code to specified redirect uri.)")
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/mail/oauth2/authorize", method = RequestMethod.GET, produces = "application/text")
public String getAuthorizationUrl(HttpServletRequest request, HttpServletResponse response) throws ThingsboardException {
@@ -431,7 +433,7 @@ public class AdminController extends BaseController {
String clientId = checkNotNull(jsonValue.get("clientId"), "No clientId was configured").asText();
String authUri = checkNotNull(jsonValue.get("authUri"), "No authorization uri was configured").asText();
String redirectUri = checkNotNull(jsonValue.get("redirectUri"), "No Redirect uri was configured").asText();
- List scope = JacksonUtil.convertValue(checkNotNull(jsonValue.get("scope"), "No scope was configured"), new TypeReference<>() {
+ List scope = JacksonUtil.convertValue(checkNotNull(jsonValue.get("scope"), "No scope was configured"), new TypeReference<>() {
});
return "\"" + new AuthorizationCodeRequestUrl(authUri, clientId)
@@ -449,7 +451,7 @@ public class AdminController extends BaseController {
Optional cookieState = CookieUtils.getCookie(request, STATE_COOKIE_NAME);
String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request);
- String prevUri = baseUrl + (prevUrlOpt.isPresent() ? prevUrlOpt.get().getValue(): "/settings/outgoing-mail");
+ String prevUri = baseUrl + (prevUrlOpt.isPresent() ? prevUrlOpt.get().getValue() : "/settings/outgoing-mail");
if (cookieState.isEmpty() || !cookieState.get().getValue().equals(state)) {
CookieUtils.deleteCookie(request, response, STATE_COOKIE_NAME);
@@ -476,8 +478,8 @@ public class AdminController extends BaseController {
log.warn("Unable to retrieve refresh token: {}", e.getMessage());
throw new ThingsboardException("Error while requesting access token: " + e.getMessage(), ThingsboardErrorCode.GENERAL);
}
- ((ObjectNode)jsonValue).put("refreshToken", tokenResponse.getRefreshToken());
- ((ObjectNode)jsonValue).put("tokenGenerated", true);
+ ((ObjectNode) jsonValue).put("refreshToken", tokenResponse.getRefreshToken());
+ ((ObjectNode) jsonValue).put("tokenGenerated", true);
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings);
response.sendRedirect(prevUri);
diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java
index acc6ec2b68..cca82f5ee5 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java
@@ -15,8 +15,10 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -35,17 +37,16 @@ import org.thingsboard.server.common.data.id.AlarmCommentId;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.alarm.TbAlarmCommentService;
import org.thingsboard.server.service.security.permission.Operation;
import static org.thingsboard.server.controller.ControllerConstants.ALARM_COMMENT_ID_PARAM_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.ALARM_COMMENT_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.ALARM_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH;
@@ -69,12 +70,12 @@ public class AlarmCommentController extends BaseController {
"\n\n To create new Alarm comment entity it is enough to specify 'comment' json element with 'text' node, for example: {\"comment\": { \"text\": \"my comment\"}}. " +
"\n\n If comment type is not specified the default value 'OTHER' will be saved. If 'alarmId' or 'userId' specified in body it will be ignored." +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH
- , produces = MediaType.APPLICATION_JSON_VALUE)
+ , responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{alarmId}/comment", method = RequestMethod.POST)
@ResponseBody
- public AlarmComment saveAlarmComment(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION)
- @PathVariable(ALARM_ID) String strAlarmId, @ApiParam(value = "A JSON value representing the comment.") @RequestBody AlarmComment alarmComment) throws ThingsboardException {
+ public AlarmComment saveAlarmComment(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION)
+ @PathVariable(ALARM_ID) String strAlarmId, @Parameter(description = "A JSON value representing the comment.") @RequestBody AlarmComment alarmComment) throws ThingsboardException {
checkParameter(ALARM_ID, strAlarmId);
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
Alarm alarm = checkAlarmInfoId(alarmId, Operation.WRITE);
@@ -83,11 +84,11 @@ public class AlarmCommentController extends BaseController {
}
@ApiOperation(value = "Delete Alarm comment (deleteAlarmComment)",
- notes = "Deletes the Alarm comment. Referencing non-existing Alarm comment Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ notes = "Deletes the Alarm comment. Referencing non-existing Alarm comment Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{alarmId}/comment/{commentId}", method = RequestMethod.DELETE)
@ResponseBody
- public void deleteAlarmComment(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId, @ApiParam(value = ALARM_COMMENT_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_COMMENT_ID) String strCommentId) throws ThingsboardException {
+ public void deleteAlarmComment(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId, @Parameter(description = ALARM_COMMENT_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_COMMENT_ID) String strCommentId) throws ThingsboardException {
checkParameter(ALARM_ID, strAlarmId);
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
Alarm alarm = checkAlarmId(alarmId, Operation.WRITE);
@@ -99,20 +100,20 @@ public class AlarmCommentController extends BaseController {
@ApiOperation(value = "Get Alarm comments (getAlarmComments)",
notes = "Returns a page of alarm comments for specified alarm. " +
- PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{alarmId}/comment", method = RequestMethod.GET)
@ResponseBody
public PageData getAlarmComments(
- @ApiParam(value = ALARM_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = ALARM_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(ALARM_ID) String strAlarmId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ALARM_COMMENT_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "id"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder
) throws Exception {
checkParameter(ALARM_ID, strAlarmId);
diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
index facf56c476..d983f08ec2 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
@@ -15,8 +15,10 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@@ -47,6 +49,7 @@ import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.alarm.TbAlarmService;
import org.thingsboard.server.service.security.permission.Operation;
@@ -61,7 +64,6 @@ import java.util.concurrent.ExecutionException;
import static org.thingsboard.server.controller.ControllerConstants.ALARM_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ALARM_INFO_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.ALARM_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.ASSIGNEE_ID;
import static org.thingsboard.server.controller.ControllerConstants.ASSIGN_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_ID;
@@ -71,7 +73,6 @@ import static org.thingsboard.server.controller.ControllerConstants.ENTITY_TYPE_
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH;
@@ -91,11 +92,8 @@ public class AlarmController extends BaseController {
private static final String ALARM_QUERY_SEARCH_STATUS_DESCRIPTION = "A string value representing one of the AlarmSearchStatus enumeration value";
private static final String ALARM_QUERY_SEARCH_STATUS_ARRAY_DESCRIPTION = "A list of string values separated by comma ',' representing one of the AlarmSearchStatus enumeration value";
- private static final String ALARM_QUERY_SEARCH_STATUS_ALLOWABLE_VALUES = "ANY, ACTIVE, CLEARED, ACK, UNACK";
private static final String ALARM_QUERY_STATUS_DESCRIPTION = "A string value representing one of the AlarmStatus enumeration value";
- private static final String ALARM_QUERY_STATUS_ALLOWABLE_VALUES = "ACTIVE_UNACK, ACTIVE_ACK, CLEARED_UNACK, CLEARED_ACK";
private static final String ALARM_QUERY_SEVERITY_ARRAY_DESCRIPTION = "A list of string values separated by comma ',' representing one of the AlarmSeverity enumeration value";
- private static final String ALARM_QUERY_SEVERITY_ALLOWABLE_VALUES = "CRITICAL, MAJOR, MINOR, WARNING, INDETERMINATE";
private static final String ALARM_QUERY_TYPE_ARRAY_DESCRIPTION = "A list of string values separated by comma ',' representing alarm types";
private static final String ALARM_QUERY_ASSIGNEE_DESCRIPTION = "A string value representing the assignee user id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
@@ -106,11 +104,11 @@ public class AlarmController extends BaseController {
"filled in the AlarmInfo object field: 'originatorName' or will returns as null.";
@ApiOperation(value = "Get Alarm (getAlarmById)",
- notes = "Fetch the Alarm object based on the provided Alarm Id. " + ALARM_SECURITY_CHECK, produces = MediaType.APPLICATION_JSON_VALUE)
+ notes = "Fetch the Alarm object based on the provided Alarm Id. " + ALARM_SECURITY_CHECK, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.GET)
@ResponseBody
- public Alarm getAlarmById(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION)
+ public Alarm getAlarmById(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION)
@PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
checkParameter(ALARM_ID, strAlarmId);
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
@@ -119,11 +117,11 @@ public class AlarmController extends BaseController {
@ApiOperation(value = "Get Alarm Info (getAlarmInfoById)",
notes = "Fetch the Alarm Info object based on the provided Alarm Id. " +
- ALARM_SECURITY_CHECK + ALARM_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ ALARM_SECURITY_CHECK + ALARM_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/info/{alarmId}", method = RequestMethod.GET)
@ResponseBody
- public AlarmInfo getAlarmInfoById(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION)
+ public AlarmInfo getAlarmInfoById(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION)
@PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
checkParameter(ALARM_ID, strAlarmId);
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
@@ -141,11 +139,11 @@ public class AlarmController extends BaseController {
"If the user clears the alarm (see 'Clear Alarm(clearAlarm)'), than new alarm with the same type and same device may be created. " +
"Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Alarm entity. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH
- , produces = MediaType.APPLICATION_JSON_VALUE)
+ , responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm", method = RequestMethod.POST)
@ResponseBody
- public Alarm saveAlarm(@ApiParam(value = "A JSON value representing the alarm.") @RequestBody Alarm alarm) throws ThingsboardException {
+ public Alarm saveAlarm(@Parameter(description = "A JSON value representing the alarm.") @RequestBody Alarm alarm) throws ThingsboardException {
alarm.setTenantId(getTenantId());
checkNotNull(alarm.getOriginator());
checkEntity(alarm.getId(), alarm, Resource.ALARM);
@@ -157,11 +155,11 @@ public class AlarmController extends BaseController {
}
@ApiOperation(value = "Delete Alarm (deleteAlarm)",
- notes = "Deletes the Alarm. Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ notes = "Deletes the Alarm. Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.DELETE)
@ResponseBody
- public Boolean deleteAlarm(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
+ public Boolean deleteAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
checkParameter(ALARM_ID, strAlarmId);
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
Alarm alarm = checkAlarmId(alarmId, Operation.DELETE);
@@ -171,11 +169,11 @@ public class AlarmController extends BaseController {
@ApiOperation(value = "Acknowledge Alarm (ackAlarm)",
notes = "Acknowledge the Alarm. " +
"Once acknowledged, the 'ack_ts' field will be set to current timestamp and special rule chain event 'ALARM_ACK' will be generated. " +
- "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{alarmId}/ack", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
- public AlarmInfo ackAlarm(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws Exception {
+ public AlarmInfo ackAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws Exception {
checkParameter(ALARM_ID, strAlarmId);
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
Alarm alarm = checkAlarmId(alarmId, Operation.WRITE);
@@ -186,11 +184,11 @@ public class AlarmController extends BaseController {
@ApiOperation(value = "Clear Alarm (clearAlarm)",
notes = "Clear the Alarm. " +
"Once cleared, the 'clear_ts' field will be set to current timestamp and special rule chain event 'ALARM_CLEAR' will be generated. " +
- "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{alarmId}/clear", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
- public AlarmInfo clearAlarm(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws Exception {
+ public AlarmInfo clearAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId) throws Exception {
checkParameter(ALARM_ID, strAlarmId);
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
Alarm alarm = checkAlarmId(alarmId, Operation.WRITE);
@@ -202,13 +200,13 @@ public class AlarmController extends BaseController {
notes = "Assign the Alarm. " +
"Once assigned, the 'assign_ts' field will be set to current timestamp and special rule chain event 'ALARM_ASSIGNED' " +
"(or ALARM_REASSIGNED in case of assigning already assigned alarm) will be generated. " +
- "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{alarmId}/assign/{assigneeId}", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
- public Alarm assignAlarm(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION)
+ public Alarm assignAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION)
@PathVariable(ALARM_ID) String strAlarmId,
- @ApiParam(value = ASSIGN_ID_PARAM_DESCRIPTION)
+ @Parameter(description = ASSIGN_ID_PARAM_DESCRIPTION)
@PathVariable(ASSIGNEE_ID) String strAssigneeId
) throws Exception {
checkParameter(ALARM_ID, strAlarmId);
@@ -223,11 +221,11 @@ public class AlarmController extends BaseController {
@ApiOperation(value = "Unassign Alarm (unassignAlarm)",
notes = "Unassign the Alarm. " +
"Once unassigned, the 'assign_ts' field will be set to current timestamp and special rule chain event 'ALARM_UNASSIGNED' will be generated. " +
- "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ "Referencing non-existing Alarm Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{alarmId}/assign", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
- public Alarm unassignAlarm(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION)
+ public Alarm unassignAlarm(@Parameter(description = ALARM_ID_PARAM_DESCRIPTION)
@PathVariable(ALARM_ID) String strAlarmId
) throws Exception {
checkParameter(ALARM_ID, strAlarmId);
@@ -238,36 +236,36 @@ public class AlarmController extends BaseController {
@ApiOperation(value = "Get Alarms (getAlarms)",
notes = "Returns a page of alarms for the selected entity. Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error. " +
- PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET)
@ResponseBody
public PageData getAlarms(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE")
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE"))
@PathVariable(ENTITY_TYPE) String strEntityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_ID) String strEntityId,
- @ApiParam(value = ALARM_QUERY_SEARCH_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_SEARCH_STATUS_ALLOWABLE_VALUES)
+ @Parameter(description = ALARM_QUERY_SEARCH_STATUS_DESCRIPTION, schema = @Schema(allowableValues = {"ANY", "ACTIVE", "CLEARED", "ACK", "UNACK"}))
@RequestParam(required = false) String searchStatus,
- @ApiParam(value = ALARM_QUERY_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_STATUS_ALLOWABLE_VALUES)
+ @Parameter(description = ALARM_QUERY_STATUS_DESCRIPTION, schema = @Schema(allowableValues = {"ACTIVE_UNACK", "ACTIVE_ACK", "CLEARED_UNACK", "CLEARED_ACK"}))
@RequestParam(required = false) String status,
- @ApiParam(value = ALARM_QUERY_ASSIGNEE_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_ASSIGNEE_DESCRIPTION)
@RequestParam(required = false) String assigneeId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ALARM_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "startTs", "endTs", "type", "ackTs", "clearTs", "severity", "status"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = ALARM_QUERY_START_TIME_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_START_TIME_DESCRIPTION)
@RequestParam(required = false) Long startTime,
- @ApiParam(value = ALARM_QUERY_END_TIME_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_END_TIME_DESCRIPTION)
@RequestParam(required = false) Long endTime,
- @ApiParam(value = ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION)
@RequestParam(required = false) Boolean fetchOriginator
) throws ThingsboardException, ExecutionException, InterruptedException {
checkParameter("EntityId", strEntityId);
@@ -294,32 +292,32 @@ public class AlarmController extends BaseController {
"If the user has the authority of 'Tenant Administrator', the server returns alarms that belongs to the tenant of current user. " +
"If the user has the authority of 'Customer User', the server returns alarms that belongs to the customer of current user. " +
"Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error. " +
- PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarms", method = RequestMethod.GET)
@ResponseBody
public PageData getAllAlarms(
- @ApiParam(value = ALARM_QUERY_SEARCH_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_SEARCH_STATUS_ALLOWABLE_VALUES)
+ @Parameter(description = ALARM_QUERY_SEARCH_STATUS_DESCRIPTION, schema = @Schema(allowableValues = {"ANY", "ACTIVE", "CLEARED", "ACK", "UNACK"}))
@RequestParam(required = false) String searchStatus,
- @ApiParam(value = ALARM_QUERY_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_STATUS_ALLOWABLE_VALUES)
+ @Parameter(description = ALARM_QUERY_STATUS_DESCRIPTION, schema = @Schema(allowableValues = {"ACTIVE_UNACK", "ACTIVE_ACK", "CLEARED_UNACK", "CLEARED_ACK"}))
@RequestParam(required = false) String status,
- @ApiParam(value = ALARM_QUERY_ASSIGNEE_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_ASSIGNEE_DESCRIPTION)
@RequestParam(required = false) String assigneeId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ALARM_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "startTs", "endTs", "type", "ackTs", "clearTs", "severity", "status"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = ALARM_QUERY_START_TIME_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_START_TIME_DESCRIPTION)
@RequestParam(required = false) Long startTime,
- @ApiParam(value = ALARM_QUERY_END_TIME_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_END_TIME_DESCRIPTION)
@RequestParam(required = false) Long endTime,
- @ApiParam(value = ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_FETCH_ORIGINATOR_DESCRIPTION)
@RequestParam(required = false) Boolean fetchOriginator
) throws ThingsboardException, ExecutionException, InterruptedException {
AlarmSearchStatus alarmSearchStatus = StringUtils.isEmpty(searchStatus) ? null : AlarmSearchStatus.valueOf(searchStatus);
@@ -343,36 +341,36 @@ public class AlarmController extends BaseController {
@ApiOperation(value = "Get Alarms (getAlarmsV2)",
notes = "Returns a page of alarms for the selected entity. " +
- PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/v2/alarm/{entityType}/{entityId}", method = RequestMethod.GET)
@ResponseBody
public PageData getAlarmsV2(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE")
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE"))
@PathVariable(ENTITY_TYPE) String strEntityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_ID) String strEntityId,
- @ApiParam(value = ALARM_QUERY_SEARCH_STATUS_ARRAY_DESCRIPTION, allowableValues = ALARM_QUERY_SEARCH_STATUS_ALLOWABLE_VALUES)
+ @Parameter(description = ALARM_QUERY_SEARCH_STATUS_ARRAY_DESCRIPTION, schema = @Schema(allowableValues = {"ANY", "ACTIVE", "CLEARED", "ACK", "UNACK"}))
@RequestParam(required = false) String[] statusList,
- @ApiParam(value = ALARM_QUERY_SEVERITY_ARRAY_DESCRIPTION, allowableValues = ALARM_QUERY_SEVERITY_ALLOWABLE_VALUES)
+ @Parameter(description = ALARM_QUERY_SEVERITY_ARRAY_DESCRIPTION, schema = @Schema(allowableValues = {"CRITICAL", "MAJOR", "MINOR", "WARNING", "INDETERMINATE"}))
@RequestParam(required = false) String[] severityList,
- @ApiParam(value = ALARM_QUERY_TYPE_ARRAY_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_TYPE_ARRAY_DESCRIPTION)
@RequestParam(required = false) String[] typeList,
- @ApiParam(value = ALARM_QUERY_ASSIGNEE_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_ASSIGNEE_DESCRIPTION)
@RequestParam(required = false) String assigneeId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ALARM_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "startTs", "endTs", "type", "ackTs", "clearTs", "severity", "status"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = ALARM_QUERY_START_TIME_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_START_TIME_DESCRIPTION)
@RequestParam(required = false) Long startTime,
- @ApiParam(value = ALARM_QUERY_END_TIME_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_END_TIME_DESCRIPTION)
@RequestParam(required = false) Long endTime
) throws ThingsboardException, ExecutionException, InterruptedException {
checkParameter("EntityId", strEntityId);
@@ -409,32 +407,32 @@ public class AlarmController extends BaseController {
notes = "Returns a page of alarms that belongs to the current user owner. " +
"If the user has the authority of 'Tenant Administrator', the server returns alarms that belongs to the tenant of current user. " +
"If the user has the authority of 'Customer User', the server returns alarms that belongs to the customer of current user. " +
- PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/v2/alarms", method = RequestMethod.GET)
@ResponseBody
public PageData getAllAlarmsV2(
- @ApiParam(value = ALARM_QUERY_SEARCH_STATUS_ARRAY_DESCRIPTION, allowableValues = ALARM_QUERY_SEARCH_STATUS_ALLOWABLE_VALUES)
+ @Parameter(description = ALARM_QUERY_SEARCH_STATUS_ARRAY_DESCRIPTION, schema = @Schema(allowableValues = {"ANY", "ACTIVE", "CLEARED", "ACK", "UNACK"}))
@RequestParam(required = false) String[] statusList,
- @ApiParam(value = ALARM_QUERY_SEVERITY_ARRAY_DESCRIPTION, allowableValues = ALARM_QUERY_SEVERITY_ALLOWABLE_VALUES)
+ @Parameter(description = ALARM_QUERY_SEVERITY_ARRAY_DESCRIPTION, schema = @Schema(allowableValues = {"CRITICAL", "MAJOR", "MINOR", "WARNING", "INDETERMINATE"}))
@RequestParam(required = false) String[] severityList,
- @ApiParam(value = ALARM_QUERY_TYPE_ARRAY_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_TYPE_ARRAY_DESCRIPTION)
@RequestParam(required = false) String[] typeList,
- @ApiParam(value = ALARM_QUERY_ASSIGNEE_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_ASSIGNEE_DESCRIPTION)
@RequestParam(required = false) String assigneeId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ALARM_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "startTs", "endTs", "type", "ackTs", "clearTs", "severity", "status"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = ALARM_QUERY_START_TIME_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_START_TIME_DESCRIPTION)
@RequestParam(required = false) Long startTime,
- @ApiParam(value = ALARM_QUERY_END_TIME_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_END_TIME_DESCRIPTION)
@RequestParam(required = false) Long endTime
) throws ThingsboardException, ExecutionException, InterruptedException {
List alarmStatusList = new ArrayList<>();
@@ -470,20 +468,20 @@ public class AlarmController extends BaseController {
@ApiOperation(value = "Get Highest Alarm Severity (getHighestAlarmSeverity)",
notes = "Search the alarms by originator ('entityType' and entityId') and optional 'status' or 'searchStatus' filters and returns the highest AlarmSeverity(CRITICAL, MAJOR, MINOR, WARNING or INDETERMINATE). " +
"Specifying both parameters 'searchStatus' and 'status' at the same time will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH
- , produces = MediaType.APPLICATION_JSON_VALUE)
+ , responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/highestSeverity/{entityType}/{entityId}", method = RequestMethod.GET)
@ResponseBody
public AlarmSeverity getHighestAlarmSeverity(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE")
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE"))
@PathVariable(ENTITY_TYPE) String strEntityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_ID) String strEntityId,
- @ApiParam(value = ALARM_QUERY_SEARCH_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_SEARCH_STATUS_ALLOWABLE_VALUES)
+ @Parameter(description = ALARM_QUERY_SEARCH_STATUS_DESCRIPTION, schema = @Schema(allowableValues = {"ANY", "ACTIVE", "CLEARED", "ACK", "UNACK"}))
@RequestParam(required = false) String searchStatus,
- @ApiParam(value = ALARM_QUERY_STATUS_DESCRIPTION, allowableValues = ALARM_QUERY_STATUS_ALLOWABLE_VALUES)
+ @Parameter(description = ALARM_QUERY_STATUS_DESCRIPTION, schema = @Schema(allowableValues = {"ACTIVE_UNACK", "ACTIVE_ACK", "CLEARED_UNACK", "CLEARED_ACK"}))
@RequestParam(required = false) String status,
- @ApiParam(value = ALARM_QUERY_ASSIGNEE_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_ASSIGNEE_DESCRIPTION)
@RequestParam(required = false) String assigneeId
) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
@@ -501,17 +499,18 @@ public class AlarmController extends BaseController {
}
@ApiOperation(value = "Get Alarm Types (getAlarmTypes)",
- notes = "Returns a set of unique alarm types based on alarms that are either owned by the tenant or assigned to the customer which user is performing the request.", produces = MediaType.APPLICATION_JSON_VALUE)
+ notes = "Returns a set of unique alarm types based on alarms that are either owned by the tenant or assigned to the customer which user is performing the request.",
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/types", method = RequestMethod.GET)
@ResponseBody
- public PageData getAlarmTypes(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ public PageData getAlarmTypes(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ALARM_QUERY_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException, ExecutionException, InterruptedException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, "type", sortOrder);
return checkNotNull(alarmService.findAlarmTypesByTenantId(getTenantId(), pageLink));
diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java
index 33a8cdca5b..6f51202c36 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java
@@ -16,8 +16,10 @@
package org.thingsboard.server.controller;
import com.google.common.util.concurrent.ListenableFuture;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
@@ -49,6 +51,7 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportRequest;
import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportResult;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent;
@@ -67,7 +70,6 @@ import static org.thingsboard.server.controller.ControllerConstants.ASSET_ID_PAR
import static org.thingsboard.server.controller.ControllerConstants.ASSET_INFO_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ASSET_NAME_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_ID_PARAM_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.ASSET_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.ASSET_TEXT_SEARCH_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ASSET_TYPE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
@@ -79,7 +81,6 @@ import static org.thingsboard.server.controller.ControllerConstants.EDGE_UNASSIG
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
@@ -102,11 +103,11 @@ public class AssetController extends BaseController {
notes = "Fetch the Asset object based on the provided Asset Id. " +
"If the user has the authority of 'Tenant Administrator', the server checks that the asset is owned by the same tenant. " +
"If the user has the authority of 'Customer User', the server checks that the asset is assigned to the same customer." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH
- , produces = MediaType.APPLICATION_JSON_VALUE)
+ , responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/asset/{assetId}", method = RequestMethod.GET)
@ResponseBody
- public Asset getAssetById(@ApiParam(value = ASSET_ID_PARAM_DESCRIPTION)
+ public Asset getAssetById(@Parameter(description = ASSET_ID_PARAM_DESCRIPTION)
@PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
checkParameter(ASSET_ID, strAssetId);
AssetId assetId = new AssetId(toUUID(strAssetId));
@@ -117,11 +118,11 @@ public class AssetController extends BaseController {
notes = "Fetch the Asset Info object based on the provided Asset Id. " +
"If the user has the authority of 'Tenant Administrator', the server checks that the asset is owned by the same tenant. " +
"If the user has the authority of 'Customer User', the server checks that the asset is assigned to the same customer. "
- + ASSET_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ + ASSET_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/asset/info/{assetId}", method = RequestMethod.GET)
@ResponseBody
- public AssetInfo getAssetInfoById(@ApiParam(value = ASSET_ID_PARAM_DESCRIPTION)
+ public AssetInfo getAssetInfoById(@Parameter(description = ASSET_ID_PARAM_DESCRIPTION)
@PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
checkParameter(ASSET_ID, strAssetId);
AssetId assetId = new AssetId(toUUID(strAssetId));
@@ -134,11 +135,11 @@ public class AssetController extends BaseController {
"Specify existing Asset id to update the asset. " +
"Referencing non-existing Asset Id will cause 'Not Found' error. " +
"Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Asset entity. "
- + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/asset", method = RequestMethod.POST)
@ResponseBody
- public Asset saveAsset(@ApiParam(value = "A JSON value representing the asset.") @RequestBody Asset asset) throws Exception {
+ public Asset saveAsset(@Parameter(description = "A JSON value representing the asset.") @RequestBody Asset asset) throws Exception {
asset.setTenantId(getTenantId());
checkEntity(asset.getId(), asset, Resource.ASSET);
return tbAssetService.save(asset, getCurrentUser());
@@ -149,7 +150,7 @@ public class AssetController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/asset/{assetId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
- public void deleteAsset(@ApiParam(value = ASSET_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_ID) String strAssetId) throws Exception {
+ public void deleteAsset(@Parameter(description = ASSET_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_ID) String strAssetId) throws Exception {
checkParameter(ASSET_ID, strAssetId);
AssetId assetId = new AssetId(toUUID(strAssetId));
Asset asset = checkAssetId(assetId, Operation.DELETE);
@@ -157,12 +158,12 @@ public class AssetController extends BaseController {
}
@ApiOperation(value = "Assign asset to customer (assignAssetToCustomer)",
- notes = "Creates assignment of the asset to customer. Customer will be able to query asset afterwards." + TENANT_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ notes = "Creates assignment of the asset to customer. Customer will be able to query asset afterwards." + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/{customerId}/asset/{assetId}", method = RequestMethod.POST)
@ResponseBody
- public Asset assignAssetToCustomer(@ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION) @PathVariable("customerId") String strCustomerId,
- @ApiParam(value = ASSET_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
+ public Asset assignAssetToCustomer(@Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION) @PathVariable("customerId") String strCustomerId,
+ @Parameter(description = ASSET_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
checkParameter(ASSET_ID, strAssetId);
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
@@ -173,11 +174,11 @@ public class AssetController extends BaseController {
}
@ApiOperation(value = "Unassign asset from customer (unassignAssetFromCustomer)",
- notes = "Clears assignment of the asset to customer. Customer will not be able to query asset afterwards." + TENANT_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ notes = "Clears assignment of the asset to customer. Customer will not be able to query asset afterwards." + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/asset/{assetId}", method = RequestMethod.DELETE)
@ResponseBody
- public Asset unassignAssetFromCustomer(@ApiParam(value = ASSET_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
+ public Asset unassignAssetFromCustomer(@Parameter(description = ASSET_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
checkParameter(ASSET_ID, strAssetId);
AssetId assetId = new AssetId(toUUID(strAssetId));
Asset asset = checkAssetId(assetId, Operation.UNASSIGN_FROM_CUSTOMER);
@@ -191,11 +192,11 @@ public class AssetController extends BaseController {
@ApiOperation(value = "Make asset publicly available (assignAssetToPublicCustomer)",
notes = "Asset will be available for non-authorized (not logged-in) users. " +
"This is useful to create dashboards that you plan to share/embed on a publicly available website. " +
- "However, users that are logged-in and belong to different tenant will not be able to access the asset." + TENANT_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ "However, users that are logged-in and belong to different tenant will not be able to access the asset." + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/public/asset/{assetId}", method = RequestMethod.POST)
@ResponseBody
- public Asset assignAssetToPublicCustomer(@ApiParam(value = ASSET_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
+ public Asset assignAssetToPublicCustomer(@Parameter(description = ASSET_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
checkParameter(ASSET_ID, strAssetId);
AssetId assetId = new AssetId(toUUID(strAssetId));
checkAssetId(assetId, Operation.ASSIGN_TO_CUSTOMER);
@@ -204,22 +205,22 @@ public class AssetController extends BaseController {
@ApiOperation(value = "Get Tenant Assets (getTenantAssets)",
notes = "Returns a page of assets owned by tenant. " +
- PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/assets", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getTenantAssets(
- @ApiParam(value = PAGE_SIZE_DESCRIPTION)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION)
@RequestParam int page,
- @ApiParam(value = ASSET_TYPE_DESCRIPTION)
+ @Parameter(description = ASSET_TYPE_DESCRIPTION)
@RequestParam(required = false) String type,
- @ApiParam(value = ASSET_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ASSET_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ASSET_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "type", "label", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@@ -232,24 +233,24 @@ public class AssetController extends BaseController {
@ApiOperation(value = "Get Tenant Asset Infos (getTenantAssetInfos)",
notes = "Returns a page of assets info objects owned by tenant. " +
- PAGE_DATA_PARAMETERS + ASSET_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS + ASSET_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/assetInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getTenantAssetInfos(
- @ApiParam(value = PAGE_SIZE_DESCRIPTION)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION)
@RequestParam int page,
- @ApiParam(value = ASSET_TYPE_DESCRIPTION)
+ @Parameter(description = ASSET_TYPE_DESCRIPTION)
@RequestParam(required = false) String type,
- @ApiParam(value = ASSET_PROFILE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = ASSET_PROFILE_ID_PARAM_DESCRIPTION)
@RequestParam(required = false) String assetProfileId,
- @ApiParam(value = ASSET_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ASSET_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ASSET_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "type", "label", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@@ -265,12 +266,12 @@ public class AssetController extends BaseController {
@ApiOperation(value = "Get Tenant Asset (getTenantAsset)",
notes = "Requested asset must be owned by tenant that the user belongs to. " +
- "Asset name is an unique property of asset. So it can be used to identify the asset." + TENANT_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ "Asset name is an unique property of asset. So it can be used to identify the asset." + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/assets", params = {"assetName"}, method = RequestMethod.GET)
@ResponseBody
public Asset getTenantAsset(
- @ApiParam(value = ASSET_NAME_DESCRIPTION)
+ @Parameter(description = ASSET_NAME_DESCRIPTION)
@RequestParam String assetName) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(assetService.findAssetByTenantIdAndName(tenantId, assetName));
@@ -278,24 +279,24 @@ public class AssetController extends BaseController {
@ApiOperation(value = "Get Customer Assets (getCustomerAssets)",
notes = "Returns a page of assets objects assigned to customer. " +
- PAGE_DATA_PARAMETERS, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/assets", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getCustomerAssets(
- @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION)
+ @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable("customerId") String strCustomerId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION)
@RequestParam int page,
- @ApiParam(value = ASSET_TYPE_DESCRIPTION)
+ @Parameter(description = ASSET_TYPE_DESCRIPTION)
@RequestParam(required = false) String type,
- @ApiParam(value = ASSET_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ASSET_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ASSET_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "type", "label", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
TenantId tenantId = getCurrentUser().getTenantId();
@@ -311,26 +312,26 @@ public class AssetController extends BaseController {
@ApiOperation(value = "Get Customer Asset Infos (getCustomerAssetInfos)",
notes = "Returns a page of assets info objects assigned to customer. " +
- PAGE_DATA_PARAMETERS + ASSET_INFO_DESCRIPTION, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS + ASSET_INFO_DESCRIPTION, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/assetInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getCustomerAssetInfos(
- @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION)
+ @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable("customerId") String strCustomerId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION)
@RequestParam int page,
- @ApiParam(value = ASSET_TYPE_DESCRIPTION)
+ @Parameter(description = ASSET_TYPE_DESCRIPTION)
@RequestParam(required = false) String type,
- @ApiParam(value = ASSET_PROFILE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = ASSET_PROFILE_ID_PARAM_DESCRIPTION)
@RequestParam(required = false) String assetProfileId,
- @ApiParam(value = ASSET_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ASSET_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ASSET_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "type", "label", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
TenantId tenantId = getCurrentUser().getTenantId();
@@ -348,12 +349,12 @@ public class AssetController extends BaseController {
}
@ApiOperation(value = "Get Assets By Ids (getAssetsByIds)",
- notes = "Requested assets must be owned by tenant or assigned to customer which user is performing the request. ", produces = MediaType.APPLICATION_JSON_VALUE)
+ notes = "Requested assets must be owned by tenant or assigned to customer which user is performing the request. ", responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/assets", params = {"assetIds"}, method = RequestMethod.GET)
@ResponseBody
public List getAssetsByIds(
- @ApiParam(value = "A list of assets ids, separated by comma ','")
+ @Parameter(description = "A list of assets ids, separated by comma ','")
@RequestParam("assetIds") String[] strAssetIds) throws ThingsboardException, ExecutionException, InterruptedException {
checkArrayParameter("assetIds", strAssetIds);
SecurityUser user = getCurrentUser();
@@ -375,7 +376,7 @@ public class AssetController extends BaseController {
@ApiOperation(value = "Find related assets (findByQuery)",
notes = "Returns all assets that are related to the specific entity. " +
"The entity id, relation type, asset types, depth of the search, and other query parameters defined using complex 'AssetSearchQuery' object. " +
- "See 'Model' tab of the Parameters for more info.", produces = MediaType.APPLICATION_JSON_VALUE)
+ "See 'Model' tab of the Parameters for more info.", responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/assets", method = RequestMethod.POST)
@ResponseBody
@@ -398,7 +399,7 @@ public class AssetController extends BaseController {
@ApiOperation(value = "Get Asset Types (getAssetTypes)",
notes = "Deprecated. See 'getAssetProfileNames' API from Asset Profile Controller instead." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/asset/types", method = RequestMethod.GET)
@ResponseBody
@@ -416,12 +417,12 @@ public class AssetController extends BaseController {
"Second, remote edge service will receive a copy of assignment asset " +
EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION +
"Third, once asset will be delivered to edge service, it's going to be available for usage on remote edge instance.",
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.POST)
@ResponseBody
- public Asset assignAssetToEdge(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION) @PathVariable(EDGE_ID) String strEdgeId,
- @ApiParam(value = ASSET_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
+ public Asset assignAssetToEdge(@Parameter(description = EDGE_ID_PARAM_DESCRIPTION) @PathVariable(EDGE_ID) String strEdgeId,
+ @Parameter(description = ASSET_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
checkParameter(ASSET_ID, strAssetId);
@@ -440,12 +441,12 @@ public class AssetController extends BaseController {
"Second, remote edge service will receive an 'unassign' command to remove asset " +
EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION +
"Third, once 'unassign' command will be delivered to edge service, it's going to remove asset locally.",
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/asset/{assetId}", method = RequestMethod.DELETE)
@ResponseBody
- public Asset unassignAssetFromEdge(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION) @PathVariable(EDGE_ID) String strEdgeId,
- @ApiParam(value = ASSET_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
+ public Asset unassignAssetFromEdge(@Parameter(description = EDGE_ID_PARAM_DESCRIPTION) @PathVariable(EDGE_ID) String strEdgeId,
+ @Parameter(description = ASSET_ID_PARAM_DESCRIPTION) @PathVariable(ASSET_ID) String strAssetId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
checkParameter(ASSET_ID, strAssetId);
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
@@ -459,28 +460,28 @@ public class AssetController extends BaseController {
@ApiOperation(value = "Get assets assigned to edge (getEdgeAssets)",
notes = "Returns a page of assets assigned to edge. " +
- PAGE_DATA_PARAMETERS, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/edge/{edgeId}/assets", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getEdgeAssets(
- @ApiParam(value = EDGE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = EDGE_ID_PARAM_DESCRIPTION)
@PathVariable(EDGE_ID) String strEdgeId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION)
@RequestParam int page,
- @ApiParam(value = ASSET_TYPE_DESCRIPTION)
+ @Parameter(description = ASSET_TYPE_DESCRIPTION)
@RequestParam(required = false) String type,
- @ApiParam(value = ASSET_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ASSET_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ASSET_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "type", "label", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = "Timestamp. Assets with creation time before it won't be queried")
+ @Parameter(description = "Timestamp. Assets with creation time before it won't be queried")
@RequestParam(required = false) Long startTime,
- @ApiParam(value = "Timestamp. Assets with creation time after it won't be queried")
+ @Parameter(description = "Timestamp. Assets with creation time after it won't be queried")
@RequestParam(required = false) Long endTime) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
TenantId tenantId = getCurrentUser().getTenantId();
@@ -509,7 +510,7 @@ public class AssetController extends BaseController {
}
@ApiOperation(value = "Import the bulk of assets (processAssetsBulkImport)",
- notes = "There's an ability to import the bulk of assets using the only .csv file.", produces = MediaType.APPLICATION_JSON_VALUE)
+ notes = "There's an ability to import the bulk of assets using the only .csv file.", responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@PostMapping("/asset/bulk_import")
public BulkImportResult processAssetsBulkImport(@RequestBody BulkImportRequest request) throws Exception {
diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java b/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java
index b2e9cd816a..a6126011cc 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AssetProfileController.java
@@ -15,11 +15,14 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
@@ -38,6 +41,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.resource.ImageService;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.asset.profile.TbAssetProfileService;
import org.thingsboard.server.service.security.model.SecurityUser;
@@ -49,7 +53,6 @@ import java.util.List;
import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_ID;
import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_INFO_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_TEXT_SEARCH_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.INLINE_IMAGES;
import static org.thingsboard.server.controller.ControllerConstants.INLINE_IMAGES_DESCRIPTION;
@@ -57,7 +60,6 @@ import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
@@ -77,14 +79,14 @@ public class AssetProfileController extends BaseController {
@ApiOperation(value = "Get Asset Profile (getAssetProfileById)",
notes = "Fetch the Asset Profile object based on the provided Asset Profile Id. " +
"The server checks that the asset profile is owned by the same tenant. " + TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/assetProfile/{assetProfileId}", method = RequestMethod.GET)
@ResponseBody
public AssetProfile getAssetProfileById(
- @ApiParam(value = ASSET_PROFILE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = ASSET_PROFILE_ID_PARAM_DESCRIPTION)
@PathVariable(ASSET_PROFILE_ID) String strAssetProfileId,
- @ApiParam(value = INLINE_IMAGES_DESCRIPTION)
+ @Parameter(description = INLINE_IMAGES_DESCRIPTION)
@RequestParam(value = INLINE_IMAGES, required = false) boolean inlineImages) throws ThingsboardException {
checkParameter(ASSET_PROFILE_ID, strAssetProfileId);
AssetProfileId assetProfileId = new AssetProfileId(toUUID(strAssetProfileId));
@@ -98,12 +100,12 @@ public class AssetProfileController extends BaseController {
@ApiOperation(value = "Get Asset Profile Info (getAssetProfileInfoById)",
notes = "Fetch the Asset Profile Info object based on the provided Asset Profile Id. "
+ ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/assetProfileInfo/{assetProfileId}", method = RequestMethod.GET)
@ResponseBody
public AssetProfileInfo getAssetProfileInfoById(
- @ApiParam(value = ASSET_PROFILE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = ASSET_PROFILE_ID_PARAM_DESCRIPTION)
@PathVariable(ASSET_PROFILE_ID) String strAssetProfileId) throws ThingsboardException {
checkParameter(ASSET_PROFILE_ID, strAssetProfileId);
AssetProfileId assetProfileId = new AssetProfileId(toUUID(strAssetProfileId));
@@ -113,7 +115,7 @@ public class AssetProfileController extends BaseController {
@ApiOperation(value = "Get Default Asset Profile (getDefaultAssetProfileInfo)",
notes = "Fetch the Default Asset Profile Info object. " +
ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/assetProfileInfo/default", method = RequestMethod.GET)
@ResponseBody
@@ -129,13 +131,12 @@ public class AssetProfileController extends BaseController {
"Asset profile name is unique in the scope of tenant. Only one 'default' asset profile may exist in scope of tenant. " +
"Remove 'id', 'tenantId' from the request body example (below) to create new Asset Profile entity. " +
TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json",
- consumes = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/assetProfile", method = RequestMethod.POST)
@ResponseBody
public AssetProfile saveAssetProfile(
- @ApiParam(value = "A JSON value representing the asset profile.")
+ @Parameter(description = "A JSON value representing the asset profile.")
@RequestBody AssetProfile assetProfile) throws Exception {
assetProfile.setTenantId(getTenantId());
checkEntity(assetProfile.getId(), assetProfile, Resource.ASSET_PROFILE);
@@ -145,12 +146,12 @@ public class AssetProfileController extends BaseController {
@ApiOperation(value = "Delete asset profile (deleteAssetProfile)",
notes = "Deletes the asset profile. Referencing non-existing asset profile Id will cause an error. " +
"Can't delete the asset profile if it is referenced by existing assets." + TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/assetProfile/{assetProfileId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
public void deleteAssetProfile(
- @ApiParam(value = ASSET_PROFILE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = ASSET_PROFILE_ID_PARAM_DESCRIPTION)
@PathVariable(ASSET_PROFILE_ID) String strAssetProfileId) throws ThingsboardException {
checkParameter(ASSET_PROFILE_ID, strAssetProfileId);
AssetProfileId assetProfileId = new AssetProfileId(toUUID(strAssetProfileId));
@@ -160,12 +161,12 @@ public class AssetProfileController extends BaseController {
@ApiOperation(value = "Make Asset Profile Default (setDefaultAssetProfile)",
notes = "Marks asset profile as default within a tenant scope." + TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/assetProfile/{assetProfileId}/default", method = RequestMethod.POST)
@ResponseBody
public AssetProfile setDefaultAssetProfile(
- @ApiParam(value = ASSET_PROFILE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = ASSET_PROFILE_ID_PARAM_DESCRIPTION)
@PathVariable(ASSET_PROFILE_ID) String strAssetProfileId) throws ThingsboardException {
checkParameter(ASSET_PROFILE_ID, strAssetProfileId);
AssetProfileId assetProfileId = new AssetProfileId(toUUID(strAssetProfileId));
@@ -177,20 +178,20 @@ public class AssetProfileController extends BaseController {
@ApiOperation(value = "Get Asset Profiles (getAssetProfiles)",
notes = "Returns a page of asset profile objects owned by tenant. " +
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/assetProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getAssetProfiles(
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = ASSET_PROFILE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ASSET_PROFILE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ASSET_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "description", "isDefault"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return checkNotNull(assetProfileService.findAssetProfiles(getTenantId(), pageLink));
@@ -199,20 +200,20 @@ public class AssetProfileController extends BaseController {
@ApiOperation(value = "Get Asset Profile infos (getAssetProfileInfos)",
notes = "Returns a page of asset profile info objects owned by tenant. " +
PAGE_DATA_PARAMETERS + ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/assetProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getAssetProfileInfos(
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = ASSET_PROFILE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ASSET_PROFILE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ASSET_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "description", "isDefault"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return checkNotNull(assetProfileService.findAssetProfileInfos(getTenantId(), pageLink));
@@ -225,7 +226,7 @@ public class AssetProfileController extends BaseController {
@RequestMapping(value = "/assetProfile/names", method = RequestMethod.GET)
@ResponseBody
public List getAssetProfileNames(
- @ApiParam(value = "Flag indicating whether to retrieve exclusively the names of asset profiles that are referenced by tenant's assets.")
+ @Parameter(description = "Flag indicating whether to retrieve exclusively the names of asset profiles that are referenced by tenant's assets.")
@RequestParam(value = "activeOnly", required = false, defaultValue = "false") boolean activeOnly) throws ThingsboardException {
SecurityUser user = getCurrentUser();
TenantId tenantId = user.getTenantId();
diff --git a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java
index f165d4d918..5e58b28ab0 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AuditLogController.java
@@ -15,8 +15,10 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
@@ -35,6 +37,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageLink;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.Arrays;
@@ -42,7 +45,6 @@ import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
-import static org.thingsboard.server.controller.ControllerConstants.AUDIT_LOG_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.AUDIT_LOG_TEXT_SEARCH_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_ID_PARAM_DESCRIPTION;
@@ -50,7 +52,6 @@ import static org.thingsboard.server.controller.ControllerConstants.ENTITY_TYPE_
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.USER_ID_PARAM_DESCRIPTION;
@@ -74,28 +75,28 @@ public class AuditLogController extends BaseController {
notes = "Returns a page of audit logs related to the targeted customer entities (devices, assets, etc.), " +
"and users actions (login, logout, etc.) that belong to this customer. " +
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/audit/logs/customer/{customerId}", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getAuditLogsByCustomerId(
- @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION)
+ @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable("customerId") String strCustomerId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION)
@RequestParam int page,
- @ApiParam(value = AUDIT_LOG_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = AUDIT_LOG_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = AUDIT_LOG_SORT_PROPERTY_DESCRIPTION, allowableValues = AUDIT_LOG_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = AUDIT_LOG_SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "entityType", "entityName", "userName", "actionType", "actionStatus"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = AUDIT_LOG_QUERY_START_TIME_DESCRIPTION)
+ @Parameter(description = AUDIT_LOG_QUERY_START_TIME_DESCRIPTION)
@RequestParam(required = false) Long startTime,
- @ApiParam(value = AUDIT_LOG_QUERY_END_TIME_DESCRIPTION)
+ @Parameter(description = AUDIT_LOG_QUERY_END_TIME_DESCRIPTION)
@RequestParam(required = false) Long endTime,
- @ApiParam(value = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION)
+ @Parameter(description = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION)
@RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException {
checkParameter("CustomerId", strCustomerId);
TenantId tenantId = getCurrentUser().getTenantId();
@@ -108,28 +109,28 @@ public class AuditLogController extends BaseController {
notes = "Returns a page of audit logs related to the actions of targeted user. " +
"For example, RPC call to a particular device, or alarm acknowledgment for a specific device, etc. " +
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/audit/logs/user/{userId}", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getAuditLogsByUserId(
- @ApiParam(value = USER_ID_PARAM_DESCRIPTION)
+ @Parameter(description = USER_ID_PARAM_DESCRIPTION)
@PathVariable("userId") String strUserId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION)
@RequestParam int page,
- @ApiParam(value = AUDIT_LOG_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = AUDIT_LOG_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = AUDIT_LOG_SORT_PROPERTY_DESCRIPTION, allowableValues = AUDIT_LOG_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = AUDIT_LOG_SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "entityType", "entityName", "userName", "actionType", "actionStatus"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = AUDIT_LOG_QUERY_START_TIME_DESCRIPTION)
+ @Parameter(description = AUDIT_LOG_QUERY_START_TIME_DESCRIPTION)
@RequestParam(required = false) Long startTime,
- @ApiParam(value = AUDIT_LOG_QUERY_END_TIME_DESCRIPTION)
+ @Parameter(description = AUDIT_LOG_QUERY_END_TIME_DESCRIPTION)
@RequestParam(required = false) Long endTime,
- @ApiParam(value = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION)
+ @Parameter(description = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION)
@RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException {
checkParameter("UserId", strUserId);
TenantId tenantId = getCurrentUser().getTenantId();
@@ -143,30 +144,30 @@ public class AuditLogController extends BaseController {
"Basically, this API call is used to get the full lifecycle of some specific entity. " +
"For example to see when a device was created, updated, assigned to some customer, or even deleted from the system. " +
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/audit/logs/entity/{entityType}/{entityId}", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getAuditLogsByEntityId(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE")
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE"))
@PathVariable("entityType") String strEntityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("entityId") String strEntityId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = AUDIT_LOG_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = AUDIT_LOG_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = AUDIT_LOG_SORT_PROPERTY_DESCRIPTION, allowableValues = AUDIT_LOG_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = AUDIT_LOG_SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "entityType", "entityName", "userName", "actionType", "actionStatus"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = AUDIT_LOG_QUERY_START_TIME_DESCRIPTION)
+ @Parameter(description = AUDIT_LOG_QUERY_START_TIME_DESCRIPTION)
@RequestParam(required = false) Long startTime,
- @ApiParam(value = AUDIT_LOG_QUERY_END_TIME_DESCRIPTION)
+ @Parameter(description = AUDIT_LOG_QUERY_END_TIME_DESCRIPTION)
@RequestParam(required = false) Long endTime,
- @ApiParam(value = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION)
+ @Parameter(description = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION)
@RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
@@ -179,26 +180,26 @@ public class AuditLogController extends BaseController {
@ApiOperation(value = "Get all audit logs (getAuditLogs)",
notes = "Returns a page of audit logs related to all entities in the scope of the current user's Tenant. " +
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/audit/logs", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getAuditLogs(
- @ApiParam(value = PAGE_SIZE_DESCRIPTION)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION)
@RequestParam int page,
- @ApiParam(value = AUDIT_LOG_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = AUDIT_LOG_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = AUDIT_LOG_SORT_PROPERTY_DESCRIPTION, allowableValues = AUDIT_LOG_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = AUDIT_LOG_SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "entityType", "entityName", "userName", "actionType", "actionStatus"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = AUDIT_LOG_QUERY_START_TIME_DESCRIPTION)
+ @Parameter(description = AUDIT_LOG_QUERY_START_TIME_DESCRIPTION)
@RequestParam(required = false) Long startTime,
- @ApiParam(value = AUDIT_LOG_QUERY_END_TIME_DESCRIPTION)
+ @Parameter(description = AUDIT_LOG_QUERY_END_TIME_DESCRIPTION)
@RequestParam(required = false) Long endTime,
- @ApiParam(value = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION)
+ @Parameter(description = AUDIT_LOG_QUERY_ACTION_TYPES_DESCRIPTION)
@RequestParam(name = "actionTypes", required = false) String actionTypesStr) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
List actionTypes = parseActionTypesStr(actionTypesStr);
diff --git a/application/src/main/java/org/thingsboard/server/controller/AuthController.java b/application/src/main/java/org/thingsboard/server/controller/AuthController.java
index 92441bf1cb..2898689760 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AuthController.java
@@ -16,8 +16,8 @@
package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.node.ObjectNode;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
@@ -48,6 +48,7 @@ import org.thingsboard.server.common.data.security.event.UserSessionInvalidation
import org.thingsboard.server.common.data.security.model.JwtPair;
import org.thingsboard.server.common.data.security.model.SecuritySettings;
import org.thingsboard.server.common.data.security.model.UserPasswordPolicy;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.cache.limits.RateLimitService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails;
@@ -60,7 +61,6 @@ import org.thingsboard.server.service.security.model.UserPrincipal;
import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
import org.thingsboard.server.service.security.system.SystemSecurityService;
-import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.net.URISyntaxException;
@@ -106,7 +106,7 @@ public class AuthController extends BaseController {
@RequestMapping(value = "/auth/changePassword", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public ObjectNode changePassword(
- @ApiParam(value = "Change Password Request")
+ @Parameter(description = "Change Password Request")
@RequestBody ChangePasswordRequest changePasswordRequest) throws ThingsboardException {
String currentPassword = changePasswordRequest.getCurrentPassword();
String newPassword = changePasswordRequest.getNewPassword();
@@ -145,7 +145,7 @@ public class AuthController extends BaseController {
"If token is not valid, returns '409 Conflict'.")
@RequestMapping(value = "/noauth/activate", params = {"activateToken"}, method = RequestMethod.GET)
public ResponseEntity checkActivateToken(
- @ApiParam(value = "The activate token string.")
+ @Parameter(description = "The activate token string.")
@RequestParam(value = "activateToken") String activateToken) {
HttpHeaders headers = new HttpHeaders();
HttpStatus responseStatus;
@@ -172,7 +172,7 @@ public class AuthController extends BaseController {
@RequestMapping(value = "/noauth/resetPasswordByEmail", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void requestResetPasswordByEmail(
- @ApiParam(value = "The JSON object representing the reset password email request.")
+ @Parameter(description = "The JSON object representing the reset password email request.")
@RequestBody ResetPasswordEmailRequest resetPasswordByEmailRequest,
HttpServletRequest request) throws ThingsboardException {
try {
@@ -195,7 +195,7 @@ public class AuthController extends BaseController {
"If token is not valid, returns '409 Conflict'.")
@RequestMapping(value = "/noauth/resetPassword", params = {"resetToken"}, method = RequestMethod.GET)
public ResponseEntity checkResetToken(
- @ApiParam(value = "The reset token string.")
+ @Parameter(description = "The reset token string.")
@RequestParam(value = "resetToken") String resetToken) {
HttpHeaders headers = new HttpHeaders();
HttpStatus responseStatus;
@@ -231,7 +231,7 @@ public class AuthController extends BaseController {
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
public JwtPair activateUser(
- @ApiParam(value = "Activate user request.")
+ @Parameter(description = "Activate user request.")
@RequestBody ActivateUserRequest activateRequest,
@RequestParam(required = false, defaultValue = "true") boolean sendActivationMail,
HttpServletRequest request) throws ThingsboardException {
@@ -267,7 +267,7 @@ public class AuthController extends BaseController {
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
public JwtPair resetPassword(
- @ApiParam(value = "Reset password request.")
+ @Parameter(description = "Reset password request.")
@RequestBody ResetPasswordRequest resetPasswordRequest,
HttpServletRequest request) throws ThingsboardException {
String resetToken = resetPasswordRequest.getResetToken();
diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
index c40760dda3..9c437821c6 100644
--- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
@@ -17,6 +17,9 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.ListenableFuture;
+import jakarta.mail.MessagingException;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.ConstraintViolation;
import lombok.Getter;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
@@ -141,7 +144,7 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.action.EntityActionService;
import org.thingsboard.server.service.component.ComponentDiscoveryService;
-import org.thingsboard.server.service.entitiy.TbNotificationEntityService;
+import org.thingsboard.server.service.entitiy.TbLogEntityActionService;
import org.thingsboard.server.service.entitiy.user.TbUserSettingsService;
import org.thingsboard.server.service.ota.OtaPackageStateService;
import org.thingsboard.server.service.profile.TbAssetProfileCache;
@@ -156,9 +159,6 @@ import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService;
import org.thingsboard.server.service.telemetry.AlarmSubscriptionService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
-import javax.mail.MessagingException;
-import javax.servlet.http.HttpServletResponse;
-import javax.validation.ConstraintViolation;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -299,7 +299,7 @@ public abstract class BaseController {
protected EdgeService edgeService;
@Autowired
- protected TbNotificationEntityService notificationEntityService;
+ protected TbLogEntityActionService logEntityActionService;
@Autowired
protected EntityActionService entityActionService;
@@ -386,7 +386,7 @@ public abstract class BaseController {
}
/**
- * Handles validation error for controller method arguments annotated with @{@link javax.validation.Valid}
+ * Handles validation error for controller method arguments annotated with @{@link jakarta.validation.Valid}
* */
@ExceptionHandler(MethodArgumentNotValidException.class)
public void handleValidationError(MethodArgumentNotValidException validationError, HttpServletResponse response) {
diff --git a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java b/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
index 53fc4f8c32..595d6feef1 100644
--- a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
@@ -15,8 +15,8 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.data.rule.RuleChainType;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.HashSet;
@@ -54,7 +55,7 @@ public class ComponentDescriptorController extends BaseController {
@RequestMapping(value = "/component/{componentDescriptorClazz:.+}", method = RequestMethod.GET)
@ResponseBody
public ComponentDescriptor getComponentDescriptorByClazz(
- @ApiParam(value = "Component Descriptor class name", required = true)
+ @Parameter(description = "Component Descriptor class name", required = true)
@PathVariable("componentDescriptorClazz") String strComponentDescriptorClazz) throws ThingsboardException {
checkParameter("strComponentDescriptorClazz", strComponentDescriptorClazz);
return checkComponentDescriptorByClazz(strComponentDescriptorClazz);
@@ -67,9 +68,9 @@ public class ComponentDescriptorController extends BaseController {
@RequestMapping(value = "/components/{componentType}", method = RequestMethod.GET)
@ResponseBody
public List getComponentDescriptorsByType(
- @ApiParam(value = "Type of the Rule Node", allowableValues = "ENRICHMENT,FILTER,TRANSFORMATION,ACTION,EXTERNAL", required = true)
+ @Parameter(description = "Type of the Rule Node", schema = @Schema(allowableValues = "ENRICHMENT,FILTER,TRANSFORMATION,ACTION,EXTERNAL", required = true))
@PathVariable("componentType") String strComponentType,
- @ApiParam(value = "Type of the Rule Chain", allowableValues = "CORE,EDGE")
+ @Parameter(description = "Type of the Rule Chain", schema = @Schema(allowableValues = "CORE,EDGE"))
@RequestParam(value = "ruleChainType", required = false) String strRuleChainType) throws ThingsboardException {
checkParameter("componentType", strComponentType);
return checkComponentDescriptorsByType(ComponentType.valueOf(strComponentType), getRuleChainType(strRuleChainType));
@@ -82,9 +83,9 @@ public class ComponentDescriptorController extends BaseController {
@RequestMapping(value = "/components", params = {"componentTypes"}, method = RequestMethod.GET)
@ResponseBody
public List getComponentDescriptorsByTypes(
- @ApiParam(value = "List of types of the Rule Nodes, (ENRICHMENT, FILTER, TRANSFORMATION, ACTION or EXTERNAL)", required = true)
+ @Parameter(description = "List of types of the Rule Nodes, (ENRICHMENT, FILTER, TRANSFORMATION, ACTION or EXTERNAL)", required = true)
@RequestParam("componentTypes") String[] strComponentTypes,
- @ApiParam(value = "Type of the Rule Chain", allowableValues = "CORE,EDGE")
+ @Parameter(description = "Type of the Rule Chain", schema = @Schema(allowableValues = "CORE,EDGE"))
@RequestParam(value = "ruleChainType", required = false) String strRuleChainType) throws ThingsboardException {
checkArrayParameter("componentTypes", strComponentTypes);
Set componentTypes = new HashSet<>();
diff --git a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
index f7e767f017..d9232cda78 100644
--- a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
+++ b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java
@@ -95,34 +95,8 @@ public class ControllerConstants {
protected static final String EVENT_TEXT_SEARCH_DESCRIPTION = "The value is not used in searching.";
protected static final String AUDIT_LOG_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on one of the next properties: entityType, entityName, userName, actionType, actionStatus.";
protected static final String SORT_PROPERTY_DESCRIPTION = "Property of entity to sort by";
- protected static final String DASHBOARD_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title";
- protected static final String CUSTOMER_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title, email, country, city";
- protected static final String RPC_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, expirationTime, request, response";
- protected static final String DEVICE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, deviceProfileName, label, customerTitle";
- protected static final String ENTITY_VIEW_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type";
- protected static final String ENTITY_VIEW_INFO_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, customerTitle";
- protected static final String USER_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, firstName, lastName, email";
- protected static final String TENANT_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title, email, country, state, city, address, address2, zip, phone, email";
- protected static final String TENANT_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, description, isDefault";
- protected static final String TENANT_PROFILE_INFO_SORT_PROPERTY_ALLOWABLE_VALUES = "id, name";
- protected static final String TENANT_INFO_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, tenantProfileName, title, email, country, state, city, address, address2, zip, phone, email";
- protected static final String DEVICE_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, transportType, description, isDefault";
-
- protected static final String ASSET_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, description, isDefault";
- protected static final String ASSET_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, label, customerTitle";
- protected static final String ALARM_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, startTs, endTs, type, ackTs, clearTs, severity, status";
- protected static final String ALARM_COMMENT_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, id";
- protected static final String EVENT_SORT_PROPERTY_ALLOWABLE_VALUES = "ts, id";
- protected static final String EDGE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, type, label, customerTitle";
- protected static final String RULE_CHAIN_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, root";
- protected static final String WIDGET_BUNDLE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title, tenantId";
- protected static final String WIDGET_TYPE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, deprecated, tenantId";
- protected static final String AUDIT_LOG_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, entityType, entityName, userName, actionType, actionStatus";
+
protected static final String SORT_ORDER_DESCRIPTION = "Sort order. ASC (ASCENDING) or DESC (DESCENDING)";
- protected static final String SORT_ORDER_ALLOWABLE_VALUES = "ASC, DESC";
- protected static final String RPC_STATUS_ALLOWABLE_VALUES = "QUEUED, SENT, DELIVERED, SUCCESSFUL, TIMEOUT, EXPIRED, FAILED";
- protected static final String RULE_CHAIN_TYPES_ALLOWABLE_VALUES = "CORE, EDGE";
- protected static final String TRANSPORT_TYPE_ALLOWABLE_VALUES = "DEFAULT, MQTT, COAP, LWM2M, SNMP";
protected static final String DEVICE_INFO_DESCRIPTION = "Device Info is an extension of the default Device object that contains information about the assigned customer name and device profile name. ";
protected static final String ASSET_INFO_DESCRIPTION = "Asset Info is an extension of the default Asset object that contains information about the assigned customer name. ";
protected static final String ALARM_INFO_DESCRIPTION = "Alarm Info is an extension of the default Alarm object that also contains name of the alarm originator.";
@@ -132,28 +106,21 @@ public class ControllerConstants {
protected static final String ASSET_PROFILE_INFO_DESCRIPTION = "Asset Profile Info is a lightweight object that includes main information about Asset Profile. ";
protected static final String QUEUE_SERVICE_TYPE_DESCRIPTION = "Service type (implemented only for the TB-RULE-ENGINE)";
- protected static final String QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES = "TB-RULE-ENGINE, TB-CORE, TB-TRANSPORT, JS-EXECUTOR";
protected static final String QUEUE_QUEUE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the queue name.";
- protected static final String QUEUE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, name, topic";
protected static final String QUEUE_ID_PARAM_DESCRIPTION = "A string value representing the queue id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'";
protected static final String QUEUE_NAME_PARAM_DESCRIPTION = "A string value representing the queue id. For example, 'Main'";
protected static final String OTA_PACKAGE_INFO_DESCRIPTION = "OTA Package Info is a lightweight object that includes main information about the OTA Package excluding the heavyweight data. ";
protected static final String OTA_PACKAGE_DESCRIPTION = "OTA Package is a heavyweight object that includes main information about the OTA Package and also data. ";
- protected static final String OTA_PACKAGE_CHECKSUM_ALGORITHM_ALLOWABLE_VALUES = "MD5, SHA256, SHA384, SHA512, CRC32, MURMUR3_32, MURMUR3_128";
protected static final String OTA_PACKAGE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the ota package title.";
- protected static final String OTA_PACKAGE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, type, title, version, tag, url, fileName, dataSize, checksum";
protected static final String RESOURCE_INFO_DESCRIPTION = "Resource Info is a lightweight object that includes main information about the Resource excluding the heavyweight data. ";
protected static final String RESOURCE_DESCRIPTION = "Resource is a heavyweight object that includes main information about the Resource and also data. ";
protected static final String RESOURCE_INCLUDE_SYSTEM_IMAGES_DESCRIPTION = "Use 'true' to include system images. Disabled by default. Ignored for requests by users with system administrator authority.";
protected static final String RESOURCE_TEXT_SEARCH_DESCRIPTION = "The case insensitive 'substring' filter based on the resource title.";
- protected static final String RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES = "createdTime, title, resourceType, tenantId";
- protected static final String RESOURCE_TYPE_PROPERTY_ALLOWABLE_VALUES = "LWM2M_MODEL, JKS, PKCS_12, JS_MODULE";
protected static final String RESOURCE_TYPE = "A string value representing the resource type.";
protected static final String LWM2M_OBJECT_DESCRIPTION = "LwM2M Object is a object that includes information about the LwM2M model which can be used in transport configuration for the LwM2M device profile. ";
- protected static final String LWM2M_OBJECT_SORT_PROPERTY_ALLOWABLE_VALUES = "id, name";
protected static final String DEVICE_NAME_DESCRIPTION = "A string value representing the Device name.";
protected static final String ASSET_NAME_DESCRIPTION = "A string value representing the Asset name.";
@@ -1632,8 +1599,6 @@ public class ControllerConstants {
protected static final String ATTRIBUTES_SCOPE_DESCRIPTION = "A string value representing the attributes scope. For example, 'SERVER_SCOPE'.";
protected static final String ATTRIBUTES_KEYS_DESCRIPTION = "A string value representing the comma-separated list of attributes keys. For example, 'active,inactivityAlarmTime'.";
- protected static final String ATTRIBUTES_SAVE_SCOPE_ALLOWED_VALUES = "SERVER_SCOPE, SHARED_SCOPE";
- protected static final String ATTRIBUTES_SCOPE_ALLOWED_VALUES = ATTRIBUTES_SAVE_SCOPE_ALLOWED_VALUES + ", CLIENT_SCOPE";
protected static final String ATTRIBUTES_JSON_REQUEST_DESCRIPTION = "A string value representing the json object. For example, '{\"key\":\"value\"}'. See API call description for more details.";
protected static final String TELEMETRY_KEYS_BASE_DESCRIPTION = "A string value representing the comma-separated list of telemetry keys.";
diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
index b21127da63..c24c345f76 100644
--- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java
@@ -17,8 +17,8 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.customer.TbCustomerService;
import org.thingsboard.server.service.security.permission.Operation;
@@ -44,13 +45,11 @@ import org.thingsboard.server.service.security.permission.Resource;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_TEXT_SEARCH_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.HOME_DASHBOARD;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
@@ -76,7 +75,7 @@ public class CustomerController extends BaseController {
@RequestMapping(value = "/customer/{customerId}", method = RequestMethod.GET)
@ResponseBody
public Customer getCustomerById(
- @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION)
+ @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable(CUSTOMER_ID) String strCustomerId) throws ThingsboardException {
checkParameter(CUSTOMER_ID, strCustomerId);
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
@@ -95,7 +94,7 @@ public class CustomerController extends BaseController {
@RequestMapping(value = "/customer/{customerId}/shortInfo", method = RequestMethod.GET)
@ResponseBody
public JsonNode getShortCustomerInfoById(
- @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION)
+ @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable(CUSTOMER_ID) String strCustomerId) throws ThingsboardException {
checkParameter(CUSTOMER_ID, strCustomerId);
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
@@ -113,7 +112,7 @@ public class CustomerController extends BaseController {
@RequestMapping(value = "/customer/{customerId}/title", method = RequestMethod.GET, produces = "application/text")
@ResponseBody
public String getCustomerTitleById(
- @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION)
+ @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable(CUSTOMER_ID) String strCustomerId) throws ThingsboardException {
checkParameter(CUSTOMER_ID, strCustomerId);
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
@@ -131,7 +130,7 @@ public class CustomerController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer", method = RequestMethod.POST)
@ResponseBody
- public Customer saveCustomer(@ApiParam(value = "A JSON value representing the customer.") @RequestBody Customer customer) throws Exception {
+ public Customer saveCustomer(@Parameter(description = "A JSON value representing the customer.") @RequestBody Customer customer) throws Exception {
customer.setTenantId(getTenantId());
checkEntity(customer.getId(), customer, Resource.CUSTOMER);
return tbCustomerService.save(customer, getCurrentUser());
@@ -144,7 +143,7 @@ public class CustomerController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/{customerId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
- public void deleteCustomer(@ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION)
+ public void deleteCustomer(@Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable(CUSTOMER_ID) String strCustomerId) throws ThingsboardException {
checkParameter(CUSTOMER_ID, strCustomerId);
CustomerId customerId = new CustomerId(toUUID(strCustomerId));
@@ -159,15 +158,15 @@ public class CustomerController extends BaseController {
@RequestMapping(value = "/customers", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getCustomers(
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = CUSTOMER_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = CUSTOMER_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = CUSTOMER_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "title", "email", "country, city"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TenantId tenantId = getCurrentUser().getTenantId();
@@ -180,7 +179,7 @@ public class CustomerController extends BaseController {
@RequestMapping(value = "/tenant/customers", params = {"customerTitle"}, method = RequestMethod.GET)
@ResponseBody
public Customer getTenantCustomer(
- @ApiParam(value = "A string value representing the Customer title.")
+ @Parameter(description = "A string value representing the Customer title.")
@RequestParam String customerTitle) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(customerService.findCustomerByTenantIdAndTitle(tenantId, customerTitle), "Customer with title [" + customerTitle + "] is not found");
diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
index 6097ef10c8..0683bc21be 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java
@@ -17,11 +17,11 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-import io.swagger.annotations.ApiResponse;
-import io.swagger.annotations.Example;
-import io.swagger.annotations.ExampleProperty;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.ExampleObject;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
@@ -52,6 +52,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.resource.ImageService;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.dashboard.TbDashboardService;
import org.thingsboard.server.service.security.model.SecurityUser;
@@ -67,7 +68,6 @@ import java.util.stream.Collectors;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.DASHBOARD_ID_PARAM_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.DASHBOARD_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.DASHBOARD_TEXT_SEARCH_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.EDGE_ASSIGN_ASYNC_FIRST_STEP_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION;
@@ -80,7 +80,6 @@ import static org.thingsboard.server.controller.ControllerConstants.INLINE_IMAGE
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH;
@@ -115,7 +114,7 @@ public class DashboardController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/dashboard/serverTime", method = RequestMethod.GET)
@ResponseBody
- @ApiResponse(code = 200, message = "OK", examples = @Example(value = @ExampleProperty(value = "1636023857137", mediaType = "application/json")))
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(mediaType = "application/json", examples = @ExampleObject(value = "1636023857137")))
public long getServerTime() throws ThingsboardException {
return System.currentTimeMillis();
}
@@ -128,20 +127,20 @@ public class DashboardController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/dashboard/maxDatapointsLimit", method = RequestMethod.GET)
@ResponseBody
- @ApiResponse(code = 200, message = "OK", examples = @Example(value = @ExampleProperty(value = "5000", mediaType = "application/json")))
+ @ApiResponse(responseCode = "200", description = "OK", content = @Content(mediaType = "application/json", examples = @ExampleObject(value = "5000")))
public long getMaxDatapointsLimit() throws ThingsboardException {
return maxDatapointsLimit;
}
@ApiOperation(value = "Get Dashboard Info (getDashboardInfoById)",
notes = "Get the information about the dashboard based on 'dashboardId' parameter. " + DASHBOARD_INFO_DEFINITION,
- produces = MediaType.APPLICATION_JSON_VALUE
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))
)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/dashboard/info/{dashboardId}", method = RequestMethod.GET)
@ResponseBody
public DashboardInfo getDashboardInfoById(
- @ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
@@ -150,15 +149,15 @@ public class DashboardController extends BaseController {
@ApiOperation(value = "Get Dashboard (getDashboardById)",
notes = "Get the dashboard based on 'dashboardId' parameter. " + DASHBOARD_DEFINITION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))
)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/dashboard/{dashboardId}", method = RequestMethod.GET)
@ResponseBody
public Dashboard getDashboardById(
- @ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId,
- @ApiParam(value = INLINE_IMAGES_DESCRIPTION)
+ @Parameter(description = INLINE_IMAGES_DESCRIPTION)
@RequestParam(value = INLINE_IMAGES, required = false) boolean inlineImages) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
@@ -176,13 +175,12 @@ public class DashboardController extends BaseController {
"Referencing non-existing dashboard Id will cause 'Not Found' error. " +
"Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Dashboard entity. " +
TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE,
- consumes = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/dashboard", method = RequestMethod.POST)
@ResponseBody
public Dashboard saveDashboard(
- @ApiParam(value = "A JSON value representing the dashboard.")
+ @Parameter(description = "A JSON value representing the dashboard.")
@RequestBody Dashboard dashboard) throws Exception {
dashboard.setTenantId(getTenantId());
checkEntity(dashboard.getId(), dashboard, Resource.DASHBOARD);
@@ -195,7 +193,7 @@ public class DashboardController extends BaseController {
@RequestMapping(value = "/dashboard/{dashboardId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
public void deleteDashboard(
- @ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
@@ -206,14 +204,14 @@ public class DashboardController extends BaseController {
@ApiOperation(value = "Assign the Dashboard (assignDashboardToCustomer)",
notes = "Assign the Dashboard to specified Customer or do nothing if the Dashboard is already assigned to that Customer. " +
"Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/{customerId}/dashboard/{dashboardId}", method = RequestMethod.POST)
@ResponseBody
public Dashboard assignDashboardToCustomer(
- @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION)
+ @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable(CUSTOMER_ID) String strCustomerId,
- @ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter(CUSTOMER_ID, strCustomerId);
checkParameter(DASHBOARD_ID, strDashboardId);
@@ -229,14 +227,14 @@ public class DashboardController extends BaseController {
@ApiOperation(value = "Unassign the Dashboard (unassignDashboardFromCustomer)",
notes = "Unassign the Dashboard from specified Customer or do nothing if the Dashboard is already assigned to that Customer. " +
"Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/{customerId}/dashboard/{dashboardId}", method = RequestMethod.DELETE)
@ResponseBody
public Dashboard unassignDashboardFromCustomer(
- @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION)
+ @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable(CUSTOMER_ID) String strCustomerId,
- @ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
checkParameter(DASHBOARD_ID, strDashboardId);
@@ -250,16 +248,15 @@ public class DashboardController extends BaseController {
@ApiOperation(value = "Update the Dashboard Customers (updateDashboardCustomers)",
notes = "Updates the list of Customers that this Dashboard is assigned to. Removes previous assignments to customers that are not in the provided list. " +
"Returns the Dashboard object. " + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE,
- consumes = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/dashboard/{dashboardId}/customers", method = RequestMethod.POST)
@ResponseBody
public Dashboard updateDashboardCustomers(
- @ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId,
- @ApiParam(value = "JSON array with the list of customer ids, or empty to remove all customers")
+ @Parameter(description = "JSON array with the list of customer ids, or empty to remove all customers")
@RequestBody(required = false) String[] strCustomerIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
@@ -271,15 +268,14 @@ public class DashboardController extends BaseController {
@ApiOperation(value = "Adds the Dashboard Customers (addDashboardCustomers)",
notes = "Adds the list of Customers to the existing list of assignments for the Dashboard. Keeps previous assignments to customers that are not in the provided list. " +
"Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE,
- consumes = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/dashboard/{dashboardId}/customers/add", method = RequestMethod.POST)
@ResponseBody
public Dashboard addDashboardCustomers(
- @ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId,
- @ApiParam(value = "JSON array with the list of customer ids")
+ @Parameter(description = "JSON array with the list of customer ids")
@RequestBody String[] strCustomerIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
@@ -291,15 +287,14 @@ public class DashboardController extends BaseController {
@ApiOperation(value = "Remove the Dashboard Customers (removeDashboardCustomers)",
notes = "Removes the list of Customers from the existing list of assignments for the Dashboard. Keeps other assignments to customers that are not in the provided list. " +
"Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE,
- consumes = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/dashboard/{dashboardId}/customers/remove", method = RequestMethod.POST)
@ResponseBody
public Dashboard removeDashboardCustomers(
- @ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId,
- @ApiParam(value = "JSON array with the list of customer ids")
+ @Parameter(description = "JSON array with the list of customer ids")
@RequestBody String[] strCustomerIds) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
@@ -315,12 +310,12 @@ public class DashboardController extends BaseController {
"Use [assign Asset to Public Customer](#!/asset-controller/assignAssetToPublicCustomerUsingPOST) and " +
"[assign Device to Public Customer](#!/device-controller/assignDeviceToPublicCustomerUsingPOST) for this purpose. " +
"Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.POST)
@ResponseBody
public Dashboard assignDashboardToPublicCustomer(
- @ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
@@ -331,12 +326,12 @@ public class DashboardController extends BaseController {
@ApiOperation(value = "Unassign the Dashboard from Public Customer (unassignDashboardFromPublicCustomer)",
notes = "Unassigns the dashboard from a special, auto-generated 'Public' Customer. Once unassigned, unauthenticated users may no longer browse the dashboard. " +
"Returns the Dashboard object." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/public/dashboard/{dashboardId}", method = RequestMethod.DELETE)
@ResponseBody
public Dashboard unassignDashboardFromPublicCustomer(
- @ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DASHBOARD_ID_PARAM_DESCRIPTION)
@PathVariable(DASHBOARD_ID) String strDashboardId) throws ThingsboardException {
checkParameter(DASHBOARD_ID, strDashboardId);
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
@@ -347,22 +342,22 @@ public class DashboardController extends BaseController {
@ApiOperation(value = "Get Tenant Dashboards by System Administrator (getTenantDashboards)",
notes = "Returns a page of dashboard info objects owned by tenant. " + DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS +
SYSTEM_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@RequestMapping(value = "/tenant/{tenantId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getTenantDashboards(
- @ApiParam(value = TENANT_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = TENANT_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(TENANT_ID) String strTenantId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = DASHBOARD_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = DASHBOARD_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = DASHBOARD_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "title"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
TenantId tenantId = TenantId.fromUUID(toUUID(strTenantId));
checkTenantId(tenantId, Operation.READ);
@@ -373,22 +368,22 @@ public class DashboardController extends BaseController {
@ApiOperation(value = "Get Tenant Dashboards (getTenantDashboards)",
notes = "Returns a page of dashboard info objects owned by the tenant of a current user. "
+ DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getTenantDashboards(
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = HIDDEN_FOR_MOBILE)
+ @Parameter(description = HIDDEN_FOR_MOBILE)
@RequestParam(required = false) Boolean mobile,
- @ApiParam(value = DASHBOARD_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = DASHBOARD_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = DASHBOARD_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "title"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@@ -402,24 +397,24 @@ public class DashboardController extends BaseController {
@ApiOperation(value = "Get Customer Dashboards (getCustomerDashboards)",
notes = "Returns a page of dashboard info objects owned by the specified customer. "
+ DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getCustomerDashboards(
- @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(CUSTOMER_ID) String strCustomerId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = HIDDEN_FOR_MOBILE)
+ @Parameter(description = HIDDEN_FOR_MOBILE)
@RequestParam(required = false) Boolean mobile,
- @ApiParam(value = DASHBOARD_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = DASHBOARD_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = DASHBOARD_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "title"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter(CUSTOMER_ID, strCustomerId);
TenantId tenantId = getCurrentUser().getTenantId();
@@ -438,7 +433,7 @@ public class DashboardController extends BaseController {
"If 'homeDashboardId' parameter is not set on the User level and the User has authority 'CUSTOMER_USER', check the same parameter for the corresponding Customer. " +
"If 'homeDashboardId' parameter is not set on the User and Customer levels then checks the same parameter for the Tenant that owns the user. "
+ DASHBOARD_DEFINITION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/dashboard/home", method = RequestMethod.GET)
@ResponseBody
@@ -471,7 +466,7 @@ public class DashboardController extends BaseController {
"If 'homeDashboardId' parameter is not set on the User level and the User has authority 'CUSTOMER_USER', check the same parameter for the corresponding Customer. " +
"If 'homeDashboardId' parameter is not set on the User and Customer levels then checks the same parameter for the Tenant that owns the user. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/dashboard/home/info", method = RequestMethod.GET)
@ResponseBody
@@ -502,7 +497,7 @@ public class DashboardController extends BaseController {
@ApiOperation(value = "Get Tenant Home Dashboard Info (getTenantHomeDashboardInfo)",
notes = "Returns the home dashboard info object that is configured as 'homeDashboardId' parameter in the 'additionalInfo' of the corresponding tenant. " +
TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.GET)
@ResponseBody
@@ -524,12 +519,12 @@ public class DashboardController extends BaseController {
@ApiOperation(value = "Update Tenant Home Dashboard Info (getTenantHomeDashboardInfo)",
notes = "Update the home dashboard assignment for the current tenant. " +
TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/dashboard/home/info", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void setTenantHomeDashboardInfo(
- @ApiParam(value = "A JSON object that represents home dashboard id and other parameters", required = true)
+ @Parameter(description = "A JSON object that represents home dashboard id and other parameters", required = true)
@RequestBody HomeDashboardInfo homeDashboardInfo) throws ThingsboardException {
if (homeDashboardInfo.getDashboardId() != null) {
@@ -537,7 +532,7 @@ public class DashboardController extends BaseController {
}
Tenant tenant = tenantService.findTenantById(getTenantId());
JsonNode additionalInfo = tenant.getAdditionalInfo();
- if (additionalInfo == null || !(additionalInfo instanceof ObjectNode)) {
+ if (!(additionalInfo instanceof ObjectNode)) {
additionalInfo = JacksonUtil.newObjectNode();
}
if (homeDashboardInfo.getDashboardId() != null) {
@@ -563,8 +558,7 @@ public class DashboardController extends BaseController {
}
return new HomeDashboardInfo(dashboardId, hideDashboardToolbar);
}
- } catch (Exception e) {
- }
+ } catch (Exception ignored) {}
return null;
}
@@ -580,8 +574,7 @@ public class DashboardController extends BaseController {
}
return new HomeDashboard(dashboard, hideDashboardToolbar);
}
- } catch (Exception e) {
- }
+ } catch (Exception ignored) {}
return null;
}
@@ -592,7 +585,7 @@ public class DashboardController extends BaseController {
EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION +
"Third, once dashboard will be delivered to edge service, it's going to be available for usage on remote edge instance." +
TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.POST)
@ResponseBody
@@ -616,7 +609,7 @@ public class DashboardController extends BaseController {
EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION +
"Third, once 'unassign' command will be delivered to edge service, it's going to remove dashboard locally." +
TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/dashboard/{dashboardId}", method = RequestMethod.DELETE)
@ResponseBody
@@ -637,22 +630,22 @@ public class DashboardController extends BaseController {
@ApiOperation(value = "Get Edge Dashboards (getEdgeDashboards)",
notes = "Returns a page of dashboard info objects assigned to the specified edge. "
+ DASHBOARD_INFO_DEFINITION + " " + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/edge/{edgeId}/dashboards", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getEdgeDashboards(
- @ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(EDGE_ID) String strEdgeId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = DASHBOARD_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = DASHBOARD_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = DASHBOARD_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "title"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
TenantId tenantId = getCurrentUser().getTenantId();
diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java
index 22a1b95843..48e99e474a 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DeviceConnectivityController.java
@@ -16,10 +16,11 @@
package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-import io.swagger.annotations.ApiResponse;
-import io.swagger.annotations.ApiResponses;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.ExampleObject;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
@@ -36,12 +37,12 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.device.DeviceConnectivityService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.system.SystemSecurityService;
-import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URISyntaxException;
@@ -67,24 +68,38 @@ public class DeviceConnectivityController extends BaseController {
notes = "Fetch the list of commands to publish device telemetry based on device profile " +
"If the user has the authority of 'Tenant Administrator', the server checks that the device is owned by the same tenant. " +
"If the user has the authority of 'Customer User', the server checks that the device is assigned to the same customer. " +
- TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
- @ApiResponses(value = {
- @ApiResponse(code = 200, message = "OK",
- examples = @io.swagger.annotations.Example(
- value = {
- @io.swagger.annotations.ExampleProperty(
- mediaType = "application/json",
- value = "{\"http\":\"curl -v -X POST http://localhost:8080/api/v1/0ySs4FTOn5WU15XLmal8/telemetry --header Content-Type:application/json --data {temperature:25}\"," +
- "\"mqtt\":\"mosquitto_pub -d -q 1 -h localhost -t v1/devices/me/telemetry -i myClient1 -u myUsername1 -P myPassword -m {temperature:25}\"," +
- "\"coap\":\"coap-client -m POST coap://localhost:5683/api/v1/0ySs4FTOn5WU15XLmal8/telemetry -t json -e {temperature:25}\"}")}))})
+ TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
+ responses = {
+ @ApiResponse(
+ responseCode = "200",
+ description = "OK",
+ content = @Content(
+ mediaType = MediaType.APPLICATION_JSON_VALUE,
+ examples = {
+ @ExampleObject(
+ name = "http",
+ value = "curl -v -X POST http://localhost:8080/api/v1/0ySs4FTOn5WU15XLmal8/telemetry --header Content-Type:application/json --data {temperature:25}"
+ ),
+ @ExampleObject(
+ name = "mqtt",
+ value = "mosquitto_pub -d -q 1 -h localhost -t v1/devices/me/telemetry -i myClient1 -u myUsername1 -P myPassword -m {temperature:25}"
+ ),
+ @ExampleObject(
+ name = "coap",
+ value = "coap-client -m POST coap://localhost:5683/api/v1/0ySs4FTOn5WU15XLmal8/telemetry -t json -e {temperature:25}"
+ )
+ }
+ )
+ )
+ })
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device-connectivity/{deviceId}", method = RequestMethod.GET)
@ResponseBody
- public JsonNode getDevicePublishTelemetryCommands(@ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
+ public JsonNode getDevicePublishTelemetryCommands(@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String strDeviceId, HttpServletRequest request) throws ThingsboardException, URISyntaxException {
checkParameter(DEVICE_ID, strDeviceId);
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
- Device device = checkDeviceId(deviceId, Operation.READ_CREDENTIALS);
+ Device device = checkDeviceId(deviceId, org.thingsboard.server.service.security.permission.Operation.READ_CREDENTIALS);
String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request);
return deviceConnectivityService.findDevicePublishTelemetryCommands(baseUrl, device);
@@ -93,7 +108,7 @@ public class DeviceConnectivityController extends BaseController {
@ApiOperation(value = "Download server certificate using file path defined in device.connectivity properties (downloadServerCertificate)", notes = "Download server certificate.")
@RequestMapping(value = "/device-connectivity/{protocol}/certificate/download", method = RequestMethod.GET)
@ResponseBody
- public ResponseEntity downloadServerCertificate(@ApiParam(value = PROTOCOL_PARAM_DESCRIPTION)
+ public ResponseEntity downloadServerCertificate(@Parameter(description = PROTOCOL_PARAM_DESCRIPTION)
@PathVariable(PROTOCOL) String protocol) throws ThingsboardException, IOException {
checkParameter(PROTOCOL, protocol);
var pemCert =
@@ -110,7 +125,7 @@ public class DeviceConnectivityController extends BaseController {
@ApiOperation(value = "Download generated docker-compose.yml file for gateway (downloadGatewayDockerCompose)", notes = "Download generated docker-compose.yml for gateway.")
@RequestMapping(value = "/device-connectivity/gateway-launch/{deviceId}/docker-compose/download", method = RequestMethod.GET)
@ResponseBody
- public ResponseEntity downloadGatewayDockerCompose(@ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
+ public ResponseEntity downloadGatewayDockerCompose(@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String strDeviceId, HttpServletRequest request) throws ThingsboardException, URISyntaxException, IOException {
checkParameter(DEVICE_ID, strDeviceId);
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
index b93f0e0321..555769370c 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
@@ -19,8 +19,11 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import jakarta.annotation.Nullable;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
@@ -62,6 +65,7 @@ import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportRequest;
import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportResult;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.device.claim.ClaimResponse;
import org.thingsboard.server.dao.device.claim.ClaimResult;
import org.thingsboard.server.dao.device.claim.ReclaimResult;
@@ -74,8 +78,7 @@ import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
-import javax.annotation.Nullable;
-import javax.validation.Valid;
+import jakarta.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -91,7 +94,6 @@ import static org.thingsboard.server.controller.ControllerConstants.DEVICE_ID_PA
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_INFO_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_NAME_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_ID_PARAM_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.DEVICE_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_TEXT_SEARCH_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_TYPE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_UPDATE_CREDENTIALS_PARAM_ACCESS_TOKEN_DESCRIPTION_MARKDOWN;
@@ -111,7 +113,6 @@ import static org.thingsboard.server.controller.ControllerConstants.EDGE_UNASSIG
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
@@ -142,7 +143,7 @@ public class DeviceController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device/{deviceId}", method = RequestMethod.GET)
@ResponseBody
- public Device getDeviceById(@ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
+ public Device getDeviceById(@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
checkParameter(DEVICE_ID, strDeviceId);
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
@@ -157,7 +158,7 @@ public class DeviceController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device/info/{deviceId}", method = RequestMethod.GET)
@ResponseBody
- public DeviceInfo getDeviceInfoById(@ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
+ public DeviceInfo getDeviceInfoById(@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
checkParameter(DEVICE_ID, strDeviceId);
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
@@ -176,17 +177,16 @@ public class DeviceController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device", method = RequestMethod.POST)
@ResponseBody
- public Device saveDevice(@ApiParam(value = "A JSON value representing the device.") @RequestBody Device device,
- @ApiParam(value = "Optional value of the device credentials to be used during device creation. " +
+ public Device saveDevice(@Parameter(description = "A JSON value representing the device.") @RequestBody Device device,
+ @Parameter(description = "Optional value of the device credentials to be used during device creation. " +
"If omitted, access token will be auto-generated.") @RequestParam(name = "accessToken", required = false) String accessToken) throws Exception {
device.setTenantId(getCurrentUser().getTenantId());
- Device oldDevice = null;
if (device.getId() != null) {
- oldDevice = checkDeviceId(device.getId(), Operation.WRITE);
+ checkDeviceId(device.getId(), Operation.WRITE);
} else {
checkEntity(null, device, Resource.DEVICE);
}
- return tbDeviceService.save(device, oldDevice, accessToken, getCurrentUser());
+ return tbDeviceService.save(device, accessToken, getCurrentUser());
}
@ApiOperation(value = "Create Device (saveDevice) with credentials ",
@@ -210,7 +210,7 @@ public class DeviceController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device-with-credentials", method = RequestMethod.POST)
@ResponseBody
- public Device saveDeviceWithCredentials(@ApiParam(value = "The JSON object with device and credentials. See method description above for example.")
+ public Device saveDeviceWithCredentials(@Parameter(description = "The JSON object with device and credentials. See method description above for example.")
@Valid @RequestBody SaveDeviceWithCredentialsRequest deviceAndCredentials) throws ThingsboardException {
Device device = deviceAndCredentials.getDevice();
DeviceCredentials credentials = deviceAndCredentials.getCredentials();
@@ -224,7 +224,7 @@ public class DeviceController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/device/{deviceId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
- public void deleteDevice(@ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
+ public void deleteDevice(@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String strDeviceId) throws Exception {
checkParameter(DEVICE_ID, strDeviceId);
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
@@ -237,9 +237,9 @@ public class DeviceController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/{customerId}/device/{deviceId}", method = RequestMethod.POST)
@ResponseBody
- public Device assignDeviceToCustomer(@ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION)
+ public Device assignDeviceToCustomer(@Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable("customerId") String strCustomerId,
- @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
checkParameter(DEVICE_ID, strDeviceId);
@@ -255,7 +255,7 @@ public class DeviceController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/device/{deviceId}", method = RequestMethod.DELETE)
@ResponseBody
- public Device unassignDeviceFromCustomer(@ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
+ public Device unassignDeviceFromCustomer(@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
checkParameter(DEVICE_ID, strDeviceId);
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
@@ -276,7 +276,7 @@ public class DeviceController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/public/device/{deviceId}", method = RequestMethod.POST)
@ResponseBody
- public Device assignDeviceToPublicCustomer(@ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
+ public Device assignDeviceToPublicCustomer(@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
checkParameter(DEVICE_ID, strDeviceId);
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
@@ -289,7 +289,7 @@ public class DeviceController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device/{deviceId}/credentials", method = RequestMethod.GET)
@ResponseBody
- public DeviceCredentials getDeviceCredentialsByDeviceId(@ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
+ public DeviceCredentials getDeviceCredentialsByDeviceId(@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
checkParameter(DEVICE_ID, strDeviceId);
DeviceId deviceId = new DeviceId(toUUID(strDeviceId));
@@ -322,7 +322,7 @@ public class DeviceController extends BaseController {
@RequestMapping(value = "/device/credentials", method = RequestMethod.POST)
@ResponseBody
public DeviceCredentials updateDeviceCredentials(
- @ApiParam(value = "A JSON value representing the device credentials.")
+ @Parameter(description = "A JSON value representing the device credentials.")
@RequestBody DeviceCredentials deviceCredentials) throws ThingsboardException {
checkNotNull(deviceCredentials);
Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS);
@@ -336,17 +336,17 @@ public class DeviceController extends BaseController {
@RequestMapping(value = "/tenant/devices", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getTenantDevices(
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = DEVICE_TYPE_DESCRIPTION)
+ @Parameter(description = DEVICE_TYPE_DESCRIPTION)
@RequestParam(required = false) String type,
- @ApiParam(value = DEVICE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = DEVICE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = DEVICE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "deviceProfileName", "label", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@@ -364,21 +364,21 @@ public class DeviceController extends BaseController {
@RequestMapping(value = "/tenant/deviceInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getTenantDeviceInfos(
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = DEVICE_TYPE_DESCRIPTION)
+ @Parameter(description = DEVICE_TYPE_DESCRIPTION)
@RequestParam(required = false) String type,
- @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
@RequestParam(required = false) String deviceProfileId,
- @ApiParam(value = DEVICE_ACTIVE_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_ACTIVE_PARAM_DESCRIPTION)
@RequestParam(required = false) Boolean active,
- @ApiParam(value = DEVICE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = DEVICE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = DEVICE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "deviceProfileName", "label", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder
) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
@@ -401,7 +401,7 @@ public class DeviceController extends BaseController {
@RequestMapping(value = "/tenant/devices", params = {"deviceName"}, method = RequestMethod.GET)
@ResponseBody
public Device getTenantDevice(
- @ApiParam(value = DEVICE_NAME_DESCRIPTION)
+ @Parameter(description = DEVICE_NAME_DESCRIPTION)
@RequestParam String deviceName) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(deviceService.findDeviceByTenantIdAndName(tenantId, deviceName));
@@ -414,19 +414,19 @@ public class DeviceController extends BaseController {
@RequestMapping(value = "/customer/{customerId}/devices", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getCustomerDevices(
- @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(CUSTOMER_ID) String strCustomerId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = DEVICE_TYPE_DESCRIPTION)
+ @Parameter(description = DEVICE_TYPE_DESCRIPTION)
@RequestParam(required = false) String type,
- @ApiParam(value = DEVICE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = DEVICE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = DEVICE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "deviceProfileName", "label", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
TenantId tenantId = getCurrentUser().getTenantId();
@@ -447,23 +447,23 @@ public class DeviceController extends BaseController {
@RequestMapping(value = "/customer/{customerId}/deviceInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getCustomerDeviceInfos(
- @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("customerId") String strCustomerId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = DEVICE_TYPE_DESCRIPTION)
+ @Parameter(description = DEVICE_TYPE_DESCRIPTION)
@RequestParam(required = false) String type,
- @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
@RequestParam(required = false) String deviceProfileId,
- @ApiParam(value = DEVICE_ACTIVE_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_ACTIVE_PARAM_DESCRIPTION)
@RequestParam(required = false) Boolean active,
- @ApiParam(value = DEVICE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = DEVICE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = DEVICE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "deviceProfileName", "label", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
TenantId tenantId = getCurrentUser().getTenantId();
@@ -488,7 +488,7 @@ public class DeviceController extends BaseController {
@RequestMapping(value = "/devices", params = {"deviceIds"}, method = RequestMethod.GET)
@ResponseBody
public List getDevicesByIds(
- @ApiParam(value = "A list of devices ids, separated by comma ','")
+ @Parameter(description = "A list of devices ids, separated by comma ','")
@RequestParam("deviceIds") String[] strDeviceIds) throws ThingsboardException, ExecutionException, InterruptedException {
checkArrayParameter("deviceIds", strDeviceIds);
SecurityUser user = getCurrentUser();
@@ -515,7 +515,7 @@ public class DeviceController extends BaseController {
@RequestMapping(value = "/devices", method = RequestMethod.POST)
@ResponseBody
public List findByQuery(
- @ApiParam(value = "The device search query JSON")
+ @Parameter(description = "The device search query JSON")
@RequestBody DeviceSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException {
checkNotNull(query);
checkNotNull(query.getParameters());
@@ -535,7 +535,7 @@ public class DeviceController extends BaseController {
@ApiOperation(value = "Get Device Types (getDeviceTypes)",
notes = "Deprecated. See 'getDeviceProfileNames' API from Device Profile Controller instead." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device/types", method = RequestMethod.GET)
@ResponseBody
@@ -557,9 +557,9 @@ public class DeviceController extends BaseController {
@PreAuthorize("hasAuthority('CUSTOMER_USER')")
@RequestMapping(value = "/customer/device/{deviceName}/claim", method = RequestMethod.POST)
@ResponseBody
- public DeferredResult claimDevice(@ApiParam(value = "Unique name of the device which is going to be claimed")
+ public DeferredResult claimDevice(@Parameter(description = "Unique name of the device which is going to be claimed")
@PathVariable(DEVICE_NAME) String deviceName,
- @ApiParam(value = "Claiming request which can optionally contain secret key")
+ @Parameter(description = "Claiming request which can optionally contain secret key")
@RequestBody(required = false) ClaimRequest claimRequest) throws ThingsboardException {
checkParameter(DEVICE_NAME, deviceName);
final DeferredResult deferredResult = new DeferredResult<>();
@@ -606,7 +606,7 @@ public class DeviceController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/device/{deviceName}/claim", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
- public DeferredResult reClaimDevice(@ApiParam(value = "Unique name of the device which is going to be reclaimed")
+ public DeferredResult reClaimDevice(@Parameter(description = "Unique name of the device which is going to be reclaimed")
@PathVariable(DEVICE_NAME) String deviceName) throws ThingsboardException {
checkParameter(DEVICE_NAME, deviceName);
final DeferredResult deferredResult = new DeferredResult<>();
@@ -646,9 +646,9 @@ public class DeviceController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/{tenantId}/device/{deviceId}", method = RequestMethod.POST)
@ResponseBody
- public Device assignDeviceToTenant(@ApiParam(value = TENANT_ID_PARAM_DESCRIPTION)
+ public Device assignDeviceToTenant(@Parameter(description = TENANT_ID_PARAM_DESCRIPTION)
@PathVariable(TENANT_ID) String strTenantId,
- @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
checkParameter(TENANT_ID, strTenantId);
checkParameter(DEVICE_ID, strDeviceId);
@@ -669,13 +669,13 @@ public class DeviceController extends BaseController {
"Second, remote edge service will receive a copy of assignment device " +
EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION +
"Third, once device will be delivered to edge service, it's going to be available for usage on remote edge instance." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.POST)
@ResponseBody
- public Device assignDeviceToEdge(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION)
+ public Device assignDeviceToEdge(@Parameter(description = EDGE_ID_PARAM_DESCRIPTION)
@PathVariable(EDGE_ID) String strEdgeId,
- @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
checkParameter(DEVICE_ID, strDeviceId);
@@ -694,13 +694,13 @@ public class DeviceController extends BaseController {
"Second, remote edge service will receive an 'unassign' command to remove device " +
EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION +
"Third, once 'unassign' command will be delivered to edge service, it's going to remove device locally." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/device/{deviceId}", method = RequestMethod.DELETE)
@ResponseBody
- public Device unassignDeviceFromEdge(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION)
+ public Device unassignDeviceFromEdge(@Parameter(description = EDGE_ID_PARAM_DESCRIPTION)
@PathVariable(EDGE_ID) String strEdgeId,
- @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String strDeviceId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
checkParameter(DEVICE_ID, strDeviceId);
@@ -719,27 +719,27 @@ public class DeviceController extends BaseController {
@RequestMapping(value = "/edge/{edgeId}/devices", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getEdgeDevices(
- @ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(EDGE_ID) String strEdgeId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = DEVICE_TYPE_DESCRIPTION)
+ @Parameter(description = DEVICE_TYPE_DESCRIPTION)
@RequestParam(required = false) String type,
- @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
@RequestParam(required = false) String deviceProfileId,
- @ApiParam(value = DEVICE_ACTIVE_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_ACTIVE_PARAM_DESCRIPTION)
@RequestParam(required = false) Boolean active,
- @ApiParam(value = DEVICE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = DEVICE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = DEVICE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "deviceProfileName", "label", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = "Timestamp. Devices with creation time before it won't be queried")
+ @Parameter(description = "Timestamp. Devices with creation time before it won't be queried")
@RequestParam(required = false) Long startTime,
- @ApiParam(value = "Timestamp. Devices with creation time after it won't be queried")
+ @Parameter(description = "Timestamp. Devices with creation time after it won't be queried")
@RequestParam(required = false) Long endTime) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
TenantId tenantId = getCurrentUser().getTenantId();
@@ -766,11 +766,11 @@ public class DeviceController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/devices/count/{otaPackageType}/{deviceProfileId}", method = RequestMethod.GET)
@ResponseBody
- public Long countByDeviceProfileAndEmptyOtaPackage
- (@ApiParam(value = "OTA package type", allowableValues = "FIRMWARE, SOFTWARE")
- @PathVariable("otaPackageType") String otaPackageType,
- @ApiParam(value = "Device Profile Id. I.g. '784f394c-42b6-435a-983c-b7beff2784f9'")
- @PathVariable("deviceProfileId") String deviceProfileId) throws ThingsboardException {
+ public Long countByDeviceProfileAndEmptyOtaPackage(
+ @Parameter(description = "OTA package type", schema = @Schema(allowableValues = {"FIRMWARE", "SOFTWARE"}))
+ @PathVariable("otaPackageType") String otaPackageType,
+ @Parameter(description = "Device Profile Id. I.g. '784f394c-42b6-435a-983c-b7beff2784f9'")
+ @PathVariable("deviceProfileId") String deviceProfileId) throws ThingsboardException {
checkParameter("OtaPackageType", otaPackageType);
checkParameter("DeviceProfileId", deviceProfileId);
return deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(
diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
index bcbd2e4ef1..02cf15c81d 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
@@ -15,12 +15,15 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
@@ -40,6 +43,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.resource.ImageService;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.device.profile.TbDeviceProfileService;
@@ -54,7 +58,6 @@ import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFI
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_ID;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_INFO_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_TEXT_SEARCH_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.INLINE_IMAGES;
import static org.thingsboard.server.controller.ControllerConstants.INLINE_IMAGES_DESCRIPTION;
@@ -62,12 +65,10 @@ import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH;
-import static org.thingsboard.server.controller.ControllerConstants.TRANSPORT_TYPE_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK;
@RestController
@@ -86,14 +87,14 @@ public class DeviceProfileController extends BaseController {
@ApiOperation(value = "Get Device Profile (getDeviceProfileById)",
notes = "Fetch the Device Profile object based on the provided Device Profile Id. " +
"The server checks that the device profile is owned by the same tenant. " + TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/deviceProfile/{deviceProfileId}", method = RequestMethod.GET)
@ResponseBody
public DeviceProfile getDeviceProfileById(
- @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId,
- @ApiParam(value = INLINE_IMAGES_DESCRIPTION)
+ @Parameter(description = INLINE_IMAGES_DESCRIPTION)
@RequestParam(value = INLINE_IMAGES, required = false) boolean inlineImages) throws ThingsboardException {
checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId);
DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
@@ -107,12 +108,12 @@ public class DeviceProfileController extends BaseController {
@ApiOperation(value = "Get Device Profile Info (getDeviceProfileInfoById)",
notes = "Fetch the Device Profile Info object based on the provided Device Profile Id. "
+ DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/deviceProfileInfo/{deviceProfileId}", method = RequestMethod.GET)
@ResponseBody
public DeviceProfileInfo getDeviceProfileInfoById(
- @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException {
checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId);
DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
@@ -122,7 +123,7 @@ public class DeviceProfileController extends BaseController {
@ApiOperation(value = "Get Default Device Profile (getDefaultDeviceProfileInfo)",
notes = "Fetch the Default Device Profile Info object. " +
DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/deviceProfileInfo/default", method = RequestMethod.GET)
@ResponseBody
@@ -136,12 +137,12 @@ public class DeviceProfileController extends BaseController {
"The call is used for auto-complete in the UI forms. " +
"The implementation limits the number of devices that participate in search to 100 as a trade of between accurate results and time-consuming queries. " +
TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/deviceProfile/devices/keys/timeseries", method = RequestMethod.GET)
@ResponseBody
public List getTimeseriesKeys(
- @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
@RequestParam(name = DEVICE_PROFILE_ID, required = false) String deviceProfileIdStr) throws ThingsboardException {
DeviceProfileId deviceProfileId;
if (StringUtils.isNotEmpty(deviceProfileIdStr)) {
@@ -160,12 +161,12 @@ public class DeviceProfileController extends BaseController {
"The call is used for auto-complete in the UI forms. " +
"The implementation limits the number of devices that participate in search to 100 as a trade of between accurate results and time-consuming queries. " +
TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/deviceProfile/devices/keys/attributes", method = RequestMethod.GET)
@ResponseBody
public List getAttributesKeys(
- @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
@RequestParam(name = DEVICE_PROFILE_ID, required = false) String deviceProfileIdStr) throws ThingsboardException {
DeviceProfileId deviceProfileId;
if (StringUtils.isNotEmpty(deviceProfileIdStr)) {
@@ -186,13 +187,12 @@ public class DeviceProfileController extends BaseController {
"Device profile name is unique in the scope of tenant. Only one 'default' device profile may exist in scope of tenant." + DEVICE_PROFILE_DATA +
"Remove 'id', 'tenantId' from the request body example (below) to create new Device Profile entity. " +
TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json",
- consumes = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/deviceProfile", method = RequestMethod.POST)
@ResponseBody
public DeviceProfile saveDeviceProfile(
- @ApiParam(value = "A JSON value representing the device profile.")
+ @Parameter(description = "A JSON value representing the device profile.")
@RequestBody DeviceProfile deviceProfile) throws Exception {
deviceProfile.setTenantId(getTenantId());
checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE);
@@ -201,13 +201,12 @@ public class DeviceProfileController extends BaseController {
@ApiOperation(value = "Delete device profile (deleteDeviceProfile)",
notes = "Deletes the device profile. Referencing non-existing device profile Id will cause an error. " +
- "Can't delete the device profile if it is referenced by existing devices." + TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ "Can't delete the device profile if it is referenced by existing devices." + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/deviceProfile/{deviceProfileId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
public void deleteDeviceProfile(
- @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException {
checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId);
DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
@@ -217,12 +216,12 @@ public class DeviceProfileController extends BaseController {
@ApiOperation(value = "Make Device Profile Default (setDefaultDeviceProfile)",
notes = "Marks device profile as default within a tenant scope." + TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/deviceProfile/{deviceProfileId}/default", method = RequestMethod.POST)
@ResponseBody
public DeviceProfile setDefaultDeviceProfile(
- @ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_PROFILE_ID) String strDeviceProfileId) throws ThingsboardException {
checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId);
DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
@@ -234,20 +233,20 @@ public class DeviceProfileController extends BaseController {
@ApiOperation(value = "Get Device Profiles (getDeviceProfiles)",
notes = "Returns a page of devices profile objects owned by tenant. " +
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/deviceProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getDeviceProfiles(
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = DEVICE_PROFILE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = DEVICE_PROFILE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = DEVICE_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "type", "transportType", "description", "isDefault"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return checkNotNull(deviceProfileService.findDeviceProfiles(getTenantId(), pageLink));
@@ -256,22 +255,22 @@ public class DeviceProfileController extends BaseController {
@ApiOperation(value = "Get Device Profiles for transport type (getDeviceProfileInfos)",
notes = "Returns a page of devices profile info objects owned by tenant. " +
PAGE_DATA_PARAMETERS + DEVICE_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/deviceProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getDeviceProfileInfos(
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = DEVICE_PROFILE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = DEVICE_PROFILE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = DEVICE_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "type", "transportType", "description", "isDefault"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = "Type of the transport", allowableValues = TRANSPORT_TYPE_ALLOWABLE_VALUES)
+ @Parameter(description = "Type of the transport", schema = @Schema(allowableValues = {"DEFAULT", "MQTT", "COAP", "LWM2M", "SNMP"}))
@RequestParam(required = false) String transportType) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return checkNotNull(deviceProfileService.findDeviceProfileInfos(getTenantId(), pageLink, transportType));
@@ -284,7 +283,7 @@ public class DeviceProfileController extends BaseController {
@RequestMapping(value = "/deviceProfile/names", method = RequestMethod.GET)
@ResponseBody
public List getDeviceProfileNames(
- @ApiParam(value = "Flag indicating whether to retrieve exclusively the names of device profiles that are referenced by tenant's devices.")
+ @Parameter(description = "Flag indicating whether to retrieve exclusively the names of device profiles that are referenced by tenant's devices.")
@RequestParam(value = "activeOnly", required = false, defaultValue = "false") boolean activeOnly) throws ThingsboardException {
SecurityUser user = getCurrentUser();
TenantId tenantId = user.getTenantId();
diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
index e07a9d5573..9781d48828 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java
@@ -16,8 +16,11 @@
package org.thingsboard.server.controller;
import com.google.common.util.concurrent.ListenableFuture;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
@@ -54,6 +57,7 @@ import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportReques
import org.thingsboard.server.common.data.sync.ie.importing.csv.BulkImportResult;
import org.thingsboard.server.common.msg.edge.FromEdgeSyncResponse;
import org.thingsboard.server.common.msg.edge.ToEdgeSyncRequest;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
@@ -67,7 +71,6 @@ import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
-import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -78,14 +81,12 @@ import java.util.stream.Collectors;
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.EDGE_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.EDGE_INFO_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.EDGE_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.EDGE_TEXT_SEARCH_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.EDGE_TYPE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.RULE_CHAIN_ID_PARAM_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
@@ -120,11 +121,11 @@ public class EdgeController extends BaseController {
@ApiOperation(value = "Get Edge (getEdgeById)",
notes = "Get the Edge object based on the provided Edge Id. " + EDGE_SECURITY_CHECK + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.GET)
@ResponseBody
- public Edge getEdgeById(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
+ public Edge getEdgeById(@Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
@@ -133,11 +134,11 @@ public class EdgeController extends BaseController {
@ApiOperation(value = "Get Edge Info (getEdgeInfoById)",
notes = "Get the Edge Info object based on the provided Edge Id. " + EDGE_SECURITY_CHECK + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/edge/info/{edgeId}", method = RequestMethod.GET)
@ResponseBody
- public EdgeInfo getEdgeInfoById(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
+ public EdgeInfo getEdgeInfoById(@Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
@@ -152,11 +153,11 @@ public class EdgeController extends BaseController {
"\n\nEdge name is unique in the scope of tenant. Use unique identifiers like MAC or IMEI for the edge names and non-unique 'label' field for user-friendly visualization purposes." +
"Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Edge entity. " +
TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge", method = RequestMethod.POST)
@ResponseBody
- public Edge saveEdge(@ApiParam(value = "A JSON value representing the edge.", required = true)
+ public Edge saveEdge(@Parameter(description = "A JSON value representing the edge.", required = true)
@RequestBody Edge edge) throws Exception {
TenantId tenantId = getTenantId();
edge.setTenantId(tenantId);
@@ -182,7 +183,7 @@ public class EdgeController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
- public void deleteEdge(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
+ public void deleteEdge(@Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
@@ -192,19 +193,19 @@ public class EdgeController extends BaseController {
@ApiOperation(value = "Get Tenant Edges (getEdges)",
notes = "Returns a page of edges owned by tenant. " +
- PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edges", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
- public PageData getEdges(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ public PageData getEdges(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = EDGE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = EDGE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EDGE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "type", "label", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TenantId tenantId = getCurrentUser().getTenantId();
@@ -213,13 +214,13 @@ public class EdgeController extends BaseController {
@ApiOperation(value = "Assign edge to customer (assignEdgeToCustomer)",
notes = "Creates assignment of the edge to customer. Customer will be able to query edge afterwards." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/{customerId}/edge/{edgeId}", method = RequestMethod.POST)
@ResponseBody
- public Edge assignEdgeToCustomer(@ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
+ public Edge assignEdgeToCustomer(@Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("customerId") String strCustomerId,
- @ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
checkParameter(EDGE_ID, strEdgeId);
@@ -232,11 +233,11 @@ public class EdgeController extends BaseController {
@ApiOperation(value = "Unassign edge from customer (unassignEdgeFromCustomer)",
notes = "Clears assignment of the edge to customer. Customer will not be able to query edge afterwards." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/edge/{edgeId}", method = RequestMethod.DELETE)
@ResponseBody
- public Edge unassignEdgeFromCustomer(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
+ public Edge unassignEdgeFromCustomer(@Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
@@ -253,11 +254,11 @@ public class EdgeController extends BaseController {
notes = "Edge will be available for non-authorized (not logged-in) users. " +
"This is useful to create dashboards that you plan to share/embed on a publicly available website. " +
"However, users that are logged-in and belong to different tenant will not be able to access the edge." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/customer/public/edge/{edgeId}", method = RequestMethod.POST)
@ResponseBody
- public Edge assignEdgeToPublicCustomer(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
+ public Edge assignEdgeToPublicCustomer(@Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
@@ -267,22 +268,22 @@ public class EdgeController extends BaseController {
@ApiOperation(value = "Get Tenant Edges (getTenantEdges)",
notes = "Returns a page of edges owned by tenant. " +
- PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/edges", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getTenantEdges(
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = EDGE_TYPE_DESCRIPTION)
+ @Parameter(description = EDGE_TYPE_DESCRIPTION)
@RequestParam(required = false) String type,
- @ApiParam(value = EDGE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = EDGE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EDGE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "type", "label", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@@ -296,22 +297,22 @@ public class EdgeController extends BaseController {
@ApiOperation(value = "Get Tenant Edge Infos (getTenantEdgeInfos)",
notes = "Returns a page of edges info objects owned by tenant. " +
PAGE_DATA_PARAMETERS + EDGE_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/edgeInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getTenantEdgeInfos(
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = EDGE_TYPE_DESCRIPTION)
+ @Parameter(description = EDGE_TYPE_DESCRIPTION)
@RequestParam(required = false) String type,
- @ApiParam(value = EDGE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = EDGE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EDGE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "type", "label", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@@ -325,11 +326,11 @@ public class EdgeController extends BaseController {
@ApiOperation(value = "Get Tenant Edge (getTenantEdge)",
notes = "Requested edge must be owned by tenant or customer that the user belongs to. " +
"Edge name is an unique property of edge. So it can be used to identify the edge." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/edges", params = {"edgeName"}, method = RequestMethod.GET)
@ResponseBody
- public Edge getTenantEdge(@ApiParam(value = "Unique name of the edge", required = true)
+ public Edge getTenantEdge(@Parameter(description = "Unique name of the edge", required = true)
@RequestParam String edgeName) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(edgeService.findEdgeByTenantIdAndName(tenantId, edgeName));
@@ -338,13 +339,13 @@ public class EdgeController extends BaseController {
@ApiOperation(value = "Set root rule chain for provided edge (setEdgeRootRuleChain)",
notes = "Change root rule chain of the edge to the new provided rule chain. \n" +
"This operation will send a notification to update root rule chain on remote edge service." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/{ruleChainId}/root", method = RequestMethod.POST)
@ResponseBody
- public Edge setEdgeRootRuleChain(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
+ public Edge setEdgeRootRuleChain(@Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(EDGE_ID) String strEdgeId,
- @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("ruleChainId") String strRuleChainId) throws Exception {
checkParameter(EDGE_ID, strEdgeId);
checkParameter("ruleChainId", strRuleChainId);
@@ -358,24 +359,24 @@ public class EdgeController extends BaseController {
@ApiOperation(value = "Get Customer Edges (getCustomerEdges)",
notes = "Returns a page of edges objects assigned to customer. " +
- PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/edges", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getCustomerEdges(
- @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION)
+ @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable("customerId") String strCustomerId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = EDGE_TYPE_DESCRIPTION)
+ @Parameter(description = EDGE_TYPE_DESCRIPTION)
@RequestParam(required = false) String type,
- @ApiParam(value = EDGE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = EDGE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EDGE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "type", "label", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
SecurityUser user = getCurrentUser();
@@ -394,24 +395,24 @@ public class EdgeController extends BaseController {
@ApiOperation(value = "Get Customer Edge Infos (getCustomerEdgeInfos)",
notes = "Returns a page of edges info objects assigned to customer. " +
- PAGE_DATA_PARAMETERS + EDGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS + EDGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/customer/{customerId}/edgeInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getCustomerEdgeInfos(
- @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION)
+ @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable("customerId") String strCustomerId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = EDGE_TYPE_DESCRIPTION)
+ @Parameter(description = EDGE_TYPE_DESCRIPTION)
@RequestParam(required = false) String type,
- @ApiParam(value = EDGE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = EDGE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EDGE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "type", "label", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
SecurityUser user = getCurrentUser();
@@ -430,12 +431,12 @@ public class EdgeController extends BaseController {
@ApiOperation(value = "Get Edges By Ids (getEdgesByIds)",
notes = "Requested edges must be owned by tenant or assigned to customer which user is performing the request." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/edges", params = {"edgeIds"}, method = RequestMethod.GET)
@ResponseBody
public List getEdgesByIds(
- @ApiParam(value = "A list of edges ids, separated by comma ','", required = true)
+ @Parameter(description = "A list of edges ids, separated by comma ','", required = true)
@RequestParam("edgeIds") String[] strEdgeIds) throws ThingsboardException, ExecutionException, InterruptedException {
checkArrayParameter("edgeIds", strEdgeIds);
SecurityUser user = getCurrentUser();
@@ -459,7 +460,7 @@ public class EdgeController extends BaseController {
notes = "Returns all edges that are related to the specific entity. " +
"The entity id, relation type, edge types, depth of the search, and other query parameters defined using complex 'EdgeSearchQuery' object. " +
"See 'Model' tab of the Parameters for more info." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/edges", method = RequestMethod.POST)
@ResponseBody
@@ -485,7 +486,7 @@ public class EdgeController extends BaseController {
@ApiOperation(value = "Get Edge Types (getEdgeTypes)",
notes = "Returns a set of unique edge types based on edges that are either owned by the tenant or assigned to the customer which user is performing the request."
+ TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/edge/types", method = RequestMethod.GET)
@ResponseBody
@@ -501,7 +502,7 @@ public class EdgeController extends BaseController {
"All entities that are assigned to particular edge are going to be send to remote edge service." + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/sync/{edgeId}", method = RequestMethod.POST)
- public DeferredResult syncEdge(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
+ public DeferredResult syncEdge(@Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("edgeId") String strEdgeId) throws ThingsboardException {
checkParameter("edgeId", strEdgeId);
final DeferredResult response = new DeferredResult<>();
@@ -531,7 +532,7 @@ public class EdgeController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/missingToRelatedRuleChains/{edgeId}", method = RequestMethod.GET)
@ResponseBody
- public String findMissingToRelatedRuleChains(@ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
+ public String findMissingToRelatedRuleChains(@Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("edgeId") String strEdgeId) throws ThingsboardException {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
edgeId = checkNotNull(edgeId);
@@ -542,7 +543,7 @@ public class EdgeController extends BaseController {
@ApiOperation(value = "Import the bulk of edges (processEdgesBulkImport)",
notes = "There's an ability to import the bulk of edges using the only .csv file." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@PostMapping("/edge/bulk_import")
public BulkImportResult processEdgesBulkImport(@RequestBody BulkImportRequest request) throws Exception {
@@ -557,14 +558,14 @@ public class EdgeController extends BaseController {
@ApiOperation(value = "Get Edge Install Instructions (getEdgeInstallInstructions)",
notes = "Get an install instructions for provided edge id." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/instructions/install/{edgeId}/{method}", method = RequestMethod.GET)
@ResponseBody
public EdgeInstructions getEdgeInstallInstructions(
- @ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("edgeId") String strEdgeId,
- @ApiParam(value = "Installation method ('docker', 'ubuntu' or 'centos')", allowableValues = "docker, ubuntu, centos")
+ @Parameter(description = "Installation method ('docker', 'ubuntu' or 'centos')", schema = @Schema(allowableValues = {"docker", "ubuntu", "centos"}))
@PathVariable("method") String installationMethod,
HttpServletRequest request) throws ThingsboardException {
if (isEdgesEnabled() && edgeInstallServiceOpt.isPresent()) {
@@ -579,14 +580,14 @@ public class EdgeController extends BaseController {
@ApiOperation(value = "Get Edge Upgrade Instructions (getEdgeUpgradeInstructions)",
notes = "Get an upgrade instructions for provided edge version." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/instructions/upgrade/{edgeVersion}/{method}", method = RequestMethod.GET)
@ResponseBody
public EdgeInstructions getEdgeUpgradeInstructions(
- @ApiParam(value = "Edge version", required = true)
+ @Parameter(description = "Edge version", required = true)
@PathVariable("edgeVersion") String edgeVersion,
- @ApiParam(value = "Upgrade method ('docker', 'ubuntu' or 'centos')", allowableValues = "docker, ubuntu, centos")
+ @Parameter(description = "Upgrade method ('docker', 'ubuntu' or 'centos')", schema = @Schema(allowableValues = {"docker", "ubuntu", "centos"}))
@PathVariable("method") String method) throws Exception {
if (isEdgesEnabled() && edgeUpgradeServiceOpt.isPresent()) {
return checkNotNull(edgeUpgradeServiceOpt.get().getUpgradeInstructions(edgeVersion, method));
@@ -601,7 +602,7 @@ public class EdgeController extends BaseController {
@RequestMapping(value = "/edge/{edgeId}/upgrade/available", method = RequestMethod.GET)
@ResponseBody
public boolean isEdgeUpgradeAvailable(
- @ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable("edgeId") String strEdgeId) throws Exception {
if (isEdgesEnabled() && edgeUpgradeServiceOpt.isPresent()) {
EdgeId edgeId = new EdgeId(toUUID(strEdgeId));
diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java
index 39e27bf260..8828a714fc 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EdgeEventController.java
@@ -15,8 +15,10 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
@@ -33,16 +35,15 @@ import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageLink;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.edge.EdgeEventService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
import static org.thingsboard.server.controller.ControllerConstants.EDGE_ID_PARAM_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.EDGE_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
@@ -59,26 +60,26 @@ public class EdgeEventController extends BaseController {
@ApiOperation(value = "Get Edge Events (getEdgeEvents)",
notes = "Returns a page of edge events for the requested edge. " +
- PAGE_DATA_PARAMETERS, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/events", method = RequestMethod.GET)
@ResponseBody
public PageData getEdgeEvents(
- @ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(EDGE_ID) String strEdgeId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = "The case insensitive 'substring' filter based on the edge event type name.")
+ @Parameter(description = "The case insensitive 'substring' filter based on the edge event type name.")
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EDGE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "type", "label", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = "Timestamp. Edge events with creation time before it won't be queried")
+ @Parameter(description = "Timestamp. Edge events with creation time before it won't be queried")
@RequestParam(required = false) Long startTime,
- @ApiParam(value = "Timestamp. Edge events with creation time after it won't be queried")
+ @Parameter(description = "Timestamp. Edge events with creation time after it won't be queried")
@RequestParam(required = false) Long endTime) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
TenantId tenantId = getCurrentUser().getTenantId();
diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java
index 1e8b14ea77..d3ca3e5ea0 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java
@@ -18,8 +18,8 @@ package org.thingsboard.server.controller;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.sync.vc.VersionLoadResult;
import org.thingsboard.server.common.data.sync.vc.VersionedEntityInfo;
import org.thingsboard.server.common.data.sync.vc.request.create.VersionCreateRequest;
import org.thingsboard.server.common.data.sync.vc.request.load.VersionLoadRequest;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
@@ -68,7 +69,6 @@ import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
@@ -192,7 +192,7 @@ public class EntitiesVersionControlController extends BaseController {
MARKDOWN_CODE_BLOCK_END +
TENANT_AUTHORITY_PARAGRAPH)
@GetMapping(value = "/version/{requestId}/status")
- public VersionCreationResult getVersionCreateRequestStatus(@ApiParam(value = VC_REQUEST_ID_PARAM_DESCRIPTION, required = true)
+ public VersionCreationResult getVersionCreateRequestStatus(@Parameter(description = VC_REQUEST_ID_PARAM_DESCRIPTION, required = true)
@PathVariable UUID requestId) throws Exception {
accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.WRITE);
return versionControlService.getVersionCreateStatus(getCurrentUser(), requestId);
@@ -235,21 +235,21 @@ public class EntitiesVersionControlController extends BaseController {
MARKDOWN_CODE_BLOCK_END +
TENANT_AUTHORITY_PARAGRAPH)
@GetMapping(value = "/version/{entityType}/{externalEntityUuid}", params = {"branch", "pageSize", "page"})
- public DeferredResult> listEntityVersions(@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
+ public DeferredResult> listEntityVersions(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable EntityType entityType,
- @ApiParam(value = "A string value representing external entity id. This is `externalId` property of an entity, or otherwise if not set - simply id of this entity.")
+ @Parameter(description = "A string value representing external entity id. This is `externalId` property of an entity, or otherwise if not set - simply id of this entity.")
@PathVariable UUID externalEntityUuid,
- @ApiParam(value = BRANCH_PARAM_DESCRIPTION)
+ @Parameter(description = BRANCH_PARAM_DESCRIPTION)
@RequestParam String branch,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = ENTITY_VERSION_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ENTITY_VERSION_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = "timestamp")
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = "timestamp"))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws Exception {
accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ);
EntityId externalEntityId = EntityIdFactory.getByTypeAndUuid(entityType, externalEntityUuid);
@@ -264,19 +264,19 @@ public class EntitiesVersionControlController extends BaseController {
"The response structure is the same as for `listEntityVersions` API method." +
TENANT_AUTHORITY_PARAGRAPH)
@GetMapping(value = "/version/{entityType}", params = {"branch", "pageSize", "page"})
- public DeferredResult> listEntityTypeVersions(@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
+ public DeferredResult> listEntityTypeVersions(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable EntityType entityType,
- @ApiParam(value = BRANCH_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = BRANCH_PARAM_DESCRIPTION, required = true)
@RequestParam String branch,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = ENTITY_VERSION_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ENTITY_VERSION_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = "timestamp")
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = "timestamp"))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws Exception {
accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@@ -289,17 +289,17 @@ public class EntitiesVersionControlController extends BaseController {
"The response format is the same as for `listEntityVersions` API method." +
TENANT_AUTHORITY_PARAGRAPH)
@GetMapping(value = "/version", params = {"branch", "pageSize", "page"})
- public DeferredResult> listVersions(@ApiParam(value = BRANCH_PARAM_DESCRIPTION, required = true)
+ public DeferredResult> listVersions(@Parameter(description = BRANCH_PARAM_DESCRIPTION, required = true)
@RequestParam String branch,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = ENTITY_VERSION_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ENTITY_VERSION_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = "timestamp")
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = "timestamp"))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws Exception {
accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@@ -313,9 +313,9 @@ public class EntitiesVersionControlController extends BaseController {
"Entities order will be the same as in the repository." +
TENANT_AUTHORITY_PARAGRAPH)
@GetMapping(value = "/entity/{entityType}/{versionId}")
- public DeferredResult> listEntitiesAtVersion(@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
+ public DeferredResult> listEntitiesAtVersion(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable EntityType entityType,
- @ApiParam(value = VERSION_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = VERSION_ID_PARAM_DESCRIPTION, required = true)
@PathVariable String versionId) throws Exception {
accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ);
return wrapFuture(versionControlService.listEntitiesAtVersion(getTenantId(), versionId, entityType));
@@ -327,7 +327,7 @@ public class EntitiesVersionControlController extends BaseController {
"Returned entities order will be the same as in the repository." +
TENANT_AUTHORITY_PARAGRAPH)
@GetMapping(value = "/entity/{versionId}")
- public DeferredResult> listAllEntitiesAtVersion(@ApiParam(value = VERSION_ID_PARAM_DESCRIPTION, required = true)
+ public DeferredResult> listAllEntitiesAtVersion(@Parameter(description = VERSION_ID_PARAM_DESCRIPTION, required = true)
@PathVariable String versionId) throws Exception {
accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ);
return wrapFuture(versionControlService.listAllEntitiesAtVersion(getTenantId(), versionId));
@@ -340,11 +340,11 @@ public class EntitiesVersionControlController extends BaseController {
"`hasCredentials` (whether stored device data has credentials)." +
TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/info/{versionId}/{entityType}/{externalEntityUuid}")
- public DeferredResult getEntityDataInfo(@ApiParam(value = VERSION_ID_PARAM_DESCRIPTION, required = true)
+ public DeferredResult getEntityDataInfo(@Parameter(description = VERSION_ID_PARAM_DESCRIPTION, required = true)
@PathVariable String versionId,
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable EntityType entityType,
- @ApiParam(value = "A string value representing external entity id", required = true)
+ @Parameter(description = "A string value representing external entity id", required = true)
@PathVariable UUID externalEntityUuid) throws Exception {
accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ);
EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, externalEntityUuid);
@@ -356,11 +356,11 @@ public class EntitiesVersionControlController extends BaseController {
"Entity data structure is the same as stored in a repository. " +
TENANT_AUTHORITY_PARAGRAPH)
@GetMapping(value = "/diff/{entityType}/{internalEntityUuid}", params = {"versionId"})
- public DeferredResult compareEntityDataToVersion(@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
+ public DeferredResult compareEntityDataToVersion(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable EntityType entityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@PathVariable UUID internalEntityUuid,
- @ApiParam(value = VERSION_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = VERSION_ID_PARAM_DESCRIPTION, required = true)
@RequestParam String versionId) throws Exception {
accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.READ);
EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, internalEntityUuid);
@@ -469,7 +469,7 @@ public class EntitiesVersionControlController extends BaseController {
TENANT_AUTHORITY_PARAGRAPH
)
@GetMapping(value = "/entity/{requestId}/status")
- public VersionLoadResult getVersionLoadRequestStatus(@ApiParam(value = VC_REQUEST_ID_PARAM_DESCRIPTION, required = true)
+ public VersionLoadResult getVersionLoadRequestStatus(@Parameter(description = VC_REQUEST_ID_PARAM_DESCRIPTION, required = true)
@PathVariable UUID requestId) throws Exception {
accessControlService.checkPermission(getCurrentUser(), Resource.VERSION_CONTROL, Operation.WRITE);
return versionControlService.getVersionLoadStatus(getCurrentUser(), requestId);
diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java
index 4d11a388ff..e6e57e8b8c 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java
@@ -15,8 +15,8 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -39,12 +39,12 @@ import org.thingsboard.server.common.data.query.EntityCountQuery;
import org.thingsboard.server.common.data.query.EntityData;
import org.thingsboard.server.common.data.query.EntityDataPageLink;
import org.thingsboard.server.common.data.query.EntityDataQuery;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.query.EntityQueryService;
import org.thingsboard.server.service.security.permission.Operation;
import static org.thingsboard.server.controller.ControllerConstants.ALARM_DATA_QUERY_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.ATTRIBUTES_SCOPE_ALLOWED_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.ATTRIBUTES_SCOPE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_COUNT_QUERY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_DATA_QUERY_DESCRIPTION;
@@ -64,7 +64,7 @@ public class EntityQueryController extends BaseController {
@RequestMapping(value = "/entitiesQuery/count", method = RequestMethod.POST)
@ResponseBody
public long countEntitiesByQuery(
- @ApiParam(value = "A JSON value representing the entity count query. See API call notes above for more details.")
+ @Parameter(description = "A JSON value representing the entity count query. See API call notes above for more details.")
@RequestBody EntityCountQuery query) throws ThingsboardException {
checkNotNull(query);
return this.entityQueryService.countEntitiesByQuery(getCurrentUser(), query);
@@ -75,7 +75,7 @@ public class EntityQueryController extends BaseController {
@RequestMapping(value = "/entitiesQuery/find", method = RequestMethod.POST)
@ResponseBody
public PageData findEntityDataByQuery(
- @ApiParam(value = "A JSON value representing the entity data query. See API call notes above for more details.")
+ @Parameter(description = "A JSON value representing the entity data query. See API call notes above for more details.")
@RequestBody EntityDataQuery query) throws ThingsboardException {
checkNotNull(query);
return this.entityQueryService.findEntityDataByQuery(getCurrentUser(), query);
@@ -86,7 +86,7 @@ public class EntityQueryController extends BaseController {
@RequestMapping(value = "/alarmsQuery/find", method = RequestMethod.POST)
@ResponseBody
public PageData findAlarmDataByQuery(
- @ApiParam(value = "A JSON value representing the alarm data query. See API call notes above for more details.")
+ @Parameter(description = "A JSON value representing the alarm data query. See API call notes above for more details.")
@RequestBody AlarmDataQuery query) throws ThingsboardException {
checkNotNull(query);
checkNotNull(query.getPageLink());
@@ -101,7 +101,7 @@ public class EntityQueryController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarmsQuery/count", method = RequestMethod.POST)
@ResponseBody
- public long countAlarmsByQuery(@ApiParam(value = "A JSON value representing the alarm count query.")
+ public long countAlarmsByQuery(@Parameter(description = "A JSON value representing the alarm count query.")
@RequestBody AlarmCountQuery query) throws ThingsboardException {
checkNotNull(query);
UserId assigneeId = query.getAssigneeId();
@@ -117,13 +117,13 @@ public class EntityQueryController extends BaseController {
@RequestMapping(value = "/entitiesQuery/find/keys", method = RequestMethod.POST)
@ResponseBody
public DeferredResult findEntityTimeseriesAndAttributesKeysByQuery(
- @ApiParam(value = "A JSON value representing the entity data query. See API call notes above for more details.")
+ @Parameter(description = "A JSON value representing the entity data query. See API call notes above for more details.")
@RequestBody EntityDataQuery query,
- @ApiParam(value = "Include all unique time-series keys to the result.")
+ @Parameter(description = "Include all unique time-series keys to the result.")
@RequestParam("timeseries") boolean isTimeseries,
- @ApiParam(value = "Include all unique attribute keys to the result.")
+ @Parameter(description = "Include all unique attribute keys to the result.")
@RequestParam("attributes") boolean isAttributes,
- @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES)
+ @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}))
@RequestParam(value = "scope", required = false) String scope) throws ThingsboardException {
TenantId tenantId = getTenantId();
checkNotNull(query);
diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
index bbd952ca7c..3932e483d5 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EntityRelationController.java
@@ -15,8 +15,9 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@@ -35,6 +36,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.EntityRelationInfo;
import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.entity.relation.TbEntityRelationService;
import org.thingsboard.server.service.security.model.SecurityUser;
@@ -79,7 +81,7 @@ public class EntityRelationController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relation", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
- public void saveRelation(@ApiParam(value = "A JSON value representing the relation.", required = true)
+ public void saveRelation(@Parameter(description = "A JSON value representing the relation.", required = true)
@RequestBody EntityRelation relation) throws ThingsboardException {
checkNotNull(relation);
checkCanCreateRelation(relation.getFrom());
@@ -96,12 +98,12 @@ public class EntityRelationController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relation", method = RequestMethod.DELETE, params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE})
@ResponseStatus(value = HttpStatus.OK)
- public void deleteRelation(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
- @ApiParam(value = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
- @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType) throws ThingsboardException {
+ public void deleteRelation(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
+ @Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
+ @Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup,
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType) throws ThingsboardException {
checkParameter(FROM_ID, strFromId);
checkParameter(FROM_TYPE, strFromType);
checkParameter(RELATION_TYPE, strRelationType);
@@ -123,8 +125,8 @@ public class EntityRelationController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations", method = RequestMethod.DELETE, params = {"entityId", "entityType"})
@ResponseStatus(value = HttpStatus.OK)
- public void deleteRelations(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam("entityId") String strId,
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam("entityType") String strType) throws ThingsboardException {
+ public void deleteRelations(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam("entityId") String strId,
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam("entityType") String strType) throws ThingsboardException {
checkParameter("entityId", strId);
checkParameter("entityType", strType);
EntityId entityId = EntityIdFactory.getByTypeAndId(strType, strId);
@@ -134,16 +136,16 @@ public class EntityRelationController extends BaseController {
@ApiOperation(value = "Get Relation (getRelation)",
notes = "Returns relation object between two specified entities if present. Otherwise throws exception. " + SECURITY_CHECKS_ENTITIES_DESCRIPTION,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relation", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE, RELATION_TYPE, TO_ID, TO_TYPE})
@ResponseBody
- public EntityRelation getRelation(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
- @ApiParam(value = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
- @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType) throws ThingsboardException {
+ public EntityRelation getRelation(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
+ @Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
+ @Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION) @RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup,
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType) throws ThingsboardException {
checkParameter(FROM_ID, strFromId);
checkParameter(FROM_TYPE, strFromType);
checkParameter(RELATION_TYPE, strRelationType);
@@ -160,13 +162,13 @@ public class EntityRelationController extends BaseController {
@ApiOperation(value = "Get List of Relations (findByFrom)",
notes = "Returns list of relation objects for the specified entity by the 'from' direction. " +
SECURITY_CHECKS_ENTITY_DESCRIPTION,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE})
@ResponseBody
- public List findByFrom(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
- @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
+ public List findByFrom(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
+ @Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
@RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
checkParameter(FROM_ID, strFromId);
checkParameter(FROM_TYPE, strFromType);
@@ -179,13 +181,13 @@ public class EntityRelationController extends BaseController {
@ApiOperation(value = "Get List of Relation Infos (findInfoByFrom)",
notes = "Returns list of relation info objects for the specified entity by the 'from' direction. " +
SECURITY_CHECKS_ENTITY_DESCRIPTION + " " + RELATION_INFO_DESCRIPTION,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE})
@ResponseBody
- public List findInfoByFrom(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
- @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
+ public List findInfoByFrom(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
+ @Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
@RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException, ExecutionException, InterruptedException {
checkParameter(FROM_ID, strFromId);
checkParameter(FROM_TYPE, strFromType);
@@ -198,14 +200,14 @@ public class EntityRelationController extends BaseController {
@ApiOperation(value = "Get List of Relations (findByFrom)",
notes = "Returns list of relation objects for the specified entity by the 'from' direction and relation type. " +
SECURITY_CHECKS_ENTITY_DESCRIPTION,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations", method = RequestMethod.GET, params = {FROM_ID, FROM_TYPE, RELATION_TYPE})
@ResponseBody
- public List findByFrom(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
- @ApiParam(value = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
- @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
+ public List findByFrom(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_ID) String strFromId,
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(FROM_TYPE) String strFromType,
+ @Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
+ @Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
@RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
checkParameter(FROM_ID, strFromId);
checkParameter(FROM_TYPE, strFromType);
@@ -219,13 +221,13 @@ public class EntityRelationController extends BaseController {
@ApiOperation(value = "Get List of Relations (findByTo)",
notes = "Returns list of relation objects for the specified entity by the 'to' direction. " +
SECURITY_CHECKS_ENTITY_DESCRIPTION,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations", method = RequestMethod.GET, params = {TO_ID, TO_TYPE})
@ResponseBody
- public List findByTo(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType,
- @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
+ public List findByTo(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType,
+ @Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
@RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
checkParameter(TO_ID, strToId);
checkParameter(TO_TYPE, strToType);
@@ -238,13 +240,13 @@ public class EntityRelationController extends BaseController {
@ApiOperation(value = "Get List of Relation Infos (findInfoByTo)",
notes = "Returns list of relation info objects for the specified entity by the 'to' direction. " +
SECURITY_CHECKS_ENTITY_DESCRIPTION + " " + RELATION_INFO_DESCRIPTION,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations/info", method = RequestMethod.GET, params = {TO_ID, TO_TYPE})
@ResponseBody
- public List findInfoByTo(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType,
- @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
+ public List findInfoByTo(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType,
+ @Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
@RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException, ExecutionException, InterruptedException {
checkParameter(TO_ID, strToId);
checkParameter(TO_TYPE, strToType);
@@ -257,14 +259,14 @@ public class EntityRelationController extends BaseController {
@ApiOperation(value = "Get List of Relations (findByTo)",
notes = "Returns list of relation objects for the specified entity by the 'to' direction and relation type. " +
SECURITY_CHECKS_ENTITY_DESCRIPTION,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations", method = RequestMethod.GET, params = {TO_ID, TO_TYPE, RELATION_TYPE})
@ResponseBody
- public List findByTo(@ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType,
- @ApiParam(value = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
- @ApiParam(value = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
+ public List findByTo(@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @RequestParam(TO_ID) String strToId,
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(TO_TYPE) String strToType,
+ @Parameter(description = RELATION_TYPE_PARAM_DESCRIPTION, required = true) @RequestParam(RELATION_TYPE) String strRelationType,
+ @Parameter(description = RELATION_TYPE_GROUP_PARAM_DESCRIPTION)
@RequestParam(value = "relationTypeGroup", required = false) String strRelationTypeGroup) throws ThingsboardException {
checkParameter(TO_ID, strToId);
checkParameter(TO_TYPE, strToType);
@@ -278,11 +280,11 @@ public class EntityRelationController extends BaseController {
@ApiOperation(value = "Find related entities (findByQuery)",
notes = "Returns all entities that are related to the specific entity. " +
"The entity id, relation type, entity types, depth of the search, and other query parameters defined using complex 'EntityRelationsQuery' object. " +
- "See 'Model' tab of the Parameters for more info.", produces = MediaType.APPLICATION_JSON_VALUE)
+ "See 'Model' tab of the Parameters for more info.", responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations", method = RequestMethod.POST)
@ResponseBody
- public List findByQuery(@ApiParam(value = "A JSON value representing the entity relations query object.", required = true)
+ public List findByQuery(@Parameter(description = "A JSON value representing the entity relations query object.", required = true)
@RequestBody EntityRelationsQuery query) throws ThingsboardException, ExecutionException, InterruptedException {
checkNotNull(query);
checkNotNull(query.getParameters());
@@ -294,11 +296,11 @@ public class EntityRelationController extends BaseController {
@ApiOperation(value = "Find related entity infos (findInfoByQuery)",
notes = "Returns all entity infos that are related to the specific entity. " +
"The entity id, relation type, entity types, depth of the search, and other query parameters defined using complex 'EntityRelationsQuery' object. " +
- "See 'Model' tab of the Parameters for more info. " + RELATION_INFO_DESCRIPTION, produces = MediaType.APPLICATION_JSON_VALUE)
+ "See 'Model' tab of the Parameters for more info. " + RELATION_INFO_DESCRIPTION, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/relations/info", method = RequestMethod.POST)
@ResponseBody
- public List findInfoByQuery(@ApiParam(value = "A JSON value representing the entity relations query object.", required = true)
+ public List findInfoByQuery(@Parameter(description = "A JSON value representing the entity relations query object.", required = true)
@RequestBody EntityRelationsQuery query) throws ThingsboardException, ExecutionException, InterruptedException {
checkNotNull(query);
checkNotNull(query.getParameters());
diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
index 66a14502d5..59877bd4d2 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EntityViewController.java
@@ -16,8 +16,10 @@
package org.thingsboard.server.controller;
import com.google.common.util.concurrent.ListenableFuture;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
@@ -45,6 +47,7 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent;
@@ -66,15 +69,12 @@ import static org.thingsboard.server.controller.ControllerConstants.EDGE_UNASSIG
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_VIEW_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_VIEW_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_VIEW_INFO_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.ENTITY_VIEW_INFO_SORT_PROPERTY_ALLOWABLE_VALUES;
-import static org.thingsboard.server.controller.ControllerConstants.ENTITY_VIEW_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_VIEW_TEXT_SEARCH_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_VIEW_TYPE;
import static org.thingsboard.server.controller.ControllerConstants.MODEL_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
@@ -98,12 +98,12 @@ public class EntityViewController extends BaseController {
@ApiOperation(value = "Get entity view (getEntityViewById)",
notes = "Fetch the EntityView object based on the provided entity view id. "
+ ENTITY_VIEW_DESCRIPTION + MODEL_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.GET)
@ResponseBody
public EntityView getEntityViewById(
- @ApiParam(value = ENTITY_VIEW_ID_PARAM_DESCRIPTION)
+ @Parameter(description = ENTITY_VIEW_ID_PARAM_DESCRIPTION)
@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
return checkEntityViewId(new EntityViewId(toUUID(strEntityViewId)), Operation.READ);
@@ -112,12 +112,12 @@ public class EntityViewController extends BaseController {
@ApiOperation(value = "Get Entity View info (getEntityViewInfoById)",
notes = "Fetch the Entity View info object based on the provided Entity View Id. "
+ ENTITY_VIEW_INFO_DESCRIPTION + MODEL_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/entityView/info/{entityViewId}", method = RequestMethod.GET)
@ResponseBody
public EntityViewInfo getEntityViewInfoById(
- @ApiParam(value = ENTITY_VIEW_ID_PARAM_DESCRIPTION)
+ @Parameter(description = ENTITY_VIEW_ID_PARAM_DESCRIPTION)
@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
@@ -128,12 +128,12 @@ public class EntityViewController extends BaseController {
notes = ENTITY_VIEW_DESCRIPTION + MODEL_DESCRIPTION +
"Remove 'id', 'tenantId' and optionally 'customerId' from the request body example (below) to create new Entity View entity." +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/entityView", method = RequestMethod.POST)
@ResponseBody
public EntityView saveEntityView(
- @ApiParam(value = "A JSON object representing the entity view.")
+ @Parameter(description = "A JSON object representing the entity view.")
@RequestBody EntityView entityView) throws Exception {
entityView.setTenantId(getCurrentUser().getTenantId());
EntityView existingEntityView = null;
@@ -153,7 +153,7 @@ public class EntityViewController extends BaseController {
@RequestMapping(value = "/entityView/{entityViewId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
public void deleteEntityView(
- @ApiParam(value = ENTITY_VIEW_ID_PARAM_DESCRIPTION)
+ @Parameter(description = ENTITY_VIEW_ID_PARAM_DESCRIPTION)
@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
@@ -163,12 +163,12 @@ public class EntityViewController extends BaseController {
@ApiOperation(value = "Get Entity View by name (getTenantEntityView)",
notes = "Fetch the Entity View object based on the tenant id and entity view name. " + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/tenant/entityViews", params = {"entityViewName"}, method = RequestMethod.GET)
@ResponseBody
public EntityView getTenantEntityView(
- @ApiParam(value = "Entity View name")
+ @Parameter(description = "Entity View name")
@RequestParam String entityViewName) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
return checkNotNull(entityViewService.findEntityViewByTenantIdAndName(tenantId, entityViewName));
@@ -180,9 +180,9 @@ public class EntityViewController extends BaseController {
@RequestMapping(value = "/customer/{customerId}/entityView/{entityViewId}", method = RequestMethod.POST)
@ResponseBody
public EntityView assignEntityViewToCustomer(
- @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION)
+ @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION)
@PathVariable(CUSTOMER_ID) String strCustomerId,
- @ApiParam(value = ENTITY_VIEW_ID_PARAM_DESCRIPTION)
+ @Parameter(description = ENTITY_VIEW_ID_PARAM_DESCRIPTION)
@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(CUSTOMER_ID, strCustomerId);
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
@@ -202,7 +202,7 @@ public class EntityViewController extends BaseController {
@RequestMapping(value = "/customer/entityView/{entityViewId}", method = RequestMethod.DELETE)
@ResponseBody
public EntityView unassignEntityViewFromCustomer(
- @ApiParam(value = ENTITY_VIEW_ID_PARAM_DESCRIPTION)
+ @Parameter(description = ENTITY_VIEW_ID_PARAM_DESCRIPTION)
@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
@@ -223,19 +223,19 @@ public class EntityViewController extends BaseController {
@RequestMapping(value = "/customer/{customerId}/entityViews", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getCustomerEntityViews(
- @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(CUSTOMER_ID) String strCustomerId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = ENTITY_VIEW_TYPE)
+ @Parameter(description = ENTITY_VIEW_TYPE)
@RequestParam(required = false) String type,
- @ApiParam(value = ENTITY_VIEW_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ENTITY_VIEW_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ENTITY_VIEW_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name, type"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter(CUSTOMER_ID, strCustomerId);
TenantId tenantId = getCurrentUser().getTenantId();
@@ -256,19 +256,19 @@ public class EntityViewController extends BaseController {
@RequestMapping(value = "/customer/{customerId}/entityViewInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getCustomerEntityViewInfos(
- @ApiParam(value = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = CUSTOMER_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(CUSTOMER_ID) String strCustomerId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = ENTITY_VIEW_TYPE)
+ @Parameter(description = ENTITY_VIEW_TYPE)
@RequestParam(required = false) String type,
- @ApiParam(value = ENTITY_VIEW_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ENTITY_VIEW_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ENTITY_VIEW_INFO_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "type", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("customerId", strCustomerId);
TenantId tenantId = getCurrentUser().getTenantId();
@@ -289,17 +289,17 @@ public class EntityViewController extends BaseController {
@RequestMapping(value = "/tenant/entityViews", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getTenantEntityViews(
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = ENTITY_VIEW_TYPE)
+ @Parameter(description = ENTITY_VIEW_TYPE)
@RequestParam(required = false) String type,
- @ApiParam(value = ENTITY_VIEW_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ENTITY_VIEW_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ENTITY_VIEW_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name, type"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@@ -318,17 +318,17 @@ public class EntityViewController extends BaseController {
@RequestMapping(value = "/tenant/entityViewInfos", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getTenantEntityViewInfos(
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = ENTITY_VIEW_TYPE)
+ @Parameter(description = ENTITY_VIEW_TYPE)
@RequestParam(required = false) String type,
- @ApiParam(value = ENTITY_VIEW_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = ENTITY_VIEW_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ENTITY_VIEW_INFO_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "type", "customerTitle"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@@ -347,7 +347,7 @@ public class EntityViewController extends BaseController {
@RequestMapping(value = "/entityViews", method = RequestMethod.POST)
@ResponseBody
public List findByQuery(
- @ApiParam(value = "The entity view search query JSON")
+ @Parameter(description = "The entity view search query JSON")
@RequestBody EntityViewSearchQuery query) throws ThingsboardException, ExecutionException, InterruptedException {
checkNotNull(query);
checkNotNull(query.getParameters());
@@ -386,7 +386,7 @@ public class EntityViewController extends BaseController {
@RequestMapping(value = "/customer/public/entityView/{entityViewId}", method = RequestMethod.POST)
@ResponseBody
public EntityView assignEntityViewToPublicCustomer(
- @ApiParam(value = ENTITY_VIEW_ID_PARAM_DESCRIPTION)
+ @Parameter(description = ENTITY_VIEW_ID_PARAM_DESCRIPTION)
@PathVariable(ENTITY_VIEW_ID) String strEntityViewId) throws ThingsboardException {
checkParameter(ENTITY_VIEW_ID, strEntityViewId);
EntityViewId entityViewId = new EntityViewId(toUUID(strEntityViewId));
@@ -400,7 +400,7 @@ public class EntityViewController extends BaseController {
"Second, remote edge service will receive a copy of assignment entity view " +
EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION +
"Third, once entity view will be delivered to edge service, it's going to be available for usage on remote edge instance.",
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.POST)
@ResponseBody
@@ -425,7 +425,7 @@ public class EntityViewController extends BaseController {
"Second, remote edge service will receive an 'unassign' command to remove entity view " +
EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION +
"Third, once 'unassign' command will be delivered to edge service, it's going to remove entity view locally.",
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/entityView/{entityViewId}", method = RequestMethod.DELETE)
@ResponseBody
diff --git a/application/src/main/java/org/thingsboard/server/controller/EventController.java b/application/src/main/java/org/thingsboard/server/controller/EventController.java
index fd203361cf..b1e2405ae6 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EventController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EventController.java
@@ -15,8 +15,10 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@@ -39,6 +41,7 @@ import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.TimePageLink;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
@@ -52,7 +55,6 @@ import static org.thingsboard.server.controller.ControllerConstants.EVENT_DEBUG_
import static org.thingsboard.server.controller.ControllerConstants.EVENT_END_TIME_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.EVENT_ERROR_FILTER_OBJ;
import static org.thingsboard.server.controller.ControllerConstants.EVENT_LC_EVENT_FILTER_OBJ;
-import static org.thingsboard.server.controller.ControllerConstants.EVENT_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.EVENT_START_TIME_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.EVENT_STATS_FILTER_OBJ;
import static org.thingsboard.server.controller.ControllerConstants.EVENT_TEXT_SEARCH_DESCRIPTION;
@@ -60,7 +62,6 @@ import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_ID;
@@ -107,32 +108,32 @@ public class EventController extends BaseController {
@ApiOperation(value = "Get Events by type (getEvents)",
notes = "Returns a page of events for specified entity by specifying event type. " +
- PAGE_DATA_PARAMETERS, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}/{eventType}", method = RequestMethod.GET)
@ResponseBody
public PageData getEvents(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_TYPE) String strEntityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_ID) String strEntityId,
- @ApiParam(value = "A string value representing event type", example = "STATS", required = true)
+ @Parameter(description = "A string value representing event type", example = "STATS", required = true)
@PathVariable("eventType") String eventType,
- @ApiParam(value = TENANT_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = TENANT_ID_PARAM_DESCRIPTION, required = true)
@RequestParam(TENANT_ID) String strTenantId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = EVENT_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = EVENT_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EVENT_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"ts", "id"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = EVENT_START_TIME_DESCRIPTION)
+ @Parameter(description = EVENT_START_TIME_DESCRIPTION)
@RequestParam(required = false) Long startTime,
- @ApiParam(value = EVENT_END_TIME_DESCRIPTION)
+ @Parameter(description = EVENT_END_TIME_DESCRIPTION)
@RequestParam(required = false) Long endTime) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
@@ -149,30 +150,30 @@ public class EventController extends BaseController {
"The call was deprecated to improve the performance of the system. " +
"Current implementation will return 'Lifecycle' events only. " +
"Use 'Get events by type' or 'Get events by filter' instead. " +
- PAGE_DATA_PARAMETERS, produces = MediaType.APPLICATION_JSON_VALUE)
+ PAGE_DATA_PARAMETERS, responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.GET)
@ResponseBody
public PageData getEvents(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_TYPE) String strEntityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_ID) String strEntityId,
- @ApiParam(value = TENANT_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = TENANT_ID_PARAM_DESCRIPTION, required = true)
@RequestParam("tenantId") String strTenantId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = EVENT_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = EVENT_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EVENT_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"ts", "id"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = EVENT_START_TIME_DESCRIPTION)
+ @Parameter(description = EVENT_START_TIME_DESCRIPTION)
@RequestParam(required = false) Long startTime,
- @ApiParam(value = EVENT_END_TIME_DESCRIPTION)
+ @Parameter(description = EVENT_END_TIME_DESCRIPTION)
@RequestParam(required = false) Long endTime) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
@@ -190,32 +191,32 @@ public class EventController extends BaseController {
notes = "Returns a page of events for the chosen entity by specifying the event filter. " +
PAGE_DATA_PARAMETERS + NEW_LINE +
EVENT_FILTER_DEFINITION,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}", method = RequestMethod.POST)
@ResponseBody
public PageData getEvents(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_TYPE) String strEntityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_ID) String strEntityId,
- @ApiParam(value = TENANT_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = TENANT_ID_PARAM_DESCRIPTION, required = true)
@RequestParam(TENANT_ID) String strTenantId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = "A JSON value representing the event filter.", required = true)
+ @Parameter(description = "A JSON value representing the event filter.", required = true)
@RequestBody EventFilter eventFilter,
- @ApiParam(value = EVENT_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = EVENT_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = EVENT_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"ts", "id"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = EVENT_START_TIME_DESCRIPTION)
+ @Parameter(description = EVENT_START_TIME_DESCRIPTION)
@RequestParam(required = false) Long startTime,
- @ApiParam(value = EVENT_END_TIME_DESCRIPTION)
+ @Parameter(description = EVENT_END_TIME_DESCRIPTION)
@RequestParam(required = false) Long endTime) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
@@ -232,15 +233,15 @@ public class EventController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/events/{entityType}/{entityId}/clear", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
- public void clearEvents(@ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
+ public void clearEvents(@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_TYPE) String strEntityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(ENTITY_ID) String strEntityId,
- @ApiParam(value = EVENT_START_TIME_DESCRIPTION)
+ @Parameter(description = EVENT_START_TIME_DESCRIPTION)
@RequestParam(required = false) Long startTime,
- @ApiParam(value = EVENT_END_TIME_DESCRIPTION)
+ @Parameter(description = EVENT_END_TIME_DESCRIPTION)
@RequestParam(required = false) Long endTime,
- @ApiParam(value = EVENT_FILTER_DEFINITION)
+ @Parameter(description = EVENT_FILTER_DEFINITION)
@RequestBody EventFilter eventFilter) throws ThingsboardException {
checkParameter("EntityId", strEntityId);
checkParameter("EntityType", strEntityType);
diff --git a/application/src/main/java/org/thingsboard/server/controller/ImageController.java b/application/src/main/java/org/thingsboard/server/controller/ImageController.java
index 516a8941a8..c8d24d6081 100644
--- a/application/src/main/java/org/thingsboard/server/controller/ImageController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/ImageController.java
@@ -15,7 +15,8 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -26,7 +27,6 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -59,14 +59,13 @@ import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
+import java.util.Base64;
import java.util.concurrent.TimeUnit;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_INCLUDE_SYSTEM_IMAGES_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_TEXT_SEARCH_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
@@ -120,9 +119,9 @@ public class ImageController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@PutMapping(IMAGE_URL)
- public TbResourceInfo updateImage(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true)
+ public TbResourceInfo updateImage(@Parameter(description = IMAGE_TYPE_PARAM_DESCRIPTION, schema = @Schema(allowableValues = {"tenant", "system"}), required = true)
@PathVariable String type,
- @ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key,
@RequestPart MultipartFile file) throws Exception {
TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE);
@@ -140,9 +139,9 @@ public class ImageController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@PutMapping(IMAGE_URL + "/info")
- public TbResourceInfo updateImageInfo(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true)
+ public TbResourceInfo updateImageInfo(@Parameter(description = IMAGE_TYPE_PARAM_DESCRIPTION, schema = @Schema(allowableValues = {"tenant", "system"}), required = true)
@PathVariable String type,
- @ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key,
@RequestBody TbResourceInfo request) throws ThingsboardException {
TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE);
@@ -153,9 +152,9 @@ public class ImageController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@PutMapping(IMAGE_URL + "/public/{isPublic}")
- public TbResourceInfo updateImagePublicStatus(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true)
+ public TbResourceInfo updateImagePublicStatus(@Parameter(description = IMAGE_TYPE_PARAM_DESCRIPTION, schema = @Schema(allowableValues = {"tenant", "system"}), required = true)
@PathVariable String type,
- @ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key,
@PathVariable boolean isPublic) throws ThingsboardException {
TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.WRITE);
@@ -166,9 +165,9 @@ public class ImageController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = IMAGE_URL, produces = "image/*")
- public ResponseEntity downloadImage(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true)
+ public ResponseEntity downloadImage(@Parameter(description = IMAGE_TYPE_PARAM_DESCRIPTION, schema = @Schema(allowableValues = {"tenant", "system"}), required = true)
@PathVariable String type,
- @ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key,
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception {
return downloadIfChanged(type, key, etag, false);
@@ -183,9 +182,9 @@ public class ImageController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = IMAGE_URL + "/export")
- public ImageExportData exportImage(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true)
+ public ImageExportData exportImage(@Parameter(description = IMAGE_TYPE_PARAM_DESCRIPTION, schema = @Schema(allowableValues = {"tenant", "system"}), required = true)
@PathVariable String type,
- @ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key) throws Exception {
TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.READ);
ImageDescriptor descriptor = imageInfo.getDescriptor(ImageDescriptor.class);
@@ -197,7 +196,7 @@ public class ImageController extends BaseController {
.resourceKey(imageInfo.getResourceKey())
.isPublic(imageInfo.isPublic())
.publicResourceKey(imageInfo.getPublicResourceKey())
- .data(Base64Utils.encodeToString(data))
+ .data(Base64.getEncoder().encodeToString(data))
.build();
}
@@ -222,15 +221,15 @@ public class ImageController extends BaseController {
ImageDescriptor descriptor = new ImageDescriptor();
descriptor.setMediaType(imageData.getMediaType());
image.setDescriptorValue(descriptor);
- image.setData(Base64Utils.decodeFromString(imageData.getData()));
+ image.setData(Base64.getDecoder().decode(imageData.getData()));
return tbImageService.save(image, user);
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = IMAGE_URL + "/preview", produces = "image/png")
- public ResponseEntity downloadImagePreview(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true)
+ public ResponseEntity downloadImagePreview(@Parameter(description = IMAGE_TYPE_PARAM_DESCRIPTION, schema = @Schema(allowableValues = {"tenant", "system"}), required = true)
@PathVariable String type,
- @ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key,
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws Exception {
return downloadIfChanged(type, key, etag, true);
@@ -238,26 +237,26 @@ public class ImageController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(IMAGE_URL + "/info")
- public TbResourceInfo getImageInfo(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true)
+ public TbResourceInfo getImageInfo(@Parameter(description = IMAGE_TYPE_PARAM_DESCRIPTION, schema = @Schema(allowableValues = {"tenant", "system"}), required = true)
@PathVariable String type,
- @ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key) throws ThingsboardException {
return checkImageInfo(type, key, Operation.READ);
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping("/api/images")
- public PageData getImages(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ public PageData getImages(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = RESOURCE_INCLUDE_SYSTEM_IMAGES_DESCRIPTION)
+ @Parameter(description = RESOURCE_INCLUDE_SYSTEM_IMAGES_DESCRIPTION)
@RequestParam(required = false) boolean includeSystemImages,
- @ApiParam(value = RESOURCE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = RESOURCE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "title", "resourceType", "tenantId"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
// PE: generic permission
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@@ -271,9 +270,9 @@ public class ImageController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@DeleteMapping(IMAGE_URL)
- public ResponseEntity deleteImage(@ApiParam(value = IMAGE_TYPE_PARAM_DESCRIPTION, allowableValues = IMAGE_TYPE_PARAM_ALLOWABLE_VALUES, required = true)
+ public ResponseEntity deleteImage(@Parameter(description = IMAGE_TYPE_PARAM_DESCRIPTION, schema = @Schema(allowableValues = {"tenant", "system"}), required = true)
@PathVariable String type,
- @ApiParam(value = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = IMAGE_KEY_PARAM_DESCRIPTION, required = true)
@PathVariable String key,
@RequestParam(name = "force", required = false) boolean force) throws ThingsboardException {
TbResourceInfo imageInfo = checkImageInfo(type, key, Operation.DELETE);
diff --git a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java
index fdfa55f5e7..f6670193d9 100644
--- a/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/Lwm2mController.java
@@ -15,11 +15,13 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
@@ -33,6 +35,7 @@ import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest;
import org.thingsboard.server.common.data.device.profile.lwm2m.bootstrap.LwM2MServerSecurityConfigDefault;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.security.DeviceCredentials;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.service.lwm2m.LwM2MService;
import java.util.Map;
@@ -57,12 +60,12 @@ public class Lwm2mController extends BaseController {
@ApiOperation(value = "Get Lwm2m Bootstrap SecurityInfo (getLwm2mBootstrapSecurityInfo)",
notes = "Get the Lwm2m Bootstrap SecurityInfo object (of the current server) based on the provided isBootstrapServer parameter. If isBootstrapServer == true, get the parameters of the current Bootstrap Server. If isBootstrapServer == false, get the parameters of the current Lwm2m Server. Used for client settings when starting the client in Bootstrap mode. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/lwm2m/deviceProfile/bootstrap/{isBootstrapServer}", method = RequestMethod.GET)
@ResponseBody
public LwM2MServerSecurityConfigDefault getLwm2mBootstrapSecurityInfo(
- @ApiParam(value = IS_BOOTSTRAP_SERVER_PARAM_DESCRIPTION)
+ @Parameter(description = IS_BOOTSTRAP_SERVER_PARAM_DESCRIPTION)
@PathVariable(IS_BOOTSTRAP_SERVER) boolean bootstrapServer) throws ThingsboardException {
return lwM2MService.getServerSecurityInfo(bootstrapServer);
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/MailConfigTemplateController.java b/application/src/main/java/org/thingsboard/server/controller/MailConfigTemplateController.java
index c1db70ebed..e17d8e55bc 100644
--- a/application/src/main/java/org/thingsboard/server/controller/MailConfigTemplateController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/MailConfigTemplateController.java
@@ -16,7 +16,6 @@
package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode;
-import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -25,6 +24,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.mail.TbMailConfigTemplateService;
import org.thingsboard.server.service.security.permission.Operation;
diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java
index d2b095c7fa..55f4e6d9fa 100644
--- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java
@@ -15,8 +15,8 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -57,6 +57,7 @@ import org.thingsboard.server.common.data.notification.template.NotificationTemp
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.notification.NotificationRequestService;
import org.thingsboard.server.dao.notification.NotificationService;
import org.thingsboard.server.dao.notification.NotificationSettingsService;
@@ -68,7 +69,6 @@ import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
-import javax.validation.Valid;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -160,17 +160,17 @@ public class NotificationController extends BaseController {
"}\n```")
@GetMapping("/notifications")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
- public PageData getNotifications(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ public PageData getNotifications(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = "Case-insensitive 'substring' filter based on notification subject or text")
+ @Parameter(description = "Case-insensitive 'substring' filter based on notification subject or text")
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION)
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION)
+ @Parameter(description = SORT_ORDER_DESCRIPTION)
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = "To search for unread notifications only")
+ @Parameter(description = "To search for unread notifications only")
@RequestParam(defaultValue = "false") boolean unreadOnly,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
// no permissions
@@ -268,7 +268,7 @@ public class NotificationController extends BaseController {
@PostMapping("/notification/request/preview")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public NotificationRequestPreview getNotificationRequestPreview(@RequestBody @Valid NotificationRequest request,
- @ApiParam(value = "Amount of the recipients to show in preview")
+ @Parameter(description = "Amount of the recipients to show in preview")
@RequestParam(defaultValue = "20") int recipientsPreviewSize,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
// PE: generic permission
@@ -376,15 +376,15 @@ public class NotificationController extends BaseController {
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/notification/requests")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
- public PageData getNotificationRequests(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ public PageData getNotificationRequests(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = "Case-insensitive 'substring' filed based on the used template name")
+ @Parameter(description = "Case-insensitive 'substring' filed based on the used template name")
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION)
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION)
+ @Parameter(description = SORT_ORDER_DESCRIPTION)
@RequestParam(required = false) String sortOrder,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
// PE: generic permission
diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java
index 159a5d00f2..f3a7309212 100644
--- a/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/NotificationRuleController.java
@@ -15,8 +15,8 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -37,13 +37,12 @@ import org.thingsboard.server.common.data.notification.rule.NotificationRuleInfo
import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
-import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.notification.NotificationRuleService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
-import javax.validation.Valid;
import java.util.UUID;
import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_END;
@@ -125,11 +124,7 @@ public class NotificationRuleController extends BaseController {
throw new IllegalArgumentException("Trigger type " + triggerType + " is not available");
}
- boolean created = notificationRule.getId() == null;
- notificationRule = doSaveAndLog(EntityType.NOTIFICATION_RULE, notificationRule, notificationRuleService::saveNotificationRule);
- tbClusterService.broadcastEntityStateChangeEvent(user.getTenantId(), notificationRule.getId(), created ?
- ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
- return notificationRule;
+ return doSaveAndLog(EntityType.NOTIFICATION_RULE, notificationRule, notificationRuleService::saveNotificationRule);
}
@ApiOperation(value = "Get notification rule by id (getNotificationRuleById)",
@@ -150,15 +145,15 @@ public class NotificationRuleController extends BaseController {
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/rules")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
- public PageData getNotificationRules(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ public PageData getNotificationRules(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = "Case-insensitive 'substring' filter based on rule's name")
+ @Parameter(description = "Case-insensitive 'substring' filter based on rule's name")
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION)
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION)
+ @Parameter(description = SORT_ORDER_DESCRIPTION)
@RequestParam(required = false) String sortOrder,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
// PE: generic permission
@@ -177,7 +172,6 @@ public class NotificationRuleController extends BaseController {
NotificationRuleId notificationRuleId = new NotificationRuleId(id);
NotificationRule notificationRule = checkEntityId(notificationRuleId, notificationRuleService::findNotificationRuleById, Operation.DELETE);
doDeleteAndLog(EntityType.NOTIFICATION_RULE, notificationRule, notificationRuleService::deleteNotificationRuleById);
- tbClusterService.broadcastEntityStateChangeEvent(user.getTenantId(), notificationRuleId, ComponentLifecycleEvent.DELETED);
}
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java
index 5464f4b8a2..8f0a2fb3cd 100644
--- a/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTargetController.java
@@ -15,8 +15,8 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
@@ -48,12 +48,12 @@ import org.thingsboard.server.common.data.notification.targets.platform.UserList
import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.notification.NotificationTargetService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
-import javax.validation.Valid;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
@@ -134,9 +134,9 @@ public class NotificationTargetController extends BaseController {
@PostMapping("/target/recipients")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public PageData getRecipientsForNotificationTargetConfig(@RequestBody NotificationTarget notificationTarget,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
// PE: generic permission
@@ -156,7 +156,7 @@ public class NotificationTargetController extends BaseController {
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping(value = "/targets", params = {"ids"})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
- public List getNotificationTargetsByIds(@ApiParam(value = "Comma-separated list of uuids representing targets ids", required = true)
+ public List getNotificationTargetsByIds(@Parameter(description = "Comma-separated list of uuids representing targets ids", required = true)
@RequestParam("ids") UUID[] ids,
@AuthenticationPrincipal SecurityUser user) {
// PE: generic permission
@@ -170,15 +170,15 @@ public class NotificationTargetController extends BaseController {
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/targets")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
- public PageData getNotificationTargets(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ public PageData getNotificationTargets(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = "Case-insensitive 'substring' filed based on the target's name")
+ @Parameter(description = "Case-insensitive 'substring' filed based on the target's name")
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION)
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION)
+ @Parameter(description = SORT_ORDER_DESCRIPTION)
@RequestParam(required = false) String sortOrder,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
// PE: generic permission
diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java
index 574a72e96f..6948218531 100644
--- a/application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/NotificationTemplateController.java
@@ -15,8 +15,8 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -42,13 +42,13 @@ import org.thingsboard.server.common.data.notification.targets.slack.SlackConver
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.notification.NotificationSettingsService;
import org.thingsboard.server.dao.notification.NotificationTemplateService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
-import javax.validation.Valid;
import java.util.List;
import java.util.UUID;
@@ -137,17 +137,17 @@ public class NotificationTemplateController extends BaseController {
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/templates")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
- public PageData getNotificationTemplates(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ public PageData getNotificationTemplates(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = "Case-insensitive 'substring' filter based on template's name and notification type")
+ @Parameter(description = "Case-insensitive 'substring' filter based on template's name and notification type")
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION)
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION)
+ @Parameter(description = SORT_ORDER_DESCRIPTION)
@RequestParam(required = false) String sortOrder,
- @ApiParam(value = "Comma-separated list of notification types to filter the templates")
+ @Parameter(description = "Comma-separated list of notification types to filter the templates")
@RequestParam(required = false) NotificationType[] notificationTypes,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
// PE: generic permission
@@ -177,7 +177,7 @@ public class NotificationTemplateController extends BaseController {
@GetMapping("/slack/conversations")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
public List listSlackConversations(@RequestParam SlackConversationType type,
- @ApiParam(value = "Slack bot token. If absent - system Slack settings will be used")
+ @Parameter(description = "Slack bot token. If absent - system Slack settings will be used")
@RequestParam(required = false) String token,
@AuthenticationPrincipal SecurityUser user) {
// PE: generic permission
diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java
index d9879b73a2..8924578900 100644
--- a/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java
@@ -15,8 +15,7 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -30,6 +29,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
@@ -63,7 +63,7 @@ public class OAuth2ConfigTemplateController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/{clientRegistrationTemplateId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
- public void deleteClientRegistrationTemplate(@ApiParam(value = "String representation of client registration template id to delete", example = "139b1f81-2f5d-11ec-9dbe-9b627e1a88f4")
+ public void deleteClientRegistrationTemplate(@Parameter(description = "String representation of client registration template id to delete", example = "139b1f81-2f5d-11ec-9dbe-9b627e1a88f4")
@PathVariable(CLIENT_REGISTRATION_TEMPLATE_ID) String strClientRegistrationTemplateId) throws ThingsboardException {
checkParameter(CLIENT_REGISTRATION_TEMPLATE_ID, strClientRegistrationTemplateId);
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.DELETE);
diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java
index eeedd2842d..c0a9d6d26f 100644
--- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java
+++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java
@@ -15,8 +15,9 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
@@ -33,13 +34,13 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2Info;
import org.thingsboard.server.common.data.oauth2.PlatformType;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import org.thingsboard.server.utils.MiscUtils;
-import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.List;
@@ -61,13 +62,13 @@ public class OAuth2Controller extends BaseController {
@RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST)
@ResponseBody
public List getOAuth2Clients(HttpServletRequest request,
- @ApiParam(value = "Mobile application package name, to find OAuth2 clients " +
+ @Parameter(description = "Mobile application package name, to find OAuth2 clients " +
"where there is configured mobile application with such package name")
@RequestParam(required = false) String pkgName,
- @ApiParam(value = "Platform type to search OAuth2 clients for which " +
+ @Parameter(description = "Platform type to search OAuth2 clients for which " +
"the usage with this platform type is allowed in the settings. " +
"If platform type is not one of allowable values - it will just be ignored",
- allowableValues = "WEB, ANDROID, IOS")
+ schema = @Schema(allowableValues = "WEB, ANDROID, IOS"))
@RequestParam(required = false) String platform) throws ThingsboardException {
if (log.isDebugEnabled()) {
log.debug("Executing getOAuth2Clients: [{}][{}][{}]", request.getScheme(), request.getServerName(), request.getServerPort());
@@ -81,7 +82,8 @@ public class OAuth2Controller extends BaseController {
if (StringUtils.isNotEmpty(platform)) {
try {
platformType = PlatformType.valueOf(platform);
- } catch (Exception e) {}
+ } catch (Exception e) {
+ }
}
return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request), pkgName, platformType);
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java
index 72b477f17a..4ceb39534f 100644
--- a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java
@@ -15,8 +15,10 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ByteArrayResource;
@@ -42,6 +44,7 @@ import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.ota.TbOtaPackageService;
import org.thingsboard.server.service.security.permission.Operation;
@@ -52,16 +55,13 @@ import java.io.IOException;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_ID_PARAM_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.OTA_PACKAGE_CHECKSUM_ALGORITHM_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.OTA_PACKAGE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.OTA_PACKAGE_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.OTA_PACKAGE_INFO_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.OTA_PACKAGE_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.OTA_PACKAGE_TEXT_SEARCH_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
@@ -84,7 +84,7 @@ public class OtaPackageController extends BaseController {
@PreAuthorize("hasAnyAuthority( 'TENANT_ADMIN')")
@RequestMapping(value = "/otaPackage/{otaPackageId}/download", method = RequestMethod.GET)
@ResponseBody
- public ResponseEntity downloadOtaPackage(@ApiParam(value = OTA_PACKAGE_ID_PARAM_DESCRIPTION)
+ public ResponseEntity downloadOtaPackage(@Parameter(description = OTA_PACKAGE_ID_PARAM_DESCRIPTION)
@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException {
checkParameter(OTA_PACKAGE_ID, strOtaPackageId);
OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId));
@@ -106,11 +106,11 @@ public class OtaPackageController extends BaseController {
@ApiOperation(value = "Get OTA Package Info (getOtaPackageInfoById)",
notes = "Fetch the OTA Package Info object based on the provided OTA Package Id. " +
OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/otaPackage/info/{otaPackageId}", method = RequestMethod.GET)
@ResponseBody
- public OtaPackageInfo getOtaPackageInfoById(@ApiParam(value = OTA_PACKAGE_ID_PARAM_DESCRIPTION)
+ public OtaPackageInfo getOtaPackageInfoById(@Parameter(description = OTA_PACKAGE_ID_PARAM_DESCRIPTION)
@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException {
checkParameter(OTA_PACKAGE_ID, strOtaPackageId);
OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId));
@@ -120,11 +120,11 @@ public class OtaPackageController extends BaseController {
@ApiOperation(value = "Get OTA Package (getOtaPackageById)",
notes = "Fetch the OTA Package object based on the provided OTA Package Id. " +
"The server checks that the OTA Package is owned by the same tenant. " + OTA_PACKAGE_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH,
- produces = APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.GET)
@ResponseBody
- public OtaPackage getOtaPackageById(@ApiParam(value = OTA_PACKAGE_ID_PARAM_DESCRIPTION)
+ public OtaPackage getOtaPackageById(@Parameter(description = OTA_PACKAGE_ID_PARAM_DESCRIPTION)
@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException {
checkParameter(OTA_PACKAGE_ID, strOtaPackageId);
OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId));
@@ -137,12 +137,11 @@ public class OtaPackageController extends BaseController {
"Specify existing OTA Package id to update the OTA Package Info. " +
"Referencing non-existing OTA Package Id will cause 'Not Found' error. " +
"\n\nOTA Package combination of the title with the version is unique in the scope of tenant. " + TENANT_AUTHORITY_PARAGRAPH,
- produces = APPLICATION_JSON_VALUE,
- consumes = APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/otaPackage", method = RequestMethod.POST)
@ResponseBody
- public OtaPackageInfo saveOtaPackageInfo(@ApiParam(value = "A JSON value representing the OTA Package.")
+ public OtaPackageInfo saveOtaPackageInfo(@Parameter(description = "A JSON value representing the OTA Package.")
@RequestBody SaveOtaPackageInfoRequest otaPackageInfo) throws ThingsboardException {
otaPackageInfo.setTenantId(getTenantId());
checkEntity(otaPackageInfo.getId(), otaPackageInfo, Resource.OTA_PACKAGE);
@@ -152,18 +151,18 @@ public class OtaPackageController extends BaseController {
@ApiOperation(value = "Save OTA Package data (saveOtaPackageData)",
notes = "Update the OTA Package. Adds the date to the existing OTA Package Info" + TENANT_AUTHORITY_PARAGRAPH,
- produces = APPLICATION_JSON_VALUE,
- consumes = MULTIPART_FORM_DATA_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE)),
+ requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(content = @Content(mediaType = MULTIPART_FORM_DATA_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.POST, consumes = MULTIPART_FORM_DATA_VALUE)
@ResponseBody
- public OtaPackageInfo saveOtaPackageData(@ApiParam(value = OTA_PACKAGE_ID_PARAM_DESCRIPTION)
+ public OtaPackageInfo saveOtaPackageData(@Parameter(description = OTA_PACKAGE_ID_PARAM_DESCRIPTION)
@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId,
- @ApiParam(value = "OTA Package checksum. For example, '0xd87f7e0c'")
+ @Parameter(description = "OTA Package checksum. For example, '0xd87f7e0c'")
@RequestParam(required = false) String checksum,
- @ApiParam(value = "OTA Package checksum algorithm.", allowableValues = OTA_PACKAGE_CHECKSUM_ALGORITHM_ALLOWABLE_VALUES)
+ @Parameter(description = "OTA Package checksum algorithm.", schema = @Schema(allowableValues = {"MD5", "SHA256", "SHA384", "SHA512", "CRC32", "MURMUR3_32", "MURMUR3_128"}))
@RequestParam(CHECKSUM_ALGORITHM) String checksumAlgorithmStr,
- @ApiParam(value = "OTA Package data.")
+ @Parameter(description = "OTA Package data.")
@RequestPart MultipartFile file) throws ThingsboardException, IOException {
checkParameter(OTA_PACKAGE_ID, strOtaPackageId);
checkParameter(CHECKSUM_ALGORITHM, checksumAlgorithmStr);
@@ -178,19 +177,19 @@ public class OtaPackageController extends BaseController {
@ApiOperation(value = "Get OTA Package Infos (getOtaPackages)",
notes = "Returns a page of OTA Package Info objects owned by tenant. " +
PAGE_DATA_PARAMETERS + OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/otaPackages", method = RequestMethod.GET)
@ResponseBody
- public PageData getOtaPackages(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ public PageData getOtaPackages(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = OTA_PACKAGE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = OTA_PACKAGE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = OTA_PACKAGE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "type", "title", "version", "tag", "url", "fileName", "dataSize", "checksum"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantId(getTenantId(), pageLink));
@@ -199,23 +198,23 @@ public class OtaPackageController extends BaseController {
@ApiOperation(value = "Get OTA Package Infos (getOtaPackages)",
notes = "Returns a page of OTA Package Info objects owned by tenant. " +
PAGE_DATA_PARAMETERS + OTA_PACKAGE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/otaPackages/{deviceProfileId}/{type}", method = RequestMethod.GET)
@ResponseBody
- public PageData getOtaPackages(@ApiParam(value = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
+ public PageData getOtaPackages(@Parameter(description = DEVICE_PROFILE_ID_PARAM_DESCRIPTION)
@PathVariable("deviceProfileId") String strDeviceProfileId,
- @ApiParam(value = "OTA Package type.", allowableValues = "FIRMWARE, SOFTWARE")
+ @Parameter(description = "OTA Package type.", schema = @Schema(allowableValues = "FIRMWARE, SOFTWARE"))
@PathVariable("type") String strType,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = OTA_PACKAGE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = OTA_PACKAGE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = OTA_PACKAGE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "type", "title", "version", "tag", "url", "fileName", "dataSize", "checksum"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("deviceProfileId", strDeviceProfileId);
checkParameter("type", strType);
@@ -227,11 +226,11 @@ public class OtaPackageController extends BaseController {
@ApiOperation(value = "Delete OTA Package (deleteOtaPackage)",
notes = "Deletes the OTA Package. Referencing non-existing OTA Package Id will cause an error. " +
"Can't delete the OTA Package if it is referenced by existing devices or device profile." + TENANT_AUTHORITY_PARAGRAPH,
- produces = APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.DELETE)
@ResponseBody
- public void deleteOtaPackage(@ApiParam(value = OTA_PACKAGE_ID_PARAM_DESCRIPTION)
+ public void deleteOtaPackage(@Parameter(description = OTA_PACKAGE_ID_PARAM_DESCRIPTION)
@PathVariable("otaPackageId") String strOtaPackageId) throws ThingsboardException {
checkParameter(OTA_PACKAGE_ID, strOtaPackageId);
OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId));
diff --git a/application/src/main/java/org/thingsboard/server/controller/QueueController.java b/application/src/main/java/org/thingsboard/server/controller/QueueController.java
index b5f00efdfe..089ba9bcca 100644
--- a/application/src/main/java/org/thingsboard/server/controller/QueueController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/QueueController.java
@@ -15,8 +15,8 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
@@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.queue.Queue;
import org.thingsboard.server.common.msg.queue.ServiceType;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.queue.TbQueueService;
import org.thingsboard.server.service.security.permission.Operation;
@@ -45,10 +46,7 @@ import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DE
import static org.thingsboard.server.controller.ControllerConstants.QUEUE_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.QUEUE_NAME_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.QUEUE_QUEUE_TEXT_SEARCH_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.QUEUE_SERVICE_TYPE_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.QUEUE_SORT_PROPERTY_ALLOWABLE_VALUES;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH;
@@ -69,17 +67,17 @@ public class QueueController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/queues", params = {"serviceType", "pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
- public PageData getTenantQueuesByServiceType(@ApiParam(value = QUEUE_SERVICE_TYPE_DESCRIPTION, allowableValues = QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES, required = true)
+ public PageData getTenantQueuesByServiceType(@Parameter(description = QUEUE_SERVICE_TYPE_DESCRIPTION, schema = @Schema(allowableValues = {"TB-RULE-ENGINE", "TB-CORE", "TB-TRANSPORT", "JS-EXECUTOR"}, required = true))
@RequestParam String serviceType,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = QUEUE_QUEUE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = QUEUE_QUEUE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = QUEUE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "topic"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("serviceType", serviceType);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@@ -97,7 +95,7 @@ public class QueueController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/queues/{queueId}", method = RequestMethod.GET)
@ResponseBody
- public Queue getQueueById(@ApiParam(value = QUEUE_ID_PARAM_DESCRIPTION)
+ public Queue getQueueById(@Parameter(description = QUEUE_ID_PARAM_DESCRIPTION)
@PathVariable("queueId") String queueIdStr) throws ThingsboardException {
checkParameter("queueId", queueIdStr);
QueueId queueId = new QueueId(UUID.fromString(queueIdStr));
@@ -110,7 +108,7 @@ public class QueueController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/queues/name/{queueName}", method = RequestMethod.GET)
@ResponseBody
- public Queue getQueueByName(@ApiParam(value = QUEUE_NAME_PARAM_DESCRIPTION)
+ public Queue getQueueByName(@Parameter(description = QUEUE_NAME_PARAM_DESCRIPTION)
@PathVariable("queueName") String queueName) throws ThingsboardException {
checkParameter("queueName", queueName);
return checkNotNull(queueService.findQueueByTenantIdAndName(getTenantId(), queueName));
@@ -126,9 +124,9 @@ public class QueueController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/queues", params = {"serviceType"}, method = RequestMethod.POST)
@ResponseBody
- public Queue saveQueue(@ApiParam(value = "A JSON value representing the queue.")
+ public Queue saveQueue(@Parameter(description = "A JSON value representing the queue.")
@RequestBody Queue queue,
- @ApiParam(value = QUEUE_SERVICE_TYPE_DESCRIPTION, allowableValues = QUEUE_SERVICE_TYPE_ALLOWABLE_VALUES, required = true)
+ @Parameter(description = QUEUE_SERVICE_TYPE_DESCRIPTION, schema = @Schema(allowableValues = {"TB-RULE-ENGINE", "TB-CORE", "TB-TRANSPORT", "JS-EXECUTOR"}, required = true))
@RequestParam String serviceType) throws ThingsboardException {
checkParameter("serviceType", serviceType);
queue.setTenantId(getCurrentUser().getTenantId());
@@ -151,7 +149,7 @@ public class QueueController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/queues/{queueId}", method = RequestMethod.DELETE)
@ResponseBody
- public void deleteQueue(@ApiParam(value = QUEUE_ID_PARAM_DESCRIPTION)
+ public void deleteQueue(@Parameter(description = QUEUE_ID_PARAM_DESCRIPTION)
@PathVariable("queueId") String queueIdStr) throws ThingsboardException {
checkParameter("queueId", queueIdStr);
QueueId queueId = new QueueId(toUUID(queueIdStr));
diff --git a/application/src/main/java/org/thingsboard/server/controller/RpcV1Controller.java b/application/src/main/java/org/thingsboard/server/controller/RpcV1Controller.java
index 0ac83bd11b..c87f5386db 100644
--- a/application/src/main/java/org/thingsboard/server/controller/RpcV1Controller.java
+++ b/application/src/main/java/org/thingsboard/server/controller/RpcV1Controller.java
@@ -15,8 +15,7 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -30,6 +29,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.DeviceId;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.UUID;
@@ -48,9 +48,9 @@ public class RpcV1Controller extends AbstractRpcController {
@RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult handleOneWayDeviceRPCRequest(
- @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable("deviceId") String deviceIdStr,
- @ApiParam(value = "A JSON value representing the RPC request.")
+ @Parameter(description = "A JSON value representing the RPC request.")
@RequestBody String requestBody) throws ThingsboardException {
return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.REQUEST_TIMEOUT, HttpStatus.CONFLICT);
}
@@ -60,9 +60,9 @@ public class RpcV1Controller extends AbstractRpcController {
@RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult handleTwoWayDeviceRPCRequest(
- @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable("deviceId") String deviceIdStr,
- @ApiParam(value = "A JSON value representing the RPC request.")
+ @Parameter(description = "A JSON value representing the RPC request.")
@RequestBody String requestBody) throws ThingsboardException {
return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.REQUEST_TIMEOUT, HttpStatus.CONFLICT);
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java
index f9de7a5b1c..6c08f4a2e2 100644
--- a/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java
+++ b/application/src/main/java/org/thingsboard/server/controller/RpcV2Controller.java
@@ -16,10 +16,11 @@
package org.thingsboard.server.controller;
import com.google.common.util.concurrent.FutureCallback;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-import io.swagger.annotations.ApiResponse;
-import io.swagger.annotations.ApiResponses;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import jakarta.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@@ -45,12 +46,12 @@ import org.thingsboard.server.common.data.rpc.Rpc;
import org.thingsboard.server.common.data.rpc.RpcStatus;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.exception.ToErrorResponseEntity;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.common.msg.rpc.RemoveRpcActorMsg;
import org.thingsboard.server.service.security.permission.Operation;
-import javax.annotation.Nullable;
import java.util.UUID;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_ID;
@@ -61,10 +62,7 @@ import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.RPC_ID;
import static org.thingsboard.server.controller.ControllerConstants.RPC_ID_PARAM_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.RPC_SORT_PROPERTY_ALLOWABLE_VALUES;
-import static org.thingsboard.server.controller.ControllerConstants.RPC_STATUS_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.RPC_TEXT_SEARCH_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
@@ -117,36 +115,36 @@ public class RpcV2Controller extends AbstractRpcController {
@ApiOperation(value = "Send one-way RPC request", notes = ONE_WAY_RPC_REQUEST_DESCRIPTION)
@ApiResponses(value = {
- @ApiResponse(code = 200, message = "Persistent RPC request was saved to the database or lightweight RPC request was sent to the device."),
- @ApiResponse(code = 400, message = "Invalid structure of the request."),
- @ApiResponse(code = 401, message = "User is not authorized to send the RPC request. Most likely, User belongs to different Customer or Tenant."),
- @ApiResponse(code = 504, message = "Timeout to process the RPC call. Most likely, device is offline."),
+ @ApiResponse(responseCode = "200", description = "Persistent RPC request was saved to the database or lightweight RPC request was sent to the device."),
+ @ApiResponse(responseCode = "400", description = "Invalid structure of the request."),
+ @ApiResponse(responseCode = "401", description = "User is not authorized to send the RPC request. Most likely, User belongs to different Customer or Tenant."),
+ @ApiResponse(responseCode = "504", description = "Timeout to process the RPC call. Most likely, device is offline."),
})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult handleOneWayDeviceRPCRequest(
- @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable("deviceId") String deviceIdStr,
- @ApiParam(value = "A JSON value representing the RPC request.")
+ @Parameter(description = "A JSON value representing the RPC request.")
@RequestBody String requestBody) throws ThingsboardException {
return handleDeviceRPCRequest(true, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.GATEWAY_TIMEOUT, HttpStatus.GATEWAY_TIMEOUT);
}
@ApiOperation(value = "Send two-way RPC request", notes = TWO_WAY_RPC_REQUEST_DESCRIPTION)
@ApiResponses(value = {
- @ApiResponse(code = 200, message = "Persistent RPC request was saved to the database or lightweight RPC response received."),
- @ApiResponse(code = 400, message = "Invalid structure of the request."),
- @ApiResponse(code = 401, message = "User is not authorized to send the RPC request. Most likely, User belongs to different Customer or Tenant."),
- @ApiResponse(code = 504, message = "Timeout to process the RPC call. Most likely, device is offline."),
+ @ApiResponse(responseCode = "200", description = "Persistent RPC request was saved to the database or lightweight RPC response received."),
+ @ApiResponse(responseCode = "400", description = "Invalid structure of the request."),
+ @ApiResponse(responseCode = "401", description = "User is not authorized to send the RPC request. Most likely, User belongs to different Customer or Tenant."),
+ @ApiResponse(responseCode = "504", description = "Timeout to process the RPC call. Most likely, device is offline."),
})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/twoway/{deviceId}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult handleTwoWayDeviceRPCRequest(
- @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION)
@PathVariable(DEVICE_ID) String deviceIdStr,
- @ApiParam(value = "A JSON value representing the RPC request.")
+ @Parameter(description = "A JSON value representing the RPC request.")
@RequestBody String requestBody) throws ThingsboardException {
return handleDeviceRPCRequest(false, new DeviceId(UUID.fromString(deviceIdStr)), requestBody, HttpStatus.GATEWAY_TIMEOUT, HttpStatus.GATEWAY_TIMEOUT);
}
@@ -156,7 +154,7 @@ public class RpcV2Controller extends AbstractRpcController {
@RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.GET)
@ResponseBody
public Rpc getPersistedRpc(
- @ApiParam(value = RPC_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = RPC_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(RPC_ID) String strRpc) throws ThingsboardException {
checkParameter("RpcId", strRpc);
RpcId rpcId = new RpcId(UUID.fromString(strRpc));
@@ -168,19 +166,19 @@ public class RpcV2Controller extends AbstractRpcController {
@RequestMapping(value = "/persistent/device/{deviceId}", method = RequestMethod.GET)
@ResponseBody
public DeferredResult getPersistedRpcByDevice(
- @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(DEVICE_ID) String strDeviceId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = "Status of the RPC", allowableValues = RPC_STATUS_ALLOWABLE_VALUES)
+ @Parameter(description = "Status of the RPC", schema = @Schema(allowableValues = {"QUEUED", "SENT", "DELIVERED", "SUCCESSFUL", "TIMEOUT", "EXPIRED", "FAILED"}))
@RequestParam(required = false) RpcStatus rpcStatus,
- @ApiParam(value = RPC_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = RPC_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RPC_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "expirationTime", "request", "response"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter("DeviceId", strDeviceId);
if (rpcStatus != null && rpcStatus.equals(RpcStatus.DELETED)) {
@@ -223,7 +221,7 @@ public class RpcV2Controller extends AbstractRpcController {
@RequestMapping(value = "/persistent/{rpcId}", method = RequestMethod.DELETE)
@ResponseBody
public void deleteRpc(
- @ApiParam(value = RPC_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = RPC_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(RPC_ID) String strRpc) throws ThingsboardException {
checkParameter("RpcId", strRpc);
RpcId rpcId = new RpcId(UUID.fromString(strRpc));
diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
index 2d21986834..e7c6e138ca 100644
--- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
@@ -20,8 +20,10 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -66,6 +68,7 @@ import org.thingsboard.server.common.data.script.ScriptLanguage;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgDataType;
import org.thingsboard.server.common.msg.TbMsgMetaData;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.rule.TbRuleChainService;
@@ -93,12 +96,9 @@ import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PA
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.RULE_CHAIN_ID_PARAM_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.RULE_CHAIN_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.RULE_CHAIN_TEXT_SEARCH_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.RULE_CHAIN_TYPES_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.RULE_CHAIN_TYPE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.RULE_NODE_ID_PARAM_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
@@ -163,7 +163,7 @@ public class RuleChainController extends BaseController {
@RequestMapping(value = "/ruleChain/{ruleChainId}", method = RequestMethod.GET)
@ResponseBody
public RuleChain getRuleChainById(
- @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
+ @Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
@@ -177,7 +177,7 @@ public class RuleChainController extends BaseController {
@RequestMapping(value = "/ruleChain/{ruleChainId}/output/labels", method = RequestMethod.GET)
@ResponseBody
public Set getRuleChainOutputLabels(
- @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
+ @Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
@@ -192,7 +192,7 @@ public class RuleChainController extends BaseController {
@RequestMapping(value = "/ruleChain/{ruleChainId}/output/labels/usage", method = RequestMethod.GET)
@ResponseBody
public List getRuleChainOutputLabelsUsage(
- @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
+ @Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
@@ -206,7 +206,7 @@ public class RuleChainController extends BaseController {
@RequestMapping(value = "/ruleChain/{ruleChainId}/metadata", method = RequestMethod.GET)
@ResponseBody
public RuleChainMetaData getRuleChainMetaData(
- @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
+ @Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
@@ -226,7 +226,7 @@ public class RuleChainController extends BaseController {
@RequestMapping(value = "/ruleChain", method = RequestMethod.POST)
@ResponseBody
public RuleChain saveRuleChain(
- @ApiParam(value = "A JSON value representing the rule chain.")
+ @Parameter(description = "A JSON value representing the rule chain.")
@RequestBody RuleChain ruleChain) throws Exception {
ruleChain.setTenantId(getCurrentUser().getTenantId());
checkEntity(ruleChain.getId(), ruleChain, Resource.RULE_CHAIN);
@@ -240,7 +240,7 @@ public class RuleChainController extends BaseController {
@RequestMapping(value = "/ruleChain/device/default", method = RequestMethod.POST)
@ResponseBody
public RuleChain saveRuleChain(
- @ApiParam(value = "A JSON value representing the request.")
+ @Parameter(description = "A JSON value representing the request.")
@RequestBody DefaultRuleChainCreateRequest request) throws Exception {
checkNotNull(request);
checkParameter(request.getName(), "name");
@@ -253,7 +253,7 @@ public class RuleChainController extends BaseController {
@RequestMapping(value = "/ruleChain/{ruleChainId}/root", method = RequestMethod.POST)
@ResponseBody
public RuleChain setRootRuleChain(
- @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
+ @Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
@@ -267,9 +267,9 @@ public class RuleChainController extends BaseController {
@RequestMapping(value = "/ruleChain/metadata", method = RequestMethod.POST)
@ResponseBody
public RuleChainMetaData saveRuleChainMetaData(
- @ApiParam(value = "A JSON value representing the rule chain metadata.")
+ @Parameter(description = "A JSON value representing the rule chain metadata.")
@RequestBody RuleChainMetaData ruleChainMetaData,
- @ApiParam(value = "Update related rule nodes.")
+ @Parameter(description = "Update related rule nodes.")
@RequestParam(value = "updateRelated", required = false, defaultValue = "true") boolean updateRelated
) throws Exception {
TenantId tenantId = getTenantId();
@@ -291,17 +291,17 @@ public class RuleChainController extends BaseController {
@RequestMapping(value = "/ruleChains", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getRuleChains(
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = RULE_CHAIN_TYPE_DESCRIPTION, allowableValues = RULE_CHAIN_TYPES_ALLOWABLE_VALUES)
+ @Parameter(description = RULE_CHAIN_TYPE_DESCRIPTION, schema = @Schema(allowableValues = {"CORE", "EDGE"}))
@RequestParam(value = "type", required = false) String typeStr,
- @ApiParam(value = RULE_CHAIN_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = RULE_CHAIN_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RULE_CHAIN_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "root"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
@@ -319,7 +319,7 @@ public class RuleChainController extends BaseController {
@RequestMapping(value = "/ruleChain/{ruleChainId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
public void deleteRuleChain(
- @ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
+ @Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
@@ -334,7 +334,7 @@ public class RuleChainController extends BaseController {
@RequestMapping(value = "/ruleNode/{ruleNodeId}/debugIn", method = RequestMethod.GET)
@ResponseBody
public JsonNode getLatestRuleNodeDebugInput(
- @ApiParam(value = RULE_NODE_ID_PARAM_DESCRIPTION)
+ @Parameter(description = RULE_NODE_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_NODE_ID) String strRuleNodeId) throws ThingsboardException {
checkParameter(RULE_NODE_ID, strRuleNodeId);
RuleNodeId ruleNodeId = new RuleNodeId(toUUID(strRuleNodeId));
@@ -369,9 +369,9 @@ public class RuleChainController extends BaseController {
@RequestMapping(value = "/ruleChain/testScript", method = RequestMethod.POST)
@ResponseBody
public JsonNode testScript(
- @ApiParam(value = "Script language: JS or TBEL")
+ @Parameter(description = "Script language: JS or TBEL")
@RequestParam(required = false) ScriptLanguage scriptLang,
- @ApiParam(value = "Test JS request. See API call description above.")
+ @Parameter(description = "Test JS request. See API call description above.")
@RequestBody JsonNode inputParams) throws ThingsboardException, JsonProcessingException {
String script = inputParams.get("script").asText();
String scriptType = inputParams.get("scriptType").asText();
@@ -443,7 +443,7 @@ public class RuleChainController extends BaseController {
@RequestMapping(value = "/ruleChains/export", params = {"limit"}, method = RequestMethod.GET)
@ResponseBody
public RuleChainData exportRuleChains(
- @ApiParam(value = "A limit of rule chains to export.", required = true)
+ @Parameter(description = "A limit of rule chains to export.", required = true)
@RequestParam("limit") int limit) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
PageLink pageLink = new PageLink(limit);
@@ -455,19 +455,12 @@ public class RuleChainController extends BaseController {
@RequestMapping(value = "/ruleChains/import", method = RequestMethod.POST)
@ResponseBody
public List importRuleChains(
- @ApiParam(value = "A JSON value representing the rule chains.")
+ @Parameter(description = "A JSON value representing the rule chains.")
@RequestBody RuleChainData ruleChainData,
- @ApiParam(value = "Enables overwrite for existing rule chains with the same name.")
+ @Parameter(description = "Enables overwrite for existing rule chains with the same name.")
@RequestParam(required = false, defaultValue = "false") boolean overwrite) throws ThingsboardException {
TenantId tenantId = getCurrentUser().getTenantId();
- List importResults = ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite, tbRuleChainService::updateRuleNodeConfiguration);
- for (RuleChainImportResult importResult : importResults) {
- if (importResult.getError() == null) {
- tbClusterService.broadcastEntityStateChangeEvent(importResult.getTenantId(), importResult.getRuleChainId(),
- importResult.isUpdated() ? ComponentLifecycleEvent.UPDATED : ComponentLifecycleEvent.CREATED);
- }
- }
- return importResults;
+ return ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite, tbRuleChainService::updateRuleNodeConfiguration);
}
private String msgToOutput(TbMsg msg) throws Exception {
@@ -507,7 +500,7 @@ public class RuleChainController extends BaseController {
EDGE_ASSIGN_RECEIVE_STEP_DESCRIPTION +
"Third, once rule chain will be delivered to edge service, it's going to start processing messages locally. " +
"\n\nOnly rule chain with type 'EDGE' can be assigned to edge." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.POST)
@ResponseBody
@@ -530,7 +523,7 @@ public class RuleChainController extends BaseController {
"Second, remote edge service will receive an 'unassign' command to remove rule chain " +
EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION +
"Third, once 'unassign' command will be delivered to edge service, it's going to remove rule chain locally." + TENANT_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.DELETE)
@ResponseBody
@@ -552,17 +545,17 @@ public class RuleChainController extends BaseController {
@RequestMapping(value = "/edge/{edgeId}/ruleChains", params = {"pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
public PageData getEdgeRuleChains(
- @ApiParam(value = EDGE_ID_PARAM_DESCRIPTION, required = true)
+ @Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
@PathVariable(EDGE_ID) String strEdgeId,
- @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = RULE_CHAIN_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = RULE_CHAIN_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RULE_CHAIN_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "name", "root"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
checkParameter(EDGE_ID, strEdgeId);
TenantId tenantId = getCurrentUser().getTenantId();
@@ -578,7 +571,7 @@ public class RuleChainController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/{ruleChainId}/edgeTemplateRoot", method = RequestMethod.POST)
@ResponseBody
- public RuleChain setEdgeTemplateRootRuleChain(@ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
+ public RuleChain setEdgeTemplateRootRuleChain(@Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
@@ -592,7 +585,7 @@ public class RuleChainController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/{ruleChainId}/autoAssignToEdge", method = RequestMethod.POST)
@ResponseBody
- public RuleChain setAutoAssignToEdgeRuleChain(@ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
+ public RuleChain setAutoAssignToEdgeRuleChain(@Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
@@ -606,7 +599,7 @@ public class RuleChainController extends BaseController {
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/ruleChain/{ruleChainId}/autoAssignToEdge", method = RequestMethod.DELETE)
@ResponseBody
- public RuleChain unsetAutoAssignToEdgeRuleChain(@ApiParam(value = RULE_CHAIN_ID_PARAM_DESCRIPTION)
+ public RuleChain unsetAutoAssignToEdgeRuleChain(@Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
checkParameter(RULE_CHAIN_ID, strRuleChainId);
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId));
diff --git a/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java
index 01a7cae420..cb2033b29d 100644
--- a/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java
@@ -17,6 +17,8 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import io.swagger.v3.oas.annotations.Hidden;
+import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -40,14 +42,12 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.UserPrincipal;
import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService;
-import springfox.documentation.annotations.ApiIgnore;
-import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
-@ApiIgnore
+@Hidden
@RestController
@TbCoreComponent
@RequestMapping("/api")
diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
index 56c67f4fa5..bf59f78519 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
@@ -15,8 +15,10 @@
*/
package org.thingsboard.server.controller;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -47,6 +49,7 @@ import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.resource.TbResourceService;
import org.thingsboard.server.service.security.permission.Operation;
@@ -60,18 +63,14 @@ import java.util.Set;
import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER;
import static org.thingsboard.server.controller.ControllerConstants.LWM2M_OBJECT_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.LWM2M_OBJECT_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_ID_PARAM_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_INFO_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_TEXT_SEARCH_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_TYPE;
-import static org.thingsboard.server.controller.ControllerConstants.RESOURCE_TYPE_PROPERTY_ALLOWABLE_VALUES;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH;
@@ -93,7 +92,7 @@ public class TbResourceController extends BaseController {
@ApiOperation(value = "Download Resource (downloadResource)", notes = "Download Resource based on the provided Resource Id." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/resource/{resourceId}/download")
- public ResponseEntity downloadResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
+ public ResponseEntity downloadResource(@Parameter(description = RESOURCE_ID_PARAM_DESCRIPTION)
@PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException {
checkParameter(RESOURCE_ID, strResourceId);
TbResourceId resourceId = new TbResourceId(toUUID(strResourceId));
@@ -111,7 +110,7 @@ public class TbResourceController extends BaseController {
@ApiOperation(value = "Download LWM2M Resource (downloadLwm2mResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/resource/lwm2m/{resourceId}/download", produces = "application/xml")
- public ResponseEntity downloadLwm2mResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
+ public ResponseEntity downloadLwm2mResourceIfChanged(@Parameter(description = RESOURCE_ID_PARAM_DESCRIPTION)
@PathVariable(RESOURCE_ID) String strResourceId,
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException {
return downloadResourceIfChanged(ResourceType.LWM2M_MODEL, strResourceId, etag);
@@ -120,7 +119,7 @@ public class TbResourceController extends BaseController {
@ApiOperation(value = "Download PKCS_12 Resource (downloadPkcs12ResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/resource/pkcs12/{resourceId}/download", method = RequestMethod.GET, produces = "application/x-pkcs12")
- public ResponseEntity downloadPkcs12ResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
+ public ResponseEntity downloadPkcs12ResourceIfChanged(@Parameter(description = RESOURCE_ID_PARAM_DESCRIPTION)
@PathVariable(RESOURCE_ID) String strResourceId,
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException {
return downloadResourceIfChanged(ResourceType.PKCS_12, strResourceId, etag);
@@ -130,7 +129,7 @@ public class TbResourceController extends BaseController {
notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/resource/jks/{resourceId}/download", produces = "application/x-java-keystore")
- public ResponseEntity downloadJksResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
+ public ResponseEntity downloadJksResourceIfChanged(@Parameter(description = RESOURCE_ID_PARAM_DESCRIPTION)
@PathVariable(RESOURCE_ID) String strResourceId,
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException {
return downloadResourceIfChanged(ResourceType.JKS, strResourceId, etag);
@@ -139,7 +138,7 @@ public class TbResourceController extends BaseController {
@ApiOperation(value = "Download JS Resource (downloadJsResourceIfChanged)", notes = DOWNLOAD_RESOURCE_IF_NOT_CHANGED + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/resource/js/{resourceId}/download", produces = "application/javascript")
- public ResponseEntity downloadJsResourceIfChanged(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
+ public ResponseEntity downloadJsResourceIfChanged(@Parameter(description = RESOURCE_ID_PARAM_DESCRIPTION)
@PathVariable(RESOURCE_ID) String strResourceId,
@RequestHeader(name = HttpHeaders.IF_NONE_MATCH, required = false) String etag) throws ThingsboardException {
return downloadResourceIfChanged(ResourceType.JS_MODULE, strResourceId, etag);
@@ -148,10 +147,10 @@ public class TbResourceController extends BaseController {
@ApiOperation(value = "Get Resource Info (getResourceInfoById)",
notes = "Fetch the Resource Info object based on the provided Resource Id. " +
RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/resource/info/{resourceId}")
- public TbResourceInfo getResourceInfoById(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
+ public TbResourceInfo getResourceInfoById(@Parameter(description = RESOURCE_ID_PARAM_DESCRIPTION)
@PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException {
checkParameter(RESOURCE_ID, strResourceId);
TbResourceId resourceId = new TbResourceId(toUUID(strResourceId));
@@ -161,11 +160,11 @@ public class TbResourceController extends BaseController {
@ApiOperation(value = "Get Resource (getResourceById)",
notes = "Fetch the Resource object based on the provided Resource Id. " +
RESOURCE_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json", hidden = true)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)), hidden = true)
@Deprecated // resource's data should be fetched with a download request
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/resource/{resourceId}")
- public TbResource getResourceById(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
+ public TbResource getResourceById(@Parameter(description = RESOURCE_ID_PARAM_DESCRIPTION)
@PathVariable(RESOURCE_ID) String strResourceId) throws ThingsboardException {
checkParameter(RESOURCE_ID, strResourceId);
TbResourceId resourceId = new TbResourceId(toUUID(strResourceId));
@@ -180,11 +179,10 @@ public class TbResourceController extends BaseController {
"\n\nResource combination of the title with the key is unique in the scope of tenant. " +
"Remove 'id', 'tenantId' from the request body example (below) to create new Resource entity." +
SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json",
- consumes = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@PostMapping(value = "/resource")
- public TbResourceInfo saveResource(@ApiParam(value = "A JSON value representing the Resource.")
+ public TbResourceInfo saveResource(@Parameter(description = "A JSON value representing the Resource.")
@RequestBody TbResource resource) throws Exception {
resource.setTenantId(getTenantId());
checkEntity(resource.getId(), resource, Resource.TB_RESOURCE);
@@ -194,20 +192,20 @@ public class TbResourceController extends BaseController {
@ApiOperation(value = "Get Resource Infos (getResources)",
notes = "Returns a page of Resource Info objects owned by tenant or sysadmin. " +
PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/resource")
- public PageData getResources(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ public PageData getResources(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = RESOURCE_TYPE, allowableValues = RESOURCE_TYPE_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = RESOURCE_TYPE, schema = @Schema(allowableValues = {"LWM2M_MODEL", "JKS", "PKCS_12", "JS_MODULE"}))
@RequestParam(required = false) String resourceType,
- @ApiParam(value = RESOURCE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = RESOURCE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "title", "resourceType", "tenantId"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TbResourceInfoFilter.TbResourceInfoFilterBuilder filter = TbResourceInfoFilter.builder();
@@ -230,18 +228,18 @@ public class TbResourceController extends BaseController {
@ApiOperation(value = "Get All Resource Infos (getAllResources)",
notes = "Returns a page of Resource Info objects owned by tenant. " +
PAGE_DATA_PARAMETERS + RESOURCE_INFO_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@GetMapping(value = "/resource/tenant")
- public PageData getTenantResources(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ public PageData getTenantResources(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = RESOURCE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = RESOURCE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = RESOURCE_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"createdTime", "title", "resourceType", "tenantId"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
TbResourceInfoFilter filter = TbResourceInfoFilter.builder()
@@ -254,18 +252,18 @@ public class TbResourceController extends BaseController {
@ApiOperation(value = "Get LwM2M Objects (getLwm2mListObjectsPage)",
notes = "Returns a page of LwM2M objects parsed from Resources with type 'LWM2M_MODEL' owned by tenant or sysadmin. " +
PAGE_DATA_PARAMETERS + LWM2M_OBJECT_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@GetMapping(value = "/resource/lwm2m/page")
- public List getLwm2mListObjectsPage(@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true)
+ public List getLwm2mListObjectsPage(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
- @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true)
+ @Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
- @ApiParam(value = RESOURCE_TEXT_SEARCH_DESCRIPTION)
+ @Parameter(description = RESOURCE_TEXT_SEARCH_DESCRIPTION)
@RequestParam(required = false) String textSearch,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = LWM2M_OBJECT_SORT_PROPERTY_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"id", "name"}))
@RequestParam(required = false) String sortProperty,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
PageLink pageLink = new PageLink(pageSize, page, textSearch);
return checkNotNull(tbResourceService.findLwM2mObjectPage(getTenantId(), sortProperty, sortOrder, pageLink));
@@ -274,14 +272,14 @@ public class TbResourceController extends BaseController {
@ApiOperation(value = "Get LwM2M Objects (getLwm2mListObjects)",
notes = "Returns a page of LwM2M objects parsed from Resources with type 'LWM2M_MODEL' owned by tenant or sysadmin. " +
"You can specify parameters to filter the results. " + LWM2M_OBJECT_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH,
- produces = "application/json")
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@GetMapping(value = "/resource/lwm2m")
- public List getLwm2mListObjects(@ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES, required = true)
+ public List getLwm2mListObjects(@Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}, required = true))
@RequestParam String sortOrder,
- @ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = LWM2M_OBJECT_SORT_PROPERTY_ALLOWABLE_VALUES, required = true)
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"id", "name"}, required = true))
@RequestParam String sortProperty,
- @ApiParam(value = "LwM2M Object ids.", required = true)
+ @Parameter(description = "LwM2M Object ids.", required = true)
@RequestParam(required = false) String[] objectIds) throws ThingsboardException {
return checkNotNull(tbResourceService.findLwM2mObject(getTenantId(), sortOrder, sortProperty, objectIds));
}
@@ -290,7 +288,7 @@ public class TbResourceController extends BaseController {
notes = "Deletes the Resource. Referencing non-existing Resource Id will cause an error." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@DeleteMapping(value = "/resource/{resourceId}")
- public void deleteResource(@ApiParam(value = RESOURCE_ID_PARAM_DESCRIPTION)
+ public void deleteResource(@Parameter(description = RESOURCE_ID_PARAM_DESCRIPTION)
@PathVariable("resourceId") String strResourceId) throws ThingsboardException {
checkParameter(RESOURCE_ID, strResourceId);
TbResourceId resourceId = new TbResourceId(toUUID(strResourceId));
diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
index 288828f046..9ca600603c 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
@@ -24,10 +24,14 @@ import com.google.common.util.concurrent.MoreExecutors;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-import io.swagger.annotations.ApiResponse;
-import io.swagger.annotations.ApiResponses;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import jakarta.annotation.Nullable;
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -48,7 +52,8 @@ import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.kv.AggregationParams;
import org.thingsboard.server.common.data.kv.IntervalType;
import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg;
-import org.thingsboard.server.common.data.DataConstants;
+import org.thingsboard.server.common.adaptor.JsonConverter;
+import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.TenantProfile;
@@ -76,7 +81,8 @@ import org.thingsboard.server.common.data.kv.ReadTsKvQuery;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
-import org.thingsboard.server.common.adaptor.JsonConverter;
+import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg;
+import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.exception.InvalidParametersException;
import org.thingsboard.server.exception.UncheckedApiException;
@@ -87,9 +93,6 @@ import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.telemetry.AttributeData;
import org.thingsboard.server.service.telemetry.TsData;
-import javax.annotation.Nullable;
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
@@ -102,8 +105,6 @@ import java.util.stream.Collectors;
import static org.thingsboard.server.controller.ControllerConstants.ATTRIBUTES_JSON_REQUEST_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ATTRIBUTES_KEYS_DESCRIPTION;
-import static org.thingsboard.server.controller.ControllerConstants.ATTRIBUTES_SAVE_SCOPE_ALLOWED_VALUES;
-import static org.thingsboard.server.controller.ControllerConstants.ATTRIBUTES_SCOPE_ALLOWED_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.ATTRIBUTES_SCOPE_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.ATTRIBUTE_DATA_EXAMPLE;
import static org.thingsboard.server.controller.ControllerConstants.DEVICE_ID;
@@ -128,7 +129,6 @@ import static org.thingsboard.server.controller.ControllerConstants.SAVE_ENTITY_
import static org.thingsboard.server.controller.ControllerConstants.SAVE_ENTITY_TIMESERIES_STATUS_OK;
import static org.thingsboard.server.controller.ControllerConstants.SAVE_ENTITY_TIMESERIES_STATUS_UNAUTHORIZED;
import static org.thingsboard.server.controller.ControllerConstants.SAVE_TIMESERIES_REQUEST_PAYLOAD;
-import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES;
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.STRICT_DATA_TYPES_DESCRIPTION;
import static org.thingsboard.server.controller.ControllerConstants.TELEMETRY_JSON_REQUEST_DESCRIPTION;
@@ -178,13 +178,13 @@ public class TelemetryController extends BaseController {
"\n * CLIENT_SCOPE - supported for devices;" +
"\n * SHARED_SCOPE - supported for devices. "
+ "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/keys/attributes", method = RequestMethod.GET)
@ResponseBody
public DeferredResult getAttributeKeys(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException {
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr, this::getAttributeKeysCallback);
}
@@ -194,14 +194,14 @@ public class TelemetryController extends BaseController {
"\n * CLIENT_SCOPE - supported for devices;" +
"\n * SHARED_SCOPE - supported for devices. "
+ "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/keys/attributes/{scope}", method = RequestMethod.GET)
@ResponseBody
public DeferredResult getAttributeKeysByScope(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
- @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope) throws ThingsboardException {
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
+ @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"})) @PathVariable("scope") AttributeScope scope) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeKeysCallback(result, tenantId, entityId, scope));
}
@@ -213,14 +213,14 @@ public class TelemetryController extends BaseController {
+ ATTRIBUTE_DATA_EXAMPLE
+ MARKDOWN_CODE_BLOCK_END
+ "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/values/attributes", method = RequestMethod.GET)
@ResponseBody
public DeferredResult getAttributes(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
- @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
+ @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr));
@@ -236,15 +236,15 @@ public class TelemetryController extends BaseController {
+ ATTRIBUTE_DATA_EXAMPLE
+ MARKDOWN_CODE_BLOCK_END
+ "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/values/attributes/{scope}", method = RequestMethod.GET)
@ResponseBody
public DeferredResult getAttributesByScope(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
- @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope,
- @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
+ @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, required = true)) @PathVariable("scope") AttributeScope scope,
+ @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
(result, tenantId, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr));
@@ -253,13 +253,13 @@ public class TelemetryController extends BaseController {
@ApiOperation(value = "Get time-series keys (getTimeseriesKeys)",
notes = "Returns a set of unique time-series key names for the selected entity. " +
"\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/keys/timeseries", method = RequestMethod.GET)
@ResponseBody
public DeferredResult getTimeseriesKeys(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException {
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
(result, tenantId, entityId) -> Futures.addCallback(tsService.findAllLatest(tenantId, entityId), getTsKeysToResponseCallback(result), MoreExecutors.directExecutor()));
}
@@ -276,15 +276,15 @@ public class TelemetryController extends BaseController {
+ LATEST_TS_STRICT_DATA_EXAMPLE
+ MARKDOWN_CODE_BLOCK_END
+ "\n\n " + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET)
@ResponseBody
public DeferredResult getLatestTimeseries(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
- @ApiParam(value = TELEMETRY_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr,
- @ApiParam(value = STRICT_DATA_TYPES_DESCRIPTION)
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
+ @Parameter(description = TELEMETRY_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr,
+ @Parameter(description = STRICT_DATA_TYPES_DESCRIPTION)
@RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException {
SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
@@ -300,34 +300,34 @@ public class TelemetryController extends BaseController {
+ TS_STRICT_DATA_EXAMPLE
+ MARKDOWN_CODE_BLOCK_END
+ "\n\n" + INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/values/timeseries", method = RequestMethod.GET, params = {"keys", "startTs", "endTs"})
@ResponseBody
public DeferredResult getTimeseries(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
- @ApiParam(value = TELEMETRY_KEYS_BASE_DESCRIPTION, required = true) @RequestParam(name = "keys") String keys,
- @ApiParam(value = "A long value representing the start timestamp of the time range in milliseconds, UTC.")
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
+ @Parameter(description = TELEMETRY_KEYS_BASE_DESCRIPTION, required = true) @RequestParam(name = "keys") String keys,
+ @Parameter(description = "A long value representing the start timestamp of the time range in milliseconds, UTC.")
@RequestParam(name = "startTs") Long startTs,
- @ApiParam(value = "A long value representing the end timestamp of the time range in milliseconds, UTC.")
+ @Parameter(description = "A long value representing the end timestamp of the time range in milliseconds, UTC.")
@RequestParam(name = "endTs") Long endTs,
- @ApiParam(value = "A string value representing the type fo the interval.", allowableValues = "MILLISECONDS, WEEK, WEEK_ISO, MONTH, QUARTER")
+ @Parameter(description = "A string value representing the type fo the interval.", schema = @Schema(allowableValues = {"MILLISECONDS", "WEEK", "WEEK_ISO", "MONTH", "QUARTER"}))
@RequestParam(name = "intervalType", required = false) IntervalType intervalType,
- @ApiParam(value = "A long value representing the aggregation interval range in milliseconds.")
+ @Parameter(description = "A long value representing the aggregation interval range in milliseconds.")
@RequestParam(name = "interval", defaultValue = "0") Long interval,
- @ApiParam(value = "A string value representing the timezone that will be used to calculate exact timestamps for 'WEEK', 'WEEK_ISO', 'MONTH' and 'QUARTER' interval types.")
+ @Parameter(description = "A string value representing the timezone that will be used to calculate exact timestamps for 'WEEK', 'WEEK_ISO', 'MONTH' and 'QUARTER' interval types.")
@RequestParam(name = "timeZone", required = false) String timeZone,
- @ApiParam(value = "An integer value that represents a max number of timeseries data points to fetch." +
- " This parameter is used only in the case if 'agg' parameter is set to 'NONE'.", defaultValue = "100")
+ @Parameter(description = "An integer value that represents a max number of timeseries data points to fetch." +
+ " This parameter is used only in the case if 'agg' parameter is set to 'NONE'.", schema = @Schema(defaultValue = "100"))
@RequestParam(name = "limit", defaultValue = "100") Integer limit,
- @ApiParam(value = "A string value representing the aggregation function. " +
+ @Parameter(description = "A string value representing the aggregation function. " +
"If the interval is not specified, 'agg' parameter will use 'NONE' value.",
- allowableValues = "MIN, MAX, AVG, SUM, COUNT, NONE")
+ schema = @Schema(allowableValues = {"MIN", "MAX", "AVG", "SUM", "COUNT", "NONE"}))
@RequestParam(name = "agg", defaultValue = "NONE") String aggStr,
- @ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES)
+ @Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}))
@RequestParam(name = "orderBy", defaultValue = "DESC") String orderBy,
- @ApiParam(value = STRICT_DATA_TYPES_DESCRIPTION)
+ @Parameter(description = STRICT_DATA_TYPES_DESCRIPTION)
@RequestParam(name = "useStrictDataTypes", required = false, defaultValue = "false") Boolean useStrictDataTypes) throws ThingsboardException {
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_TELEMETRY, entityType, entityIdStr,
(result, tenantId, entityId) -> {
@@ -349,23 +349,23 @@ public class TelemetryController extends BaseController {
notes = "Creates or updates the device attributes based on device id and specified attribute scope. " +
SAVE_ATTRIBUTES_REQUEST_PAYLOAD
+ TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@ApiResponses(value = {
- @ApiResponse(code = 200, message = SAVE_ATTIRIBUTES_STATUS_OK +
+ @ApiResponse(responseCode = "200", description = SAVE_ATTIRIBUTES_STATUS_OK +
"Platform creates an audit log event about device attributes updates with action type 'ATTRIBUTES_UPDATED', " +
"and also sends event msg to the rule engine with msg type 'ATTRIBUTES_UPDATED'."),
- @ApiResponse(code = 400, message = SAVE_ATTIRIBUTES_STATUS_BAD_REQUEST),
- @ApiResponse(code = 401, message = "User is not authorized to save device attributes for selected device. Most likely, User belongs to different Customer or Tenant."),
- @ApiResponse(code = 500, message = "The exception was thrown during processing the request. " +
+ @ApiResponse(responseCode = "400", description = SAVE_ATTIRIBUTES_STATUS_BAD_REQUEST),
+ @ApiResponse(responseCode = "401", description = "User is not authorized to save device attributes for selected device. Most likely, User belongs to different Customer or Tenant."),
+ @ApiResponse(responseCode = "500", description = "The exception was thrown during processing the request. " +
"Platform creates an audit log event about device attributes updates with action type 'ATTRIBUTES_UPDATED' that includes an error stacktrace."),
})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult saveDeviceAttributes(
- @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("deviceId") String deviceIdStr,
- @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SAVE_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope,
- @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException {
+ @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("deviceId") String deviceIdStr,
+ @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, required = true)) @PathVariable("scope") AttributeScope scope,
+ @Parameter(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr);
return saveAttributes(getTenantId(), entityId, scope, request);
}
@@ -375,21 +375,21 @@ public class TelemetryController extends BaseController {
ENTITY_SAVE_ATTRIBUTE_SCOPES +
SAVE_ATTRIBUTES_REQUEST_PAYLOAD
+ INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@ApiResponses(value = {
- @ApiResponse(code = 200, message = SAVE_ATTIRIBUTES_STATUS_OK + SAVE_ENTITY_ATTRIBUTES_STATUS_OK),
- @ApiResponse(code = 400, message = SAVE_ATTIRIBUTES_STATUS_BAD_REQUEST),
- @ApiResponse(code = 401, message = SAVE_ENTITY_ATTRIBUTES_STATUS_UNAUTHORIZED),
- @ApiResponse(code = 500, message = SAVE_ENTITY_ATTRIBUTES_STATUS_INTERNAL_SERVER_ERROR),
+ @ApiResponse(responseCode = "200", description = SAVE_ATTIRIBUTES_STATUS_OK + SAVE_ENTITY_ATTRIBUTES_STATUS_OK),
+ @ApiResponse(responseCode = "400", description = SAVE_ATTIRIBUTES_STATUS_BAD_REQUEST),
+ @ApiResponse(responseCode = "401", description = SAVE_ENTITY_ATTRIBUTES_STATUS_UNAUTHORIZED),
+ @ApiResponse(responseCode = "500", description = SAVE_ENTITY_ATTRIBUTES_STATUS_INTERNAL_SERVER_ERROR),
})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/{scope}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult saveEntityAttributesV1(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
- @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SAVE_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope,
- @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException {
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
+ @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"})) @PathVariable("scope")AttributeScope scope,
+ @Parameter(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveAttributes(getTenantId(), entityId, scope, request);
}
@@ -399,21 +399,21 @@ public class TelemetryController extends BaseController {
ENTITY_SAVE_ATTRIBUTE_SCOPES +
SAVE_ATTRIBUTES_REQUEST_PAYLOAD
+ INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@ApiResponses(value = {
- @ApiResponse(code = 200, message = SAVE_ATTIRIBUTES_STATUS_OK + SAVE_ENTITY_ATTRIBUTES_STATUS_OK),
- @ApiResponse(code = 400, message = SAVE_ATTIRIBUTES_STATUS_BAD_REQUEST),
- @ApiResponse(code = 401, message = SAVE_ENTITY_ATTRIBUTES_STATUS_UNAUTHORIZED),
- @ApiResponse(code = 500, message = SAVE_ENTITY_ATTRIBUTES_STATUS_INTERNAL_SERVER_ERROR),
+ @ApiResponse(responseCode = "200", description = SAVE_ATTIRIBUTES_STATUS_OK + SAVE_ENTITY_ATTRIBUTES_STATUS_OK),
+ @ApiResponse(responseCode = "400", description = SAVE_ATTIRIBUTES_STATUS_BAD_REQUEST),
+ @ApiResponse(responseCode = "401", description = SAVE_ENTITY_ATTRIBUTES_STATUS_UNAUTHORIZED),
+ @ApiResponse(responseCode = "500", description = SAVE_ENTITY_ATTRIBUTES_STATUS_INTERNAL_SERVER_ERROR),
})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/attributes/{scope}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult saveEntityAttributesV2(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
- @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SAVE_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope,
- @ApiParam(value = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException {
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
+ @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, required = true)) @PathVariable("scope")AttributeScope scope,
+ @Parameter(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveAttributes(getTenantId(), entityId, scope, request);
}
@@ -424,21 +424,21 @@ public class TelemetryController extends BaseController {
SAVE_TIMESERIES_REQUEST_PAYLOAD +
"\n\n The scope parameter is not used in the API call implementation but should be specified whatever value because it is used as a path variable. "
+ INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@ApiResponses(value = {
- @ApiResponse(code = 200, message = SAVE_ENTITY_TIMESERIES_STATUS_OK),
- @ApiResponse(code = 400, message = INVALID_STRUCTURE_OF_THE_REQUEST),
- @ApiResponse(code = 401, message = SAVE_ENTITY_TIMESERIES_STATUS_UNAUTHORIZED),
- @ApiResponse(code = 500, message = SAVE_ENTITY_TIMESERIES_STATUS_INTERNAL_SERVER_ERROR),
+ @ApiResponse(responseCode = "200", description = SAVE_ENTITY_TIMESERIES_STATUS_OK),
+ @ApiResponse(responseCode = "400", description = INVALID_STRUCTURE_OF_THE_REQUEST),
+ @ApiResponse(responseCode = "401", description = SAVE_ENTITY_TIMESERIES_STATUS_UNAUTHORIZED),
+ @ApiResponse(responseCode = "500", description = SAVE_ENTITY_TIMESERIES_STATUS_INTERNAL_SERVER_ERROR),
})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/timeseries/{scope}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult saveEntityTelemetry(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
- @ApiParam(value = TELEMETRY_SCOPE_DESCRIPTION, required = true, allowableValues = "ANY") @PathVariable("scope") String scope,
- @ApiParam(value = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody String requestBody) throws ThingsboardException {
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
+ @Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope")String scope,
+ @Parameter(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveTelemetry(getTenantId(), entityId, requestBody, 0L);
}
@@ -449,22 +449,22 @@ public class TelemetryController extends BaseController {
"\n\n The scope parameter is not used in the API call implementation but should be specified whatever value because it is used as a path variable. "
+ "\n\nThe ttl parameter takes affect only in case of Cassandra DB."
+ INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@ApiResponses(value = {
- @ApiResponse(code = 200, message = SAVE_ENTITY_TIMESERIES_STATUS_OK),
- @ApiResponse(code = 400, message = INVALID_STRUCTURE_OF_THE_REQUEST),
- @ApiResponse(code = 401, message = SAVE_ENTITY_TIMESERIES_STATUS_UNAUTHORIZED),
- @ApiResponse(code = 500, message = SAVE_ENTITY_TIMESERIES_STATUS_INTERNAL_SERVER_ERROR),
+ @ApiResponse(responseCode = "200", description = SAVE_ENTITY_TIMESERIES_STATUS_OK),
+ @ApiResponse(responseCode = "400", description = INVALID_STRUCTURE_OF_THE_REQUEST),
+ @ApiResponse(responseCode = "401", description = SAVE_ENTITY_TIMESERIES_STATUS_UNAUTHORIZED),
+ @ApiResponse(responseCode = "500", description = SAVE_ENTITY_TIMESERIES_STATUS_INTERNAL_SERVER_ERROR),
})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/timeseries/{scope}/{ttl}", method = RequestMethod.POST)
@ResponseBody
public DeferredResult saveEntityTelemetryWithTTL(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
- @ApiParam(value = TELEMETRY_SCOPE_DESCRIPTION, required = true, allowableValues = "ANY") @PathVariable("scope") String scope,
- @ApiParam(value = "A long value representing TTL (Time to Live) parameter.", required = true) @PathVariable("ttl") Long ttl,
- @ApiParam(value = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody String requestBody) throws ThingsboardException {
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
+ @Parameter(description = TELEMETRY_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = "ANY")) @PathVariable("scope")AttributeScope scope,
+ @Parameter(description = "A long value representing TTL (Time to Live) parameter.", required = true)@PathVariable("ttl")Long ttl,
+ @Parameter(description = TELEMETRY_JSON_REQUEST_DESCRIPTION, required = true)@RequestBody String requestBody) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveTelemetry(getTenantId(), entityId, requestBody, ttl);
}
@@ -477,31 +477,31 @@ public class TelemetryController extends BaseController {
" Use 'rewriteLatestIfDeleted' to rewrite latest value (stored in separate table for performance) if the value's timestamp matches the time-range and 'deleteLatest' param is true." +
" The replacement value will be fetched from the 'time-series' table, and its timestamp will be the most recent one before the defined time-range. " +
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@ApiResponses(value = {
- @ApiResponse(code = 200, message = "Timeseries for the selected keys in the request was removed. " +
+ @ApiResponse(responseCode = "200", description = "Timeseries for the selected keys in the request was removed. " +
"Platform creates an audit log event about entity timeseries removal with action type 'TIMESERIES_DELETED'."),
- @ApiResponse(code = 400, message = "Platform returns a bad request in case if keys list is empty or start and end timestamp values is empty when deleteAllDataForKeys is set to false."),
- @ApiResponse(code = 401, message = "User is not authorized to delete entity timeseries for selected entity. Most likely, User belongs to different Customer or Tenant."),
- @ApiResponse(code = 500, message = "The exception was thrown during processing the request. " +
+ @ApiResponse(responseCode = "400", description = "Platform returns a bad request in case if keys list is empty or start and end timestamp values is empty when deleteAllDataForKeys is set to false."),
+ @ApiResponse(responseCode = "401", description = "User is not authorized to delete entity timeseries for selected entity. Most likely, User belongs to different Customer or Tenant."),
+ @ApiResponse(responseCode = "500", description = "The exception was thrown during processing the request. " +
"Platform creates an audit log event about entity timeseries removal with action type 'TIMESERIES_DELETED' that includes an error stacktrace."),
})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/timeseries/delete", method = RequestMethod.DELETE)
@ResponseBody
public DeferredResult deleteEntityTimeseries(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
- @ApiParam(value = TELEMETRY_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr,
- @ApiParam(value = "A boolean value to specify if should be deleted all data for selected keys or only data that are in the selected time range.")
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
+ @Parameter(description = TELEMETRY_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr,
+ @Parameter(description = "A boolean value to specify if should be deleted all data for selected keys or only data that are in the selected time range.")
@RequestParam(name = "deleteAllDataForKeys", defaultValue = "false") boolean deleteAllDataForKeys,
- @ApiParam(value = "A long value representing the start timestamp of removal time range in milliseconds.")
+ @Parameter(description = "A long value representing the start timestamp of removal time range in milliseconds.")
@RequestParam(name = "startTs", required = false) Long startTs,
- @ApiParam(value = "A long value representing the end timestamp of removal time range in milliseconds.")
+ @Parameter(description = "A long value representing the end timestamp of removal time range in milliseconds.")
@RequestParam(name = "endTs", required = false) Long endTs,
- @ApiParam(value = "If the parameter is set to true, the latest telemetry can be removed, otherwise, in case that parameter is set to false the latest value will not removed.")
+ @Parameter(description = "If the parameter is set to true, the latest telemetry can be removed, otherwise, in case that parameter is set to false the latest value will not removed.")
@RequestParam(name = "deleteLatest", required = false, defaultValue = "true") boolean deleteLatest,
- @ApiParam(value = "If the parameter is set to true, the latest telemetry will be rewritten in case that current latest value was removed, otherwise, in case that parameter is set to false the new latest value will not set.")
+ @Parameter(description = "If the parameter is set to true, the latest telemetry will be rewritten in case that current latest value was removed, otherwise, in case that parameter is set to false the new latest value will not set.")
@RequestParam(name = "rewriteLatestIfDeleted", defaultValue = "false") boolean rewriteLatestIfDeleted) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return deleteTimeseries(entityId, keysStr, deleteAllDataForKeys, startTs, endTs, rewriteLatestIfDeleted, deleteLatest);
@@ -553,22 +553,22 @@ public class TelemetryController extends BaseController {
@ApiOperation(value = "Delete device attributes (deleteDeviceAttributes)",
notes = "Delete device attributes using provided Device Id, scope and a list of keys. " +
"Referencing a non-existing Device Id will cause an error" + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@ApiResponses(value = {
- @ApiResponse(code = 200, message = "Device attributes was removed for the selected keys in the request. " +
+ @ApiResponse(responseCode = "200", description = "Device attributes was removed for the selected keys in the request. " +
"Platform creates an audit log event about device attributes removal with action type 'ATTRIBUTES_DELETED'."),
- @ApiResponse(code = 400, message = "Platform returns a bad request in case if keys or scope are not specified."),
- @ApiResponse(code = 401, message = "User is not authorized to delete device attributes for selected entity. Most likely, User belongs to different Customer or Tenant."),
- @ApiResponse(code = 500, message = "The exception was thrown during processing the request. " +
+ @ApiResponse(responseCode = "400", description = "Platform returns a bad request in case if keys or scope are not specified."),
+ @ApiResponse(responseCode = "401", description = "User is not authorized to delete device attributes for selected entity. Most likely, User belongs to different Customer or Tenant."),
+ @ApiResponse(responseCode = "500", description = "The exception was thrown during processing the request. " +
"Platform creates an audit log event about device attributes removal with action type 'ATTRIBUTES_DELETED' that includes an error stacktrace."),
})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{deviceId}/{scope}", method = RequestMethod.DELETE)
@ResponseBody
public DeferredResult deleteDeviceAttributes(
- @ApiParam(value = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(DEVICE_ID) String deviceIdStr,
- @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES, required = true) @PathVariable("scope") String scope,
- @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException {
+ @Parameter(description = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(DEVICE_ID) String deviceIdStr,
+ @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, required = true)) @PathVariable("scope")AttributeScope scope,
+ @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true)@RequestParam(name = "keys")String keysStr) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr);
return deleteAttributes(entityId, scope, keysStr);
}
@@ -576,64 +576,58 @@ public class TelemetryController extends BaseController {
@ApiOperation(value = "Delete entity attributes (deleteEntityAttributes)",
notes = "Delete entity attributes using provided Entity Id, scope and a list of keys. " +
INVALID_ENTITY_ID_OR_ENTITY_TYPE_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH,
- produces = MediaType.APPLICATION_JSON_VALUE)
+ responses = @ApiResponse(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)))
@ApiResponses(value = {
- @ApiResponse(code = 200, message = "Entity attributes was removed for the selected keys in the request. " +
+ @ApiResponse(responseCode = "200", description = "Entity attributes was removed for the selected keys in the request. " +
"Platform creates an audit log event about entity attributes removal with action type 'ATTRIBUTES_DELETED'."),
- @ApiResponse(code = 400, message = "Platform returns a bad request in case if keys or scope are not specified."),
- @ApiResponse(code = 401, message = "User is not authorized to delete entity attributes for selected entity. Most likely, User belongs to different Customer or Tenant."),
- @ApiResponse(code = 500, message = "The exception was thrown during processing the request. " +
+ @ApiResponse(responseCode = "400", description = "Platform returns a bad request in case if keys or scope are not specified."),
+ @ApiResponse(responseCode = "401", description = "User is not authorized to delete entity attributes for selected entity. Most likely, User belongs to different Customer or Tenant."),
+ @ApiResponse(responseCode = "500", description = "The exception was thrown during processing the request. " +
"Platform creates an audit log event about entity attributes removal with action type 'ATTRIBUTES_DELETED' that includes an error stacktrace."),
})
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/{entityType}/{entityId}/{scope}", method = RequestMethod.DELETE)
@ResponseBody
public DeferredResult deleteEntityAttributes(
- @ApiParam(value = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, defaultValue = "DEVICE") @PathVariable("entityType") String entityType,
- @ApiParam(value = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
- @ApiParam(value = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, allowableValues = ATTRIBUTES_SCOPE_ALLOWED_VALUES) @PathVariable("scope") String scope,
- @ApiParam(value = ATTRIBUTES_KEYS_DESCRIPTION, required = true) @RequestParam(name = "keys") String keysStr) throws ThingsboardException {
+ @Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
+ @Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
+ @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, required = true, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"})) @PathVariable("scope")AttributeScope scope,
+ @Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true)@RequestParam(name = "keys")String keysStr) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return deleteAttributes(entityId, scope, keysStr);
}
- private DeferredResult deleteAttributes(EntityId entityIdSrc, String scope, String keysStr) throws ThingsboardException {
+ private DeferredResult deleteAttributes(EntityId entityIdSrc, AttributeScope scope, String keysStr) throws ThingsboardException {
List keys = toKeysList(keysStr);
if (keys.isEmpty()) {
return getImmediateDeferredResult("Empty keys: " + keysStr, HttpStatus.BAD_REQUEST);
}
SecurityUser user = getCurrentUser();
- if (DataConstants.SERVER_SCOPE.equals(scope) ||
- DataConstants.SHARED_SCOPE.equals(scope) ||
- DataConstants.CLIENT_SCOPE.equals(scope)) {
- return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> {
- tsSubService.deleteAndNotify(tenantId, entityId, scope, keys, new FutureCallback() {
- @Override
- public void onSuccess(@Nullable Void tmp) {
- logAttributesDeleted(user, entityId, scope, keys, null);
- if (entityIdSrc.getEntityType().equals(EntityType.DEVICE)) {
- DeviceId deviceId = new DeviceId(entityId.getId());
- tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(
- user.getTenantId(), deviceId, scope, keys), null);
- }
- result.setResult(new ResponseEntity<>(HttpStatus.OK));
+ return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.WRITE_ATTRIBUTES, entityIdSrc, (result, tenantId, entityId) -> {
+ tsSubService.deleteAndNotify(tenantId, entityId, scope.name(), keys, new FutureCallback() {
+ @Override
+ public void onSuccess(@Nullable Void tmp) {
+ logAttributesDeleted(user, entityId, scope, keys, null);
+ if (entityIdSrc.getEntityType().equals(EntityType.DEVICE)) {
+ DeviceId deviceId = new DeviceId(entityId.getId());
+ tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(
+ user.getTenantId(), deviceId, scope.name(), keys), null);
}
+ result.setResult(new ResponseEntity<>(HttpStatus.OK));
+ }
- @Override
- public void onFailure(Throwable t) {
- logAttributesDeleted(user, entityId, scope, keys, t);
- result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
- }
- });
+ @Override
+ public void onFailure(Throwable t) {
+ logAttributesDeleted(user, entityId, scope, keys, t);
+ result.setResult(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
+ }
});
- } else {
- return getImmediateDeferredResult("Invalid attribute scope: " + scope, HttpStatus.BAD_REQUEST);
- }
+ });
}
- private DeferredResult saveAttributes(TenantId srcTenantId, EntityId entityIdSrc, String scope, JsonNode json) throws ThingsboardException {
- if (!DataConstants.SERVER_SCOPE.equals(scope) && !DataConstants.SHARED_SCOPE.equals(scope)) {
+ private DeferredResult saveAttributes(TenantId srcTenantId, EntityId entityIdSrc, AttributeScope scope, JsonNode json) throws ThingsboardException {
+ if (AttributeScope.SERVER_SCOPE != scope && AttributeScope.SHARED_SCOPE != scope) {
return getImmediateDeferredResult("Invalid scope: " + scope, HttpStatus.BAD_REQUEST);
}
if (json.isObject()) {
@@ -722,10 +716,10 @@ public class TelemetryController extends BaseController {
Futures.addCallback(future, getTsKvListCallback(result, useStrictDataTypes), MoreExecutors.directExecutor());
}
- private void getAttributeValuesCallback(@Nullable DeferredResult result, SecurityUser user, EntityId entityId, String scope, String keys) {
+ private void getAttributeValuesCallback(@Nullable DeferredResult result, SecurityUser user, EntityId entityId, AttributeScope scope, String keys) {
List keyList = toKeysList(keys);
FutureCallback> callback = getAttributeValuesToResponseCallback(result, user, scope, entityId, keyList);
- if (!StringUtils.isEmpty(scope)) {
+ if (scope != null) {
if (keyList != null && !keyList.isEmpty()) {
Futures.addCallback(attributesService.find(user.getTenantId(), entityId, scope, keyList), callback, MoreExecutors.directExecutor());
} else {
@@ -733,7 +727,7 @@ public class TelemetryController extends BaseController {
}
} else {
List>> futures = new ArrayList<>();
- for (String tmpScope : DataConstants.allScopes()) {
+ for (AttributeScope tmpScope : AttributeScope.values()) {
if (keyList != null && !keyList.isEmpty()) {
futures.add(attributesService.find(user.getTenantId(), entityId, tmpScope, keyList));
} else {
@@ -747,13 +741,13 @@ public class TelemetryController extends BaseController {
}
}
- private void getAttributeKeysCallback(@Nullable DeferredResult result, TenantId tenantId, EntityId entityId, String scope) {
+ private void getAttributeKeysCallback(@Nullable DeferredResult result, TenantId tenantId, EntityId entityId, AttributeScope scope) {
Futures.addCallback(attributesService.findAll(tenantId, entityId, scope), getAttributeKeysToResponseCallback(result), MoreExecutors.directExecutor());
}
private void getAttributeKeysCallback(@Nullable DeferredResult result, TenantId tenantId, EntityId entityId) {
List>> futures = new ArrayList<>();
- for (String scope : DataConstants.allScopes()) {
+ for (AttributeScope scope : AttributeScope.values()) {
futures.add(attributesService.findAll(tenantId, entityId, scope));
}
@@ -796,7 +790,7 @@ public class TelemetryController extends BaseController {
}
private FutureCallback> getAttributeValuesToResponseCallback(final DeferredResult response,
- final SecurityUser user, final String scope,
+ final SecurityUser user, final AttributeScope scope,
final EntityId entityId, final List keyList) {
return new FutureCallback<>() {
@Override
@@ -838,28 +832,28 @@ public class TelemetryController extends BaseController {
}
private void logTimeseriesDeleted(SecurityUser user, EntityId entityId, List keys, long startTs, long endTs, Throwable e) {
- notificationEntityService.logEntityAction(user.getTenantId(), entityId, ActionType.TIMESERIES_DELETED, user,
+ logEntityActionService.logEntityAction(user.getTenantId(), entityId, ActionType.TIMESERIES_DELETED, user,
toException(e), keys, startTs, endTs);
}
private void logTelemetryUpdated(SecurityUser user, EntityId entityId, List telemetry, Throwable e) {
- notificationEntityService.logEntityAction(user.getTenantId(), entityId, ActionType.TIMESERIES_UPDATED, user,
+ logEntityActionService.logEntityAction(user.getTenantId(), entityId, ActionType.TIMESERIES_UPDATED, user,
toException(e), telemetry);
}
- private void logAttributesDeleted(SecurityUser user, EntityId entityId, String scope, List keys, Throwable e) {
- notificationEntityService.logEntityAction(user.getTenantId(), (UUIDBased & EntityId) entityId,
+ private void logAttributesDeleted(SecurityUser user, EntityId entityId, AttributeScope scope, List keys, Throwable e) {
+ logEntityActionService.logEntityAction(user.getTenantId(), (UUIDBased & EntityId) entityId,
ActionType.ATTRIBUTES_DELETED, user, toException(e), scope, keys);
}
- private void logAttributesUpdated(SecurityUser user, EntityId entityId, String scope, List attributes, Throwable e) {
- notificationEntityService.logEntityAction(user.getTenantId(), entityId, ActionType.ATTRIBUTES_UPDATED, user,
+ private void logAttributesUpdated(SecurityUser user, EntityId entityId, AttributeScope scope, List attributes, Throwable e) {
+ logEntityActionService.logEntityAction(user.getTenantId(), entityId, ActionType.ATTRIBUTES_UPDATED, user,
toException(e), scope, attributes);
}
- private void logAttributesRead(SecurityUser user, EntityId entityId, String scope, List keys, Throwable e) {
- notificationEntityService.logEntityAction(user.getTenantId(), entityId, ActionType.ATTRIBUTES_READ, user,
+ private void logAttributesRead(SecurityUser user, EntityId entityId, AttributeScope scope, List