Commit 712e756e589d671d1423b3e60a51015e6f7bfd65
1 parent
ae856411
Refactoring to simplify reading and merge
Showing
11 changed files
with
63 additions
and
719 deletions
... | ... | @@ -21,11 +21,13 @@ export interface SysParamsState { |
21 | 21 | allowedDashboardIds: string[]; |
22 | 22 | edgesSupportEnabled: boolean; |
23 | 23 | } |
24 | + | |
24 | 25 | export interface AuthPayload extends SysParamsState { |
25 | 26 | authUser: AuthUser; |
26 | 27 | userDetails: User; |
27 | 28 | forceFullscreen: boolean; |
28 | 29 | } |
30 | + | |
29 | 31 | export interface AuthState extends AuthPayload { |
30 | 32 | isAuthenticated: boolean; |
31 | 33 | isUserLoaded: boolean; | ... | ... |
... | ... | @@ -295,9 +295,9 @@ export class RuleChainService { |
295 | 295 | ); |
296 | 296 | } |
297 | 297 | |
298 | - public getEdgeRuleChains(edgeId: string, pageLink: PageLink, config?:RequestConfig): Observable<PageData<RuleChain>> { | |
298 | + public getEdgeRuleChains(edgeId: string, pageLink: PageLink, config?: RequestConfig): Observable<PageData<RuleChain>> { | |
299 | 299 | return this.http.get<PageData<RuleChain>>(`/api/edge/${edgeId}/ruleChains${pageLink.toQuery()}`, |
300 | - defaultHttpOptionsFromConfig(config) ) | |
300 | + defaultHttpOptionsFromConfig(config)); | |
301 | 301 | } |
302 | 302 | |
303 | 303 | public assignRuleChainToEdge(edgeId: string, ruleChainId: string, config?: RequestConfig): Observable<RuleChain> { | ... | ... |
... | ... | @@ -26,7 +26,6 @@ import { NULL_UUID } from '@shared/models/id/has-uuid'; |
26 | 26 | import { ActionNotificationShow } from '@core/notification/notification.actions'; |
27 | 27 | import { generateSecret, guid } from '@core/utils'; |
28 | 28 | import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; |
29 | -import { WINDOW } from '@core/services/window.service'; | |
30 | 29 | |
31 | 30 | @Component({ |
32 | 31 | selector: 'tb-edge', |
... | ... | @@ -43,15 +42,14 @@ export class EdgeComponent extends EntityComponent<EdgeInfo> { |
43 | 42 | protected translate: TranslateService, |
44 | 43 | @Inject('entity') protected entityValue: EdgeInfo, |
45 | 44 | @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig<EdgeInfo>, |
46 | - public fb: FormBuilder, | |
47 | - @Inject(WINDOW) protected window: Window) { | |
45 | + public fb: FormBuilder) { | |
48 | 46 | super(store, fb, entityValue, entitiesTableConfigValue); |
49 | 47 | } |
50 | 48 | |
51 | 49 | ngOnInit() { |
52 | 50 | this.edgeScope = this.entitiesTableConfig.componentsData.edgeScope; |
53 | 51 | this.entityForm.patchValue({ |
54 | - cloudEndpoint: this.window.location.origin | |
52 | + cloudEndpoint: window.location.origin | |
55 | 53 | }); |
56 | 54 | super.ngOnInit(); |
57 | 55 | } |
... | ... | @@ -94,7 +92,7 @@ export class EdgeComponent extends EntityComponent<EdgeInfo> { |
94 | 92 | name: entity.name, |
95 | 93 | type: entity.type, |
96 | 94 | label: entity.label, |
97 | - cloudEndpoint: entity.cloudEndpoint ? entity.cloudEndpoint : this.window.location.origin, | |
95 | + cloudEndpoint: entity.cloudEndpoint ? entity.cloudEndpoint : window.location.origin, | |
98 | 96 | edgeLicenseKey: entity.edgeLicenseKey, |
99 | 97 | routingKey: entity.routingKey, |
100 | 98 | secret: entity.secret, | ... | ... |
... | ... | @@ -49,6 +49,7 @@ import { isUndefined } from '@core/utils'; |
49 | 49 | import { PageLink } from '@shared/models/page/page-link'; |
50 | 50 | import { Edge } from '@shared/models/edge.models'; |
51 | 51 | import { mergeMap } from 'rxjs/operators'; |
52 | +import { PageData } from '@shared/models/page/page-data'; | |
52 | 53 | |
53 | 54 | @Injectable() |
54 | 55 | export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig<RuleChain>> { |
... | ... | @@ -83,25 +84,27 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
83 | 84 | } |
84 | 85 | |
85 | 86 | resolve(route: ActivatedRouteSnapshot): EntityTableConfig<RuleChain> { |
86 | - const routeParams = route.params; | |
87 | + const edgeId = route.params?.edgeId; | |
88 | + const ruleChainScope = route.data?.ruleChainsType ? route.data?.ruleChainsType : 'tenant'; | |
87 | 89 | this.config.componentsData = { |
88 | - ruleChainScope: route.data.ruleChainsType, | |
89 | - edgeId: routeParams.edgeId | |
90 | + ruleChainScope, | |
91 | + edgeId | |
90 | 92 | }; |
91 | - this.config.columns = this.configureEntityTableColumns(this.config.componentsData.ruleChainScope); | |
92 | - this.configureEntityFunctions(this.config.componentsData.ruleChainScope); | |
93 | - this.config.groupActionDescriptors = this.configureGroupActions(this.config.componentsData.ruleChainScope); | |
94 | - this.config.addActionDescriptors = this.configureAddActions(this.config.componentsData.ruleChainScope); | |
95 | - this.config.cellActionDescriptors = this.configureCellActions(this.config.componentsData.ruleChainScope); | |
96 | - if (this.config.componentsData.ruleChainScope === 'tenant' || this.config.componentsData.ruleChainScope === 'edges') { | |
93 | + this.config.columns = this.configureEntityTableColumns(ruleChainScope); | |
94 | + this.config.entitiesFetchFunction = this.configureEntityFunctions(ruleChainScope, edgeId); | |
95 | + this.config.groupActionDescriptors = this.configureGroupActions(ruleChainScope); | |
96 | + this.config.addActionDescriptors = this.configureAddActions(ruleChainScope); | |
97 | + this.config.cellActionDescriptors = this.configureCellActions(ruleChainScope); | |
98 | + if (ruleChainScope === 'tenant' || ruleChainScope === 'edges') { | |
97 | 99 | this.config.entitySelectionEnabled = ruleChain => ruleChain && !ruleChain.root; |
98 | 100 | this.config.deleteEnabled = (ruleChain) => ruleChain && !ruleChain.root; |
99 | 101 | this.config.entitiesDeleteEnabled = true; |
100 | - } else if (this.config.componentsData.ruleChainScope === 'edge') { | |
101 | - this.config.entitySelectionEnabled = ruleChain => this.config.componentsData.edge.rootRuleChainId.id != ruleChain.id.id; | |
102 | - this.edgeService.getEdge(this.config.componentsData.edgeId).subscribe(edge => { | |
102 | + this.config.tableTitle = this.configureTableTitle(ruleChainScope, null); | |
103 | + } else if (ruleChainScope === 'edge') { | |
104 | + this.config.entitySelectionEnabled = ruleChain => this.config.componentsData.edge.rootRuleChainId.id !== ruleChain.id.id; | |
105 | + this.edgeService.getEdge(edgeId).subscribe(edge => { | |
103 | 106 | this.config.componentsData.edge = edge; |
104 | - this.config.tableTitle = edge.name + ': ' + this.translate.instant('edge.rulechains'); | |
107 | + this.config.tableTitle = this.configureTableTitle(ruleChainScope, edge); | |
105 | 108 | }); |
106 | 109 | this.config.entitiesDeleteEnabled = false; |
107 | 110 | } |
... | ... | @@ -110,24 +113,23 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
110 | 113 | |
111 | 114 | configureEntityTableColumns(ruleChainScope: string): Array<EntityColumn<RuleChain>> { |
112 | 115 | const columns: Array<EntityColumn<RuleChain>> = []; |
116 | + columns.push( | |
117 | + new DateEntityTableColumn<RuleChain>('createdTime', 'common.created-time', this.datePipe, '150px'), | |
118 | + new EntityTableColumn<RuleChain>('name', 'rulechain.name', '100%') | |
119 | + ); | |
113 | 120 | if (ruleChainScope === 'tenant' || ruleChainScope === 'edge') { |
114 | 121 | columns.push( |
115 | - new DateEntityTableColumn<RuleChain>('createdTime', 'common.created-time', this.datePipe, '150px'), | |
116 | - new EntityTableColumn<RuleChain>('name', 'rulechain.name', '100%'), | |
117 | 122 | new EntityTableColumn<RuleChain>('root', 'rulechain.root', '60px', |
118 | 123 | entity => { |
119 | 124 | if (ruleChainScope === 'edge') { |
120 | - return checkBoxCell((this.config.componentsData.edge.rootRuleChainId.id == entity.id.id)); | |
125 | + return checkBoxCell((this.config.componentsData.edge.rootRuleChainId.id === entity.id.id)); | |
121 | 126 | } else { |
122 | 127 | return checkBoxCell(entity.root); |
123 | 128 | } |
124 | 129 | }) |
125 | 130 | ); |
126 | - } | |
127 | - if (ruleChainScope === 'edges') { | |
131 | + } else if (ruleChainScope === 'edges') { | |
128 | 132 | columns.push( |
129 | - new DateEntityTableColumn<RuleChain>('createdTime', 'common.created-time', this.datePipe, '150px'), | |
130 | - new EntityTableColumn<RuleChain>('name', 'rulechain.name', '100%'), | |
131 | 133 | new EntityTableColumn<RuleChain>('root', 'rulechain.edge-template-root', '60px', |
132 | 134 | entity => { |
133 | 135 | return checkBoxCell(entity.root); |
... | ... | @@ -157,7 +159,7 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
157 | 159 | isEnabled: () => true, |
158 | 160 | onAction: ($event) => this.importRuleChain($event) |
159 | 161 | } |
160 | - ) | |
162 | + ); | |
161 | 163 | } |
162 | 164 | if (ruleChainScope === 'edge') { |
163 | 165 | actions.push( |
... | ... | @@ -167,20 +169,28 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
167 | 169 | isEnabled: () => true, |
168 | 170 | onAction: ($event) => this.addRuleChainsToEdge($event) |
169 | 171 | } |
170 | - ) | |
172 | + ); | |
171 | 173 | } |
172 | 174 | return actions; |
173 | 175 | } |
174 | 176 | |
175 | - configureEntityFunctions(ruleChainScope: string): void { | |
177 | + configureEntityFunctions(ruleChainScope: string, edgeId: string): (pageLink) => Observable<PageData<RuleChain>> { | |
176 | 178 | if (ruleChainScope === 'tenant') { |
177 | - this.config.tableTitle = this.translate.instant('rulechain.rulechains'); | |
178 | - this.config.entitiesFetchFunction = pageLink => this.fetchRuleChains(pageLink); | |
179 | + return pageLink => this.fetchRuleChains(pageLink); | |
179 | 180 | } else if (ruleChainScope === 'edges') { |
180 | - this.config.tableTitle = this.translate.instant('edge.rulechain-templates'); | |
181 | - this.config.entitiesFetchFunction = pageLink => this.fetchEdgeRuleChains(pageLink); | |
181 | + return pageLink => this.fetchEdgeRuleChains(pageLink); | |
182 | 182 | } else if (ruleChainScope === 'edge') { |
183 | - this.config.entitiesFetchFunction = pageLink => this.ruleChainService.getEdgeRuleChains(this.config.componentsData.edgeId, pageLink); | |
183 | + return pageLink => this.ruleChainService.getEdgeRuleChains(edgeId, pageLink); | |
184 | + } | |
185 | + } | |
186 | + | |
187 | + configureTableTitle(ruleChainScope: string, edge: Edge): string { | |
188 | + if (ruleChainScope === 'tenant') { | |
189 | + return this.translate.instant('rulechain.rulechains'); | |
190 | + } else if (ruleChainScope === 'edges') { | |
191 | + return this.translate.instant('edge.rulechain-templates'); | |
192 | + } else if (ruleChainScope === 'edge') { | |
193 | + return this.config.tableTitle = edge.name + ': ' + this.translate.instant('edge.rulechains'); | |
184 | 194 | } |
185 | 195 | } |
186 | 196 | |
... | ... | @@ -194,7 +204,7 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
194 | 204 | isEnabled: true, |
195 | 205 | onAction: ($event, entities) => this.unassignRuleChainsFromEdge($event, entities) |
196 | 206 | } |
197 | - ) | |
207 | + ); | |
198 | 208 | } |
199 | 209 | return actions; |
200 | 210 | } |
... | ... | @@ -215,7 +225,7 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
215 | 225 | isEnabled: () => true, |
216 | 226 | onAction: ($event, entity) => this.exportRuleChain($event, entity) |
217 | 227 | } |
218 | - ) | |
228 | + ); | |
219 | 229 | if (ruleChainScope === 'tenant') { |
220 | 230 | actions.push( |
221 | 231 | { |
... | ... | @@ -224,7 +234,7 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
224 | 234 | isEnabled: (entity) => this.isNonRootRuleChain(entity), |
225 | 235 | onAction: ($event, entity) => this.setRootRuleChain($event, entity) |
226 | 236 | } |
227 | - ) | |
237 | + ); | |
228 | 238 | } |
229 | 239 | if (ruleChainScope === 'edges') { |
230 | 240 | actions.push( |
... | ... | @@ -246,7 +256,7 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
246 | 256 | isEnabled: (entity) => this.isAutoAssignToEdgeRuleChain(entity), |
247 | 257 | onAction: ($event, entity) => this.unsetAutoAssignToEdgeRuleChain($event, entity) |
248 | 258 | } |
249 | - ) | |
259 | + ); | |
250 | 260 | } |
251 | 261 | } |
252 | 262 | if (ruleChainScope === 'edge') { |
... | ... | @@ -260,10 +270,10 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
260 | 270 | { |
261 | 271 | name: this.translate.instant('edge.unassign-from-edge'), |
262 | 272 | icon: 'assignment_return', |
263 | - isEnabled: (entity) => entity.id.id != this.config.componentsData.edge.rootRuleChainId.id, | |
273 | + isEnabled: (entity) => entity.id.id !== this.config.componentsData.edge.rootRuleChainId.id, | |
264 | 274 | onAction: ($event, entity) => this.unassignFromEdge($event, entity) |
265 | 275 | } |
266 | - ) | |
276 | + ); | |
267 | 277 | } |
268 | 278 | return actions; |
269 | 279 | } |
... | ... | @@ -298,9 +308,9 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
298 | 308 | |
299 | 309 | saveRuleChain(ruleChain: RuleChain) { |
300 | 310 | if (isUndefined(ruleChain.type)) { |
301 | - if (this.config.componentsData.ruleChainScope == 'tenant') { | |
311 | + if (this.config.componentsData.ruleChainScope === 'tenant') { | |
302 | 312 | ruleChain.type = RuleChainType.CORE; |
303 | - } else if (this.config.componentsData.ruleChainScope == 'edges') { | |
313 | + } else if (this.config.componentsData.ruleChainScope === 'edges') { | |
304 | 314 | ruleChain.type = RuleChainType.EDGE; |
305 | 315 | } else { |
306 | 316 | // safe fallback to default core type |
... | ... | @@ -335,13 +345,13 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
335 | 345 | this.config.componentsData.edge = edge; |
336 | 346 | this.config.table.updateData(); |
337 | 347 | } |
338 | - ) | |
348 | + ); | |
339 | 349 | } else { |
340 | 350 | this.ruleChainService.setRootRuleChain(ruleChain.id.id).subscribe( |
341 | 351 | () => { |
342 | 352 | this.config.table.updateData(); |
343 | 353 | } |
344 | - ) | |
354 | + ); | |
345 | 355 | } |
346 | 356 | } |
347 | 357 | } |
... | ... | @@ -415,14 +425,14 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
415 | 425 | this.edgeService.findMissingToRelatedRuleChains(this.config.componentsData.edgeId).subscribe( |
416 | 426 | (missingRuleChains) => { |
417 | 427 | if (missingRuleChains && Object.keys(missingRuleChains).length > 0) { |
418 | - let formattedMissingRuleChains: Array<string> = new Array<string>(); | |
428 | + const formattedMissingRuleChains: Array<string> = new Array<string>(); | |
419 | 429 | for (const missingRuleChain of Object.keys(missingRuleChains)) { |
420 | 430 | const arrayOfMissingRuleChains = missingRuleChains[missingRuleChain]; |
421 | - const tmp = "- '" + missingRuleChain + "': '" + arrayOfMissingRuleChains.join("', ") + "'"; | |
431 | + const tmp = '- \'' + missingRuleChain + '\': \'' + arrayOfMissingRuleChains.join('\', ') + '\''; | |
422 | 432 | formattedMissingRuleChains.push(tmp); |
423 | 433 | } |
424 | 434 | const message = this.translate.instant('edge.missing-related-rule-chains-text', |
425 | - {missingRuleChains: formattedMissingRuleChains.join("<br>")}); | |
435 | + {missingRuleChains: formattedMissingRuleChains.join('<br>')}); | |
426 | 436 | this.dialogService.alert(this.translate.instant('edge.missing-related-rule-chains-title'), |
427 | 437 | message, this.translate.instant('action.close'), true).subscribe( |
428 | 438 | () => { |
... | ... | @@ -433,10 +443,10 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
433 | 443 | this.config.table.updateData(); |
434 | 444 | } |
435 | 445 | } |
436 | - ) | |
446 | + ); | |
437 | 447 | } |
438 | 448 | } |
439 | - ) | |
449 | + ); | |
440 | 450 | } |
441 | 451 | |
442 | 452 | unassignFromEdge($event: Event, ruleChain: RuleChain) { |
... | ... | @@ -505,7 +515,7 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
505 | 515 | () => { |
506 | 516 | this.config.table.updateData(); |
507 | 517 | } |
508 | - ) | |
518 | + ); | |
509 | 519 | } |
510 | 520 | } |
511 | 521 | ); |
... | ... | @@ -527,7 +537,7 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
527 | 537 | () => { |
528 | 538 | this.config.table.updateData(); |
529 | 539 | } |
530 | - ) | |
540 | + ); | |
531 | 541 | } |
532 | 542 | } |
533 | 543 | ); |
... | ... | @@ -536,7 +546,7 @@ export class RuleChainsTableConfigResolver implements Resolve<EntityTableConfig< |
536 | 546 | isNonRootRuleChain(ruleChain: RuleChain) { |
537 | 547 | if (this.config.componentsData.ruleChainScope === 'edge') { |
538 | 548 | return this.config.componentsData.edge.rootRuleChainId && |
539 | - this.config.componentsData.edge.rootRuleChainId.id != ruleChain.id.id; | |
549 | + this.config.componentsData.edge.rootRuleChainId.id !== ruleChain.id.id; | |
540 | 550 | } |
541 | 551 | return !ruleChain.root; |
542 | 552 | } | ... | ... |
ui/src/app/edge/downlinks/downlink.scss
deleted
100644 → 0
1 | -/** | |
2 | - * Copyright © 2016-2020 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | -md-list.tb-edge-downlinks-table { | |
17 | - padding: 0; | |
18 | - | |
19 | - md-list-item { | |
20 | - padding: 0; | |
21 | - } | |
22 | - | |
23 | - .tb-row { | |
24 | - height: 48px; | |
25 | - padding: 0; | |
26 | - overflow: hidden; | |
27 | - | |
28 | - .tb-cell { | |
29 | - text-overflow: ellipsis; | |
30 | - | |
31 | - &.tb-scroll { | |
32 | - overflow-x: auto; | |
33 | - overflow-y: hidden; | |
34 | - white-space: nowrap; | |
35 | - } | |
36 | - | |
37 | - &.tb-nowrap { | |
38 | - white-space: nowrap; | |
39 | - } | |
40 | - } | |
41 | - } | |
42 | - | |
43 | - .tb-row:hover { | |
44 | - background-color: #eee; | |
45 | - } | |
46 | - | |
47 | - .tb-header:hover { | |
48 | - background: none; | |
49 | - } | |
50 | - | |
51 | - .tb-header { | |
52 | - .tb-cell { | |
53 | - font-size: 12px; | |
54 | - font-weight: 700; | |
55 | - color: rgba(0, 0, 0, .54); | |
56 | - white-space: nowrap; | |
57 | - background: none; | |
58 | - } | |
59 | - } | |
60 | - | |
61 | - .tb-cell { | |
62 | - &:first-child { | |
63 | - padding-left: 14px; | |
64 | - } | |
65 | - | |
66 | - &:last-child { | |
67 | - padding-right: 14px; | |
68 | - } | |
69 | - padding: 0 6px; | |
70 | - margin: auto 0; | |
71 | - overflow: hidden; | |
72 | - font-size: 13px; | |
73 | - color: rgba(0, 0, 0, .87); | |
74 | - text-align: left; | |
75 | - vertical-align: middle; | |
76 | - | |
77 | - .md-button { | |
78 | - padding: 0; | |
79 | - margin: 0; | |
80 | - } | |
81 | - } | |
82 | - | |
83 | - .tb-cell.tb-number { | |
84 | - text-align: right; | |
85 | - } | |
86 | -} | |
87 | - | |
88 | -#tb-edge-downlinks-content { | |
89 | - width: 100%; | |
90 | - min-width: 400px; | |
91 | - height: 100%; | |
92 | - min-height: 50px; | |
93 | -} |
ui/src/app/edge/downlinks/edge-downlinks-content-dialog.controller.js
deleted
100644 → 0
1 | -/* | |
2 | - * Copyright © 2016-2020 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | -import $ from 'jquery'; | |
17 | -import 'brace/ext/language_tools'; | |
18 | -import 'brace/ext/searchbox'; | |
19 | -import 'brace/mode/java'; | |
20 | -import 'brace/theme/github'; | |
21 | -import beautify from 'js-beautify'; | |
22 | - | |
23 | -/* eslint-disable angular/angularelement */ | |
24 | - | |
25 | -const js_beautify = beautify.js; | |
26 | - | |
27 | -/*@ngInject*/ | |
28 | -export default function EdgeDownlinksContentDialogController($mdDialog, types, content, contentType, title, showingCallback) { | |
29 | - | |
30 | - var vm = this; | |
31 | - | |
32 | - showingCallback.onShowing = function(scope, element) { | |
33 | - updateEditorSize(element); | |
34 | - } | |
35 | - | |
36 | - vm.content = content; | |
37 | - vm.title = title; | |
38 | - | |
39 | - var mode; | |
40 | - if (contentType) { | |
41 | - mode = types.contentType[contentType].code; | |
42 | - if (contentType == types.contentType.JSON.value && vm.content) { | |
43 | - vm.content = js_beautify(vm.content, {indent_size: 4}); | |
44 | - } | |
45 | - } else { | |
46 | - mode = 'java'; | |
47 | - } | |
48 | - | |
49 | - vm.contentOptions = { | |
50 | - useWrapMode: false, | |
51 | - mode: mode, | |
52 | - showGutter: false, | |
53 | - showPrintMargin: false, | |
54 | - theme: 'github', | |
55 | - advanced: { | |
56 | - enableSnippets: false, | |
57 | - enableBasicAutocompletion: false, | |
58 | - enableLiveAutocompletion: false | |
59 | - }, | |
60 | - onLoad: function (_ace) { | |
61 | - vm.editor = _ace; | |
62 | - } | |
63 | - }; | |
64 | - | |
65 | - function updateEditorSize(element) { | |
66 | - var newHeight = 400; | |
67 | - var newWidth = 600; | |
68 | - if (vm.content && vm.content.length > 0) { | |
69 | - var lines = vm.content.split('\n'); | |
70 | - newHeight = 16 * lines.length + 16; | |
71 | - var maxLineLength = 0; | |
72 | - for (var i = 0; i < lines.length; i++) { | |
73 | - var line = lines[i].replace(/\t/g, ' ').replace(/\n/g, ''); | |
74 | - var lineLength = line.length; | |
75 | - maxLineLength = Math.max(maxLineLength, lineLength); | |
76 | - } | |
77 | - newWidth = 8 * maxLineLength + 16; | |
78 | - } | |
79 | - $('#tb-edge-downlinks-content', element).height(newHeight.toString() + "px") | |
80 | - .width(newWidth.toString() + "px"); | |
81 | - vm.editor.resize(); | |
82 | - } | |
83 | - | |
84 | - vm.close = close; | |
85 | - | |
86 | - function close () { | |
87 | - $mdDialog.hide(); | |
88 | - } | |
89 | - | |
90 | -} | |
91 | - | |
92 | -/* eslint-enable angular/angularelement */ |
ui/src/app/edge/downlinks/edge-downlinks-content-dialog.tpl.html
deleted
100644 → 0
1 | -<!-- | |
2 | - | |
3 | - Copyright © 2016-2020 The Thingsboard Authors | |
4 | - | |
5 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | - you may not use this file except in compliance with the License. | |
7 | - You may obtain a copy of the License at | |
8 | - | |
9 | - http://www.apache.org/licenses/LICENSE-2.0 | |
10 | - | |
11 | - Unless required by applicable law or agreed to in writing, software | |
12 | - distributed under the License is distributed on an "AS IS" BASIS, | |
13 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | - See the License for the specific language governing permissions and | |
15 | - limitations under the License. | |
16 | - | |
17 | ---> | |
18 | -<md-dialog aria-label="{{ vm.title | translate }}"> | |
19 | - <md-toolbar> | |
20 | - <div class="md-toolbar-tools"> | |
21 | - <h2 translate>{{ vm.title }}</h2> | |
22 | - <span flex></span> | |
23 | - <md-button class="md-icon-button" ng-click="vm.close()"> | |
24 | - <ng-md-icon icon="close" aria-label="{{ 'dialog.close' | translate }}"></ng-md-icon> | |
25 | - </md-button> | |
26 | - </div> | |
27 | - </md-toolbar> | |
28 | - <md-dialog-content> | |
29 | - <div class="md-dialog-content"> | |
30 | - <div flex id="tb-edge-downlinks-content" readonly | |
31 | - ui-ace="vm.contentOptions" | |
32 | - ng-model="vm.content"> | |
33 | - </div> | |
34 | - </div> | |
35 | - </md-dialog-content> | |
36 | - <md-dialog-actions layout="row"> | |
37 | - <span flex></span> | |
38 | - <md-button ng-disabled="$root.loading" ng-click="vm.close()" style="margin-right:20px;">{{ 'action.close' | | |
39 | - translate }} | |
40 | - </md-button> | |
41 | - </md-dialog-actions> | |
42 | -</md-dialog> |
ui/src/app/edge/downlinks/edge-downlinks-header.directive.js
deleted
100644 → 0
1 | -/* | |
2 | - * Copyright © 2016-2020 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | -/* eslint-disable import/no-unresolved, import/default */ | |
17 | - | |
18 | -import edgeDownlinksHeaderTemplate from './edge-downlinks-header.tpl.html' | |
19 | - | |
20 | -/* eslint-enable import/no-unresolved, import/default */ | |
21 | - | |
22 | -/*@ngInject*/ | |
23 | -export default function EdgeDownlinksHeaderDirective($compile, $templateCache) { | |
24 | - | |
25 | - var linker = function (scope, element) { | |
26 | - | |
27 | - var template = edgeDownlinksHeaderTemplate; | |
28 | - | |
29 | - element.html($templateCache.get(template)); | |
30 | - $compile(element.contents())(scope); | |
31 | - } | |
32 | - | |
33 | - return { | |
34 | - restrict: "A", | |
35 | - replace: false, | |
36 | - link: linker, | |
37 | - scope: false | |
38 | - }; | |
39 | -} |
ui/src/app/edge/downlinks/edge-downlinks-row.directive.js
deleted
100644 → 0
1 | -/* | |
2 | - * Copyright © 2016-2020 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | -/* eslint-disable import/no-unresolved, import/default */ | |
17 | - | |
18 | -import edgeDownlinksContentTemplate from './edge-downlinks-content-dialog.tpl.html'; | |
19 | -import edgeDownlinlsRowTemplate from './edge-downlinks-row.tpl.html'; | |
20 | - | |
21 | -/* eslint-enable import/no-unresolved, import/default */ | |
22 | - | |
23 | -/*@ngInject*/ | |
24 | -export default function EdgeDownlinksRowDirective($compile, $templateCache, $mdDialog, $document, $translate, | |
25 | - types, utils, toast, entityService, ruleChainService) { | |
26 | - | |
27 | - var linker = function (scope, element, attrs) { | |
28 | - | |
29 | - var template = edgeDownlinlsRowTemplate; | |
30 | - | |
31 | - element.html($templateCache.get(template)); | |
32 | - $compile(element.contents())(scope); | |
33 | - | |
34 | - scope.types = types; | |
35 | - scope.downlink = attrs.downlink; | |
36 | - | |
37 | - scope.showEdgeEntityContent = function($event, title, contentType) { | |
38 | - var onShowingCallback = { | |
39 | - onShowing: function(){} | |
40 | - } | |
41 | - if (!contentType) { | |
42 | - contentType = null; | |
43 | - } | |
44 | - var content = ''; | |
45 | - switch(scope.downlink.type) { | |
46 | - case types.edgeEventType.relation: | |
47 | - content = angular.toJson(scope.downlink.body); | |
48 | - showDialog(); | |
49 | - break; | |
50 | - case types.edgeEventType.ruleChainMetaData: | |
51 | - content = ruleChainService.getRuleChainMetaData(scope.downlink.entityId, {ignoreErrors: true}).then( | |
52 | - function success(info) { | |
53 | - showDialog(); | |
54 | - return angular.toJson(info); | |
55 | - }, function fail() { | |
56 | - showError(); | |
57 | - }); | |
58 | - break; | |
59 | - default: | |
60 | - content = entityService.getEntity(scope.downlink.type, scope.downlink.entityId, {ignoreErrors: true}).then( | |
61 | - function success(info) { | |
62 | - showDialog(); | |
63 | - return angular.toJson(info); | |
64 | - }, function fail() { | |
65 | - showError(); | |
66 | - }); | |
67 | - break; | |
68 | - } | |
69 | - function showDialog() { | |
70 | - $mdDialog.show({ | |
71 | - controller: 'EdgeDownlinksContentDialogController', | |
72 | - controllerAs: 'vm', | |
73 | - templateUrl: edgeDownlinksContentTemplate, | |
74 | - locals: {content: content, title: title, contentType: contentType, showingCallback: onShowingCallback}, | |
75 | - parent: angular.element($document[0].body), | |
76 | - fullscreen: true, | |
77 | - targetEvent: $event, | |
78 | - multiple: true, | |
79 | - onShowing: function(scope, element) { | |
80 | - onShowingCallback.onShowing(scope, element); | |
81 | - } | |
82 | - }); | |
83 | - } | |
84 | - function showError() { | |
85 | - toast.showError($translate.instant('edge.load-entity-error')); | |
86 | - } | |
87 | - } | |
88 | - | |
89 | - scope.checkEdgeDownlinksType = function (type) { | |
90 | - return !(type === types.edgeEventType.widgetType || | |
91 | - type === types.edgeEventType.adminSettings || | |
92 | - type === types.edgeEventType.widgetsBundle ); | |
93 | - } | |
94 | - | |
95 | - scope.checkTooltip = function($event) { | |
96 | - var el = $event.target; | |
97 | - var $el = angular.element(el); | |
98 | - if(el.offsetWidth < el.scrollWidth && !$el.attr('title')){ | |
99 | - $el.attr('title', $el.text()); | |
100 | - } | |
101 | - } | |
102 | - | |
103 | - $compile(element.contents())(scope); | |
104 | - | |
105 | - scope.updateStatus = function(downlinkCreatedTime) { | |
106 | - var status; | |
107 | - if (downlinkCreatedTime < scope.queueStartTs) { | |
108 | - status = $translate.instant(types.edgeEventStatus.DEPLOYED.name); | |
109 | - scope.statusColor = types.edgeEventStatus.DEPLOYED.color; | |
110 | - } else { | |
111 | - status = $translate.instant(types.edgeEventStatus.PENDING.name); | |
112 | - scope.statusColor = types.edgeEventStatus.PENDING.color; | |
113 | - } | |
114 | - return status; | |
115 | - } | |
116 | - } | |
117 | - | |
118 | - return { | |
119 | - restrict: "A", | |
120 | - replace: false, | |
121 | - link: linker, | |
122 | - scope: false | |
123 | - }; | |
124 | -} |
ui/src/app/edge/downlinks/edge-downlinks-table.directive.js
deleted
100644 → 0
1 | -/* | |
2 | - * Copyright © 2016-2020 The Thingsboard Authors | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | -import './downlink.scss'; | |
17 | - | |
18 | -/* eslint-disable import/no-unresolved, import/default */ | |
19 | - | |
20 | -import edgeDownlinksTableTemplate from './edge-downlinks-table.tpl.html'; | |
21 | - | |
22 | -/* eslint-enable import/no-unresolved, import/default */ | |
23 | - | |
24 | -/*@ngInject*/ | |
25 | -export default function EdgeDownlinksDirective($compile, $templateCache, $rootScope, $translate, types, edgeService, attributeService) { | |
26 | - | |
27 | - var linker = function (scope, element) { | |
28 | - | |
29 | - var template = $templateCache.get(edgeDownlinksTableTemplate); | |
30 | - element.html(template); | |
31 | - | |
32 | - var pageSize = 20; | |
33 | - var startTime = 0; | |
34 | - var endTime = 0; | |
35 | - | |
36 | - scope.timewindow = { | |
37 | - history: { | |
38 | - timewindowMs: 24 * 60 * 60 * 1000 // 1 day | |
39 | - } | |
40 | - } | |
41 | - | |
42 | - scope.topIndex = 0; | |
43 | - | |
44 | - scope.theDownlinks = { | |
45 | - getItemAtIndex: function (index) { | |
46 | - if (index > scope.downlinks.data.length) { | |
47 | - scope.theDownlinks.fetchMoreItems_(index); | |
48 | - return null; | |
49 | - } | |
50 | - var item = scope.downlinks.data[index]; | |
51 | - if (item) { | |
52 | - item.indexNumber = index + 1; | |
53 | - } | |
54 | - return item; | |
55 | - }, | |
56 | - | |
57 | - getLength: function () { | |
58 | - if (scope.downlinks.hasNext) { | |
59 | - return scope.downlinks.data.length + scope.downlinks.nextPageLink.limit; | |
60 | - } else { | |
61 | - return scope.downlinks.data.length; | |
62 | - } | |
63 | - }, | |
64 | - | |
65 | - fetchMoreItems_: function () { | |
66 | - if (scope.downlinks.hasNext && !scope.downlinks.pending) { | |
67 | - if (scope.entityType && scope.entityId && scope.tenantId) { | |
68 | - scope.loadEdgeInfo(); | |
69 | - scope.downlinks.pending = true; | |
70 | - edgeService.getEdgeEvents(scope.entityId, scope.downlinks.nextPageLink).then( | |
71 | - function success(downlinks) { | |
72 | - scope.downlinks.data = scope.downlinks.data.concat(prepareEdgeDownlinksData(downlinks.data)); | |
73 | - scope.downlinks.nextPageLink = downlinks.nextPageLink; | |
74 | - scope.downlinks.hasNext = downlinks.hasNext; | |
75 | - if (scope.downlinks.hasNext) { | |
76 | - scope.downlinks.nextPageLink.limit = pageSize; | |
77 | - } | |
78 | - scope.downlinks.pending = false; | |
79 | - }, | |
80 | - function fail() { | |
81 | - scope.downlinks.hasNext = false; | |
82 | - scope.downlinks.pending = false; | |
83 | - }); | |
84 | - } else { | |
85 | - scope.downlinks.hasNext = false; | |
86 | - } | |
87 | - } | |
88 | - } | |
89 | - }; | |
90 | - | |
91 | - scope.$watch("entityId", function(newVal, prevVal) { | |
92 | - if (newVal && !angular.equals(newVal, prevVal)) { | |
93 | - scope.resetFilter(); | |
94 | - scope.reload(); | |
95 | - } | |
96 | - }); | |
97 | - | |
98 | - scope.$watch("timewindow", function(newVal, prevVal) { | |
99 | - if (newVal && !angular.equals(newVal, prevVal)) { | |
100 | - scope.reload(); | |
101 | - } | |
102 | - }, true); | |
103 | - | |
104 | - scope.resetFilter = function() { | |
105 | - scope.timewindow = { | |
106 | - history: { | |
107 | - timewindowMs: 24 * 60 * 60 * 1000 // 1 day | |
108 | - } | |
109 | - }; | |
110 | - } | |
111 | - | |
112 | - scope.updateTimeWindowRange = function() { | |
113 | - if (scope.timewindow.history.timewindowMs) { | |
114 | - var currentTime = (new Date).getTime(); | |
115 | - startTime = currentTime - scope.timewindow.history.timewindowMs; | |
116 | - endTime = currentTime; | |
117 | - } else { | |
118 | - startTime = scope.timewindow.history.fixedTimewindow.startTimeMs; | |
119 | - endTime = scope.timewindow.history.fixedTimewindow.endTimeMs; | |
120 | - } | |
121 | - } | |
122 | - | |
123 | - scope.reload = function() { | |
124 | - scope.topIndex = 0; | |
125 | - scope.selected = []; | |
126 | - scope.updateTimeWindowRange(); | |
127 | - scope.downlinks = { | |
128 | - data: [], | |
129 | - nextPageLink: { | |
130 | - limit: pageSize, | |
131 | - startTime: startTime, | |
132 | - endTime: endTime | |
133 | - }, | |
134 | - hasNext: true, | |
135 | - pending: false | |
136 | - }; | |
137 | - scope.theDownlinks.getItemAtIndex(pageSize); | |
138 | - } | |
139 | - | |
140 | - scope.noData = function() { | |
141 | - return scope.downlinks.data.length == 0 && !scope.downlinks.hasNext; | |
142 | - } | |
143 | - | |
144 | - scope.hasData = function() { | |
145 | - return scope.downlinks.data.length > 0; | |
146 | - } | |
147 | - | |
148 | - scope.loading = function() { | |
149 | - return $rootScope.loading; | |
150 | - } | |
151 | - | |
152 | - scope.hasScroll = function() { | |
153 | - var repeatContainer = scope.repeatContainer[0]; | |
154 | - if (repeatContainer) { | |
155 | - var scrollElement = repeatContainer.children[0]; | |
156 | - if (scrollElement) { | |
157 | - return scrollElement.scrollHeight > scrollElement.clientHeight; | |
158 | - } | |
159 | - } | |
160 | - return false; | |
161 | - } | |
162 | - | |
163 | - scope.subscriptionId = null; | |
164 | - | |
165 | - scope.loadEdgeInfo = function() { | |
166 | - attributeService.getEntityAttributesValues( | |
167 | - scope.entityType, | |
168 | - scope.entityId, | |
169 | - types.attributesScope.server.value, | |
170 | - types.edgeAttributeKeys.queueStartTs, | |
171 | - null).then( | |
172 | - function success(attributes) { | |
173 | - attributes.length > 0 ? scope.onEdgeAttributesUpdate(attributes) : scope.queueStartTs = 0; | |
174 | - }); | |
175 | - scope.checkSubscription(); | |
176 | - } | |
177 | - | |
178 | - scope.onEdgeAttributesUpdate = function(attributes) { | |
179 | - let edgeAttributes = attributes.reduce(function (map, attribute) { | |
180 | - map[attribute.key] = attribute; | |
181 | - return map; | |
182 | - }, {}); | |
183 | - if (edgeAttributes.queueStartTs) { | |
184 | - scope.queueStartTs = edgeAttributes.queueStartTs.lastUpdateTs; | |
185 | - } | |
186 | - } | |
187 | - | |
188 | - scope.checkSubscription = function() { | |
189 | - var newSubscriptionId = null; | |
190 | - if (scope.entityId && scope.entityType && types.attributesScope.server.value) { | |
191 | - newSubscriptionId = | |
192 | - attributeService.subscribeForEntityAttributes(scope.entityType, scope.entityId, types.attributesScope.server.value); | |
193 | - } | |
194 | - if (scope.subscriptionId && scope.subscriptionId != newSubscriptionId) { | |
195 | - attributeService.unsubscribeForEntityAttributes(scope.subscriptionId); | |
196 | - } | |
197 | - scope.subscriptionId = newSubscriptionId; | |
198 | - } | |
199 | - | |
200 | - scope.$on('$destroy', function () { | |
201 | - if (scope.subscriptionId) { | |
202 | - attributeService.unsubscribeForEntityAttributes(scope.subscriptionId); | |
203 | - } | |
204 | - }); | |
205 | - | |
206 | - scope.reload(); | |
207 | - | |
208 | - $compile(element.contents())(scope); | |
209 | - } | |
210 | - function prepareEdgeDownlinksData(data) { | |
211 | - | |
212 | - data.forEach( | |
213 | - edgeDownlink => { | |
214 | - edgeDownlink.edgeEventActionText = $translate.instant(types.edgeEventActionTypeTranslations[edgeDownlink.action].name); | |
215 | - edgeDownlink.edgeEventTypeText = $translate.instant(types.edgeEventTypeTranslations[edgeDownlink.type].name); | |
216 | - } | |
217 | - ); | |
218 | - return data; | |
219 | - } | |
220 | - | |
221 | - return { | |
222 | - restrict: "E", | |
223 | - link: linker, | |
224 | - scope: { | |
225 | - entityType: '=', | |
226 | - entityId: '=', | |
227 | - tenantId: '=' | |
228 | - } | |
229 | - }; | |
230 | -} |
ui/src/app/edge/downlinks/edge-downlinks-table.tpl.html
deleted
100644 → 0
1 | -<!-- | |
2 | - | |
3 | - Copyright © 2016-2020 The Thingsboard Authors | |
4 | - | |
5 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | - you may not use this file except in compliance with the License. | |
7 | - You may obtain a copy of the License at | |
8 | - | |
9 | - http://www.apache.org/licenses/LICENSE-2.0 | |
10 | - | |
11 | - Unless required by applicable law or agreed to in writing, software | |
12 | - distributed under the License is distributed on an "AS IS" BASIS, | |
13 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | - See the License for the specific language governing permissions and | |
15 | - limitations under the License. | |
16 | - | |
17 | ---> | |
18 | -<md-content flex class="md-padding tb-absolute-fill" layout="column"> | |
19 | - <section layout="row"> | |
20 | - <tb-timewindow flex ng-model="timewindow" history-only as-button="true"></tb-timewindow> | |
21 | - <md-button ng-disabled="$root.loading" | |
22 | - class="md-icon-button" ng-click="reload()"> | |
23 | - <md-icon>refresh</md-icon> | |
24 | - <md-tooltip md-direction="top"> | |
25 | - {{ 'action.refresh' | translate }} | |
26 | - </md-tooltip> | |
27 | - </md-button> | |
28 | - </section> | |
29 | - <md-list flex layout="column" class="md-whiteframe-z1 tb-edge-downlinks-table"> | |
30 | - <md-list class="tb-row tb-header" layout="row" layout-align="start center" tb-edge-downlinks-header> | |
31 | - </md-list> | |
32 | - <md-progress-linear style="max-height: 0px;" md-mode="indeterminate" ng-disabled="!$root.loading" | |
33 | - ng-show="$root.loading"></md-progress-linear> | |
34 | - <md-divider></md-divider> | |
35 | - <span translate layout-align="center center" | |
36 | - style="margin-top: 25px;" | |
37 | - class="tb-prompt" ng-show="noData()">edge.no-downlinks-prompt</span> | |
38 | - <md-virtual-repeat-container ng-show="hasData()" flex md-top-index="topIndex" tb-scope-element="repeatContainer"> | |
39 | - <md-list-item md-virtual-repeat="downlink in theDownlinks" md-on-demand flex ng-style="hasScroll() ? {'margin-right':'-15px'} : {}"> | |
40 | - <md-list class="tb-row" flex layout="row" layout-align="start center" tb-edge-downlinks-row downlink="{{downlink}}"> | |
41 | - </md-list> | |
42 | - <md-divider flex></md-divider> | |
43 | - </md-list-item> | |
44 | - </md-virtual-repeat-container> | |
45 | - </md-list> | |
46 | -</md-content> |