rotor-framework 0.3.2

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 (39) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +120 -0
  3. package/package.json +59 -0
  4. package/src/source/RotorFramework.bs +654 -0
  5. package/src/source/RotorFrameworkTask.bs +278 -0
  6. package/src/source/base/BaseModel.bs +52 -0
  7. package/src/source/base/BasePlugin.bs +48 -0
  8. package/src/source/base/BaseReducer.bs +184 -0
  9. package/src/source/base/BaseStack.bs +92 -0
  10. package/src/source/base/BaseViewModel.bs +124 -0
  11. package/src/source/base/BaseWidget.bs +104 -0
  12. package/src/source/base/DispatcherCreator.bs +193 -0
  13. package/src/source/base/DispatcherExternal.bs +260 -0
  14. package/src/source/base/ListenerForDispatchers.bs +246 -0
  15. package/src/source/engine/Constants.bs +74 -0
  16. package/src/source/engine/animator/Animator.bs +334 -0
  17. package/src/source/engine/builder/Builder.bs +213 -0
  18. package/src/source/engine/builder/NodePool.bs +236 -0
  19. package/src/source/engine/builder/PluginAdapter.bs +139 -0
  20. package/src/source/engine/builder/PostProcessor.bs +331 -0
  21. package/src/source/engine/builder/Processor.bs +156 -0
  22. package/src/source/engine/builder/Tree.bs +278 -0
  23. package/src/source/engine/builder/TreeBase.bs +313 -0
  24. package/src/source/engine/builder/WidgetCreate.bs +322 -0
  25. package/src/source/engine/builder/WidgetRemove.bs +72 -0
  26. package/src/source/engine/builder/WidgetUpdate.bs +113 -0
  27. package/src/source/engine/providers/Dispatcher.bs +72 -0
  28. package/src/source/engine/providers/DispatcherProvider.bs +95 -0
  29. package/src/source/engine/services/I18n.bs +169 -0
  30. package/src/source/libs/animate/Animate.bs +753 -0
  31. package/src/source/libs/animate/LICENSE.txt +21 -0
  32. package/src/source/plugins/DispatcherProviderPlugin.bs +127 -0
  33. package/src/source/plugins/FieldsPlugin.bs +180 -0
  34. package/src/source/plugins/FocusPlugin.bs +1522 -0
  35. package/src/source/plugins/FontStylePlugin.bs +159 -0
  36. package/src/source/plugins/ObserverPlugin.bs +548 -0
  37. package/src/source/utils/ArrayUtils.bs +495 -0
  38. package/src/source/utils/GeneralUtils.bs +181 -0
  39. package/src/source/utils/NodeUtils.bs +180 -0
