@stormstreaming/stormstreamer 0.9.2-beta.2 → 0.9.3-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +130 -3
  2. package/dist/amd/index.js +2206 -105
  3. package/dist/cjs/index.js +3 -3
  4. package/dist/esm/index.js +3 -3
  5. package/dist/iife/index.js +4 -4
  6. package/dist/types/StormStreamer.d.ts +394 -3
  7. package/dist/types/config/AudioData.d.ts +46 -0
  8. package/dist/types/config/ConfigManager.d.ts +47 -0
  9. package/dist/types/config/DebugData.d.ts +108 -0
  10. package/dist/types/config/IConfig.d.ts +3 -0
  11. package/dist/types/config/SettingsData.d.ts +114 -0
  12. package/dist/types/config/StorageData.d.ts +46 -0
  13. package/dist/types/config/StreamData.d.ts +75 -0
  14. package/dist/types/config/VideoData.d.ts +115 -0
  15. package/dist/types/config/enum/LogType.d.ts +3 -0
  16. package/dist/types/config/enum/ProtocolType.d.ts +3 -0
  17. package/dist/types/config/enum/ScalingType.d.ts +3 -0
  18. package/dist/types/config/enum/SecurityType.d.ts +3 -0
  19. package/dist/types/config/enum/SizeCalculationType.d.ts +3 -0
  20. package/dist/types/events/EventDispatcher.d.ts +34 -0
  21. package/dist/types/graph/MicrophoneGraph.d.ts +11 -0
  22. package/dist/types/logger/Logger.d.ts +103 -0
  23. package/dist/types/model/AbstractSourceItem.d.ts +23 -0
  24. package/dist/types/model/GatewayServerItem.d.ts +53 -0
  25. package/dist/types/model/IServerItem.d.ts +3 -0
  26. package/dist/types/model/ISourceItem.d.ts +3 -0
  27. package/dist/types/model/IStreamItem.d.ts +3 -0
  28. package/dist/types/model/RTMPSourceItem.d.ts +50 -0
  29. package/dist/types/model/RTSPSourceItem.d.ts +50 -0
  30. package/dist/types/model/StormMetaDataItem.d.ts +3 -0
  31. package/dist/types/model/StormServerItem.d.ts +53 -0
  32. package/dist/types/model/StormSourceItem.d.ts +27 -0
  33. package/dist/types/model/StreamInfo.d.ts +46 -0
  34. package/dist/types/network/AbstractSocket.d.ts +94 -0
  35. package/dist/types/network/NetworkController.d.ts +33 -0
  36. package/dist/types/network/WowzaConnection.d.ts +63 -0
  37. package/dist/types/network/WowzaStatusConnection.d.ts +54 -0
  38. package/dist/types/playback/CooldownMonitor.d.ts +17 -0
  39. package/dist/types/playback/StreamerController.d.ts +267 -1
  40. package/dist/types/playback/enum/ConnectionState.d.ts +3 -0
  41. package/dist/types/playback/player/AbstractPlayer.d.ts +23 -0
  42. package/dist/types/playback/task/IPlaybackTask.d.ts +3 -0
  43. package/dist/types/stage/ScreenElement.d.ts +54 -0
  44. package/dist/types/stage/StageController.d.ts +86 -0
  45. package/dist/types/statistics/StatsController.d.ts +8 -0
  46. package/dist/types/storage/StorageManager.d.ts +37 -0
  47. package/dist/types/utilities/DomUtilities.d.ts +7 -0
  48. package/dist/types/utilities/NumberUtilities.d.ts +7 -0
  49. package/dist/types/utilities/UserCapabilities.d.ts +44 -0
  50. package/dist/umd/index.js +4 -4
  51. package/package.json +1 -1
@@ -16,84 +16,475 @@ import { IGraph } from "./graph/IGraph";
16
16
  import { StatsController } from "./statistics/StatsController";
17
17
  import { BitrateGraph } from "./graph/BitrateGraph";
18
18
  import { FPSGraph } from "./graph/FPSGraph";
