@gcorevideo/player 2.20.21 → 2.21.1

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 (76) hide show
  1. package/assets/audio-selector/style.scss +48 -82
  2. package/assets/audio-selector/track-selector.ejs +3 -3
  3. package/assets/bottom-gear/bottomgear.ejs +10 -12
  4. package/assets/bottom-gear/gear-sub-menu.scss +0 -15
  5. package/assets/bottom-gear/gear.scss +3 -32
  6. package/assets/media-control/media-control.ejs +5 -20
  7. package/assets/media-control/media-control.scss +124 -34
  8. package/assets/media-control/width370.scss +32 -104
  9. package/assets/picture-in-picture/button.ejs +1 -1
  10. package/assets/picture-in-picture/button.scss +5 -4
  11. package/dist/core.js +151 -24
  12. package/dist/index.css +1063 -1149
  13. package/dist/index.js +287 -283
  14. package/dist/player.d.ts +19 -16
  15. package/dist/plugins/index.css +953 -1039
  16. package/dist/plugins/index.js +709 -23402
  17. package/docs/api/player.audioselector.md +4 -59
  18. package/docs/api/player.md +1 -1
  19. package/docs/api/player.mediacontrol.getelement.md +5 -0
  20. package/docs/api/player.mediacontrol.md +14 -0
  21. package/docs/api/{player.audioselector.updatecurrenttrack.md → player.mediacontrol.putelement.md} +7 -7
  22. package/docs/api/player.mediacontrolelement.md +1 -1
  23. package/docs/api/{player.audioselector.starttrackswitch.md → player.pictureinpicture.attributes.md} +5 -7
  24. package/docs/api/player.pictureinpicture.md +45 -0
  25. package/lib/playback/BasePlayback.d.ts +0 -1
  26. package/lib/playback/BasePlayback.d.ts.map +1 -1
  27. package/lib/playback/BasePlayback.js +0 -1
  28. package/lib/playback/HTML5Video.d.ts +4 -0
  29. package/lib/playback/HTML5Video.d.ts.map +1 -1
  30. package/lib/playback/HTML5Video.js +57 -6
  31. package/lib/playback/dash-playback/DashPlayback.d.ts +5 -0
  32. package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
  33. package/lib/playback/dash-playback/DashPlayback.js +48 -4
  34. package/lib/playback/hls-playback/HlsPlayback.d.ts +31 -25
  35. package/lib/playback/hls-playback/HlsPlayback.d.ts.map +1 -1
  36. package/lib/playback/hls-playback/HlsPlayback.js +47 -14
  37. package/lib/plugins/audio-selector/AudioSelector.d.ts +12 -11
  38. package/lib/plugins/audio-selector/AudioSelector.d.ts.map +1 -1
  39. package/lib/plugins/audio-selector/AudioSelector.js +65 -185
  40. package/lib/plugins/bottom-gear/BottomGear.d.ts +1 -1
  41. package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -1
  42. package/lib/plugins/bottom-gear/BottomGear.js +10 -9
  43. package/lib/plugins/level-selector/LevelSelector.js +1 -1
  44. package/lib/plugins/media-control/MediaControl.d.ts +3 -3
  45. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  46. package/lib/plugins/media-control/MediaControl.js +17 -9
  47. package/lib/plugins/picture-in-picture/PictureInPicture.d.ts +3 -0
  48. package/lib/plugins/picture-in-picture/PictureInPicture.d.ts.map +1 -1
  49. package/lib/plugins/picture-in-picture/PictureInPicture.js +6 -1
  50. package/lib/plugins/source-controller/SourceController.d.ts.map +1 -1
  51. package/lib/plugins/source-controller/SourceController.js +0 -1
  52. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.d.ts +0 -2
  53. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.d.ts.map +1 -1
  54. package/lib/plugins/spinner-three-bounce/SpinnerThreeBounce.js +1 -18
  55. package/lib/testUtils.d.ts.map +1 -1
  56. package/lib/testUtils.js +2 -0
  57. package/package.json +1 -1
  58. package/src/playback/BasePlayback.ts +0 -1
  59. package/src/playback/HTML5Video.ts +61 -6
  60. package/src/playback/dash-playback/DashPlayback.ts +64 -6
  61. package/src/playback/hls-playback/HlsPlayback.ts +82 -40
  62. package/src/plugins/audio-selector/AudioSelector.ts +84 -278
  63. package/src/plugins/bottom-gear/BottomGear.ts +11 -10
  64. package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +1 -3
  65. package/src/plugins/bottom-gear/__tests__/__snapshots__/BottomGear.test.ts.snap +14 -37
  66. package/src/plugins/level-selector/LevelSelector.ts +1 -1
  67. package/src/plugins/media-control/MediaControl.ts +18 -13
  68. package/src/plugins/picture-in-picture/PictureInPicture.ts +7 -1
  69. package/src/plugins/source-controller/SourceController.ts +0 -1
  70. package/src/plugins/spinner-three-bounce/SpinnerThreeBounce.ts +1 -20
  71. package/src/testUtils.ts +2 -0
  72. package/src/typings/globals.d.ts +19 -0
  73. package/temp/player.api.json +102 -143
  74. package/tsconfig.tsbuildinfo +1 -1
  75. package/assets/media-control/plugins.scss +0 -94
  76. package/docs/api/player.audioselector.highlightcurrenttrack.md +0 -18