@@ -0,0 +1,278 @@
1
+ '''''''''
2
+ ' ▗▄▄▖ ▗▄▖▗▄▄▄▖▗▄▖ ▗▄▄▖ ▗▄▄▄▖▗▄▄▖ ▗▄▖ ▗▖ ▗▖▗▄▄▄▖▗▖ ▗▖ ▗▄▖ ▗▄▄▖ ▗▖ ▗▖
3
+ ' ▐▌ ▐▌▐▌ ▐▌ █ ▐▌ ▐▌▐▌ ▐▌ ▐▌ ▐▌ ▐▌▐▌ ▐▌▐▛▚▞▜▌▐▌ ▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌▗▞▘
4
+ ' ▐▛▀▚▖▐▌ ▐▌ █ ▐▌ ▐▌▐▛▀▚▖ ▐▛▀▀▘▐▛▀▚▖▐▛▀▜▌▐▌ ▐▌▐▛▀▀▘▐▌ ▐▌▐▌ ▐▌▐▛▀▚▖▐▛▚▖
5
+ ' ▐▌ ▐▌▝▚▄▞▘ █ ▝▚▄▞▘▐▌ ▐▌ ▐▌ ▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▙▄▄▖▐▙█▟▌▝▚▄▞▘▐▌ ▐▌▐▌ ▐▌
6
+ ' Rotor Framework™ © 2025 Balázs Molnár. All rights reserved.
7
+ ' Version 0.3.2
8
+ '''''''''
9
+
10
+ ' constants
11
+ import "engine/Constants.bs"
12
+
13
+ ' engine
14
+ import "engine/providers/DispatcherProvider.bs"
15
+ import "engine/providers/Dispatcher.bs"
16
+
17
+ ' base classes
18
+ import "base/DispatcherCreator.bs"
19
+ import "base/DispatcherExternal.bs"
20
+ import "base/BaseReducer.bs"
21
+ import "base/BaseModel.bs"
22
+ import "base/BaseStack.bs"
23
+
24
+ ' utils
25
+ import "utils/GeneralUtils.bs"
26
+ import "utils/NodeUtils.bs"
27
+ import "utils/ArrayUtils.bs"
28
+
29
+ namespace Rotor
30
+ ' =====================================================================
31
+ ' FrameworkTask - Task thread version of Rotor Framework for MVI
32
+ '
33
+ ' Task thread version of the Rotor Framework that enables cross-thread MVI
34
+ ' (Model-View-Intent) architecture. This class manages state and dispatchers
35
+ ' on a separate task thread, allowing heavy computations and state management
36
+ ' to run off the render thread for better performance.
37
+ '
38
+ ' Configuration:
39
+ ' - tasks (array, optional): List of additional task node names to synchronize with.
40
+ ' Allows multiple task threads to communicate and share
41
+ ' dispatchers across different threads.
42
+ '
43
+ ' USAGE NOTES:
44
+ ' The FrameworkTask must be instantiated in the task's init() function and the sync()
45
+ ' method MUST be called at the end of your task function to establish the message loop.
46
+ '
47
+ ' IMPORTANT: The sync() method creates an infinite loop that handles cross-thread
48
+ ' communication and dispatcher synchronization. This call should be the LAST statement
49
+ ' in your task function, after all dispatcher initialization.
50
+ '
51
+ ' Example:
52
+ ' File: MyTask.task.bs
53
+ ' import "pkg:/source/RotorFrameworkTask.bs"
54
+ ' import "pkg:/source/MyDispatcher.bs"
55
+ '
56
+ ' sub init()
57
+ ' m.top.functionName = "task"
58
+ ' m.appFw = new Rotor.FrameworkTask({
59
+ ' tasks: ["AnotherTask"]
60
+ ' })
61
+ ' end sub
62
+ '
63
+ ' sub task()
64
+ ' m.fooDispatcher = createFooDispatcher()
65
+ ' m.barDispatcher = createBarDispatcher()
66
+ ' m.appFw.sync()
67
+ ' end sub
68
+ ' =====================================================================
69
+ class FrameworkTask
70
+
71
+ name = "Rotor Framework"
72
+ version = "0.3.2"
73
+
74
+ config = {
75
+ tasks: invalid, ' optional
76
+ debug: {
77
+ }
78
+ }
79
+
80
+ threadType = Rotor.Const.ThreadType.TASK
81
+
82
+ keepAlive = true
83
+
84
+ ' helper vars
85
+ taskNode as object
86
+ dispatcherProvider as object
87
+ port as object
88
+
89
+ ' ---------------------------------------------------------------------
90
+ ' new - Initializes the FrameworkTask instance
91
+ '
92
+ ' Sets up the task thread dispatcher provider, message port, and
93
+ ' global framework helper for cross-thread communication.
94
+ '
95
+ ' @param {object} config - Configuration object (see class documentation)
96
+ '
97
+ sub new(config = {} as object)
98
+
99
+ Rotor.Utils.deepExtendAA(m.config, config)
100
+
101
+ globalScope = GetGlobalAA()
102
+ globalScope.rotor_framework_helper = { ' this give to dispatcher instance the possibility to self-register
103
+ threadType: m.threadType,
104
+ frameworkInstance: m
105
+ }
106
+ m.taskNode = globalScope.top
107
+
108
+ m.dispatcherProvider = new Rotor.DispatcherProvider(m.threadType)
109
+
110
+ m.taskNode.addField("rotorSync", "assocarray", true)
111
+ m.port = CreateObject("roMessagePort")
112
+ m.taskNode.observeFieldScoped("rotorSync", m.port)
113
+
114
+ end sub
115
+
116
+ ' =====================================================================
117
+ ' PUBLIC API
118
+ ' =====================================================================
119
+
120
+ ' ---------------------------------------------------------------------
121
+ ' getDispatcher - Gets dispatcher facade by ID
122
+ '
123
+ ' @param {string} dispatcherId - Dispatcher identifier
124
+ ' @returns {object} Dispatcher facade instance
125
+ '
126
+ public function getDispatcher(dispatcherId as string) as object
127
+ return m.dispatcherProvider.getFacade(dispatcherId, GetGlobalAA())
128
+ end function
129
+
130
+ ' ---------------------------------------------------------------------
131
+ ' sync - Starts the message loop for cross-thread communication
132
+ '
133
+ ' IMPORTANT: This method creates an infinite loop that handles:
134
+ ' - Intent dispatching from render thread
135
+ ' - External dispatcher registration
136
+ ' - State change notifications
137
+ ' - Async reducer callbacks
138
+ '
139
+ ' This method MUST be the last call in your task function, as it
140
+ ' blocks execution until the framework is destroyed.
141
+ '
142
+ sub sync()
143
+ m.notifySyncStatus(Rotor.Const.ThreadSyncType.TASK_SYNCING)
144
+
145
+ keepAlive = true
146
+
147
+ while true and keepAlive = true
148
+ msg = wait(0, m.port)
149
+ if msg <> invalid
150
+ msgType = type(msg)
151
+ if msgType = "roSGNodeEvent"
152
+ fieldId = msg.getField()
153
+
154
+ if fieldId = "rotorSync"
155
+
156
+ sync = msg.getData() ' @type:AA
157
+
158
+ if sync.type = Rotor.Const.ThreadSyncType.DISPATCH
159
+
160
+
161
+ dispatcherId = sync.payload.dispatcherId
162
+ intent = sync.payload.intent
163
+ dispatcherInstance = m.dispatcherProvider.stack.LookupCI(dispatcherId)
164
+
165
+ ' taskIntent = Rotor.Utils.deepCopy(intent)
166
+ dispatcherInstance.dispatch(intent)
167
+
168
+ else if sync.type = Rotor.Const.ThreadSyncType.REGISTER_EXTERNAL_DISPATCHER
169
+
170
+ for each item in sync.externalDispatcherList
171
+ m.dispatcherProvider.registerExternalDispatchers(item.dispatcherId, item.externalTaskNode)
172
+ end for
173
+
174
+ m.notifySyncStatus(Rotor.Const.ThreadSyncType.TASK_SYNCED)
175
+
176
+ else if sync.type = Rotor.Const.ThreadSyncType.DESTROY
177
+
178
+ keepAlive = false
179
+
180
+ end if
181
+ else
182
+
183
+ data = msg.getData()
184
+ extraInfo = msg.GetInfo() ' Info AA passed during observeFieldScoped
185
+
186
+ if extraInfo?.asyncReducerCallbackId <> invalid and m.dispatcherProvider.get(extraInfo?.asyncReducerCallbackId) <> invalid
187
+ ' Catch by dispatcherId
188
+ m.dispatcherProvider.get(extraInfo?.asyncReducerCallbackId).asyncReducerCallback(msg)
189
+ else
190
+ dispatcherId = fieldId
191
+ dispatcherInstance = m.dispatcherProvider.get(dispatcherId)
192
+ dispatcherInstance.notifyListeners(data)
193
+ end if
194
+
195
+ end if
196
+ end if
197
+ end if
198
+ end while
199
+ m.destroy()
200
+ end sub
201
+
202
+ ' =====================================================================
203
+ ' INTERNAL METHODS
204
+ ' =====================================================================
205
+
206
+ ' ---------------------------------------------------------------------
207
+ ' notifySyncStatus - Notifies render thread of sync status
208
+ '
209
+ ' Sends sync status message to render thread via rotorSync field.
210
+ '
211
+ ' @param {string} status - Sync status type (TASK_SYNCING or TASK_SYNCED)
212
+ '
213
+ sub notifySyncStatus(status as string)
214
+
215
+ payload = {
216
+ type: status,
217
+ taskNode: m.taskNode
218
+ }
219
+
220
+ if status = Rotor.Const.ThreadSyncType.TASK_SYNCING
221
+ payload.append({
222
+ dispatcherIds: m.dispatcherProvider.stack.Keys(),
223
+ tasks: m.config.tasks
224
+ })
225
+ end if
226
+
227
+ m.taskNode.rootNode.setField("rotorSync", payload)
228
+
229
+ end sub
230
+
231
+ ' ---------------------------------------------------------------------
232
+ ' addObserver - Adds field observer to task thread message port
233
+ '
234
+ ' @param {string} fieldId - Field name to observe
235
+ ' @param {object} node - SceneGraph node to observe
236
+ '
237
+ sub addObserver(fieldId as string, node)
238
+ node.observeFieldScoped(fieldId, m.port)
239
+ end sub
240
+
241
+ ' ---------------------------------------------------------------------
242
+ ' removeObserver - Removes field observer from node
243
+ '
244
+ ' @param {string} fieldId - Field name to stop observing
245
+ ' @param {object} node - SceneGraph node to unobserve
246
+ '
247
+ sub removeObserver(fieldId as string, node)
248
+ node.unobserveFieldScoped(fieldId)
249
+ end sub
250
+
251
+ ' =====================================================================
252
+ ' CLEANUP
253
+ ' =====================================================================
254
+
255
+ ' ---------------------------------------------------------------------
256
+ ' destroy - Cleans up task thread resources
257
+ '
258
+ ' Destroys dispatcher provider and clears global framework helper.
259
+ '
260
+ public sub destroy()
261
+ m.dispatcherProvider.destroy()
262
+
263
+ globalScope = GetGlobalAA()
264
+ globalScope.rotor_framework_helper = {
265
+ frameworkInstance: invalid
266
+ }
267
+
268
+ m.taskNode.rootNode = invalid
269
+ m.taskNode = invalid
270
+ end sub
271
+
272
+ end class
273
+
274
+ end namespace
275
+
276
+
277
+
278
+
@@ -0,0 +1,52 @@
1
+ namespace Rotor
2
+
3
+ ' =====================================================================
4
+ ' Model (BaseModel) - Base model class for MVI pattern state management
5
+ '
6
+ ' Responsibilities:
7
+ ' - Holds application state as immutable container
8
+ ' - Works with Dispatchers and Reducers for state changes
9
+ ' - Provides cleanup mechanism
10
+ '
11
+ ' Usage in MVI:
12
+ ' Model → Holds current state
13
+ ' Reducer → Creates new state based on intents
14
+ ' Dispatcher → Routes intents and state updates
15
+ ' =====================================================================
16
+ class Model
17
+
18
+ ' =============================================================
19
+ ' MEMBER VARIABLES
20
+ ' =============================================================
21
+
22
+ state as object ' Application state container
23
+
24
+ ' =============================================================
25
+ ' CONSTRUCTOR
26
+ ' =============================================================
27
+
28
+ ' ---------------------------------------------------------------------
29
+ ' Constructor - Initializes model with optional initial state
30
+ '
31
+ ' @param {object} state - Initial state object (optional)
32
+ '
33
+ sub new(state = invalid as object)
34
+ if state <> invalid
35
+ m.state = state
36
+ end if
37
+ end sub
38
+
39
+ ' =============================================================
40
+ ' CLEANUP
41
+ ' =============================================================
42
+
43
+ ' ---------------------------------------------------------------------
44
+ ' destroy - Cleans up model by invalidating state reference
45
+ '
46
+ sub destroy()
47
+ m.state = invalid
48
+ end sub
49
+
50
+ end class
51
+
52
+ end namespace
@@ -0,0 +1,48 @@
1
+ namespace Rotor
2
+
3
+ ' =====================================================================
4
+ ' BasePlugin - Base class for all Rotor Framework plugins
5
+ '
6
+ ' Provides:
7
+ ' - Plugin identification and lifecycle management
8
+ ' - Enable/disable functionality
9
+ ' - Parameter storage
10
+ '
11
+ ' Plugin Types:
12
+ ' - Lifecycle plugins: Implement hooks {} for beforeMount, beforeUpdate, etc.
13
+ ' - Widget method plugins: Implement widgetMethods {} to inject methods
14
+ ' =====================================================================
15
+ class BasePlugin
16
+
17
+ ' =============================================================
18
+ ' MEMBER VARIABLES
19
+ ' =============================================================
20
+
21
+ isEnabled = true ' Plugin enabled/disabled flag
22
+ key as string ' Plugin identifier (unique key)
23
+ scope as object ' Plugin scope (m context in plugin definitions)
24
+ params as object ' Plugin initialization parameters
25
+
26
+ ' Optional plugin features (implemented by subclasses):
27
+ ' hooks as object ' Widget lifecycle hooks (beforeMount, beforeUpdate, etc.)
28
+ ' widgetMethods as object ' Methods to inject into widgets
29
+
30
+ ' =============================================================
31
+ ' CONSTRUCTOR
32
+ ' =============================================================
33
+
34
+ ' ---------------------------------------------------------------------
35
+ ' Constructor - Initializes plugin with key and parameters
36
+ '
37
+ ' @param {string} key - Plugin identifier (auto-generated if empty)
38
+ ' @param {object} params - Plugin initialization parameters
39
+ '
40
+ sub new(key = "" as string, params = invalid as object)
41
+ if key = "" then key = Rotor.Utils.getUUIDHex(8)
42
+ m.params = params
43
+ m.key = key
44
+ end sub
45
+
46
+ end class
47
+
48
+ end namespace
@@ -0,0 +1,184 @@
1
+ namespace Rotor
2
+
3
+ ' =====================================================================
4
+ ' Reducer (BaseReducer) - Base class for state reducers in the MVI pattern
5
+ '
6
+ ' Responsibilities:
7
+ ' - Computes next state from current state + intent
8
+ ' - Applies middleware pipeline for async operations, logging, validation
9
+ ' - Provides pure state transformation logic
10
+ ' - Integrates with Dispatcher for state updates
11
+ '
12
+ ' MVI Flow:
13
+ ' Intent → Middleware → Reducer → New State → Model → View Update
14
+ '
15
+ ' Override Points:
16
+ ' - reducer(state, intent): Define state transitions
17
+ ' - applyMiddlewares(): Return array of middleware functions
18
+ ' - middlewares: Set middleware array directly
19
+ ' =====================================================================
20
+ class Reducer
21
+
22
+ ' =============================================================
23
+ ' MEMBER VARIABLES
24
+ ' =============================================================
25
+
26
+ model as object ' Reference to Model holding state
27
+ middlewares = [] as function[] ' Middleware function array
28
+ port as object ' Framework port for async operations
29
+
30
+ ' Dispatcher integration
31
+ getDispatcher as function ' Get dispatcher facade by ID
32
+ dispatch as function ' Dispatch intent to owner dispatcher
33
+ ownerDispatcher as object ' Owning dispatcher instance
34
+ ownerDispatcherId as string ' Owning dispatcher ID
35
+
36
+ ' Internal
37
+ middlewareFnScoped as dynamic ' Currently executing middleware
38
+
39
+ ' =============================================================
40
+ ' CONSTRUCTOR
41
+ ' =============================================================
42
+
43
+ ' ---------------------------------------------------------------------
44
+ ' Constructor - Initializes reducer with framework integration
45
+ '
46
+ sub new()
47
+ frameworkInstance = GetGlobalAA().rotor_framework_helper.frameworkInstance
48
+ m.port = frameworkInstance.port
49
+
50
+ ' Inject getDispatcher method
51
+ m.getDispatcher = function(dispatcherId as string) as object
52
+ return GetGlobalAA().rotor_framework_helper.frameworkInstance.dispatcherProvider.getFacade(dispatcherId, m)
53
+ end function
54
+
55
+ ' Inject dispatch method for self-dispatching
56
+ m.dispatch = sub(intent as object)
57
+ m.ownerDispatcher.dispatch(intent)
58
+ end sub
59
+ end sub
60
+
61
+ ' =============================================================
62
+ ' REDUCER LOGIC
63
+ ' =============================================================
64
+
65
+ ' ---------------------------------------------------------------------
66
+ ' reducer - Pure function that computes next state
67
+ '
68
+ ' Override this method to implement custom state transitions based on intent type.
69
+ ' Should return a new state object (immutable pattern).
70
+ '
71
+ ' @param {object} state - Current state
72
+ ' @param {Intent} intent - Intent object { type, payload, ... }
73
+ ' @return {object} New state
74
+ '
75
+ ' Example:
76
+ ' function reducer(state as object, intent as Intent)
77
+ ' if intent.type = "INCREMENT"
78
+ ' newState = Rotor.Utils.deepCopy(state)
79
+ ' newState.count = state.count + 1
80
+ ' return newState
81
+ ' end if
82
+ ' return state
83
+ ' end function
84
+ '
85
+ public function reducer(state as object, intent as Intent)
86
+ return state
87
+ end function
88
+
89
+ ' =============================================================
90
+ ' MIDDLEWARE
91
+ ' =============================================================
92
+
93
+ ' ---------------------------------------------------------------------
94
+ ' applyMiddlewares - Returns middleware function array
95
+ '
96
+ ' Override to implement middleware logic for:
97
+ ' - Async operations (API calls, timers)
98
+ ' - Logging and debugging
99
+ ' - Intent validation
100
+ ' - Intent transformation
101
+ '
102
+ ' @return {function[]} Array of middleware functions
103
+ '
104
+ ' Middleware signature:
105
+ ' function(intent as Intent, state as object) as Dynamic
106
+ ' - Return intent to continue pipeline
107
+ ' - Return modified intent to transform
108
+ ' - Return invalid to cancel reducer execution
109
+ '
110
+ public function applyMiddlewares() as function[]
111
+ return [] as function[]
112
+ end function
113
+
114
+ ' =============================================================
115
+ ' REDUCE PIPELINE
116
+ ' =============================================================
117
+
118
+ ' ---------------------------------------------------------------------
119
+ ' reduce - Executes middleware pipeline and reducer
120
+ '
121
+ ' Process flow:
122
+ ' 1. Validate intent payload
123
+ ' 2. Execute middleware pipeline
124
+ ' 3. If intent survives, execute reducer
125
+ ' 4. Return new state or invalid
126
+ '
127
+ ' @param {object} state - Current state
128
+ ' @param {Intent} intent - Intent to process
129
+ ' @return {object} New state, or invalid if cancelled
130
+ '
131
+ function reduce(state as object, intent as Intent) as object
132
+ if intent?.payload <> invalid and intent.payload.Count() > 1 and intent.payload = invalid
133
+ throw "[WARNING] Intent payload is invalid."
134
+ end if
135
+
136
+ ' Execute middleware pipeline
137
+ middlewares = m.applyMiddlewares()
138
+ mwIndex = 0
139
+ mwCount = middlewares.Count()
140
+ while intent <> invalid and mwIndex < mwCount
141
+ middlewareFnScoped = middlewares[mwIndex]
142
+ m.middlewareFnScoped = middlewareFnScoped
143
+ intent = m.middlewareFnScoped(intent, state)
144
+ mwIndex++
145
+ end while
146
+ m.middlewareFnScoped = invalid
147
+
148
+ ' Middleware cancelled intent
149
+ if intent = invalid then return invalid
150
+
151
+ ' Execute reducer
152
+ newState = m.reducer(state, intent)
153
+
154
+ return newState
155
+ end function
156
+
157
+ ' =============================================================
158
+ ' ASYNC CALLBACK
159
+ ' =============================================================
160
+
161
+ ' ---------------------------------------------------------------------
162
+ ' asyncReducerCallback - Callback for async middleware operations
163
+ '
164
+ ' @param {object} msg - Message from async operation
165
+ '
166
+ sub asyncReducerCallback(msg)
167
+ ' Override in subclass to handle async responses
168
+ end sub
169
+
170
+ ' =============================================================
171
+ ' CLEANUP
172
+ ' =============================================================
173
+
174
+ ' ---------------------------------------------------------------------
175
+ ' destroy - Cleans up reducer references
176
+ '
177
+ sub destroy()
178
+ m.ownerDispatcher = invalid
179
+ m.port = invalid
180
+ end sub
181
+
182
+ end class
183
+
184
+ end namespace
@@ -0,0 +1,92 @@
1
+ namespace Rotor
2
+
3
+ ' =====================================================================
4
+ ' BaseStack - Generic stack/collection base class for managing keyed items
5
+ '
6
+ ' Provides:
7
+ ' - Add/set items by ID
8
+ ' - Get items by ID (case-insensitive)
9
+ ' - Remove items by ID
10
+ ' - Clear all items
11
+ ' - Check item existence
12
+ '
13
+ ' Used by:
14
+ ' - ObserverStack (observer management)
15
+ ' - FocusStack (focus state management)
16
+ ' - Other framework collection needs
17
+ ' =====================================================================
18
+ class BaseStack
19
+
20
+ ' =============================================================
21
+ ' MEMBER VARIABLES
22
+ ' =============================================================
23
+
24
+ stack = {} ' Internal associative array storage
25
+
26
+ ' =============================================================
27
+ ' STACK OPERATIONS
28
+ ' =============================================================
29
+
30
+ ' ---------------------------------------------------------------------
31
+ ' set - Adds or replaces item in stack
32
+ '
33
+ ' If item exists, removes old item first.
34
+ '
35
+ ' @param {string} id - Item identifier
36
+ ' @param {object} newItem - Item to store
37
+ '
38
+ sub set(id as string, newItem as object)
39
+ if m.has(id) = true
40
+ m.remove(id)
41
+ end if
42
+ m.stack[id] = newItem
43
+ end sub
44
+
45
+ ' ---------------------------------------------------------------------
46
+ ' get - Retrieves item by ID (case-insensitive)
47
+ '
48
+ ' @param {string} id - Item identifier
49
+ ' @returns {object} Item if found, or invalid
50
+ '
51
+ function get(id as string) as object
52
+ return m.stack.LookupCI(id)
53
+ end function
54
+
55
+ ' ---------------------------------------------------------------------
56
+ ' getAll - Returns all items in stack
57
+ '
58
+ ' @returns {object} Associative array of all items
59
+ '
60
+ function getAll() as object
61
+ return m.stack
62
+ end function
63
+
64
+ ' ---------------------------------------------------------------------
65
+ ' remove - Removes item from stack
66
+ '
67
+ ' @param {string} id - Item identifier to remove
68
+ '
69
+ sub remove(id as string)
70
+ m.stack.delete(id)
71
+ end sub
72
+
73
+ ' ---------------------------------------------------------------------
74
+ ' clear - Removes all items from stack
75
+ '
76
+ sub clear()
77
+ m.stack.Clear()
78
+ end sub
79
+
80
+ ' ---------------------------------------------------------------------
81
+ ' has - Checks if item exists in stack
82
+ '
83
+ ' @param {string} id - Item identifier to check
84
+ ' @returns {boolean} True if item exists
85
+ '
86
+ function has(id as string) as boolean
87
+ return m.stack.doesExist(id)
88
+ end function
89
+
90
+ end class
91
+
92
+ end namespace