19
+ import { MicrophoneGraph } from "./graph/MicrophoneGraph";
20
+ /**
21
+ * Main class of the player. The player itself has no GUI, but can be controlled via provided API.
22
+ */
19
23
  export declare class StormStreamer extends EventDispatcher {
24
+ /**
25
+ * Next ID for the streamer instance. Each subsequent instance has a higher number.
26
+ * @private
27
+ */
20
28
  private static NEXT_STREAMER_ID;
29
+ /**
30
+ * Indicates whether the streamer object is in development mode (provides more debug options)
31
+ * @private
32
+ */
21
33
  private readonly DEV_MODE;
34
+ /**
35
+ * Version of this streamer in SemVer format (Major.Minor.Patch).
36
+ * @private
37
+ */
22
38
  private readonly STREAMER_VERSION;
39
+ /**
40
+ * Compile date for this streamer
41
+ * @private
42
+ */
23
43
  private readonly COMPILE_DATE;
44
+ /**
45
+ * Defines from which branch this streamer comes from e.g. "Main", "Experimental"
46
+ * @private
47
+ */
24
48
  private readonly STREAMER_BRANCH;
49
+ /**
50
+ * Defines number of streamer protocol that is required on server-side
51
+ * @private
52
+ */
25
53
  readonly STREAMER_PROTOCOL_VERSION: number;
54
+ /**
55
+ * ID of the streamer within StormStreamerArray collection
56
+ * @private
57
+ */
26
58
  private readonly _streamerID;
59
+ /**
60
+ * Contains all streamer configuration objects
61
+ * @private
62
+ */
27
63
  private _configManager;
64
+ /**
65
+ * Indicates whether streamer was initialized or not
66
+ * @private
67
+ */
28
68
  private _initialized;
69
+ /**
70
+ * Manages data storage like bandwidth settings, volume preferences
71
+ * @private
72
+ */
29
73
  private _storageManager;
74
+ /**
75
+ * Controls all HTML elements and visual presentation
76
+ * @private
77
+ */
30
78
  private _stageController;
79
+ /**
80
+ * Controls all streaming and media operations
81
+ * @private
82
+ */
31
83
  private _streamerController;
84
+ /**
85
+ * Controls all network communication with the server
86
+ * @private
87
+ */
32
88
  private _networkController;
89
+ /**
90
+ * Collects and manages streaming statistics
91
+ * @private
92
+ */
33
93
  private _statsController;
94
+ /**
95
+ * Collection of active performance graphs
96
+ * @private
97
+ */
34
98
  private _graphs;
99
+ /**
100
+ * Constructor - creates a new StormStreamer instance
101
+ *
102
+ * @param streamConfig - Configuration object for the streamer
103
+ * @param autoInitialize - Whether to automatically initialize the streamer after creation
104
+ */
35
105
  constructor(streamConfig?: StreamerConfig, autoInitialize?: boolean);
106
+ /**
107
+ * Initializes the streamer object. From this point, a connection to the server is established and authentication occurs.
108
+ * It is recommended to add all event listeners before calling this method to ensure they can be properly captured.
109
+ */
36
110
  initialize(): void;
111
+ /**
112
+ * Sets stream config for the streamer (or overwrites an existing one).
113
+ *
114
+ * @param streamConfig - New configuration object for the streamer
115
+ */
37
116
  setStreamConfig(streamConfig: StreamerConfig): void;
117
+ /**
118
+ * Returns true if this streamer instance is currently connected to a Storm Server/Cloud instance.
119
+ *
120
+ * @returns Boolean indicating connection status
121
+ */
38
122
  isConnected(): boolean;
123
+ /**
124
+ * Mutes the streamer's video object. Audio output will be silenced.
125
+ */
39
126
  mute(): void;
127
+ /**
128
+ * Unmutes the streamer's video object. Audio output will be restored.
129
+ */
40
130
  unmute(): void;
131
+ /**
132
+ * Checks whether the streamer audio is currently muted.
133
+ *
134
+ * @returns Boolean indicating mute status
135
+ */
41
136
  isMute(): boolean;
137
+ /**
138
+ * Toggles between mute and unmute states. Returns the new mute state.
139
+ *
140
+ * @returns New mute state (true = muted, false = unmuted)
141
+ */
42
142
  toggleMute(): boolean;
143
+ /**
144
+ * Sets new volume for the streamer (0-100). Once the method is performed, the volumeChange event will be triggered.
145
+ * If the video was muted prior to the volume change, it will be automatically unmuted.
146
+ *
147
+ * @param newVolume - Volume level (0-100)
148
+ */
43
149
  setVolume(newVolume: number): void;
150
+ /**
151
+ * Returns current streamer volume (0-100).
152
+ *
153
+ * @returns Current volume level
154
+ */
44
155
  getVolume(): number;
156
+ /**
157
+ * Returns the list of available camera devices.
158
+ *
159
+ * @returns Array of camera input devices
160
+ */
45
161
  getCameraList(): InputDevice[];
162
+ /**
163
+ * Returns the list of available microphone devices.
164
+ *
165
+ * @returns Array of microphone input devices
166
+ */
46
167
  getMicrophoneList(): InputDevice[];
168
+ /**
169
+ * Sets the active camera device by ID.
170
+ *
171
+ * @param cameraID - ID of the camera device to use
172
+ */
47
173
  setCamera(cameraID: string): void;
174
+ /**
175
+ * Sets the active microphone device by ID.
176
+ *
177
+ * @param microphoneID - ID of the microphone device to use
178
+ */
48
179
  setMicrophone(microphoneID: string): void;
180
+ /**
181
+ * Returns the currently active camera device.
182
+ *
183
+ * @returns Current camera device or null if none is active
184
+ */
49
185
  getCurrentCamera(): InputDevice | null;
186
+ /**
187
+ * Returns the currently active microphone device.
188
+ *
189
+ * @returns Current microphone device or null if none is active
190
+ */
50
191
  getCurrentMicrophone(): InputDevice | null;
192
+ /**
193
+ * Mutes or unmutes the microphone.
194
+ *
195
+ * @param microphoneState - True to mute, false to unmute
196
+ */
51
197
  muteMicrophone(microphoneState: boolean): void;
198
+ /**
199
+ * Checks if the microphone is currently muted.
200
+ *
201
+ * @returns Boolean indicating if microphone is muted
202
+ */
52
203
  isMicrophoneMuted(): boolean;
204
+ /**
205
+ * Returns the current publishing state of the streamer.
206
+ *
207
+ * @returns Current publishing state
208
+ */
53
209
  getPublishState(): PublishState;
210
+ /**
211
+ * Returns the total time the stream has been publishing in milliseconds.
212
+ *
213
+ * @returns Publishing time in milliseconds
214
+ */
54
215
  getPublishTime(): number;
216
+ /**
217
+ * Starts publishing a stream with the given stream key.
218
+ *
219
+ * @param streamKey - Key identifying the stream to publish
220
+ * @returns Boolean indicating if publishing was successfully initiated
221
+ */
55
222
  publish(streamKey: string): boolean;
223
+ /**
224
+ * Stops publishing the current stream.
225
+ */
226
+ unpublish(): void;
227
+ /**
228
+ * Returns the current state of input devices (camera and microphone).
229
+ *
230
+ * @returns Current state of input devices
231
+ */
56
232
  getInputDevicesState(): InputDevicesState;
57
- getCamerState(): DeviceState;
233
+ /**
234
+ * Returns the current state of the camera device.
235
+ *
236
+ * @returns Current camera device state
237
+ */
238
+ getCameraState(): DeviceState;
239
+ /**
240
+ * Returns the current state of the microphone device.
241
+ *
242
+ * @returns Current microphone device state
243
+ */
58
244
  getMicrophoneState(): DeviceState;
245
+ /**
246
+ * Clears saved device preferences from storage.
247
+ */
59
248
  clearSavedDevices(): void;
249
+ /**
250
+ * Randomizes saved device preferences (for testing purposes).
251
+ */
60
252
  messSavedDevices(): void;
253
+ /**
254
+ * Checks if the stream is ready for publishing.
255
+ *
256
+ * @returns Boolean indicating if stream is ready
257
+ */
61
258
  isStreamReady(): boolean;
62
- unpublish(): void;
259
+ /**
260
+ * Attaches the streamer to a new parent container using either a container ID (string) or a reference to an HTMLElement.
261
+ * If the instance is already attached, it will be moved to a new parent.
262
+ *
263
+ * @param container - Container ID (string) or HTMLElement reference
264
+ * @returns Boolean indicating if attachment was successful
265
+ */
63
266
  attachToContainer(container: string | HTMLElement): boolean;
267
+ /**
268
+ * Detaches the streamer from the current parent element, if possible.
269
+ *
270
+ * @returns Boolean indicating if detachment was successful
271
+ */
64
272
  detachFromContainer(): boolean;
273
+ /**
274
+ * Returns the current parent element of the streamer, or null if none exists.
275
+ *
276
+ * @returns Current container element or null
277
+ */
65
278
  getContainer(): HTMLElement | null;
279
+ /**
280
+ * Sets a new width and height for the streamer. The values can be given as a number (in which case they are
281
+ * treated as the number of pixels), or as a string ending with "px" (this will also be the number of pixels) or "%",
282
+ * where the number is treated as a percentage of the parent container's value.
283
+ *
284
+ * @param width - Can be provided as number or a string with "%" or "px" suffix
285
+ * @param height - Can be provided as number or a string with "%" or "px" suffix
286
+ */
66
287
  setSize(width: number | string, height: number | string): void;
288
+ /**
289
+ * Sets a new width for the streamer. The value can be given as a number (in which case it is treated as the
290
+ * number of pixels), or as a string ending with "px" (this will also be the number of pixels) or "%", where the
291
+ * number is treated as a percentage of the parent container's value.
292
+ *
293
+ * @param width - Can be provided as number or a string with "%" or "px" suffix
294
+ */
67
295
  setWidth(width: number | string): void;
296
+ /**
297
+ * Sets a new height for the streamer. The value can be given as a number (in which case it is treated as the
298
+ * number of pixels), or as a string ending with "px" (this will also be the number of pixels) or "%", where the
299
+ * number is treated as a percentage of the parent container's value.
300
+ *
301
+ * @param height - Can be provided as number or a string with "%" or "px" suffix
302
+ */
68
303
  setHeight(height: number | string): void;
304
+ /**
305
+ * Returns current streamer width in pixels.
306
+ *
307
+ * @returns Current width in pixels
308
+ */
69
309
  getWidth(): number;
310
+ /**
311
+ * Returns current streamer height in pixels.
312
+ *
313
+ * @returns Current height in pixels
314
+ */
70
315
  getHeight(): number;
316
+ /**
317
+ * Changes the streamer scaling mode. Available modes include fill, letterbox, original, and crop.
318
+ *
319
+ * @param newMode - New scaling mode name (fill, letterbox, original, crop)
320
+ */
71
321
  setScalingMode(newMode: string): void;
322
+ /**
323
+ * Returns the current streamer scaling mode.
324
+ *
325
+ * @returns Current scaling mode
326
+ */
72
327
  getScalingMode(): ScalingType;
328
+ /**
329
+ * Forces the streamer to recalculate its size based on parent internal dimensions.
330
+ */
73
331
  updateToSize(): void;
332
+ /**
333
+ * Returns a promise that resolves with a screenshot of the video element as a blob, or null if taking the
334
+ * screenshot was not possible.
335
+ *
336
+ * @returns Promise resolving to a Blob containing the screenshot or null
337
+ */
74
338
  makeScreenshot(): Promise<Blob | null>;
339
+ /**
340
+ * Creates a FPS performance graph in the specified location (container ID or reference). The graph is a
341
+ * separate object that must be started using its start() method and stopped using its stop() method. The dimensions
342
+ * of the graph depend on the dimensions of the specified container.
343
+ *
344
+ * @param container - Element ID or reference to HTMLElement
345
+ * @returns FPS graph instance
346
+ */
75
347
  createFPSGraph(container: string | HTMLElement): FPSGraph;
348
+ /**
349
+ * Creates a bitrate performance graph in the specified location (container ID or reference). The graph is a
350
+ * separate object that must be started using its start() method and stopped using its stop() method. The dimensions
351
+ * of the graph depend on the dimensions of the specified container.
352
+ *
353
+ * @param container - Element ID or reference to HTMLElement
354
+ * @returns Bitrate graph instance
355
+ */
76
356
  createBitrateGraph(container: string | HTMLElement): BitrateGraph;
357
+ /**
358
+ * Creates a microphone graph in the specified location (container ID or reference). The graph is a
359
+ * separate object that must be started using its start() method and stopped using its stop() method. The dimensions
360
+ * of the graph depend on the dimensions of the specified container.
361
+ *
362
+ * @param container - Element ID or reference to HTMLElement
363
+ * @returns Bitrate graph instance
364
+ */
365
+ createMicrophoneGraph(container: string | HTMLElement): MicrophoneGraph;
366
+ /**
367
+ * Adds new graph to the internal collection of active graphs.
368
+ *
369
+ * @param newGraph - Graph instance to add
370
+ */
77
371
  addGraph(newGraph: IGraph): void;
372
+ /**
373
+ * Stops all active performance graphs.
374
+ */
78
375
  stopAllGraphs(): void;
376
+ /**
377
+ * Enters fullscreen mode for the streamer container.
378
+ */
79
379
  enterFullScreen(): void;
380
+ /**
381
+ * Exits fullscreen mode.
382
+ */
80
383
  exitFullScreen(): void;
384
+ /**
385
+ * Returns true if the streamer is currently in fullscreen mode.
386
+ *
387
+ * @returns Boolean indicating fullscreen status
388
+ */
81
389
  isFullScreenMode(): boolean;
390
+ /**
391
+ * Returns the current stream key or null if none is set.
392
+ *
393
+ * @returns Current stream key or null
394
+ */
82
395
  getStreamKey(): string | null;
396
+ /**
397
+ * Returns the Stats Controller instance which contains statistical data about streaming performance.
398
+ *
399
+ * @returns Stats controller instance or null
400
+ */
83
401
  getStatsController(): StatsController | null;
402
+ /**
403
+ * Returns the unique ID of this streamer instance. Each subsequent instance has a higher number.
404
+ *
405
+ * @returns Streamer instance ID
406
+ */
84
407
  getStreamerID(): number;
408
+ /**
409
+ * Returns the logger instance used by this streamer.
410
+ *
411
+ * @returns Logger instance
412
+ */
85
413
  getLogger(): Logger;
414
+ /**
415
+ * Returns the configuration manager for this streamer.
416
+ *
417
+ * @returns Config manager instance or null
418
+ */
86
419
  getConfigManager(): ConfigManager | null;
420
+ /**
421
+ * Returns the network controller which manages all server communication.
422
+ *
423
+ * @returns Network controller instance or null
424
+ */
87
425
  getNetworkController(): NetworkController | null;
426
+ /**
427
+ * Returns the streamer controller which manages media stream operations.
428
+ *
429
+ * @returns Streamer controller instance or null
430
+ */
88
431
  getStreamerController(): StreamerController | null;
432
+ /**
433
+ * Returns the stage controller which manages visual presentation.
434
+ *
435
+ * @returns Stage controller instance or null
436
+ */
89
437
  getStageController(): StageController | null;
438
+ /**
439
+ * Returns the storage manager which handles persistent data storage.
440
+ *
441
+ * @returns Storage manager instance or null
442
+ */
443
+ getStorageManager(): StorageManager | null;
444
+ /**
445
+ * Returns the HTML video element used by this streamer instance.
446
+ *
447
+ * @returns Video element or null
448
+ */
90
449
  getVideoElement(): HTMLVideoElement | null;
450
+ /**
451
+ * Returns true if this streamer instance has already been initialized.
452
+ *
453
+ * @returns Boolean indicating initialization status
454
+ */
91
455
  isInitialized(): boolean;
456
+ /**
457
+ * Returns the version of this streamer instance. The version is returned in the SemVer format (Major.Minor.Patch).
458
+ *
459
+ * @returns Streamer version string
460
+ */
92
461
  getVersion(): string;
462
+ /**
463
+ * Returns the development branch of this streamer (e.g., main, experimental).
464
+ *
465
+ * @returns Branch name string
466
+ */
93
467
  getBranch(): string;
94
- getStorageManager(): StorageManager | null;
468
+ /**
469
+ * Dispatches an event with the specified name and data.
470
+ *
471
+ * @param eventName - Name of the event to dispatch
472
+ * @param event - Object containing event data
473
+ */
95
474
  dispatchEvent<K extends keyof StormStreamerEvent>(eventName: K, event: StormStreamerEvent[K]): void;
475
+ /**
476
+ * Starts the streaming process.
477
+ *
478
+ * @returns Promise that resolves when streaming has started
479
+ */
96
480
  start(): Promise<void>;
481
+ /**
482
+ * Stops the streaming process.
483
+ */
97
484
  stop(): void;
485
+ /**
486
+ * Destroys this instance of StormStreamer, releasing all resources and disconnecting from the server.
487
+ * After calling this method, the instance should not be used anymore.
488
+ */
98
489
  destroy(): void;
99
490
  }
