@lightningjs/blits 2.4.1 → 2.6.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.
@@ -23,6 +23,7 @@ import Router, {
23
23
  } from '../router/router.js'
24
24
  import symbols from '../lib/symbols.js'
25
25
  import Focus from '../focus/focus.js'
26
+ import { platform } from '../platform.js'
26
27
 
27
28
  /** @typedef {{ $input?: (event: any) => boolean, $focus?: (event: any) => void }} RouterViewParent */
28
29
 
@@ -46,6 +47,9 @@ export default () =>
46
47
  'color',
47
48
  'src',
48
49
  'texture',
50
+ 'image',
51
+ 'map',
52
+ 'frame',
49
53
  'fit',
50
54
  'rtt',
51
55
  'mount',
@@ -156,11 +160,18 @@ export default () =>
156
160
  Router.navigate.apply(this)
157
161
  }
158
162
 
159
- window.addEventListener('hashchange', this.hashchangeHandler)
163
+ const viewport = platform.viewport
164
+ if (viewport !== undefined) {
165
+ this.hashchangeViewport = viewport
166
+ viewport.addEventListener('hashchange', this.hashchangeHandler)
167
+ }
160
168
  },
161
169
  destroy() {
162
170
  unregisterRouterView(this)
163
- window.removeEventListener('hashchange', this.hashchangeHandler, false)
171
+ if (this.hashchangeViewport !== undefined) {
172
+ this.hashchangeViewport.removeEventListener('hashchange', this.hashchangeHandler, false)
173
+ this.hashchangeViewport = undefined
174
+ }
164
175
  },
