• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jumpinjackie / mapguide-react-layout / 22997640305

12 Mar 2026 10:32AM UTC coverage: 36.597% (-0.3%) from 36.937%
22997640305

Pull #1603

github

web-flow
Merge 59c28113e into d20ddfb87
Pull Request #1603: Add declarative map swipe (layer compare) feature

1618 of 2109 branches covered (76.72%)

77 of 431 new or added lines in 17 files covered. (17.87%)

408 existing lines in 7 files now uncovered.

8897 of 24311 relevant lines covered (36.6%)

8.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.13
/src/api/builders/deArrayify.ts
1
/**
2
 * deArrayify.ts
3
 *
4
 * This module provides JSON sanitization of JSON responses from the mapagent
5
 *
6
 * Being a transformation of the XML form, and taking a lowest-common-denominator
7
 * approach to JSON conversion, the JSON responses from MapGuide are un-wieldy to
8
 * use from the client-side due to:
9
 *
10
 *  a) All properties being arrays
11
 *  b) All property values being strings
12
 *
13
 * These functions help "clean" those responses to be of the form we expect (and prefer)
14
 */
15
import { MgError } from "../error";
1✔
16
import { ActiveSelectedFeature } from "../common";
17
import { RuleInfo, FeatureStyleInfo, ScaleRangeInfo, FeatureSourceInfo, MapLayer, MapGroup, CoordinateSystemType, EnvCoordinate, Envelope, RuntimeMap } from '../contracts/runtime-map';
18
import { FeatureSetClass, FeatureSetLayer, FeatureSet, SelectionImage, FeatureProperty, SelectedFeature, LayerPropertyMetadata, LayerMetadata, SelectedLayer, SelectedFeatureSet, QueryMapFeaturesResponse } from '../contracts/query';
19
import { WebLayoutMap, WebLayoutControl, WebLayoutInfoPane, MapView, TaskButton, WebLayoutTaskBar, UIItem, WebLayoutTaskPane, CommandUIItem, FlyoutUIItem, ResultColumnSet, ResultColumn, LayerSet, ParameterPair, CommandDef, BasicCommandDef, InvokeScriptCommandDef, InvokeURLCommandDef, SearchCommandDef, WebLayoutCommandSet, WebLayout, WebLayoutToolbar, WebLayoutContextMenu, WebLayoutStatusBar, WebLayoutZoomControl } from '../contracts/weblayout';
20
import { MapSetGroup, MapInitialView, MapConfiguration, MapSet, ContainerItem, FlyoutItem, WidgetItem, ContainerDefinition, Widget, UIWidget, MapWidget, WidgetSet, ApplicationDefinition } from '../contracts/fusion';
21
import { MDF_INFINITY } from '../../constants';
1✔
22
import { MapDefinition, MapDefinitionLayerGroup, MapDefinitionLayer as MdfLayer, TileSetSource } from "../contracts/map-definition";
23
import { BaseMapLayer, BaseMapLayerGroup, TileSetDefinition, TileStoreParameters } from "../contracts/tile-set-definition";
24
import { SiteVersionResponse } from '../contracts/common';
25

26
type ElementType = "string" | "boolean" | "int" | "float";
27

28
function buildPropertyGetter<T>() {
823✔
29
    return (el: any, name: keyof T, type: ElementType = "string") => {
823✔
30
        return tryGetAsProperty<T>(el, name, type);
5,628✔
31
    }
5,628✔
32
}
823✔
33

34
function tryGetAsProperty<T>(el: any, name: keyof T, type: ElementType = "string"): any {
5,628✔
35
    if (!el[name]) {
5,628✔
36
        return null;
536✔
37
    } else if (el[name].length === 1) {
5,628✔
38
        const val: string = el[name][0];
5,092✔
39
        switch (type) {
5,092✔
40
            case "int":
5,092✔
41
                return parseInt(val, 10);
121✔
42
            case "float":
5,092✔
43
                return parseFloat(val);
133✔
44
            case "boolean":
5,092✔
45
                return val.toLowerCase() === "true";
744✔
46
            default:
5,092✔
47
                return val;
4,094✔
48
        }
5,092✔
49
    }
5,092✔
50
}
5,628✔
51

52
function deArrayifyRules(rules: any[]): RuleInfo[] {
46✔
53
    const getter = buildPropertyGetter<RuleInfo>();
46✔
54
    return rules.map(r => {
46✔
55
        const rule: RuleInfo = {
101✔
56
            LegendLabel: getter(r, "LegendLabel"),
101✔
57
            Filter: getter(r, "Filter"),
101✔
58
            Icon: getter(r, "Icon")
101✔
59
        };
101✔
60
        return rule;
101✔
61
    });
46✔
62
}
46✔
63

64
function deArrayifyFeatureStyles(fts: any[]): FeatureStyleInfo[] {
46✔
65
    if (!fts) {
46!
66
        return [];
×
67
    }
×
68
    const getter = buildPropertyGetter<FeatureStyleInfo>();
46✔
69
    return fts.map(ft => {
46✔
70
        const featureStyle: FeatureStyleInfo = {
46✔
71
            Type: getter(ft, "Type", "int"),
46✔
72
            Rule: deArrayifyRules(ft.Rule)
46✔
73
        };
46✔
74
        return featureStyle;
46✔
75
    });
46✔
76
}
46✔
77

78
function deArrayifyScaleRanges(scales: any[]): ScaleRangeInfo[] {
44✔
79
    if (!scales) { //Happens with raster layers (this is probably a bug in CREATERUNTIMEMAP)
44!
80
        const defaultRange: ScaleRangeInfo = {
×
81
            MinScale: 0,
×
82
            MaxScale: MDF_INFINITY,
×
83
            FeatureStyle: []
×
84
        };
×
85
        return [defaultRange];
×
86
    }
×
87
    
88
    const getter = buildPropertyGetter<ScaleRangeInfo>();
44✔
89
    return scales.map(sc => {
44✔
90
        const scale: ScaleRangeInfo = {
46✔
91
            MinScale: getter(sc, "MinScale", "float"),
46✔
92
            MaxScale: getter(sc, "MaxScale", "float"),
46✔
93
            FeatureStyle: deArrayifyFeatureStyles(sc.FeatureStyle)
46✔
94
        };
46✔
95
        return scale;
46✔
96
    });
44✔
97
}
44✔
98

99
function deArrayifyFeatureSourceInfo(fs: any[]): FeatureSourceInfo | undefined {
44✔
100
    if (!fs || fs.length !== 1) {
44!
101
        return undefined;
×
102
    }
×
103
    const getter = buildPropertyGetter<FeatureSourceInfo>();
44✔
104
    return {
44✔
105
        ResourceId: getter(fs[0], "ResourceId"),
44✔
106
        ClassName: getter(fs[0], "ClassName"),
44✔
107
        Geometry: getter(fs[0], "Geometry")
44✔
108
    };
44✔
109
}
44✔
110

