onelaraveljs 1.0.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.
- package/README.md +87 -0
- package/docs/integration_analysis.md +116 -0
- package/docs/onejs_analysis.md +108 -0
- package/docs/optimization_implementation_group2.md +458 -0
- package/docs/optimization_plan.md +130 -0
- package/index.js +16 -0
- package/package.json +13 -0
- package/src/app.js +61 -0
- package/src/core/API.js +72 -0
- package/src/core/ChildrenRegistry.js +410 -0
- package/src/core/DOMBatcher.js +207 -0
- package/src/core/ErrorBoundary.js +226 -0
- package/src/core/EventDelegator.js +416 -0
- package/src/core/Helper.js +817 -0
- package/src/core/LoopContext.js +97 -0
- package/src/core/OneDOM.js +246 -0
- package/src/core/OneMarkup.js +444 -0
- package/src/core/Router.js +996 -0
- package/src/core/SEOConfig.js +321 -0
- package/src/core/SectionEngine.js +75 -0
- package/src/core/TemplateEngine.js +83 -0
- package/src/core/View.js +273 -0
- package/src/core/ViewConfig.js +229 -0
- package/src/core/ViewController.js +1410 -0
- package/src/core/ViewControllerOptimized.js +164 -0
- package/src/core/ViewIdentifier.js +361 -0
- package/src/core/ViewLoader.js +272 -0
- package/src/core/ViewManager.js +1962 -0
- package/src/core/ViewState.js +761 -0
- package/src/core/ViewSystem.js +301 -0
- package/src/core/ViewTemplate.js +4 -0
- package/src/core/helpers/BindingHelper.js +239 -0
- package/src/core/helpers/ConfigHelper.js +37 -0
- package/src/core/helpers/EventHelper.js +172 -0
- package/src/core/helpers/LifecycleHelper.js +17 -0
- package/src/core/helpers/ReactiveHelper.js +169 -0
- package/src/core/helpers/RenderHelper.js +15 -0
- package/src/core/helpers/ResourceHelper.js +89 -0
- package/src/core/helpers/TemplateHelper.js +11 -0
- package/src/core/managers/BindingManager.js +671 -0
- package/src/core/managers/ConfigurationManager.js +136 -0
- package/src/core/managers/EventManager.js +309 -0
- package/src/core/managers/LifecycleManager.js +356 -0
- package/src/core/managers/ReactiveManager.js +334 -0
- package/src/core/managers/RenderEngine.js +292 -0
- package/src/core/managers/ResourceManager.js +441 -0
- package/src/core/managers/ViewHierarchyManager.js +258 -0
- package/src/core/managers/ViewTemplateManager.js +127 -0
- package/src/core/reactive/ReactiveComponent.js +592 -0
- package/src/core/services/EventService.js +418 -0
- package/src/core/services/HttpService.js +106 -0
- package/src/core/services/LoggerService.js +57 -0
- package/src/core/services/StateService.js +512 -0
- package/src/core/services/StorageService.js +856 -0
- package/src/core/services/StoreService.js +258 -0
- package/src/core/services/TemplateDetectorService.js +361 -0
- package/src/core/services/Test.js +18 -0
- package/src/helpers/devWarnings.js +205 -0
- package/src/helpers/performance.js +226 -0
- package/src/helpers/utils.js +287 -0
- package/src/init.js +343 -0
- package/src/plugins/auto-plugin.js +34 -0
- package/src/services/Test.js +18 -0
- package/src/types/index.js +193 -0
- package/src/utils/date-helper.js +51 -0
- package/src/utils/helpers.js +39 -0
- package/src/utils/validation.js +32 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import EventDelegator from '../EventDelegator.js';
|
|
2
|
+
import logger from '../services/LoggerService.js';
|
|
3
|
+
import { devLog } from '../../helpers/devWarnings.js';
|
|
4
|
+
|
|
5
|
+
export default class EventHelper {
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Start event listeners using delegation
|
|
9
|
+
* @param {Object} controller - The ViewController instance
|
|
10
|
+
*/
|
|
11
|
+
static startEventListener(controller) {
|
|
12
|
+
if (controller.eventListenerStatus) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const state = controller._internal.events;
|
|
17
|
+
const needDeleted = [];
|
|
18
|
+
let attachedCount = 0;
|
|
19
|
+
|
|
20
|
+
if (!controller.events) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
Object.entries(controller.events).forEach(([eventType, eventMap]) => {
|
|
25
|
+
Object.entries(eventMap).forEach(([eventID, handlers]) => {
|
|
26
|
+
const selector = `[data-${eventType}-id="${eventID}"]`;
|
|
27
|
+
const delegateKey = `${eventType}:${eventID}:${controller.id}`;
|
|
28
|
+
|
|
29
|
+
// Parse Handlers
|
|
30
|
+
const parsedHandlers = EventHelper.parseEventHandlerFunctions(controller, handlers);
|
|
31
|
+
|
|
32
|
+
if (parsedHandlers.length > 0) {
|
|
33
|
+
// Cleanup old if exists
|
|
34
|
+
if (state.unsubscribers.has(delegateKey)) {
|
|
35
|
+
state.unsubscribers.get(delegateKey)();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Attach via Delegator
|
|
39
|
+
const unsubscribe = EventDelegator.on(eventType, selector, (event) => {
|
|
40
|
+
// Iterate through handlers
|
|
41
|
+
parsedHandlers.forEach(({ handler, params, preventDefault, stopPropagation }) => {
|
|
42
|
+
if (preventDefault) event.preventDefault();
|
|
43
|
+
if (stopPropagation) event.stopPropagation();
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
// Add delegateTarget to event for handler access
|
|
47
|
+
if (!event.delegateTarget) {
|
|
48
|
+
Object.defineProperty(event, 'delegateTarget', {
|
|
49
|
+
value: event.target.closest(selector) || event.target,
|
|
50
|
+
writable: false,
|
|
51
|
+
enumerable: true,
|
|
52
|
+
configurable: true
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
handler.apply(controller.view, [event, ...params]);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
logger.error(`Error executing event handler for ${selector} in ${controller.path}:`, error);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}, { root: document }); // Use document as root for now, could be app container
|
|
62
|
+
|
|
63
|
+
state.unsubscribers.set(delegateKey, unsubscribe);
|
|
64
|
+
attachedCount++;
|
|
65
|
+
} else {
|
|
66
|
+
needDeleted.push({ eventType, eventID });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Cleanup orphaned events logic (optional, based on original EventManager)
|
|
72
|
+
if (needDeleted.length > 0) {
|
|
73
|
+
needDeleted.forEach(({ eventType, eventID }) => {
|
|
74
|
+
delete controller.events[eventType][eventID];
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
controller.eventListenerStatus = true;
|
|
79
|
+
|
|
80
|
+
if (attachedCount > 0) {
|
|
81
|
+
devLog(`[EventHelper] Attached ${attachedCount} delegated listeners for ${controller.path}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Stop all listeners
|
|
87
|
+
* @param {Object} controller - The ViewController instance
|
|
88
|
+
*/
|
|
89
|
+
static stopEventListener(controller) {
|
|
90
|
+
if (!controller.eventListenerStatus) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const state = controller._internal.events;
|
|
95
|
+
|
|
96
|
+
// Clear delegated listeners
|
|
97
|
+
state.unsubscribers.forEach(unsubscribe => unsubscribe());
|
|
98
|
+
state.unsubscribers.clear();
|
|
99
|
+
|
|
100
|
+
// Clear legacy listeners if any (from old ViewControllers)
|
|
101
|
+
if (controller.eventListeners && controller.eventListeners.length > 0) {
|
|
102
|
+
controller.eventListeners.forEach(({ element, eventType, handler }) => {
|
|
103
|
+
try {
|
|
104
|
+
element.removeEventListener(eventType, handler);
|
|
105
|
+
} catch (e) { /* ignore */ }
|
|
106
|
+
});
|
|
107
|
+
controller.eventListeners = [];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
controller.eventListenerStatus = false;
|
|
111
|
+
devLog(`[EventHelper] Stopped listeners for ${controller.path}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Parse array of handler definitions
|
|
116
|
+
* @param {Object} controller
|
|
117
|
+
* @param {Array} handlers
|
|
118
|
+
* @returns {Array} Parsed handlers
|
|
119
|
+
*/
|
|
120
|
+
static parseEventHandlerFunctions(controller, handlers) {
|
|
121
|
+
if (!handlers || !Array.isArray(handlers)) return [];
|
|
122
|
+
|
|
123
|
+
return handlers.map(handlerDef => {
|
|
124
|
+
return EventHelper.parseHandlerFunction(controller, handlerDef);
|
|
125
|
+
}).filter(h => h !== null);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Parse single handler definition
|
|
130
|
+
* @param {Object} controller
|
|
131
|
+
* @param {Object} handlerDef
|
|
132
|
+
* @returns {Object|null}
|
|
133
|
+
*/
|
|
134
|
+
static parseHandlerFunction(controller, handlerDef) {
|
|
135
|
+
if (!handlerDef || typeof handlerDef !== 'object') return null;
|
|
136
|
+
|
|
137
|
+
const { method, params, preventDefault, stopPropagation } = handlerDef;
|
|
138
|
+
let handlerFunc = null;
|
|
139
|
+
|
|
140
|
+
// 1. Check in View Methods
|
|
141
|
+
if (typeof controller.view[method] === 'function') {
|
|
142
|
+
handlerFunc = controller.view[method];
|
|
143
|
+
}
|
|
144
|
+
// 2. Check in Controller Properties
|
|
145
|
+
else if (typeof controller[method] === 'function') {
|
|
146
|
+
handlerFunc = controller[method];
|
|
147
|
+
}
|
|
148
|
+
// 3. Check globally (window)
|
|
149
|
+
else if (typeof window[method] === 'function') {
|
|
150
|
+
handlerFunc = window[method];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!handlerFunc) {
|
|
154
|
+
logger.warn(`Event handler method '${method}' not found in view ${controller.path}`);
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Resolve params
|
|
159
|
+
const resolvedParams = Array.isArray(params) ? params.map(p => {
|
|
160
|
+
// Basic param resolution (strings, numbers, booleans)
|
|
161
|
+
// Complex resolution (like referencing other state) can be added here
|
|
162
|
+
return p;
|
|
163
|
+
}) : [];
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
handler: handlerFunc,
|
|
167
|
+
params: resolvedParams,
|
|
168
|
+
preventDefault: !!preventDefault,
|
|
169
|
+
stopPropagation: !!stopPropagation
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LifecycleHelper
|
|
3
|
+
* Handles lifecycle methods call
|
|
4
|
+
*/
|
|
5
|
+
import logger from '../services/LoggerService.js';
|
|
6
|
+
|
|
7
|
+
export default class LifecycleHelper {
|
|
8
|
+
static callHook(controller, hookName) {
|
|
9
|
+
if (typeof controller.view[hookName] === 'function') {
|
|
10
|
+
try {
|
|
11
|
+
controller.view[hookName]();
|
|
12
|
+
} catch (e) {
|
|
13
|
+
logger.error(`Error in lifecycle hook ${hookName} for ${controller.path}`, e);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { ReactiveComponent } from '../reactive/ReactiveComponent.js';
|
|
2
|
+
|
|
3
|
+
export default class ReactiveHelper {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Render Output Component (Thay thế renderOutputComponent)
|
|
7
|
+
*/
|
|
8
|
+
static renderOutputComponent(controller, stateKeys = [], renderBlock = () => '') {
|
|
9
|
+
const state = controller._internal.reactive;
|
|
10
|
+
|
|
11
|
+
// Logic Virtual Rendering for SSR hydration sequencing or Dry Run
|
|
12
|
+
if (controller.isVirtualRendering) {
|
|
13
|
+
controller.isVirtualRendering = false; // Disable flag temporarily to scan
|
|
14
|
+
let result = ReactiveHelper.renderOutputComponentScan(controller, stateKeys, renderBlock);
|
|
15
|
+
controller.isVirtualRendering = true; // Restore flag
|
|
16
|
+
if (result !== false) return result;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Tạo instance ReactiveComponent
|
|
20
|
+
const rc = new ReactiveComponent({
|
|
21
|
+
stateKeys,
|
|
22
|
+
renderBlock,
|
|
23
|
+
controller: controller,
|
|
24
|
+
App: controller.App,
|
|
25
|
+
type: 'output',
|
|
26
|
+
escapeHTML: false
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Store active component in new state structure
|
|
30
|
+
state.ids.push(rc.id);
|
|
31
|
+
state.components.set(rc.id, rc);
|
|
32
|
+
|
|
33
|
+
return rc.render();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Quét Output Component trong quá trình Virtual Render / SSR hydration
|
|
38
|
+
*/
|
|
39
|
+
static renderOutputComponentScan(controller, stateKeys, renderBlock, escapeHTML = false) {
|
|
40
|
+
const state = controller._internal.reactive;
|
|
41
|
+
|
|
42
|
+
// Truy cập state.scanIndex thay vì this.reactiveComponentScanIndex
|
|
43
|
+
let reactiveComponentIndex = state.scanIndex;
|
|
44
|
+
|
|
45
|
+
// Check if config exists for this index
|
|
46
|
+
if (!state.config || !state.config[reactiveComponentIndex]) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let reactiveComponentConfig = state.config[reactiveComponentIndex];
|
|
51
|
+
|
|
52
|
+
const { id: renderID } = reactiveComponentConfig;
|
|
53
|
+
|
|
54
|
+
const rc = new ReactiveComponent({
|
|
55
|
+
renderID,
|
|
56
|
+
stateKeys,
|
|
57
|
+
renderBlock,
|
|
58
|
+
controller: controller, // Pass controller explicitly
|
|
59
|
+
App: controller.App,
|
|
60
|
+
type: 'output',
|
|
61
|
+
escapeHTML
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
state.ids.push(rc.id);
|
|
65
|
+
state.components.set(rc.id, rc);
|
|
66
|
+
state.scanIndex++; // Increment scan index
|
|
67
|
+
|
|
68
|
+
return rc.render();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Render Watch Component (Re-renders sections of DOM)
|
|
73
|
+
*/
|
|
74
|
+
static renderWatchComponent(controller, stateKeys = [], renderBlock = () => '') {
|
|
75
|
+
const state = controller._internal.reactive;
|
|
76
|
+
|
|
77
|
+
// Virtual Rendering for Hydration/Dry-Run
|
|
78
|
+
if (controller.isVirtualRendering) {
|
|
79
|
+
controller.isVirtualRendering = false;
|
|
80
|
+
let result = ReactiveHelper.renderWatchComponentScan(controller, stateKeys, renderBlock);
|
|
81
|
+
controller.isVirtualRendering = true;
|
|
82
|
+
if (result !== false) return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const rc = new ReactiveComponent({
|
|
86
|
+
stateKeys,
|
|
87
|
+
renderBlock,
|
|
88
|
+
controller: controller,
|
|
89
|
+
App: controller.App,
|
|
90
|
+
type: 'watch',
|
|
91
|
+
parentWatchComponent: state.parentWatch, // Access parent from global tracking or specific logic
|
|
92
|
+
escapeHTML: false
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
state.ids.push(rc.id);
|
|
96
|
+
state.components.set(rc.id, rc);
|
|
97
|
+
|
|
98
|
+
// Update current rendering component tracking
|
|
99
|
+
const previousRenderingComponent = controller._reactiveManager?.currentRenderingComponent; // Keep legacy prop for compatibility or move to state
|
|
100
|
+
// We might want to move currentRenderingComponent to _internal later, but for now assuming legacy prop usage in ViewHierarchy
|
|
101
|
+
|
|
102
|
+
return rc.render();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
static renderWatchComponentScan(controller, stateKeys, renderBlock) {
|
|
106
|
+
const state = controller._internal.reactive;
|
|
107
|
+
|
|
108
|
+
let reactiveComponentIndex = state.scanIndex;
|
|
109
|
+
if (!state.config || !state.config[reactiveComponentIndex]) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let reactiveComponentConfig = state.config[reactiveComponentIndex];
|
|
114
|
+
const { id: renderID } = reactiveComponentConfig;
|
|
115
|
+
|
|
116
|
+
const rc = new ReactiveComponent({
|
|
117
|
+
renderID,
|
|
118
|
+
stateKeys,
|
|
119
|
+
renderBlock,
|
|
120
|
+
controller: controller,
|
|
121
|
+
App: controller.App,
|
|
122
|
+
type: 'watch',
|
|
123
|
+
escapeHTML: false
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
state.ids.push(rc.id);
|
|
127
|
+
state.components.set(rc.id, rc);
|
|
128
|
+
state.scanIndex++;
|
|
129
|
+
|
|
130
|
+
return rc.render();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Clear reactive state for refresh
|
|
135
|
+
*/
|
|
136
|
+
static clearForRefresh(controller) {
|
|
137
|
+
const state = controller._internal.reactive;
|
|
138
|
+
if (state.components.size > 0) {
|
|
139
|
+
state.components.forEach(c => c.destroy());
|
|
140
|
+
state.components.clear();
|
|
141
|
+
}
|
|
142
|
+
state.ids = [];
|
|
143
|
+
state.renderIDs = [];
|
|
144
|
+
state.prerenderIDs = [];
|
|
145
|
+
state.scanIndex = 0;
|
|
146
|
+
state.followingIDs = [];
|
|
147
|
+
state.followingRenderIDs = [];
|
|
148
|
+
state.followingPrerenderIDs = [];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Main Destroy method
|
|
153
|
+
*/
|
|
154
|
+
static destroy(controller) {
|
|
155
|
+
const state = controller._internal.reactive;
|
|
156
|
+
|
|
157
|
+
if (state.components.size > 0) {
|
|
158
|
+
state.components.forEach(c => {
|
|
159
|
+
try {
|
|
160
|
+
c.destroy();
|
|
161
|
+
} catch (e) { console.warn('Error destroying reactive component', e); }
|
|
162
|
+
});
|
|
163
|
+
state.components.clear();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
state.ids = [];
|
|
167
|
+
state.config = null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RenderHelper
|
|
3
|
+
* Handles rendering logic previously in RenderEngine
|
|
4
|
+
*/
|
|
5
|
+
import OneMarkup from '../OneMarkup.js';
|
|
6
|
+
import { ATTR } from '../ViewConfig.js';
|
|
7
|
+
|
|
8
|
+
export default class RenderHelper {
|
|
9
|
+
static render(controller) {
|
|
10
|
+
// Logic render HTML string
|
|
11
|
+
// ... implementation dependent on framework internals ...
|
|
12
|
+
// Typically returns a string of HTML
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export default class ResourceHelper {
|
|
2
|
+
/**
|
|
3
|
+
* Insert view resources (JS/CSS)
|
|
4
|
+
*/
|
|
5
|
+
static insertResources(controller) {
|
|
6
|
+
ResourceHelper.insertStyles(controller);
|
|
7
|
+
ResourceHelper.insertScripts(controller);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static insertStyles(controller) {
|
|
11
|
+
if (!controller.styles || controller.styles.length === 0) return;
|
|
12
|
+
|
|
13
|
+
const state = controller._internal.resources;
|
|
14
|
+
|
|
15
|
+
controller.styles.forEach(style => {
|
|
16
|
+
const resourceKey = controller.App.View.Engine.getResourceKey({
|
|
17
|
+
...style,
|
|
18
|
+
viewPath: controller.path,
|
|
19
|
+
resourceType: 'style'
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Check global registry logic (simplified for Helper)
|
|
23
|
+
let registryEntry = controller.App.View.Engine.resourceRegistry.get(resourceKey);
|
|
24
|
+
|
|
25
|
+
if (registryEntry) {
|
|
26
|
+
if (!registryEntry.viewPaths.has(controller.path)) {
|
|
27
|
+
registryEntry.viewPaths.add(controller.path);
|
|
28
|
+
registryEntry.referenceCount++;
|
|
29
|
+
state.insertedKeys.add(resourceKey); // Track locally
|
|
30
|
+
}
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Create Element logic
|
|
35
|
+
let element;
|
|
36
|
+
if (style.type === 'href') {
|
|
37
|
+
element = document.createElement('link');
|
|
38
|
+
element.rel = 'stylesheet';
|
|
39
|
+
element.href = style.href;
|
|
40
|
+
// ... add attributes ...
|
|
41
|
+
} else {
|
|
42
|
+
element = document.createElement('style');
|
|
43
|
+
element.textContent = style.content;
|
|
44
|
+
// ... add attributes ...
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
element.setAttribute('data-view-path', controller.path);
|
|
48
|
+
element.setAttribute('data-resource-key', resourceKey);
|
|
49
|
+
|
|
50
|
+
document.head.appendChild(element);
|
|
51
|
+
|
|
52
|
+
// Add to Registry
|
|
53
|
+
controller.App.View.Engine.resourceRegistry.set(resourceKey, {
|
|
54
|
+
element,
|
|
55
|
+
viewPaths: new Set([controller.path]),
|
|
56
|
+
referenceCount: 1,
|
|
57
|
+
resourceType: 'style'
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
state.insertedKeys.add(resourceKey);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static insertScripts(controller) {
|
|
65
|
+
// ... Similar logic to insertStyles but for scripts ...
|
|
66
|
+
// Needs careful porting of execution logic (onload, onerror)
|
|
67
|
+
if (!controller.scripts || controller.scripts.length === 0) return;
|
|
68
|
+
|
|
69
|
+
const state = controller._internal.resources;
|
|
70
|
+
|
|
71
|
+
controller.scripts.forEach(script => {
|
|
72
|
+
// ... logic ...
|
|
73
|
+
// For brevity, skipping full implementation but structure is identical
|
|
74
|
+
// Use state.insertedKeys to track
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
static removeResources(controller) {
|
|
79
|
+
const state = controller._internal.resources;
|
|
80
|
+
|
|
81
|
+
// Use state.insertedKeys to know what to try and remove
|
|
82
|
+
// Access global registry to decrement counts
|
|
83
|
+
// If count == 0, remove DOM element
|
|
84
|
+
|
|
85
|
+
// ... logic ...
|
|
86
|
+
|
|
87
|
+
state.insertedKeys.clear();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TemplateHelper
|
|
3
|
+
* Handles template wrapping and block logic
|
|
4
|
+
*/
|
|
5
|
+
import { ATTR } from '../ViewConfig.js';
|
|
6
|
+
|
|
7
|
+
export default class TemplateHelper {
|
|
8
|
+
static wrapperAttribute(controller) {
|
|
9
|
+
return ` ${ATTR.KEYS.VIEW_WRAPPER}="${controller.id}"`;
|
|
10
|
+
}
|
|
11
|
+
}
|