@@ -1,16 +1,62 @@
1
1
  import { Logger } from "../logger/Logger";
2
2
  import { IConfig } from "./IConfig";
3
3
  import { AudioConfig } from "../types/AudioConfig";
4
+ /**
5
+ * Class contains all parameters related to volume
6
+ */
4
7
  export declare class AudioData implements IConfig {
8
+ /**
9
+ * Decides whenever player will print this config data on startup
10
+ *
11
+ * @private
12
+ */
5
13
  private static readonly PRINT_ON_STARTUP;
14
+ /**
15
+ * Original config object
16
+ * @private
17
+ */
6
18
  private _audioConfig;
19
+ /**
20
+ * Default value for volume
21
+ * @private
22
+ */
7
23
  private _startVolume;
24
+ /**
25
+ * Whenever video is muted
26
+ * @private
27
+ */
8
28
  private _isMuted;
29
+ /**
30
+ * Constructor
31
+ * @param volumeConfig
32
+ */
9
33
  constructor(volumeConfig: AudioConfig | null);
34
+ /**
35
+ * Parses provided config
36
+ */
10
37
  parse(config: AudioConfig | null): void;
38
+ /**
39
+ * Returns player start volume
40
+ */
11
41
  get startVolume(): number;
42
+ /**
43
+ * Sets start volume for the player (by default it's 100)
44
+ * @param newValue
45
+ */
12
46
  set startVolume(newValue: number);
47
+ /**
48
+ * Returns whenever library should be muted on start
49
+ */
13
50
  get muted(): boolean;
51
+ /**
52
+ * Sets whenever library should be muted or not
53
+ * @param newValue
54
+ */
14
55
  set muted(newValue: boolean);
56
+ /**
57
+ * Prints current settings
58
+ *
59
+ * @param logger
60
+ */
15
61
  print(logger: Logger, force?: boolean): void;
16
62
  }