111
function deArrayifyLayers(layers: any[]): MapLayer[] {
3✔
112
    if (!layers)
3✔
113
        return layers;
3!
114

115
    const getter = buildPropertyGetter<MapLayer>();
3✔
116
    return layers.map(lyr => {
3✔
117
        const layer: MapLayer = {
44✔
118
            Type: getter(lyr, "Type", "int"),
44✔
119
            Selectable: getter(lyr, "Selectable", "boolean"),
44✔
120
            LayerDefinition: getter(lyr, "LayerDefinition"),
44✔
121
            Name: getter(lyr, "Name"),
44✔
122
            LegendLabel: getter(lyr, "LegendLabel"),
44✔
123
            ObjectId: getter(lyr, "ObjectId"),
44✔
124
            ParentId: getter(lyr, "ParentId"),
44✔
125
            DisplayInLegend: getter(lyr, "DisplayInLegend", "boolean"),
44✔
126
            ExpandInLegend: getter(lyr, "ExpandInLegend", "boolean"),
44✔
127
            Visible: getter(lyr, "Visible", "boolean"),
44✔
128
            ActuallyVisible: getter(lyr, "ActuallyVisible", "boolean"),
44✔
129
            FeatureSource: deArrayifyFeatureSourceInfo(lyr.FeatureSource),
44✔
130
            ScaleRange: deArrayifyScaleRanges(lyr.ScaleRange)
44✔
131
        };
44✔
132
        //This is either a raster or drawing layer, in this case disregard the
133
        //selectability flag (and set it to false). This is to prevent false positive
134
        //errors trying to do tooltip/selections against raster/drawing layers
135
        if (!lyr.ScaleRange) {
44!
136
            layer.Selectable = false;
×
137
        }
×
138
        return layer;
44✔
139
    });
3✔
140
}
3✔
141

142
function deArrayifyGroups(groups: any[]): MapGroup[] | undefined {
3✔
143
    if (!groups)
3✔
144
        return undefined;
3!
145

146
    const getter = buildPropertyGetter<MapGroup>();
3✔
147
    return groups.map(grp => {
3✔
148
        const group: MapGroup = {
16✔
149
            Type: getter(grp, "Type", "int"),
16✔
150
            Name: getter(grp, "Name"),
16✔
151
            LegendLabel: getter(grp, "LegendLabel"),
16✔
152
            ObjectId: getter(grp, "ObjectId"),
16✔
153
            ParentId: getter(grp, "ParentId"),
16✔
154
            DisplayInLegend: getter(grp, "DisplayInLegend", "boolean"),
16✔
155
            ExpandInLegend: getter(grp, "ExpandInLegend", "boolean"),
16✔
156
            Visible: getter(grp, "Visible", "boolean"),
16✔
157
            ActuallyVisible: getter(grp, "ActuallyVisible", "boolean")
16✔
158
        };
16✔
159
        return group;
16✔
160
    });
3✔
161
}
3✔
162

163
function deArrayifyCoordinateSystem(cs: any[]): CoordinateSystemType {
3✔
164
    if (!cs || cs.length !== 1) {
3!
165
        throw new MgError("Malformed input. Expected CoordinateSystem element");
×
166
    }
×
167
    const getter = buildPropertyGetter<CoordinateSystemType>();
3✔
168
    const res: CoordinateSystemType = {
3✔
169
        Wkt: getter(cs[0], "Wkt"),
3✔
170
        MentorCode: getter(cs[0], "MentorCode"),
3✔
171
        EpsgCode: getter(cs[0], "EpsgCode"),
3✔
172
        MetersPerUnit: getter(cs[0], "MetersPerUnit", "float")
3✔
173
    };
3✔
174
    return res;
3✔
175
}
3✔
176

177
function deArrayifyCoordinate(coord: any[]): EnvCoordinate {
6✔
178
    if (!coord || coord.length !== 1) {
6!
179
        throw new MgError("Malformed input. Expected coordinate array");
×
180
    }
×
181
    const getter = buildPropertyGetter<EnvCoordinate>();
6✔
182
    return {
6✔
183
        X: getter(coord[0], "X", "float"),
6✔
184
        Y: getter(coord[0], "Y", "float")
6✔
185
    };
6✔
186
}
6✔
187

188
function deArrayifyExtents(extents: any[]): Envelope {
3✔
189
    if (!extents || extents.length !== 1) {
3!
190
        throw new MgError("Malformed input. Expected extent element");
×
191
    }
×
192
    const env: Envelope = {
3✔
193
        LowerLeftCoordinate: deArrayifyCoordinate(extents[0].LowerLeftCoordinate),
3✔
194
        UpperRightCoordinate: deArrayifyCoordinate(extents[0].UpperRightCoordinate)
3✔
195
    };
3✔
196
    return env;
3✔
197
}
3✔
198

199
function deArrayifyFiniteDisplayScales(fds: any[]): number[] | undefined {
3✔
200
    if (!fds)
3✔
201
        return undefined;
3!
202

203
    return fds.map(parseFloat);
×
204
}
×
205

206
function deArrayifyRuntimeMap(json: any): RuntimeMap {
3✔
207
    const root = json;
3✔
208
    const getter = buildPropertyGetter<RuntimeMap>();
3✔
209
    const rtMap: RuntimeMap = {
3✔
210
        SessionId: getter(root, "SessionId"),
3✔
211
        SiteVersion: getter(root, "SiteVersion"),
3✔
212
        Name: getter(root, "Name"),
3✔
213
        MapDefinition: getter(root, "MapDefinition"),
3✔
214
        TileSetDefinition: getter(root, "TileSetDefinition"),
3✔
215
        TileWidth: getter(root, "TileWidth", "int"),
3✔
216
        TileHeight: getter(root, "TileHeight", "int"),
3✔
217
        BackgroundColor: getter(root, "BackgroundColor"),
3✔
218
        DisplayDpi: getter(root, "DisplayDpi", "int"),
3✔
219
        IconMimeType: getter(root, "IconMimeType"),
3✔
220
        CoordinateSystem: deArrayifyCoordinateSystem(root.CoordinateSystem),
3✔
221
        Extents: deArrayifyExtents(root.Extents),
3✔
222
        Group: deArrayifyGroups(root.Group),
3✔
223
        Layer: deArrayifyLayers(root.Layer),
3✔
224
        FiniteDisplayScale: deArrayifyFiniteDisplayScales(root.FiniteDisplayScale),
3✔
225
        TilePixelRatio: getter(root, "TilePixelRatio", "int"),
3✔
226
        TileSetProvider: getter(root, "TileSetProvider")
3✔
227
    };
3✔
228
    return rtMap;
3✔
229
}
3✔
230

231
function deArrayifyFeatureSetClass(json: any): FeatureSetClass {
1✔
232
    const root = json;
1✔
233
    const getter = buildPropertyGetter<FeatureSetClass>();
1✔
234
    if (root.length != 1) {
1!
235
        throw new MgError("Malformed input. Expected Class element");
×
236
    }
×
237
    const cls = {
1✔
238
        "@id": getter(root[0], "@id"),
1✔
239
        ID: root[0].ID
1✔
240
    };
1✔
241
    return cls;
1✔
242
}
1✔
243

244
function deArrayifyFeatureSetLayers(json: any[]): FeatureSetLayer[] {
1✔
245
    const getter = buildPropertyGetter<FeatureSetLayer>();
1✔
246
    return (json || []).map(root => {
1!
247
        const layer = {
1✔
248
            "@id": getter(root, "@id"),
1✔
249
            "@name": getter(root, "@name"),
1✔
250
            Class: deArrayifyFeatureSetClass(root.Class)
1✔
251
        };
1✔
252
        return layer;
1✔
253
    });
1✔
254
}
1✔
255

256
function deArrayifyFeatureSet(json: any): FeatureSet | undefined {
1✔
257
    const root = json;
1✔
258
    if (root == null || root.length != 1) {
1!
259
        return undefined;
×
260
    }
×
261
    const fs = {
1✔
262
        Layer: deArrayifyFeatureSetLayers(root[0].Layer)
1✔
263
    };
1✔
264
    return fs;
1✔
265
}
1✔
266