165
176
  focus() {
166
177
  if (this.activeView && Focus.get() === this) {
@@ -17,10 +17,8 @@
17
17
 
18
18
  import Circle from './Circle.js'
19
19
  import RouterView from './RouterView.js'
20
- import Sprite from './Sprite.js'
21
20
 
22
21
  export default () => ({
23
22
  Circle: Circle(),
24
23
  RouterView: RouterView(),
25
- Sprite: Sprite(),
26
24
  })
@@ -29,9 +29,14 @@ import { Log } from '../../lib/log.js'
29
29
  import symbols from '../../lib/symbols.js'
30
30
  import Settings from '../../settings.js'
31
31
  import shaders from '../../lib/shaders/shaders.js'
32
+ import { resolveSpriteTexture } from './spriteTexture.js'
32
33
 
33
34
  const holderComponentMap = new WeakMap()
34
35
 
36
+ /** @param {any} frame */
37
+ const spriteFrameKey = (frame) =>
38
+ typeof frame === 'object' && frame !== null ? JSON.stringify(frame) : frame
39
+
35
40
  /**
36
41
  * Creates a padding object from a value and direction.
37
42
  * @param {number|object|string|undefined} padding - The padding value.
@@ -291,6 +296,24 @@ const propsTransformer = {
291
296
  this.props['color'] = 0xffffffff
292
297
  }
293
298
  },
299
+ set image(v) {
300
+ this.raw['image'] = v
301
+ if (this.element[symbols.isSprite] === true && this.element.node) {
302
+ this.element._scheduleNativeSpriteSync()
303
+ }
304
+ },
305
+ set map(v) {
306
+ this.raw['map'] = v
307
+ if (this.element[symbols.isSprite] === true && this.element.node) {
308
+ this.element._scheduleNativeSpriteSync()
309
+ }
310
+ },
311
+ set frame(v) {
312
+ this.raw['frame'] = v
313
+ if (this.element[symbols.isSprite] === true && this.element.node) {
314
+ this.element._scheduleNativeSpriteSync()
315
+ }
316
+ },
294
317
  set fit(v) {
295
318
  const resizeMode = {}
296
319
 
@@ -548,6 +571,7 @@ export const elementAttributes = Object.keys(propsTransformer)
548
571
  const Element = {
549
572
  /**
550
573
  * Populates the element with data
574
+ * @this {import('../../component.js').BlitsElement}
551
575
  * @param {import('../../component.js').BlitsElementProps} props
552
576
  */
553
577
  populate(props) {
@@ -557,6 +581,21 @@ const Element = {
557
581
  this[symbols.isSlot] = true
558
582
  }
559
583
 
584
+ if (props[symbols.isSprite] === true) {
585
+ this[symbols.isSprite] = true
586
+ this._spriteState = {
587
+ spriteTexture: null,
588
+ currentSrc: null,
589
+ _loadedCb: null,
590
+ _failedCb: null,
591
+ _syncScheduled: false,
592
+ lastTexture: null,
593
+ lastImage: null,
594
+ lastMap: null,
595
+ lastFrameKey: undefined,
596
+ }
597
+ }
598
+
560
599
  this.props.element = this
561
600
 
562
601
  this.props['parent'] = props['parent'] || this.config.parent
@@ -600,16 +639,20 @@ const Element = {
600
639
  holderComponentMap.set(this.node, this.component)
601
640
  }
602
641
 
603
- if (props['@loaded'] !== undefined && typeof props['@loaded'] === 'function') {
604
- this.node.on('loaded', (el, { type, dimensions }) => {
605
- props['@loaded']({ w: dimensions.w, h: dimensions.h, type }, this)
606
- })
607
- }
642
+ if (this[symbols.isSprite] === true) {
643
+ this._syncNativeSprite()
644
+ } else {
645
+ if (props['@loaded'] !== undefined && typeof props['@loaded'] === 'function') {
646
+ this.node.on('loaded', (el, { type, dimensions }) => {
647
+ props['@loaded']({ w: dimensions.w, h: dimensions.h, type }, this)
648
+ })
649
+ }
608
650
 
609
- if (props['@error'] !== undefined && typeof props['@error'] === 'function') {
610
- this.node.on('failed', (el, error) => {
611
- props['@error'](error, this)
612
- })
651
+ if (props['@error'] !== undefined && typeof props['@error'] === 'function') {
652
+ this.node.on('failed', (el, error) => {
653
+ props['@error'](error, this)
654
+ })
655
+ }
613
656
  }
614
657
 
615
658
  if (props.__layout === true) {
@@ -657,6 +700,78 @@ const Element = {
657
700
  this.node.data = { ...this.props.props['data'] }
658
701
  }
659
702
  },
703
+ /**
704
+ * @this {import('../../component.js').BlitsElement}
705
+ */
706
+ _scheduleNativeSpriteSync() {
707
+ if (this[symbols.isSprite] !== true || !this.node || this.eol === true) return
708
+ const st = this._spriteState
709
+ if (st === undefined) return
710
+ if (st._syncScheduled === true) return
711
+ st._syncScheduled = true
712
+ queueMicrotask(() => {
713
+ st._syncScheduled = false
714
+ if (this.eol === true || !this.node) return
715
+ this._syncNativeSprite()
716
+ })
717
+ },
718
+ /**
719
+ * @this {import('../../component.js').BlitsElement}
720
+ */
721
+ _syncNativeSprite() {
722
+ if (this[symbols.isSprite] !== true || !this.node) return
723
+ const st = this._spriteState
724
+ if (st === undefined) return
725
+
726
+ const raw = this.props.raw
727
+ const image = raw['image']
728
+ const map = raw['map']
729
+ const frameKey = spriteFrameKey(raw['frame'])
730
+
731
+ if (
732
+ st.lastTexture != null &&
733
+ image === st.lastImage &&
734
+ map === st.lastMap &&
735
+ frameKey === st.lastFrameKey
736
+ ) {
737
+ return
738
+ }
739
+
740
+ const imageChanged = image !== st.lastImage
741
+ st.lastImage = image
742
+ st.lastMap = map
743
+ st.lastFrameKey = frameKey
744
+
745
+ const texture = resolveSpriteTexture(this, renderer, st)
746
+ if (texture === st.lastTexture) return
747
+
748
+ st.lastTexture = texture
749
+ this.set('texture', texture)
750
+
751
+ if (imageChanged !== true) return
752
+
753
+ const prevL = st._loadedCb
754
+ const prevF = st._failedCb
755
+ st._loadedCb =
756
+ raw['@loaded'] && typeof raw['@loaded'] === 'function'
757
+ ? (payload) => {
758
+ const d = payload?.dimensions
759
+ const w = payload?.w ?? payload?.width ?? d?.width ?? d?.w
760
+ const h = payload?.h ?? payload?.height ?? d?.height ?? d?.h
761
+ raw['@loaded']({ w, h }, this)
762
+ }
763
+ : null
764
+ st._failedCb =
765
+ raw['@error'] && typeof raw['@error'] === 'function'
766
+ ? (payload) => raw['@error'](payload, this)
767
+ : null
768
+ if (st.spriteTexture) {
769
+ if (prevL) st.spriteTexture.off('loaded', prevL)
770
+ if (prevF) st.spriteTexture.off('failed', prevF)
771
+ if (st._loadedCb) st.spriteTexture.on('loaded', st._loadedCb)
772
+ if (st._failedCb) st.spriteTexture.on('failed', st._failedCb)
773
+ }
774
+ },
660
775
  /**
661
776
  * Set an individual property on the node
662
777
  *
@@ -823,6 +938,16 @@ const Element = {
823
938
 
824
939
  Log.debug('Deleting Node', this.nodeId)
825
940
 
941
+ if (this[symbols.isSprite] === true && this._spriteState) {
942
+ const st = this._spriteState
943
+ if (st.spriteTexture) {
944
+ if (st._loadedCb) st.spriteTexture.off('loaded', st._loadedCb)
945
+ if (st._failedCb) st.spriteTexture.off('failed', st._failedCb)
946
+ }
947
+ if (st.subTextures) st.subTextures.clear()
948
+ this._spriteState = null
949
+ }
950
+
826
951
  // Clear all pending debounce timeouts
827
952
  const debounceProps = Object.keys(this.debounceTimeouts)
828
953
  for (let i = 0; i < debounceProps.length; i++) {
@@ -25,6 +25,7 @@ import { SCREEN_RESOLUTIONS, RENDER_QUALITIES } from '../../constants.js'
25
25
  import colors from '../../lib/colors/colors.js'
26
26
  import fontLoader from './fontLoader.js'
27
27
  import shaderLoader from './shaderLoader.js'
28
+ import { platform } from '../../platform.js'
28
29
 
29
30
  /** @type {RendererMain|{}} */
30
31
  export let renderer = {}
@@ -73,6 +74,11 @@ const textureMemorySettings = (settings) => {
73
74
  *
74
75
  */
75
76
  export default (App, target, settings = {}) => {
77
+ const screenHeight = settings.platform ? settings.platform.screenHeight : platform.screenHeight
78
+ const hardwareConcurrency = settings.platform
79
+ ? settings.platform.hardwareConcurrency
80
+ : platform.hardwareConcurrency
81
+
76
82
  renderer = new RendererMain(
77
83
  {
78
84
  ...{
@@ -84,12 +90,10 @@ export default (App, target, settings = {}) => {
84
90
  deviceLogicalPixelRatio:
85
91
  settings.pixelRatio ||
86
92
  SCREEN_RESOLUTIONS[settings.screenResolution] ||
87
- SCREEN_RESOLUTIONS[window.innerHeight] ||
93
+ SCREEN_RESOLUTIONS[screenHeight] ||
88
94
  1,
89
95
  numImageWorkers:
90
- 'webWorkersLimit' in settings
91
- ? settings.webWorkersLimit
92
- : window.navigator.hardwareConcurrency || 2,
96
+ 'webWorkersLimit' in settings ? settings.webWorkersLimit : hardwareConcurrency || 2,
93
97
  clearColor: (settings.canvasColor && colors.normalize(settings.canvasColor)) || 0x00000000,
94
98
  inspector: settings.inspector === true ? Inspector : undefined,
95
99
  boundsMargin: settings.viewportMargin || 0,
@@ -100,7 +104,7 @@ export default (App, target, settings = {}) => {
100
104
  textureMemory: textureMemorySettings(settings),
101
105
  createImageBitmapSupport: 'auto',
102
106
  targetFPS: 'maxFPS' in settings ? settings.maxFPS : 0,
103
- platform: 'platform' in settings ? settings.platform : null,
107
+ platform: settings.rendererPlatform || null,
104
108
  },
105
109
  ...(settings.advanced || {}),
106
110
  },
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Resolves a sprite sheet frame from map + frame (template Sprite element path on L3).
3
+ * @param {import('../../component.js').BlitsElement} element
4
+ * @param {object} r - renderer
5
+ * @param {any} state - mutated sprite cache (see L3 element _spriteState)
6
+ * @returns {any|null} texture for the node
7
+ */
8
+ export function resolveSpriteTexture(element, r, state) {
9
+ const image = element.props.raw['image']
10
+ if (image === undefined || image === null) {
11
+ return null
12
+ }
13
+ if (r === null || r === undefined || r.createTexture === undefined) {
14
+ return null
15
+ }
16
+
17
+ if (
18
+ state.spriteTexture === undefined ||
19
+ state.spriteTexture === null ||
20
+ state.currentSrc !== image
21
+ ) {
22
+ state.spriteTexture = r.createTexture('ImageTexture', {
23
+ src: image,
24
+ })
25
+ state.currentSrc = image
26
+ state.subTextures = null
27
+ state.currentMap = null
28
+ }
29
+
30
+ const map = element.props.raw['map']
31
+
32
+ if (state.currentMap !== map) {
33
+ state.currentMap = map
34
+ state.subTextures = null
35
+ }
36
+ const frame = element.props.raw['frame']
37
+
38
+ let options = null
39
+ if (map != null && frame != null) {
40
+ if (map.frames != null && map.frames !== undefined && frame in map.frames) {
41
+ options = Object.assign({}, map.defaults || {}, map.frames[frame])
42
+ } else if (frame in map) {
43
+ options = map[frame]
44
+ }
45
+ }
46
+
47
+ if ((options === null || options === undefined) && typeof frame === 'object' && frame !== null) {
48
+ options = frame
49
+ }
50
+
51
+ if (options != null) {
52
+ const key = typeof frame === 'object' ? JSON.stringify(frame) : String(frame)
53
+ if (!state.subTextures) {
54
+ state.subTextures = new Map()
55
+ }
56
+ const cached = state.subTextures.get(key)
57
+ if (cached !== undefined) {
58
+ return cached
59
+ }
60
+ const subTexture = r.createTexture('SubTexture', {
61
+ texture: state.spriteTexture,
62
+ x: options.x,
63
+ y: options.y,
64
+ w: options.w,
65
+ h: options.h,
66
+ })
67
+ state.subTextures.set(key, subTexture)
68
+ return subTexture
69
+ }
70
+ return state.spriteTexture
71
+ }
@@ -21,7 +21,7 @@ import Settings from '../settings.js'
21
21
  import { DEFAULT_HOLD_TIMEOUT_MS } from '../constants.js'
22
22
  import { Log } from '../lib/log.js'
23
23
  import { getAncestors } from './helpers.js'
24
- import InternalKeyboardEvent from '../lib/events/internalkeyboardevent.js'
24
+ import { platform } from '../platform.js'
25
25
 
26
26
  let focusedComponent = null
27
27
  let focusChain = []
@@ -136,8 +136,11 @@ const setFocus = (component, event) => {
136
136
  focusedComponent = component
137
137
  component[symbols.lifecycle].state = 'focus'
138
138
 
139
- if (event instanceof KeyboardEvent) {
140
- const internalEvent = new InternalKeyboardEvent('keydown', event)
141
- document.dispatchEvent(internalEvent)
139
+ if (platform.isKeyboardEvent && platform.isKeyboardEvent(event) && platform.dispatchEvent) {
140
+ const internalEvent = platform.createKeyboardEvent
141
+ ? platform.createKeyboardEvent('keydown', event)
142
+ : event
143
+ internalEvent[symbols.internalEvent] = true
144
+ platform.dispatchEvent(internalEvent)
142
145
  }
143
146
  }
package/src/launch.js CHANGED
@@ -19,6 +19,7 @@ import Settings from './settings.js'
19
19
  import { initLog, Log } from './lib/log.js'
20
20
  import engine from './engine.js'
21
21
  import blitsPackageInfo from '../package.json' with { type: 'json' }
22
+ import { configurePlatform } from './platform.js'
22
23
 
23
24
  /**
24
25
  * @typedef {Object} Font
@@ -86,10 +87,12 @@ async function rendererVersion() {
86
87
  * @property {number} [holdTimeout] - Time after which a key press is considered a hold
87
88
  * @property {HTMLCanvasElement} [canvas] - Custom canvas object used to render the App
88
89
  * @property {number} [textureProcessingTimeLimit] - Max time renderer can process textures in a frame
90
+ * @property {import('@lightningjs/renderer').RendererMainSettings['platform']} [rendererPlatform] - Custom platform used by the renderer
89
91
  * @property {Partial<import('@lightningjs/renderer').RendererMainSettings>} [advanced] - Advanced renderer settings
90
92
  * @property {boolean} [announcer] - Whether or not the announcer should be activated on initialization
91
93
  * @property {boolean} [enableMouse] - Enable mouse support (hover and click-to-focus). Defaults to false.
92
94
  * @property {number} [maxFPS] - Maximum FPS
95
+ * @property {function} [platform] - Platform factory for non-browser environments
93
96
  */
94
97
 
95
98
  /**
@@ -109,10 +112,30 @@ async function rendererVersion() {
109
112
  * @returns {void}
110
113
  */
111
114
  export default (App, target, settings) => {
115
+ let warnLegacyRendererPlatform = false
116
+
117
+ if (settings !== undefined && 'platform' in settings) {
118
+ if (typeof settings.platform === 'function') {
119
+ settings.platform = configurePlatform(settings.platform)
120
+ } else {
121
+ if ('rendererPlatform' in settings === false) {
122
+ settings.rendererPlatform = settings.platform
123
+ }
124
+ delete settings.platform
125
+ warnLegacyRendererPlatform = true
126
+ }
127
+ }
128
+
112
129
  Settings.set(settings)
113
130
 
114
131
  initLog()
115
132
 
133
+ if (warnLegacyRendererPlatform === true) {
134
+ Log.warn(
135
+ 'Passing a renderer platform via settings.platform is deprecated. Use settings.rendererPlatform instead.'
136
+ )
137
+ }
138
+
116
139
  rendererVersion().then((v) => {
117
140
  Log.info('Blits Version ', blitsPackageInfo.version)
118
141
  Log.info('Renderer Version ', v)
@@ -206,6 +206,10 @@ const generateElementCode = function (
206
206
  renderCode.push(`elementConfigs[${counter}][Symbol.for('isSlot')] = true`)
207
207
  }
208
208
 
209
+ if (templateObject[Symbol.for('componentType')] === 'Sprite') {
210
+ renderCode.push(`elementConfigs[${counter}][Symbol.for('isSprite')] = true`)
211
+ }
212
+
209
213
  if (options.holder) {
210
214
  renderCode.push(`elementConfigs[${counter}]['holder'] = true`)
211
215
  }
@@ -611,11 +615,16 @@ const generateForLoopCode = function (templateObject, parent) {
611
615
  if (
612
616
  templateObject[Symbol.for('componentType')] === 'Element' ||
613
617
  templateObject[Symbol.for('componentType')] === 'Slot' ||
614
- templateObject[Symbol.for('componentType')] === 'Text'
618
+ templateObject[Symbol.for('componentType')] === 'Text' ||
619
+ templateObject[Symbol.for('componentType')] === 'Sprite' ||
620
+ templateObject[Symbol.for('componentType')] === 'Layout'
615
621
  ) {
616
622
  if (templateObject[Symbol.for('componentType')] === 'Text') {
617
623
  templateObject.__textnode = 'true'
618
624
  }
625
+ if (templateObject[Symbol.for('componentType')] === 'Layout') {
626
+ templateObject.__layout = 'true'
627
+ }
619
628
  generateElementCode.call(ctx, templateObject, parent, {
620
629
  key: 'scope.key',
621
630
  component: 'scope.',
@@ -788,7 +797,8 @@ const generateCode = function (templateObject, parent = false, options = {}) {
788
797
  childTemplateObject[Symbol.for('componentType')] === 'Element' ||
789
798
  childTemplateObject[Symbol.for('componentType')] === 'Slot' ||
790
799
  childTemplateObject[Symbol.for('componentType')] === 'Text' ||
791
- childTemplateObject[Symbol.for('componentType')] === 'Layout'
800
+ childTemplateObject[Symbol.for('componentType')] === 'Layout' ||
801
+ childTemplateObject[Symbol.for('componentType')] === 'Sprite'
792
802
  ) {
793
803
  if (childTemplateObject[Symbol.for('componentType')] === 'Text') {
794
804
  childTemplateObject.__textnode = 'true'
@@ -200,8 +200,12 @@ const processComputedBlock = (configObject) => {
200
200
  currentPos = skipNonCode(computedObj, currentPos)
201
201
  if (currentPos >= computedObj.length - 1) break
202
202
 
203
- // Find the next function name (computed properties are `name() { ... }`)
204
- const functionNameMatch = /^(\w+)\s*\(\)\s*/.exec(computedObj.substring(currentPos))
203
+ // Find the next function name (computed properties are `name() { ... }`
204
+ // or long-form `name: function() { ... }`)
205
+ const functionNameMatch = /^(\w+)\s*(?:\(\)|:\s*function\s*\([^)]*\))\s*/.exec(
206
+ computedObj.substring(currentPos)
207
+ )
208
+
205
209
  if (!functionNameMatch) {
206
210
  const nextPos = skipComputedMember(computedObj, currentPos)
207
211
  if (nextPos === -1) break
@@ -61,6 +61,7 @@
61
61
  * @property {symbol} components
62
62
  * @property {symbol} config
63
63
  * @property {symbol} isSlot
64
+ * @property {symbol} isSprite
64
65
  * @property {symbol} props
65
66
  * @property {symbol} parent
66
67
  * @property {symbol} slots
@@ -68,6 +69,7 @@
68
69
  * @property {symbol} isComponent
69
70
  * @property {symbol} effects
70
71
  * @property {symbol} removeGlobalEffects
72
+ * @property {symbol} tagContent
71
73
  */
72
74
 
73
75
  /**
@@ -126,6 +128,8 @@ export default {
126
128
  config: Symbol.for('config'),
127
129
  // Symbol 'isSlot' utilized within generated code
128
130
  isSlot: Symbol.for('isSlot'),
131
+ // Symbol 'isSprite' utilized within generated code (native Sprite on L3 element)
132
+ isSprite: Symbol.for('isSprite'),
129
133
  // Symbol 'props' utilized within generated code
130
134
  props: Symbol.for('props'),
131
135
  // Symbol 'slots' utilized within generated code
@@ -0,0 +1,117 @@
1
+ /*
2
+ * Copyright 2026 Comcast Cable Communications Management, LLC
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ *
15
+ * SPDX-License-Identifier: Apache-2.0
16
+ */
17
+
18
+ const globalScope = globalThis
19
+
20
+ const createKeyboardEventFactory = (KeyboardEventConstructor) => {
21
+ if (KeyboardEventConstructor === undefined) return undefined
22
+
23
+ return (type, init = {}) => {
24
+ const event = new KeyboardEventConstructor(type, init)
25
+ if ('keyCode' in init && event.keyCode !== init.keyCode) {
26
+ Object.defineProperty(event, 'keyCode', {
27
+ get: () => init.keyCode,
28
+ })
29
+ }
30
+ return event
31
+ }
32
+ }
33
+
34
+ const browserPlatform = () => {
35
+ const windowRef = globalScope.window
36
+ const documentRef = globalScope.document || (windowRef && windowRef.document)
37
+ const selfRef = globalScope.self || windowRef || globalScope
38
+ const navigatorRef =
39
+ globalScope.navigator || (windowRef && windowRef.navigator) || (selfRef && selfRef.navigator)
40
+ const KeyboardEventConstructor =
41
+ globalScope.KeyboardEvent || (windowRef && windowRef.KeyboardEvent)
42
+ let localStorage
43
+
44
+ try {
45
+ localStorage = (windowRef && windowRef.localStorage) || globalScope.localStorage
46
+ } catch {
47
+ localStorage = undefined
48
+ }
49
+
50
+ const createKeyboardEvent = createKeyboardEventFactory(KeyboardEventConstructor)
51
+
52
+ return {
53
+ input: documentRef,
54
+ viewport: windowRef,
55
+ dispatchEvent:
56
+ documentRef && documentRef.dispatchEvent
57
+ ? documentRef.dispatchEvent.bind(documentRef)
58
+ : undefined,
59
+ localStorage,
60
+ getCookie: () => (documentRef && documentRef.cookie) || '',
61
+ setCookie: (value) => {
62
+ if (documentRef !== undefined) documentRef.cookie = value
63
+ },
64
+ historyBack: () => {
65
+ if (windowRef && windowRef.history && windowRef.history.back) windowRef.history.back()
66
+ },
67
+ screenHeight: windowRef && windowRef.innerHeight,
68
+ hardwareConcurrency: navigatorRef && navigatorRef.hardwareConcurrency,
69
+ userAgent: navigatorRef && navigatorRef.userAgent,
70
+ KeyboardEvent: KeyboardEventConstructor,
71
+ isKeyboardEvent:
72
+ KeyboardEventConstructor !== undefined
73
+ ? (event) => event instanceof KeyboardEventConstructor
74
+ : undefined,
75
+ createKeyboardEvent,
76
+ SpeechSynthesisUtterance:
77
+ globalScope.SpeechSynthesisUtterance || (windowRef && windowRef.SpeechSynthesisUtterance),
78
+ speechSynthesis:
79
+ (selfRef && selfRef.speechSynthesis) || (windowRef && windowRef.speechSynthesis),
80
+ now:
81
+ globalScope.performance && globalScope.performance.now !== undefined
82
+ ? globalScope.performance.now.bind(globalScope.performance)
83
+ : Date.now,
84
+ }
85
+ }
86
+
87
+ export let platform = browserPlatform()
88
+
89
+ export const configurePlatform = (customPlatform) => {
90
+ const basePlatform = browserPlatform()
91
+ const resolvedCustomPlatform = customPlatform(basePlatform) || {}
92
+
93
+ platform = {
94
+ ...basePlatform,
95
+ ...resolvedCustomPlatform,
96
+ }
97
+
98
+ platform.KeyboardEvent = platform.KeyboardEvent || basePlatform.KeyboardEvent
99
+ platform.isKeyboardEvent =
100
+ resolvedCustomPlatform.isKeyboardEvent ||
101
+ (platform.KeyboardEvent !== undefined
102
+ ? (event) => event instanceof platform.KeyboardEvent
103
+ : basePlatform.isKeyboardEvent)
104
+ platform.createKeyboardEvent =
105
+ resolvedCustomPlatform.createKeyboardEvent ||
106
+ createKeyboardEventFactory(platform.KeyboardEvent) ||
107
+ basePlatform.createKeyboardEvent
108
+ platform.getCookie = platform.getCookie || basePlatform.getCookie
109
+ platform.setCookie = platform.setCookie || basePlatform.setCookie
110
+ platform.historyBack = platform.historyBack || basePlatform.historyBack
111
+ platform.SpeechSynthesisUtterance =
112
+ platform.SpeechSynthesisUtterance || basePlatform.SpeechSynthesisUtterance
113
+ platform.speechSynthesis = platform.speechSynthesis || basePlatform.speechSynthesis
114
+ platform.now = platform.now || basePlatform.now
115
+
116
+ return platform
117
+ }
@@ -0,0 +1 @@
1
+ export { WebPlatform, WebPlatformNext, WebPlatformChrome50, WebPlatformLegacy } from '@lightningjs/renderer/platforms'
@@ -0,0 +1,23 @@
1
+ /*
2
+ * Copyright 2023 Comcast Cable Communications Management, LLC
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ *
15
+ * SPDX-License-Identifier: Apache-2.0
16
+ */
17
+
18
+ export {
19
+ WebPlatform,
20
+ WebPlatformNext,
21
+ WebPlatformChrome50,
22
+ WebPlatformLegacy,
23
+ } from '@lightningjs/renderer/platforms'