@@ -3,16 +3,63 @@ import { StreamData } from "./StreamData";
3
3
  import { SettingsData } from "./SettingsData";
4
4
  import { Logger } from "../logger/Logger";
5
5
  import { StreamerConfig } from "../types/StreamerConfig";
6
+ /**
7
+ * Class responsible for parsing config object.
8
+ */
6
9
  export declare class ConfigManager implements IConfig {
10
+ /**
11
+ * Decides whenever player will print this config data on startup
12
+ *
13
+ * @private
14
+ */
7
15
  private readonly PRINT_ON_STARTUP;
16
+ /**
17
+ * Original config template provided via constructor
18
+ * @private
19
+ */
8
20
  private configTemplate;
21
+ /**
22
+ * Contains configurations related to servers and streams
23
+ * @private
24
+ */
9
25
  private streamData;
26
+ /**
27
+ * Contains configuration related to player settings
28
+ * @private
29
+ */
10
30
  private settingsData;
31
+ /**
32
+ * Whenver we're in demo mode
33
+ * @private
34
+ */
11
35
  private demoMode;
36
+ /**
37
+ * Constuctor
38
+ * @param config config object
39
+ */
12
40
  constructor(config: StreamerConfig);
41
+ /**
42
+ * Parses config objects into smaller chunkes related to their kind.
43
+ * @private
44
+ */
13
45
  parse(config: StreamerConfig): void;
46
+ /**
47
+ * Returns the part of the config with player stream data (what to play)
48
+ */
14
49
  getStreamData(): StreamData;
50
+ /**
51
+ * Returns the part of the config with player settings
52
+ */
15
53
  getSettingsData(): SettingsData;
54
+ /**
55
+ * Returns true/false whenever we're in demo mode or not
56
+ */
16
57
  getIfDemoMode(): boolean;
58
+ /**
59
+ * Prints current settings.
60
+ *
61
+ * @param logger reference to logger
62
+ * @param force if printing is disabled, this parameter can overwrite it
63
+ */
17
64
  print(logger: Logger, force?: boolean): void;
18
65
  }