267
function deArrayifyInlineSelectionImage(json: any): SelectionImage | undefined {
1✔
268
    const root = json;
1✔
269
    if (root == null || root.length != 1) {
1!
270
        return undefined;
1✔
271
    }
1!
272
    const getter = buildPropertyGetter<SelectionImage>();
×
273
    const img = {
×
274
        MimeType: getter(root[0], "MimeType"),
×
275
        Content: getter(root[0], "Content")
×
276
    };
×
277
    return img;
×
278
}
×
279

280
function deArrayifyFeatureProperties(json: any[]): FeatureProperty[] {
21✔
281
    const getter = buildPropertyGetter<FeatureProperty>();
21✔
282
    return (json || []).map(root => {
21!
283
        const prop = {
210✔
284
            Name: getter(root, "Name"),
210✔
285
            Value: getter(root, "Value")
210✔
286
        };
210✔
287
        return prop;
210✔
288
    });
21✔
289
}
21✔
290

291
function deArrayifyFeatures(json: any[]): SelectedFeature[] {
1✔
292
    const getter = buildPropertyGetter<SelectedFeature>();
1✔
293
    return (json || []).map(root => {
1!
294
        const feat = {
21✔
295
            Bounds: getter(root, "Bounds"),
21✔
296
            Property: deArrayifyFeatureProperties(root.Property),
21✔
297
            SelectionKey: getter(root, "SelectionKey")
21✔
298
        };
21✔
299
        return feat;
21✔
300
    });
1✔
301
}
1✔
302

303
function deArrayifyLayerMetadataProperties(json: any[]): LayerPropertyMetadata[] {
1✔
304
    const getter = buildPropertyGetter<LayerPropertyMetadata>();
1✔
305
    return (json || []).map(root => {
1!
306
        const prop = {
10✔
307
            DisplayName: getter(root, "DisplayName"),
10✔
308
            Name: getter(root, "Name"),
10✔
309
            Type: getter(root, "Type", "int")
10✔
310
        };
10✔
311
        return prop;
10✔
312
    });
1✔
313
}
1✔
314

315
function deArrayifyLayerMetadata(json: any): LayerMetadata | undefined {
1✔
316
    const root = json;
1✔
317
    //NOTE: root could be null if the layer selected has no properties beyond id/geom
318
    if (root == null || root.length != 1) {
1!
319
        return undefined;
×
320
    }
×
321
    const meta = {
1✔
322
        Property: deArrayifyLayerMetadataProperties(root[0].Property)
1✔
323
    };
1✔
324
    return meta;
1✔
325
}
1✔
326

327
function deArrayifySelectedLayer(json: any[]): SelectedLayer[] {
1✔
328
    const getter = buildPropertyGetter<SelectedLayer>();
1✔
329
    return (json || []).map(root => {
1!
330
        const layer = {
1✔
331
            "@id": getter(root, "@id"),
1✔
332
            "@name": getter(root, "@name"),
1✔
333
            Feature: deArrayifyFeatures(root.Feature),
1✔
334
            LayerMetadata: deArrayifyLayerMetadata(root.LayerMetadata)
1✔
335
        };
1✔
336
        return layer;
1✔
337
    });
1✔
338
}
1✔
339

340
function deArrayifySelectedFeatures(json: any): SelectedFeatureSet | undefined {
1✔
341
    const root = json;
1✔
342
    if (root == null || root.length != 1) {
1!
343
        return undefined;
×
344
    }
×
345
    const sf = {
1✔
346
        SelectedLayer: deArrayifySelectedLayer(root[0].SelectedLayer)
1✔
347
    };
1✔
348
    return sf;
1✔
349
}
1✔
350

351
function deArrayifyFeatureInformation(json: any): QueryMapFeaturesResponse {
1✔
352
    const root = json;
1✔
353
    const getter = buildPropertyGetter<QueryMapFeaturesResponse>();
1✔
354
    const resp = {
1✔
355
        FeatureSet: deArrayifyFeatureSet(root.FeatureSet),
1✔
356
        Hyperlink: getter(root, "Hyperlink"),
1✔
357
        InlineSelectionImage: deArrayifyInlineSelectionImage(root.InlineSelectionImage),
1✔
358
        SelectedFeatures: deArrayifySelectedFeatures(root.SelectedFeatures),
1✔
359
        Tooltip: getter(root, "Tooltip")
1✔
360
    };
1✔
361
    return resp;
1✔
362
}
1✔
363

364
function deArrayifyWebLayoutControl<T extends WebLayoutControl>(json: any): T {
2✔
365
    const root = json;
2✔
366
    if (root == null || root.length != 1) {
2!
367
        throw new MgError("Malformed input. Expected control element");
×
368
    }
×
369
    const getter = buildPropertyGetter<T>();
2✔
370
    const control: any = {
2✔
371
        Visible: getter(root[0], "Visible", "boolean")
2✔
372
    };
2✔
373
    return control;
2✔
374
}
2✔
375

376
function deArrayifyWebLayoutInfoPane(json: any): WebLayoutInfoPane {
1✔
377
    const root = json;
1✔
378
    if (root == null || root.length != 1) {
1!
379
        throw new MgError("Malformed input. Expected InformationPane element");
×
380
    }
×
381
    const getter = buildPropertyGetter<WebLayoutInfoPane>();
1✔
382
    const infoPane = {
1✔
383
        Visible: getter(root[0], "Visible", "boolean"),
1✔
384
        Width: getter(root[0], "Width", "int"),
1✔
385
        LegendVisible: getter(root[0], "LegendVisible", "boolean"),
1✔
386
        PropertiesVisible: getter(root[0], "PropertiesVisible", "boolean")
1✔
387
    };
1✔
388
    return infoPane;
1✔
389
}
1✔
390

391
function deArrayifyWebLayoutInitialView(json: any): MapView | undefined {
1✔
392
    const root = json;
1✔
393
    if (root == null || root.length != 1) {
1!
394
        return undefined;
1✔
395
    }
1!
396
    const getter = buildPropertyGetter<MapView>();
×
397
    const view = {
×
398
        CenterX: getter(root[0], "CenterX", "float"),
×
399
        CenterY: getter(root[0], "CenterY", "float"),
×
400
        Scale: getter(root[0], "Scale", "float")
×
401
    };
×
402
    return view;
×
403
}
×
404

405
function deArrayifyWebLayoutMap(json: any): WebLayoutMap {
1✔
406
    const root = json;
1✔
407
    if (root == null || root.length != 1) {
1!
408
        throw new MgError("Malformed input. Expected Map element");
×
409
    }
×
410
    const getter = buildPropertyGetter<WebLayoutMap>();
1✔
411
    const map = {
1✔
412
        ResourceId: getter(root[0], "ResourceId"),
1✔
413
        InitialView: deArrayifyWebLayoutInitialView(root[0].InitialView),
1✔
414
        HyperlinkTarget: getter(root[0], "HyperlinkTarget"),
1✔
415
        HyperlinkTargetFrame: getter(root[0], "HyperlinkTargetFrame")
1✔
416
    };
1✔
417
    return map;
1✔
418
}
1✔
419