@@ -27,7 +27,6 @@ import { ZeptoResult } from '../../types.js'
27
27
  import { getPageX, isFullscreen } from '../utils.js'
28
28
 
29
29
  import '../../../assets/media-control/media-control.scss'
30
- import '../../../assets/media-control/plugins.scss'
31
30
 
32
31
  import mediaControlHTML from '../../../assets/media-control/media-control.ejs'
33
32
  import playIcon from '../../../assets/icons/new/play.svg'
@@ -44,7 +43,6 @@ import fullscreenOnIcon from '../../../assets/icons/new/fullscreen-on.svg'
44
43
  */
45
44
  export type MediaControlElement =
46
45
  | 'audioTracksSelector'
47
- | 'bottomGear' // an alias of gear
48
46
  | 'clipText'
49
47
  | 'gear'
50
48
  | 'pip'
@@ -143,8 +141,6 @@ export class MediaControl extends UICorePlugin {
143
141
 
144
142
  private $audioTracksSelector: ZeptoResult | null = null
145
143
 
146
- private $bottomGear: ZeptoResult | null = null
147
-
148
144
  private $clipText: ZeptoResult | null = null
149
145
 
150
146
  private $clipTextContainer: ZeptoResult | null = null
@@ -1059,11 +1055,6 @@ export class MediaControl extends UICorePlugin {
1059
1055
  this.$volumeBarBackground = this.$el.find('.bar-background[data-volume]')
1060
1056
  this.$volumeBarFill = this.$el.find('.bar-fill-1[data-volume]')
1061
1057
  this.$volumeBarScrubber = this.$el.find('.bar-scrubber[data-volume]')
1062
- this.$bottomGear = this.$el.find('.media-control-bottomgear')
1063
- this.$pip = this.$el.find('.media-control-pip')
1064
- this.$audioTracksSelector = this.$el.find(
1065
- '.media-control-audio-tracks[data-audiotracks]',
1066
- )
1067
1058
  this.$subtitlesSelector = this.$el.find(
1068
1059
  '.media-control-subtitles[data-subtitles]',
1069
1060
  )
@@ -1086,6 +1077,7 @@ export class MediaControl extends UICorePlugin {
1086
1077
  * Get a media control element DOM node
1087
1078
  * @param name - The name of the media control element
1088
1079
  * @returns The DOM node to render to or extend
1080
+ * @deprecated Use {@link MediaControl.putElement} instead
1089
1081
  * @remarks
1090
1082
  * Use this method to render custom media control UI in a plugin
1091
1083
  * @example
@@ -1103,14 +1095,13 @@ export class MediaControl extends UICorePlugin {
1103
1095
  getElement(name: MediaControlElement): ZeptoResult | null {
1104
1096
  switch (name) {
1105
1097
  case 'audioTracksSelector':
1106
- return this.$audioTracksSelector
1098
+ return null
1107
1099
  case 'clipText':
1108
1100
  return this.$clipText
1109
- case 'bottomGear':
1110
1101
  case 'gear':
1111
- return this.$bottomGear
1102
+ return null
1112
1103
  case 'pip':
1113
- return this.$pip
1104
+ return null
1114
1105
  case 'playbackRate':
1115
1106
  return this.$playbackRate
1116
1107
  case 'seekBarContainer':
@@ -1120,6 +1111,20 @@ export class MediaControl extends UICorePlugin {
1120
1111
  }
1121
1112
  }
1122
1113
 
1114
+ putElement(name: MediaControlElement, element: ZeptoResult) {
1115
+ switch (name) {
1116
+ case 'audioTracksSelector':
1117
+ this.getRightPanel().append(element)
1118
+ break
1119
+ case 'pip':
1120
+ this.getRightPanel().append(element)
1121
+ break
1122
+ case 'gear':
1123
+ this.getRightPanel().append(element)
1124
+ break
1125
+ }
1126
+ }
1127
+
1123
1128
  /**
1124
1129
  * Get the right panel area to append custom elements to
1125
1130
  * @returns ZeptoSelector of the right panel element
@@ -52,6 +52,12 @@ export class PictureInPicture extends UICorePlugin {
52
52
  };
53
53
  }
54
54
 
55
+ override get attributes() {
56
+ return {
57
+ 'class': 'media-control-pip',
58
+ };
59
+ }
60
+
55
61
  private get videoElement() {
56
62
  return this.core.activePlayback.el;
57
63
  }
@@ -86,7 +92,7 @@ export class PictureInPicture extends UICorePlugin {
86
92
 
87
93
  const mediaControl = this.core.getPlugin('media_control');
88
94
  if (mediaControl) {
89
- mediaControl.getElement('pip')?.html(this.el);
95
+ mediaControl.putElement('pip', this.el);
90
96
  }
91
97
 
92
98
  return this;
@@ -139,7 +139,6 @@ export class SourceController extends CorePlugin {
139
139
  private onCoreReady() {
140
140
  trace(`${T} onCoreReady`)
141
141
  this.core.getPlugin('error_screen')?.disable() // TODO test
142
- // this.core.getPlugin('spinner')?.showOnError()
143
142
  }
144
143
 
145
144
  private onActiveContainerChanged() {
@@ -73,8 +73,6 @@ export class SpinnerThreeBounce extends UIContainerPlugin {
73
73
 
74
74
  private hasBuffering = false
75
75
 
76
- private _showOnError = false
77
-
78
76
  constructor(container: Container) {
79
77
  super(container)
80
78
  this.listenTo(
@@ -94,10 +92,6 @@ export class SpinnerThreeBounce extends UIContainerPlugin {
94
92
  this.listenTo(this.container, ClapprEvents.CONTAINER_READY, this.render)
95
93
  }
96
94
 
97
- public showOnError() {
98
- this._showOnError = true
99
- }
100
-
101
95
  private onBuffering() {
102
96
  this.hasBuffering = true
103
97
  this._show()
@@ -117,12 +111,8 @@ export class SpinnerThreeBounce extends UIContainerPlugin {
117
111
 
118
112
  private onStop() {
119
113
  trace(`${T} onStop`, {
120
- showOnError: this._showOnError,
121
114
  hasFatalError: this.hasFatalError,
122
115
  })
123
- // if (!(this.hasFatalError && this._showOnError)) {
124
- // this._hide()
125
- // }
126
116
  this._hide()
127
117
  }
128
118
 
@@ -130,15 +120,9 @@ export class SpinnerThreeBounce extends UIContainerPlugin {
130
120
  this.hasFatalError = e.code === PlaybackErrorCode.MediaSourceUnavailable
131
121
  trace(`${T} onError`, {
132
122
  e,
133
- showOnError: this._showOnError,
134
123
  hasFatalError: this.hasFatalError,
135
124
  error: e.code,
136
125
  })
137
- // if (this._showOnError) {
138
- // this._show()
139
- // } else {
140
- // this._hide()
141
- // }
142
126
  this._hide()
143
127
  }
144
128
 
@@ -189,18 +173,15 @@ export class SpinnerThreeBounce extends UIContainerPlugin {
189
173
  * @internal
190
174
  */
191
175
  override render() {
192
- // TODO control via sourcecontroller instead
193
- const showOnStart = this.options.spinner?.showOnStart
194
176
  trace(`${T} render`, {
195
177
  buffering: this.container.buffering,
196
- showOnStart,
197
178
  })
198
179
  this.$el.html(this.template())
199
180
  this.el.firstElementChild?.addEventListener('animationiteration', () => {
200
181
  this.trigger(SpinnerEvents.SYNC)
201
182
  })
202
183
  this.container.$el.append(this.$el[0])
203
- if (showOnStart || this.container.buffering) {
184
+ if (this.container.buffering) {
204
185
  this._show()
205
186
  } else {
206
187
  this._hide()
package/src/testUtils.ts CHANGED
@@ -199,6 +199,8 @@ export function createMockMediaControl(core: any) {
199
199
  // @ts-ignore
200
200
  mediaControl.getElement = vi.fn().mockImplementation((name) => elements[name])
201
201
  // @ts-ignore
202
+ mediaControl.putElement = vi.fn()
203
+ // @ts-ignore
202
204
  mediaControl.getLeftPanel = vi.fn().mockImplementation(() => mediaControl.$el.find('.media-control-left-panel'))
203
205
  // @ts-ignore
204
206
  mediaControl.getRightPanel = vi.fn().mockImplementation(() => mediaControl.$el.find('.media-control-right-panel'))
@@ -2,6 +2,25 @@ declare interface HTMLVideoElement {
2
2
  webkitDisplayingFullscreen?: boolean;
3
3
  }
4
4
 
5
+ declare interface HTMLMediaElement {
6
+ audioTracks?: AudioTrackList;
7
+ }
8
+
9
+ declare interface AudioTrackW3C {
10
+ readonly id: string;
11
+ enabled: boolean;
12
+ readonly kind: string;
13
+ readonly label: string;
14
+ readonly language: string;
15
+ readonly sourceBuffer: SourceBuffer | null;
16
+ }
17
+
18
+ declare interface AudioTrackList {
19
+ getTrackById(id: string): AudioTrackW3C | null;
20
+ length: number;
21
+ [index: number]: AudioTrackW3C;
22
+ }
23
+
5
24
  declare module "*.css";
6
25
  declare module "*.scss";
7
26
  declare module "*.svg";
@@ -175,7 +175,7 @@
175
175
  {
176
176
  "kind": "Class",
177
177
  "canonicalReference": "@gcorevideo/player!AudioSelector:class",
178
- "docComment": "/**\n * `PLUGIN` that adds an audio track selector to the media control UI.\n *\n * @beta\n */\n",
178
+ "docComment": "/**\n * `PLUGIN` that makes possible to switch audio tracks via the media control UI.\n *\n * @remarks\n *\n * The plugin is activated when there are multiple audio tracks available. The plugin adds a button showing the current audio track and a dropdown to switch to another audio track. Depends on:\n *\n * - {@link MediaControl}\n *\n * @beta\n */\n",
179
179
  "excerptTokens": [
180
180
  {
181
181
  "kind": "Content",
@@ -196,145 +196,7 @@
196
196
  "isAbstract": false,
197
197
  "name": "AudioSelector",
198
198
  "preserveMemberOrder": false,
199
- "members": [
200
- {
201
- "kind": "Method",
202
- "canonicalReference": "@gcorevideo/player!AudioSelector#highlightCurrentTrack:member(1)",
203
- "docComment": "",
204
- "excerptTokens": [
205
- {
206
- "kind": "Content",
207
- "text": "highlightCurrentTrack(): "
208
- },
209
- {
210
- "kind": "Content",
211
- "text": "void"
212
- },
213
- {
214
- "kind": "Content",
215
- "text": ";"
216
- }
217
- ],
218
- "isStatic": false,
219
- "returnTypeTokenRange": {
220
- "startIndex": 1,
221
- "endIndex": 2
222
- },
223
- "releaseTag": "Beta",
224
- "isProtected": false,
225
- "overloadIndex": 1,
226
- "parameters": [],
227
- "isOptional": false,
228
- "isAbstract": false,
229
- "name": "highlightCurrentTrack"
230
- },
231
- {
232
- "kind": "Method",
233
- "canonicalReference": "@gcorevideo/player!AudioSelector#startTrackSwitch:member(1)",
234
- "docComment": "",
235
- "excerptTokens": [
236
- {
237
- "kind": "Content",
238
- "text": "startTrackSwitch(): "
239
- },
240
- {
241
- "kind": "Content",
242
- "text": "void"
243
- },
244
- {
245
- "kind": "Content",
246
- "text": ";"
247
- }
248
- ],
249
- "isStatic": false,
250
- "returnTypeTokenRange": {
251
- "startIndex": 1,
252
- "endIndex": 2
253
- },
254
- "releaseTag": "Beta",
255
- "isProtected": false,
256
- "overloadIndex": 1,
257
- "parameters": [],
258
- "isOptional": false,
259
- "isAbstract": false,
260
- "name": "startTrackSwitch"
261
- },
262
- {
263
- "kind": "Method",
264
- "canonicalReference": "@gcorevideo/player!AudioSelector#updateCurrentTrack:member(1)",
265
- "docComment": "",
266
- "excerptTokens": [
267
- {
268
- "kind": "Content",
269
- "text": "updateCurrentTrack(e: "
270
- },
271
- {
272
- "kind": "Reference",
273
- "text": "HlsEvents.AUDIO_TRACK_SWITCHED",
274
- "canonicalReference": "hls.js!Events.AUDIO_TRACK_SWITCHED:member"
275
- },
276
- {
277
- "kind": "Content",
278
- "text": ", info: "
279
- },
280
- {
281
- "kind": "Reference",
282
- "text": "AudioTrackSwitchedData",
283
- "canonicalReference": "hls.js!AudioTrackSwitchedData:interface"
284
- },
285
- {
286
- "kind": "Content",
287
- "text": " | "
288
- },
289
- {
290
- "kind": "Reference",
291
- "text": "AudioTrackLoadedData",
292
- "canonicalReference": "hls.js!AudioTrackLoadedData:interface"
293
- },
294
- {
295
- "kind": "Content",
296
- "text": "): "
297
- },
298
- {
299
- "kind": "Content",
300
- "text": "void"
301
- },
302
- {
303
- "kind": "Content",
304
- "text": ";"
305
- }
306
- ],
307
- "isStatic": false,
308
- "returnTypeTokenRange": {
309
- "startIndex": 7,
310
- "endIndex": 8
311
- },
312
- "releaseTag": "Beta",
313
- "isProtected": false,
314
- "overloadIndex": 1,
315
- "parameters": [
316
- {
317
- "parameterName": "e",
318
- "parameterTypeTokenRange": {
319
- "startIndex": 1,
320
- "endIndex": 2
321
- },
322
- "isOptional": false
323
- },
324
- {
325
- "parameterName": "info",
326
- "parameterTypeTokenRange": {
327
- "startIndex": 3,
328
- "endIndex": 6
329
- },
330
- "isOptional": false
331
- }
332
- ],
333
- "isOptional": false,
334
- "isAbstract": false,
335
- "name": "updateCurrentTrack"
336
- }
337
- ],
199
+ "members": [],
338
200
  "extendsTokenRange": {
339
201
  "startIndex": 1,
340
202
  "endIndex": 2
@@ -2837,7 +2699,7 @@
2837
2699
  {
2838
2700
  "kind": "Method",
2839
2701
  "canonicalReference": "@gcorevideo/player!MediaControl#getElement:member(1)",
2840
- "docComment": "/**\n * Get a media control element DOM node\n *\n * @remarks\n *\n * Use this method to render custom media control UI in a plugin\n *\n * @param name - The name of the media control element\n *\n * @returns The DOM node to render to or extend\n *\n * @example\n * ```ts\n * class MyPlugin extends UICorePlugin {\n * override render() {\n * const mediaControl = this.core.getPlugin('media_control')\n * const clipText = mediaControl.getElement('clipText')\n * clipText?.el.text('Here we go')\n * return this\n * }\n * }\n * ```\n *\n */\n",
2702
+ "docComment": "/**\n * Get a media control element DOM node\n *\n * @remarks\n *\n * Use this method to render custom media control UI in a plugin\n *\n * @deprecated\n *\n * Use {@link MediaControl.putElement} instead\n *\n * @param name - The name of the media control element\n *\n * @returns The DOM node to render to or extend\n *\n * @example\n * ```ts\n * class MyPlugin extends UICorePlugin {\n * override render() {\n * const mediaControl = this.core.getPlugin('media_control')\n * const clipText = mediaControl.getElement('clipText')\n * clipText?.el.text('Here we go')\n * return this\n * }\n * }\n * ```\n *\n */\n",
2841
2703
  "excerptTokens": [
2842
2704
  {
2843
2705
  "kind": "Content",
@@ -2980,6 +2842,72 @@
2980
2842
  "isProtected": false,
2981
2843
  "isAbstract": false
2982
2844
  },
2845
+ {
2846
+ "kind": "Method",
2847
+ "canonicalReference": "@gcorevideo/player!MediaControl#putElement:member(1)",
2848
+ "docComment": "",
2849
+ "excerptTokens": [
2850
+ {
2851
+ "kind": "Content",
2852
+ "text": "putElement(name: "
2853
+ },
2854
+ {
2855
+ "kind": "Reference",
2856
+ "text": "MediaControlElement",
2857
+ "canonicalReference": "@gcorevideo/player!MediaControlElement:type"
2858
+ },
2859
+ {
2860
+ "kind": "Content",
2861
+ "text": ", element: "
2862
+ },
2863
+ {
2864
+ "kind": "Reference",
2865
+ "text": "ZeptoResult",
2866
+ "canonicalReference": "@gcorevideo/player!ZeptoResult:type"
2867
+ },
2868
+ {
2869
+ "kind": "Content",
2870
+ "text": "): "
2871
+ },
2872
+ {
2873
+ "kind": "Content",
2874
+ "text": "void"
2875
+ },
2876
+ {
2877
+ "kind": "Content",
2878
+ "text": ";"
2879
+ }
2880
+ ],
2881
+ "isStatic": false,
2882
+ "returnTypeTokenRange": {
2883
+ "startIndex": 5,
2884
+ "endIndex": 6
2885
+ },
2886
+ "releaseTag": "Beta",
2887
+ "isProtected": false,
2888
+ "overloadIndex": 1,
2889
+ "parameters": [
2890
+ {
2891
+ "parameterName": "name",
2892
+ "parameterTypeTokenRange": {
2893
+ "startIndex": 1,
2894
+ "endIndex": 2
2895
+ },
2896
+ "isOptional": false
2897
+ },
2898
+ {
2899
+ "parameterName": "element",
2900
+ "parameterTypeTokenRange": {
2901
+ "startIndex": 3,
2902
+ "endIndex": 4
2903
+ },
2904
+ "isOptional": false
2905
+ }
2906
+ ],
2907
+ "isOptional": false,
2908
+ "isAbstract": false,
2909
+ "name": "putElement"
2910
+ },
2983
2911
  {
2984
2912
  "kind": "Method",
2985
2913
  "canonicalReference": "@gcorevideo/player!MediaControl#setInitialVolume:member(1)",
@@ -3123,7 +3051,7 @@
3123
3051
  },
3124
3052
  {
3125
3053
  "kind": "Content",
3126
- "text": "'audioTracksSelector' | 'bottomGear' | 'clipText' | 'gear' | 'pip' | 'playbackRate' | 'seekBarContainer' | 'subtitlesSelector'"
3054
+ "text": "'audioTracksSelector' | 'clipText' | 'gear' | 'pip' | 'playbackRate' | 'seekBarContainer' | 'subtitlesSelector'"
3127
3055
  },
3128
3056
  {
3129
3057
  "kind": "Content",
@@ -3602,7 +3530,38 @@
3602
3530
  "isAbstract": false,
3603
3531
  "name": "PictureInPicture",
3604
3532
  "preserveMemberOrder": false,
3605
- "members": [],
3533
+ "members": [
3534
+ {
3535
+ "kind": "Property",
3536
+ "canonicalReference": "@gcorevideo/player!PictureInPicture#attributes:member",
3537
+ "docComment": "",
3538
+ "excerptTokens": [
3539
+ {
3540
+ "kind": "Content",
3541
+ "text": "get attributes(): "
3542
+ },
3543
+ {
3544
+ "kind": "Content",
3545
+ "text": "{\n class: string;\n }"
3546
+ },
3547
+ {
3548
+ "kind": "Content",
3549
+ "text": ";"
3550
+ }
3551
+ ],
3552
+ "isReadonly": true,
3553
+ "isOptional": false,
3554
+ "releaseTag": "Beta",
3555
+ "name": "attributes",
3556
+ "propertyTypeTokenRange": {
3557
+ "startIndex": 1,
3558
+ "endIndex": 2
3559
+ },
3560
+ "isStatic": false,
3561
+ "isProtected": false,
3562
+ "isAbstract": false
3563
+ }
3564
+ ],
3606
3565
  "extendsTokenRange": {
3607
3566
  "startIndex": 1,
3608
3567
  "endIndex": 2