420
function deArrayifyTaskButton(json: any): TaskButton {
4✔
421
    const root = json;
4✔
422
    if (root == null || root.length != 1) {
4!
423
        throw new MgError("Malformed input. Expected TaskButton element");
×
424
    }
×
425
    const getter = buildPropertyGetter<TaskButton>();
4✔
426
    const button = {
4✔
427
        Name: getter(root[0], "Name"),
4✔
428
        Tooltip: getter(root[0], "Tooltip"),
4✔
429
        Description: getter(root[0], "Description"),
4✔
430
        ImageURL: getter(root[0], "ImageURL"),
4✔
431
        DisabledImageURL: getter(root[0], "DisabledImageURL")
4✔
432
    };
4✔
433
    return button;
4✔
434
}
4✔
435

436
function deArrayifyWebLayoutTaskBar(json: any): WebLayoutTaskBar {
1✔
437
    const root = json;
1✔
438
    if (root == null || root.length != 1) {
1!
439
        throw new MgError("Malformed input. Expected TaskBar element");
×
440
    }
×
441
    const getter = buildPropertyGetter<WebLayoutTaskBar>();
1✔
442
    const taskbar = {
1✔
443
        Visible: getter(root[0], "Visible", "boolean"),
1✔
444
        Home: deArrayifyTaskButton(root[0].Home),
1✔
445
        Forward: deArrayifyTaskButton(root[0].Forward),
1✔
446
        Back: deArrayifyTaskButton(root[0].Back),
1✔
447
        Tasks: deArrayifyTaskButton(root[0].Tasks),
1✔
448
        MenuButton: [] as UIItem[]
1✔
449
    };
1✔
450
    if (root[0].MenuButton) {
1✔
451
        for (const mb of root[0].MenuButton) {
1✔
452
            taskbar.MenuButton.push(deArrayifyUIItem(mb));
8✔
453
        }
8✔
454
    }
1✔
455
    return taskbar;
1✔
456
}
1✔
457

458
function deArrayifyWebLayoutTaskPane(json: any): WebLayoutTaskPane {
1✔
459
    const root = json;
1✔
460
    if (root == null || root.length != 1) {
1!
461
        throw new MgError("Malformed input. Expected TaskPane element");
×
462
    }
×
463
    const getter = buildPropertyGetter<WebLayoutTaskPane>();
1✔
464
    const taskPane = {
1✔
465
        Visible: getter(root[0], "Visible", "boolean"),
1✔
466
        InitialTask: getter(root[0], "InitialTask"),
1✔
467
        Width: getter(root[0], "Width", "int"),
1✔
468
        TaskBar: deArrayifyWebLayoutTaskBar(root[0].TaskBar)
1✔
469
    };
1✔
470
    return taskPane;
1✔
471
}
1✔
472

473
function deArrayifyUIItem(json: any): UIItem {
58✔
474
    const root = json;
58✔
475
    const getter = buildPropertyGetter<UIItem & CommandUIItem & FlyoutUIItem>();
58✔
476
    const func: string = getter(root, "Function");
58✔
477
    //Wouldn't it be nice if we could incrementally build up a union type that then becomes a specific
478
    //type once certain properties are set?
479
    //
480
    //Well, that's currently not possible. So we have to resort to "any"
481
    const item: any = {
58✔
482
        Function: func
58✔
483
    };
58✔
484
    switch (func) {
58✔
485
        case "Command":
58✔
486
            item.Command = getter(root, "Command");
43✔
487
            break;
43✔
488
        case "Flyout":
58✔
489
            item.Label = getter(root, "Label");
3✔
490
            item.Tooltip = getter(root, "Tooltip");
3✔
491
            item.Description = getter(root, "Description");
3✔
492
            item.ImageURL = getter(root, "ImageURL");
3✔
493
            item.DisabledImageURL = getter(root, "DisabledImageURL");
3✔
494
            item.SubItem = [];
3✔
495
            for (const si of root.SubItem) {
3✔
496
                item.SubItem.push(deArrayifyUIItem(si));
11✔
497
            }
11✔
498
            break;
3✔
499
    }
58✔
500
    return item;
58✔
501
}
58✔
502

503
function deArrayifyItemContainer<T extends WebLayoutControl>(json: any, name: string): T {
2✔
504
    const root = json;
2✔
505
    if (root == null || root.length != 1) {
2!
506
        throw new MgError("Malformed input. Expected container element");
×
507
    }
×
508
    const getter = buildPropertyGetter<T>();
2✔
509
    const container: any = {};
2✔
510
    container[name] = [];
2✔
511
    for (const item of root[0][name]) {
2✔
512
        container[name].push(deArrayifyUIItem(item));
39✔
513
    }
39✔
514
    if (typeof (root[0].Visible) != 'undefined') {
2✔
515
        container.Visible = getter(root[0], "Visible", "boolean");
2✔
516
    }
2✔
517
    return container;
2✔
518
}
2✔
519

520
function deArrayifyWebLayoutSearchResultColumnSet(json: any): ResultColumnSet {
×
521
    const root = json;
×
522
    if (root == null || root.length != 1) {
×
523
        throw new MgError("Malformed input. Expected ResultColumns element");
×
524
    }
×
525
    const getter = buildPropertyGetter<ResultColumn>();
×
526
    const res = {
×
527
        Column: [] as ResultColumn[]
×
528
    };
×
529
    for (const col of root[0].Column) {
×
530
        res.Column.push({
×
531
            Name: getter(col, "Name"),
×
532
            Property: getter(col, "Property")
×
533
        });
×
534
    }
×
535
    return res;
×
536
}
×
537

538
function deArrayifyWebLayoutInvokeURLLayerSet(json: any): LayerSet | undefined {
5✔
539
    const root = json;
5✔
540
    if (root == null || root.length != 1) {
5!
541
        return undefined;
5✔
542
    }
5!
543
    const layerset = {
×
544
        Layer: root[0].Layer
×
545
    };
×
546
    return layerset;
×
547
}
×
548

549
function deArrayifyWebLayoutParameterPairs(json: any): ParameterPair[] {
5✔
550
    const root = json;
5✔
551
    const pairs = [] as ParameterPair[]
5✔
552
    if (!root) {
5✔
553
        return pairs;
5✔
554
    }
5!
555
    const getter = buildPropertyGetter<ParameterPair>();
×
556
    for (const kvp of root) {
×
557
        pairs.push({
×
558
            Key: getter(kvp, "Key"),
×
559
            Value: getter(kvp, "Value")
×
560
        });
×
561
    }
×
562
    return pairs;
×
563
}
×
564

565
function deArrayifyCommand(json: any): CommandDef {
33✔
566
    const root = json;
33✔
567
    const getter = buildPropertyGetter<CommandDef & BasicCommandDef & InvokeScriptCommandDef & InvokeURLCommandDef & SearchCommandDef>();
33✔
568
    const cmd: any = {
33✔
569
        "@xsi:type": getter(root, "@xsi:type"),
33✔
570
        Name: getter(root, "Name"),
33✔
571
        Label: getter(root, "Label"),
33✔
572
        Tooltip: getter(root, "Tooltip"),
33✔
573
        Description: getter(root, "Description"),
33✔
574
        ImageURL: getter(root, "ImageURL"),
33✔
575
        DisabledImageURL: getter(root, "DisabledImageURL"),
33✔
576
        TargetViewer: getter(root, "TargetViewer")
33✔
577
    };
33✔
578
    //Basic
579
    if (typeof (root.Action) != 'undefined') {
33✔
580
        cmd.Action = getter(root, "Action");
21✔
581
    }
21✔
582
    //Targeted
583
    if (typeof (root.Target) != 'undefined') {
33✔
584
        cmd.Target = getter(root, "Target");
11✔
585
    }
11✔
586
    if (typeof (root.TargetFrame) != 'undefined') {
33!
587
        cmd.TargetFrame = getter(root, "TargetFrame");
×
588
    }
×
589
    //Search
590
    if (typeof (root.Layer) != 'undefined') {
33!
591
        cmd.Layer = getter(root, "Layer");
×
592
        cmd.Prompt = getter(root, "Prompt");
×
593
        cmd.ResultColumns = deArrayifyWebLayoutSearchResultColumnSet(root.ResultColumns);
×
594
        cmd.Filter = getter(root, "Filter");
×
595
        cmd.MatchLimit = getter(root, "MatchLimit", "int");
×
596
    }
×
597
    //InvokeURL | Help
598
    if (typeof (root.URL) != 'undefined') {
33✔
599
        cmd.URL = getter(root, "URL");
5✔
600
    }
5✔
601
    if (typeof (root.DisableIfSelectionEmpty) != 'undefined') {
33✔
602
        cmd.LayerSet = deArrayifyWebLayoutInvokeURLLayerSet(root.LayerSet);
5✔
603
        cmd.AdditionalParameter = deArrayifyWebLayoutParameterPairs(root.AdditionalParameter);
5✔
604
        cmd.DisableIfSelectionEmpty = getter(root, "DisableIfSelectionEmpty", "boolean");
5✔
605
    }
5✔
606
    //InvokeScript
607
    if (typeof (root.Script) != 'undefined') {
33!
608
        cmd.Script = getter(root, "Script");
×
609
    }
×
610
    return cmd;
33✔
611
}
33✔
612

613
function deArrayifyWebLayoutCommandSet(json: any): WebLayoutCommandSet {
1✔
614
    const root = json;
1✔
615
    if (root == null || root.length != 1) {
1!
616
        throw new MgError("Malformed input. Expected CommandSet element");
×
617
    }
×
618
    const set = {
1✔
619
        Command: [] as CommandDef[]
1✔
620
    };
1✔
621
    if (root[0].Command) {
1✔
622
        for (const cmd of root[0].Command) {
1✔
623
            set.Command.push(deArrayifyCommand(cmd));
33✔
624
        }
33✔
625
    }
1✔
626
    return set;
1✔
627
}
1✔
628

629
function deArrayifyWebLayout(json: any): WebLayout {
1✔
630
    const root = json;
1✔
631
    const getter = buildPropertyGetter<WebLayout>();
1✔
632
    const resp = {
1✔
633
        Title: getter(root, "Title"),
1✔
634
        Map: deArrayifyWebLayoutMap(root.Map),
1✔
635
        EnablePingServer: getter(root, "EnablePingServer", "boolean"),
1✔
636
        SelectionColor: getter(root, "SelectionColor"),
1✔
637
        PointSelectionBuffer: getter(root, "PointSelectionBuffer", "int"),
1✔
638
        MapImageFormat: getter(root, "MapImageFormat"),
1✔
639
        SelectionImageFormat: getter(root, "SelectionImageFormat"),
1✔
640
        StartupScript: getter(root, "StartupScript"),
1✔
641
        ToolBar: deArrayifyItemContainer<WebLayoutToolbar>(root.ToolBar, "Button"),
1✔
642
        InformationPane: deArrayifyWebLayoutInfoPane(root.InformationPane),
1✔
643
        ContextMenu: deArrayifyItemContainer<WebLayoutContextMenu>(root.ContextMenu, "MenuItem"),
1✔
644
        TaskPane: deArrayifyWebLayoutTaskPane(root.TaskPane),
1✔
645
        StatusBar: deArrayifyWebLayoutControl<WebLayoutStatusBar>(root.StatusBar),
1✔
646
        ZoomControl: deArrayifyWebLayoutControl<WebLayoutZoomControl>(root.ZoomControl),
1✔
647
        CommandSet: deArrayifyWebLayoutCommandSet(root.CommandSet)
1✔
648
    };
1✔
649
    return resp;
1✔
650
}
1✔
651

652
function deArrayifyMapGroup(json: any): MapSetGroup {
12✔
653
    const root = json;
12✔
654
    if (root == null) {
12!
655
        throw new MgError("Malformed input. Expected MapGroup element");
×
656
    }
×
657
    const getter = buildPropertyGetter<MapSetGroup & MapInitialView & MapConfiguration>();
12✔
658
    const mapGroup: MapSetGroup = {
12✔
659
        "@id": getter(root, "@id", "string"),
12✔
660
        InitialView: undefined,
12✔
661
        Map: [] as MapConfiguration[]
12✔
662
    };
12✔
663
    if (root.InitialView) {
12✔
664
        const iview = root.InitialView[0];
2✔
665
        mapGroup.InitialView = {
2✔
666
            CenterX: getter(iview, "CenterX", "float"),
2✔
667
            CenterY: getter(iview, "CenterY", "float"),
2✔
668
            Scale: getter(iview, "Scale", "float")
2✔
669
        };
2✔
670
    }
2✔
671
    if (root.Map) {
12✔
672
        for (const m of root.Map) {
12✔
673
            mapGroup.Map.push({
22✔
674
                Type: getter(m, "Type", "string"),
22✔
675
                //SingleTile: getter(m, "SingleTile", "boolean"),
676
                Extension: deArrayifyExtension(m.Extension)
22✔
677
            });
22✔
678
        }
22✔
679
    }
12✔
680
    if (root.Extension) {
12!
NEW
681
        mapGroup.Extension = deArrayifyExtension(root.Extension);
×
NEW
682
    }
×
683
    return mapGroup;
12✔
684
}
12✔
685

686
function deArrayifyMapSet(json: any): MapSet | undefined {
3✔
687
    const root = json;
3✔
688
    if (root == null || root.length != 1) {
3!
689
        throw new MgError("Malformed input. Expected MapSet element");
×
690
    }
×
691
    const set = {
3✔
692
        MapGroup: [] as MapSetGroup[]
3✔
693
    };
3✔
694
    if (root[0].MapGroup) {
3✔
695
        for (const map of root[0].MapGroup) {
3✔
696
            set.MapGroup.push(deArrayifyMapGroup(map));
12✔
697
        }
12✔
698
    }
3✔
699
    return set;
3✔
700
}
3✔
701

702
function deArrayifyContainerItems(json: any[]): ContainerItem[] {
33✔
703
    const items = [] as ContainerItem[];
33✔
704
    const getter = buildPropertyGetter<ContainerItem & FlyoutItem & WidgetItem>();
33✔
705
    if (json && json.length) {
33✔
706
        for (const i of json) {
28✔
707
            const func = getter(i, "Function", "string");
256✔
708
            switch (func) {
256✔
709
                case "Separator":
256✔
710
                    items.push({
25✔
711
                        Function: "Separator"
25✔
712
                    });
25✔
713
                    break;
25✔
714
                case "Widget":
256✔
715
                    items.push({
219✔
716
                        Function: "Widget",
219✔
717
                        Widget: getter(i, "Widget", "string")
219✔
718
                    })
219✔
719
                    break;
219✔
720
                case "Flyout":
256✔
721
                    items.push({
12✔
722
                        Function: "Flyout",
12✔
723
                        Label: getter(i, "Label", "string"),
12✔
724
                        Tooltip: getter(i, "Tooltip", "string"),
12✔
725
                        ImageUrl: getter(i, "ImageUrl", "string"),
12✔
726
                        ImageClass: getter(i, "ImageClass", "string"),
12✔
727
                        Item: deArrayifyContainerItems(i.Item || [])
12✔
728
                    })
12✔
729
                    break;
12✔
730
            }
256✔
731
        }
256✔
732
    }
28✔
733
    return items;
33✔
734
}
33✔
735

736
function deArrayifyContainer(json: any[]): ContainerDefinition[] {
3✔
737
    const containers = [] as ContainerDefinition[];
3✔
738
    const getter = buildPropertyGetter<ContainerDefinition>();
3✔
739
    for (const c of json) {
3✔
740
        containers.push({
21✔
741
            Name: getter(c, "Name", "string"),
21✔
742
            Type: getter(c, "Type", "string"),
21✔
743
            //Position: getter(c, "Position", "string"),
744
            Extension: deArrayifyExtension(c.Extension),
21✔
745
            Item: deArrayifyContainerItems(c.Item)
21✔
746
        });
21✔
747
    }
21✔
748
    return containers;
3✔
749
}
3✔
750

751
function deArrayifyWidgets(json: any[]): Widget[] {
3✔
752
    const widgets = [] as Widget[];
3✔
753
    for (const w of json) {
3✔
754
        if (w["@xsi:type"] == "UiWidgetType") {
211✔
755
            const uiw = deArrayifyUiWidget(w);
174✔
756
            widgets.push(uiw);
174✔
757
        } else {
211✔
758
            widgets.push(deArrayifyWidget(w));
37✔
759
        }
37✔
760
    }
211✔
761
    return widgets;
3✔
762
}
3✔
763

764
function deArrayifyWidget(json: any): Widget {
37✔
765
    const root = json;
37✔
766
    if (root == null) {
37!
767
        throw new MgError("Malformed input. Expected Widget element");
×
768
    }
×
769
    const getter = buildPropertyGetter<Widget & { "@xsi:type": string }>();
37✔
770
    const w: Widget = {
37✔
771
        WidgetType: getter(root, "@xsi:type", "string"),
37✔
772
        Name: getter(root, "Name", "string"),
37✔
773
        Type: getter(root, "Type", "string"),
37✔
774
        //Location: getter(root, "Location", "string"),
775
        Extension: deArrayifyExtension(root.Extension)
37✔
776
    };
37✔
777
    return w;
37✔
778
}
37✔
779

780
function deArrayifyExtension(json: any, arrayCheck: boolean = true): any {
301✔
781
    const root = json;
301✔
782
    if (root == null) {
301✔
783
        return null;
93✔
784
    }
93✔
785
    if (arrayCheck && root.length != 1) {
301!
786
        throw new MgError("Malformed input. Expected Extension element");
×
787
    }
✔
788
    const getter = buildPropertyGetter<{ Key: string, Value: string, [key: string]: string}>();
208✔
789
    const ext: any = {};
208✔
790
    for (const key in root[0]) {
301✔
791
        if (Array.isArray(root[0][key])) {
571✔
792
            //Special case handling
793
            switch (key) {
530✔
794
                case "AdditionalParameter":
530!
795
                    {
×
796
                        const params = [];
×
797
                        for (const p of root[0][key]) {
×
798
                            params.push({
×
799
                                Key: getter(p, "Key", "string"),
×
800
                                Value: getter(p, "Value", "string")
×
801
                            });
×
802
                        }
×
803
                        ext[key] = params;
×
804
                    }
×
805
                    break;
×
806
                case "Projection":
530✔
807
                    {
2✔
808
                        ext[key] = root[0][key];
2✔
809
                    }
2✔
810
                    break;
2✔
811
                default:
530✔
812
                    ext[key] = getter(root[0], key, "string");
528✔
813
                    break;
528✔
814
            }
530✔
815
        } else {
571✔
816
            ext[key] = deArrayifyExtension(root[0][key], false);
41✔
817
        }
41✔
818
    }
571✔
819
    return ext;
208✔
820
}
208✔
821

822
function deArrayifyUiWidget(json: any): UIWidget {
174✔
823
    const root = json;
174✔
824
    if (root == null) {
174!
825
        throw new MgError("Malformed input. Expected Widget element");
×
826
    }
×
827
    const getter = buildPropertyGetter<UIWidget & { "@xsi:type": string }>();
174✔
828
    const w: UIWidget = {
174✔
829
        WidgetType: getter(root, "@xsi:type", "string"),
174✔
830
        ImageUrl: getter(root, "ImageUrl", "string"),
174✔
831
        ImageClass: getter(root, "ImageClass", "string"),
174✔
832
        Label: getter(root, "Label", "string"),
174✔
833
        Tooltip: getter(root, "Tooltip", "string"),
174✔
834
        StatusText: getter(root, "StatusText", "string"),
174✔
835
        Disabled: getter(root, "Disabled", "boolean"),
174✔
836
        Name: getter(root, "Name", "string"),
174✔
837
        Type: getter(root, "Type", "string"),
174✔
838
        //Location: getter(root, "Location", "string"),
839
        Extension: deArrayifyExtension(root.Extension)
174✔
840
    };
174✔
841
    return w;
174✔
842
}
174✔
843

844
function deArrayifyMapWidget(json: any): MapWidget {
3✔
845
    const root = json;
3✔
846
    if (root == null || root.length != 1) {
3!
847
        throw new MgError("Malformed input. Expected MapWidget element");
×
848
    }
×
849
    const getter = buildPropertyGetter<MapWidget & { "@xsi:type": string }>();
3✔
850
    const mw: MapWidget = {
3✔
851
        WidgetType: getter(root, "@xsi:type", "string"),
3✔
852
        MapId: getter(root, "MapId", "string"),
3✔
853
        Name: getter(root, "Name", "string"),
3✔
854
        Type: getter(root, "Type", "string"),
3✔
855
        //Location: getter(root, "Location", "string"),
856
        Extension: deArrayifyExtension(root.Extension)
3✔
857
    };
3✔
858
    return mw;
3✔
859
}
3✔
860

861
function deArrayifyWidgetSet(json: any): WidgetSet[] {
3✔
862
    const widgetSet = [] as WidgetSet[];
3✔
863
    for (const ws of json) {
3✔
864
        widgetSet.push({
3✔
865
            Container: deArrayifyContainer(ws.Container),
3✔
866
            MapWidget: deArrayifyMapWidget(ws.MapWidget),
3✔
867
            Widget: deArrayifyWidgets(ws.Widget)
3✔
868
        })
3✔
869
    }
3✔
870
    return widgetSet;
3✔
871
}
3✔
872

873
function deArrayifyFlexibleLayout(json: any): ApplicationDefinition {
3✔
874
    const root = json;
3✔
875
    const getter = buildPropertyGetter<ApplicationDefinition>();
3✔
876
    const resp = {
3✔
877
        Title: getter(root, "Title"),
3✔
878
        TemplateUrl: getter(root, "TemplateUrl"),
3✔
879
        MapSet: deArrayifyMapSet(root.MapSet),
3✔
880
        WidgetSet: deArrayifyWidgetSet(root.WidgetSet),
3✔
881
        Extension: deArrayifyExtension(root.Extension)
3✔
882
    };
3✔
883
    return resp;
3✔
884
}
3✔
885

886
function deArrayifyMapDefinitionGroups(json: any): MapDefinitionLayerGroup[] {
4✔
887
    const groups = [] as MapDefinitionLayerGroup[];
4✔
888
    const getter = buildPropertyGetter<MapDefinitionLayerGroup>();
4✔
889
    for (const g of json) {
4✔
890
        groups.push({
16✔
891
            Name: getter(g, "Name"),
16✔
892
            ExpandInLegend: getter(g, "ExpandInLegend", "boolean"),
16✔
893
            ShowInLegend: getter(g, "ShowInLegend", "boolean"),
16✔
894
            Visible: getter(g, "Visible", "boolean"),
16✔
895
            LegendLabel: getter(g, "LegendLabel"),
16✔
896
            Group: getter(g, "Group")
16✔
897
        });
16✔
898
    }
16✔
899
    return groups;
4✔
900
}
4✔
901

902
function deArrayifyMapDefinitionLayers(json: any): MdfLayer[] {
4✔
903
    const layers = [] as MdfLayer[];
4✔
904
    const getter = buildPropertyGetter<MdfLayer>();
4✔
905
    for (const g of json) {
4✔
906
        layers.push({
50✔
907
            Name: getter(g, "Name"),
50✔
908
            ResourceId: getter(g, "ResourceId"),
50✔
909
            ExpandInLegend: getter(g, "ExpandInLegend", "boolean"),
50✔
910
            ShowInLegend: getter(g, "ShowInLegend", "boolean"),
50✔
911
            Selectable: getter(g, "Selectable", "boolean"),
50✔
912
            Visible: getter(g, "Visible", "boolean"),
50✔
913
            LegendLabel: getter(g, "LegendLabel"),
50✔
914
            Group: getter(g, "Group"),
50✔
915
        });
50✔
916
    }
50✔
917
    return layers;
4✔
918
}
4✔
919

920
function deArrayifyMapDefinition(json: any): MapDefinition {
4✔
921
    const root = json;
4✔
922
    const getter = buildPropertyGetter<MapDefinition>();
4✔
923
    const eGetter = buildPropertyGetter<MapDefinition["Extents"]>();
4✔
924
    const resp: MapDefinition = {
4✔
925
        BackgroundColor: getter(root, "BackgroundColor"),
4✔
926
        CoordinateSystem: getter(root, "CoordinateSystem"),
4✔
927
        Extents: {
4✔
928
            MinX: eGetter(root.Extents[0], "MinX", "float"),
4✔
929
            MinY: eGetter(root.Extents[0], "MinY", "float"),
4✔
930
            MaxX: eGetter(root.Extents[0], "MaxX", "float"),
4✔
931
            MaxY: eGetter(root.Extents[0], "MaxY", "float")
4✔
932
        },
4✔
933
        MapLayer: deArrayifyMapDefinitionLayers(root.MapLayer ?? []),
4!
934
        MapLayerGroup: deArrayifyMapDefinitionGroups(root.MapLayerGroup ?? [])
4✔
935
    };
4✔
936
    if (root.TileSetSource) {
4!
937
        const tGetter = buildPropertyGetter<TileSetSource>();
×
938
        resp.TileSetSource = {
×
939
            ResourceId: tGetter(root.TileSetSource, "ResourceId")
×
940
        };
×
941
    }
×
942
    return resp;
4✔
943
}
4✔
944

945
function deArrayifyTileSetDefinitionLayers(json: any): BaseMapLayer[] {
1✔
946
    const getter = buildPropertyGetter<BaseMapLayer>();
1✔
947
    const layers = [] as BaseMapLayer[];
1✔
948
    for (const l of json) {
1✔
949
        layers.push({
7✔
950
            Name: getter(l, "Name"),
7✔
951
            ResourceId: getter(l, "ResourceId"),
7✔
952
            Selectable: getter(l, "Selectable", "boolean"),
7✔
953
            ShowInLegend: getter(l, "ShowInLegend", "boolean"),
7✔
954
            LegendLabel: getter(l, "LegendLabel"),
7✔
955
            ExpandInLegend: getter(l, "ExpandInLegend", "boolean")
7✔
956
        })
7✔
957
    }
7✔
958
    return layers;
1✔
959
}
1✔
960

961
function deArrayifyTileSetDefinitionGroups(json: any): BaseMapLayerGroup[] {
1✔
962
    const getter = buildPropertyGetter<BaseMapLayerGroup>();
1✔
963
    const groups = []  as BaseMapLayerGroup[];
1✔
964
    for (const g of json) {
1✔
965
        groups.push({
1✔
966
            Name: getter(g, "Name"),
1✔
967
            Visible: getter(g, "Visible", "boolean"),
1✔
968
            ShowInLegend: getter(g, "ShowInLegend", "boolean"),
1✔
969
            ExpandInLegend: getter(g, "ExpandInLegend", "boolean"),
1✔
970
            LegendLabel: getter(g, "LegendLabel"),
1✔
971
            BaseMapLayer: deArrayifyTileSetDefinitionLayers(g.BaseMapLayer)
1✔
972
        });
1✔
973
    }
1✔
974
    return groups;
1✔
975
}
1✔
976

977
function deArrayifyTileSetDefinitionParamList(root: any): { Name: string, Value: string }[] {
1✔
978
    const getter = buildPropertyGetter<{ Name: string, Value: string }>();
1✔
979
    const params = [] as { Name: string, Value: string }[];
1✔
980
    for (const p of root) {
1✔
981
        params.push({
2✔
982
            Name: getter(p, "Name"),
2✔
983
            Value: getter(p, "Value")
2✔
984
        });
2✔
985
    }
2✔
986
    return params;
1✔
987
}
1✔
988

989
function deArrayifyTileSetDefinitionParams(root: any): TileStoreParameters {
1✔
990
    const getter = buildPropertyGetter<TileStoreParameters>();
1✔
991
    const tsp: TileStoreParameters = {
1✔
992
        TileProvider: getter(root, "TileProvider"),
1✔
993
        Parameter: deArrayifyTileSetDefinitionParamList(root[0].Parameter)
1✔
994
    };
1✔
995
    return tsp;
1✔
996
}
1✔
997

998
function deArrayifyTileSetDefinition(json: any): TileSetDefinition {
1✔
999
    const root = json;
1✔
1000
    const eGetter = buildPropertyGetter<TileSetDefinition["Extents"]>();
1✔
1001
    const resp: TileSetDefinition = {
1✔
1002
        Extents: {
1✔
1003
            MinX: eGetter(root.Extents[0], "MinX", "float"),
1✔
1004
            MinY: eGetter(root.Extents[0], "MinY", "float"),
1✔
1005
            MaxX: eGetter(root.Extents[0], "MaxX", "float"),
1✔
1006
            MaxY: eGetter(root.Extents[0], "MaxY", "float")
1✔
1007
        },
1✔
1008
        TileStoreParameters: deArrayifyTileSetDefinitionParams(json.TileStoreParameters),
1✔
1009
        BaseMapLayerGroup: deArrayifyTileSetDefinitionGroups(json.BaseMapLayerGroup)
1✔
1010
    };
1✔
1011
    return resp;
1✔
1012
}
1✔
1013

1014
/**
1015
 * Indicates if the de-arrayified result is a {@link WebLayout}
1016
 * 
1017
 * @since 0.14
1018
 */
1019
export function isWebLayout(arg: DeArrayifiedResult): arg is WebLayout {
1✔
1020
    return (arg as any).CommandSet != null
1✔
1021
        && (arg as any).ContextMenu != null
1✔
1022
        && (arg as any).Map != null
1✔
1023
}
1✔
1024

1025
/**
1026
 * Indicates if the de-arrayified result is an {@link ApplicationDefinition}
1027
 * 
1028
 * @since 0.14
1029
 */
1030
export function isAppDef(arg: DeArrayifiedResult): arg is ApplicationDefinition {
1✔
1031
    return (arg as any).WidgetSet != null;
3✔
1032
}
3✔
1033

1034
/**
1035
 * Indicates if the de-arrayified result is a {@link MapDefinition}
1036
 * 
1037
 * @since 0.14
1038
 */
1039
export function isMapDef(arg: DeArrayifiedResult): arg is MapDefinition {
1✔
1040
    return (arg as any).Extents != null
4✔
1041
        && (arg as any).BackgroundColor != null
4✔
1042
        && (arg as any).CoordinateSystem != null
4✔
1043
        && (arg as any).MapLayer != null
4✔
1044
        && (arg as any).MapLayerGroup != null;
4✔
1045
}
4✔
1046

1047
/**
1048
 * Indicates if the de-arrayified result is a {@link TileSetDefinition}
1049
 * 
1050
 * @since 0.14
1051
 */
1052
export function isTileSet(arg: DeArrayifiedResult): arg is TileSetDefinition {
1✔
1053
    return (arg as any).Extents != null
1✔
1054
        && (arg as any).TileStoreParameters != null
1✔
1055
        && (arg as any).BaseMapLayerGroup != null;
1✔
1056
}
1✔
1057

1058
/**
1059
 * Indicates if the de-arrayified result is a {@link SiteVersionResponse}
1060
 * 
1061
 * @since 0.14
1062
 */
1063
export function isSiteVersion(arg: DeArrayifiedResult): arg is SiteVersionResponse {
1✔
1064
    return (arg as any).Version != null;
×
1065
}
×
1066

1067
/**
1068
 * Indicates if the de-arrayified result is a {@link QueryMapFeaturesResponse}
1069
 * 
1070
 * @since 0.14
1071
 */
1072
export function isQueryMapFeaturesResponse(arg: DeArrayifiedResult): arg is QueryMapFeaturesResponse {
1✔
1073
    return (arg as any).FeatureSet != null
1!
1074
        || (arg as any).Hyperlink != null
×
1075
        || (arg as any).InlineSelectionImage != null
×
1076
        || (arg as any).SelectedFeatures != null
×
1077
        || (arg as any).Tooltip != null;
×
1078
}
1✔
1079

1080
/**
1081
 * The result of the normalization of JSON from the mapagent
1082
 * 
1083
 * @since 0.14
1084
 */
1085
export type DeArrayifiedResult = RuntimeMap | QueryMapFeaturesResponse | WebLayout | ApplicationDefinition | MapDefinition | TileSetDefinition | SiteVersionResponse;
1086

1087
/**
1088
 * Normalizes the given JSON object to match the content model of its original XML form
1089
 *
1090
 * @param {*} json The JSON object to normalize
1091
 * @returns {*} The normalized JSON object
1092
 */
1093
export function deArrayify(json: any): DeArrayifiedResult {
1✔
1094
    if (json["RuntimeMap"]) {
13✔
1095
        return deArrayifyRuntimeMap(json.RuntimeMap);
3✔
1096
    }
3✔
1097
    if (json["FeatureInformation"]) {
13✔
1098
        return deArrayifyFeatureInformation(json.FeatureInformation);
1✔
1099
    }
1✔
1100
    if (json["WebLayout"]) {
13✔
1101
        return deArrayifyWebLayout(json.WebLayout);
1✔
1102
    }
1✔
1103
    if (json["ApplicationDefinition"]) {
13✔
1104
        return deArrayifyFlexibleLayout(json.ApplicationDefinition);
3✔
1105
    }
3✔
1106
    if (json["MapDefinition"]) {
13✔
1107
        return deArrayifyMapDefinition(json.MapDefinition);
4✔
1108
    }
4✔
1109
    if (json["TileSetDefinition"]) {
1✔
1110
        return deArrayifyTileSetDefinition(json.TileSetDefinition);
1✔
1111
    }
1!
1112
    if (json["SiteVersion"]) {
×
1113
        return {
×
1114
            Version: json.SiteVersion.Version[0]
×
1115
        } as SiteVersionResponse;
×
1116
    }
×
1117
    const keys = [] as string[];
×
1118
    for (const k in json) {
×
1119
        keys.push(k);
×
1120
    }
×
1121
    throw new MgError(`Unsure how to process JSON response. Root elements are: (${keys.join(", ")})`);
×
1122
}
×
1123

1124
/**
1125
 * Builds an XML selection string from the given selection set.
1126
 *
1127
 * @param {(FeatureSet | undefined)} selection The selection set
1128
 * @param {string[]} [layerIds] If specified, the selection XML will only include selections from the specified layers
1129
 * @returns {string} The selection XML string
1130
 */
1131
export function buildSelectionXml(selection: FeatureSet | undefined, layerIds?: string[]): string {
1✔
1132
    let idCount = 0;
×
1133
    let xml = '<?xml version="1.0" encoding="utf-8"?>';
×
1134
    xml += '<FeatureSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FeatureSet-1.0.0.xsd">';
×
1135
    if (selection) {
×
1136
        const selLayers = selection.Layer;
×
1137
        for (const layer of selLayers) {
×
1138
            const layerId = layer["@id"];
×
1139
            if (layerIds != null && layerIds.indexOf(layerId) < 0) {
×
1140
                continue;
×
1141
            }
×
1142
            const cls = layer.Class;
×
1143
            if (cls.ID.length === 0) { // Don't bother writing out empty Layer/Class elements
×
1144
                continue;
×
1145
            }
×
1146
            xml += `<Layer id="${layerId}">`;
×
1147
            xml += `<Class id="${cls["@id"]}">`;
×
1148
            for (const id of cls.ID) {
×
1149
                xml += `<ID>${id}</ID>`;
×
1150
                idCount++;
×
1151
            }
×
1152
            xml += '</Class>';
×
1153
            xml += '</Layer>';
×
1154
        }
×
1155
    }
×
1156
    xml += '</FeatureSet>';
×
1157
    // If we didn't write any ids, just return an empty string instead
1158
    return idCount > 0 ? xml : '';
×
1159
}
×
1160

1161
/**
1162
 * Can only be used for a v4.0.0 or higher QUERYMAPFEATURES request
1163
 * 
1164
 * @param selection Current selection set
1165
 * @param feat The active selected feature
1166
 */
1167
export function getActiveSelectedFeatureXml(selection: FeatureSet, feat: ActiveSelectedFeature): string | undefined {
1✔
1168
    for (const layer of selection.Layer) {
×
1169
        const layerId = layer["@id"];
×
1170
        if (layerId == feat.layerId) {
×
1171
            const key = feat.selectionKey;
×
1172
            let xml = '<?xml version="1.0" encoding="utf-8"?>';
×
1173
            xml += '<FeatureSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FeatureSet-1.0.0.xsd">';
×
1174
            xml += `<Layer id="${layerId}">`;
×
1175
            xml += `<Class id="${layer.Class["@id"]}">`;
×
1176
            xml += `<ID>${key}</ID>`;
×
1177
            xml += '</Class>';
×
1178
            xml += '</Layer>';
×
1179
            xml += '</FeatureSet>';
×
1180
            return xml;
×
1181
        }
×
1182
    }
×
1183
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc