rad-coder 1.0.3 → 1.0.5
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 +30 -7
- package/bin/cli.js +97 -46
- package/package.json +3 -3
- package/public/test.html +3 -16
- package/server/index.js +52 -4
- package/templates/AGENTS.md +29 -0
package/README.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
A development environment for testing ResponsiveAds creative custom JavaScript with hot-reload.
|
|
4
4
|
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
https://github.com/user-attachments/assets/ce7515c5-0920-4f02-b430-6af69fc2d44d
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
5
11
|
## Quick Start
|
|
6
12
|
|
|
7
13
|
```bash
|
|
@@ -33,14 +39,30 @@ npx rad-coder https://studio.responsiveads.com/creatives/697b80fcc6e904025f5147a
|
|
|
33
39
|
### Basic Usage
|
|
34
40
|
|
|
35
41
|
```bash
|
|
36
|
-
#
|
|
37
|
-
mkdir my-creative
|
|
38
|
-
cd my-creative
|
|
39
|
-
|
|
40
|
-
# Start rad-coder with your creative ID
|
|
42
|
+
# Start rad-coder with your creative ID (creates a ./<id> folder)
|
|
41
43
|
npx rad-coder 697b80fcc6e904025f5147a0
|
|
42
44
|
```
|
|
43
45
|
|
|
46
|
+
### Continue Working
|
|
47
|
+
|
|
48
|
+
Next time, just `cd` into the project folder and run without arguments:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
cd 697b80fcc6e904025f5147a0
|
|
52
|
+
npx rad-coder
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The CLI auto-detects the creative from `.rad-coder.json` (or the folder name). Your local `custom.js` is used as-is — no prompts.
|
|
56
|
+
|
|
57
|
+
### Options
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npx rad-coder <id> --reset # Overwrite local custom.js with the remote version
|
|
61
|
+
npx rad-coder <id> --fresh # Delete folder and start from scratch
|
|
62
|
+
npx rad-coder <id> --editor=cursor # Use a specific editor
|
|
63
|
+
npx rad-coder <id> --no-editor # Don't auto-open editor
|
|
64
|
+
```
|
|
65
|
+
|
|
44
66
|
### With AI Assistants
|
|
45
67
|
|
|
46
68
|
The generated `AGENTS.md` file contains instructions for AI coding assistants. When using VS Code with Copilot or other AI tools, they can read this file to understand:
|
|
@@ -52,10 +74,11 @@ The generated `AGENTS.md` file contains instructions for AI coding assistants. W
|
|
|
52
74
|
|
|
53
75
|
### Workflow
|
|
54
76
|
|
|
55
|
-
1. Run `npx rad-coder <creativeId>`
|
|
77
|
+
1. Run `npx rad-coder <creativeId>` (first time — creates project folder)
|
|
56
78
|
2. Edit `custom.js` in your favorite editor
|
|
57
79
|
3. Save the file - browser auto-reloads
|
|
58
80
|
4. See your changes applied to the creative instantly
|
|
81
|
+
5. Next session: `cd <creativeId> && npx rad-coder` to continue
|
|
59
82
|
|
|
60
83
|
## Features
|
|
61
84
|
|
|
@@ -93,7 +116,7 @@ Instructions for developers who want to modify rad-coder itself.
|
|
|
93
116
|
|
|
94
117
|
```bash
|
|
95
118
|
# Clone the repository
|
|
96
|
-
git clone https://github.com/
|
|
119
|
+
git clone https://github.com/ResponsiveAds/rad-coder.git
|
|
97
120
|
cd rad-coder
|
|
98
121
|
|
|
99
122
|
# Install dependencies
|
package/bin/cli.js
CHANGED
|
@@ -118,12 +118,18 @@ const args = process.argv.slice(2);
|
|
|
118
118
|
let input = null;
|
|
119
119
|
let editorFlag = null;
|
|
120
120
|
let noEditor = false;
|
|
121
|
+
let resetFlag = false;
|
|
122
|
+
let freshFlag = false;
|
|
121
123
|
|
|
122
124
|
for (const arg of args) {
|
|
123
125
|
if (arg.startsWith('--editor=')) {
|
|
124
126
|
editorFlag = arg.split('=')[1];
|
|
125
127
|
} else if (arg === '--no-editor') {
|
|
126
128
|
noEditor = true;
|
|
129
|
+
} else if (arg === '--reset') {
|
|
130
|
+
resetFlag = true;
|
|
131
|
+
} else if (arg === '--fresh') {
|
|
132
|
+
freshFlag = true;
|
|
127
133
|
} else if (!arg.startsWith('--')) {
|
|
128
134
|
input = arg;
|
|
129
135
|
}
|
|
@@ -137,7 +143,33 @@ if (noEditor) {
|
|
|
137
143
|
process.env.RAD_CODER_NO_EDITOR = '1';
|
|
138
144
|
}
|
|
139
145
|
|
|
140
|
-
|
|
146
|
+
let creativeId = extractCreativeId(input);
|
|
147
|
+
|
|
148
|
+
// Auto-detect creative ID when no argument is given
|
|
149
|
+
if (!creativeId) {
|
|
150
|
+
const cwd = process.cwd();
|
|
151
|
+
|
|
152
|
+
// 1. Check for .rad-coder.json in current directory
|
|
153
|
+
const configPath = path.join(cwd, '.rad-coder.json');
|
|
154
|
+
if (fs.existsSync(configPath)) {
|
|
155
|
+
try {
|
|
156
|
+
const saved = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
157
|
+
if (saved.creativeId) {
|
|
158
|
+
creativeId = saved.creativeId;
|
|
159
|
+
console.log(`Detected creative from .rad-coder.json: ${creativeId}`);
|
|
160
|
+
}
|
|
161
|
+
} catch (_) { /* ignore malformed config */ }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 2. Check if current directory name looks like a creative ID (24-char hex)
|
|
165
|
+
if (!creativeId) {
|
|
166
|
+
const dirName = path.basename(cwd);
|
|
167
|
+
if (/^[a-f0-9]{24}$/i.test(dirName)) {
|
|
168
|
+
creativeId = dirName;
|
|
169
|
+
console.log(`Detected creative from folder name: ${creativeId}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
141
173
|
|
|
142
174
|
if (!creativeId) {
|
|
143
175
|
console.log('Usage: npx rad-coder <creativeId or previewUrl> [options]');
|
|
@@ -145,11 +177,16 @@ if (!creativeId) {
|
|
|
145
177
|
console.log('Options:');
|
|
146
178
|
console.log(' --editor=<cmd> Set code editor command (default: code)');
|
|
147
179
|
console.log(' --no-editor Don\'t auto-open code editor');
|
|
180
|
+
console.log(' --reset Overwrite local custom.js with remote version');
|
|
181
|
+
console.log(' --fresh Delete local folder and start from scratch');
|
|
148
182
|
console.log('');
|
|
149
183
|
console.log('Examples:');
|
|
150
184
|
console.log(' npx rad-coder 697b80fcc6e904025f5147a0');
|
|
151
185
|
console.log(' npx rad-coder https://studio.responsiveads.com/creatives/697b80fcc6e904025f5147a0/preview');
|
|
152
186
|
console.log(' npx rad-coder 697b80fcc6e904025f5147a0 --editor=cursor');
|
|
187
|
+
console.log('');
|
|
188
|
+
console.log('Continue working (from inside a project folder):');
|
|
189
|
+
console.log(' cd 697b80fcc6e904025f5147a0 && npx rad-coder');
|
|
153
190
|
process.exit(1);
|
|
154
191
|
}
|
|
155
192
|
|
|
@@ -158,22 +195,36 @@ async function main() {
|
|
|
158
195
|
const cwd = process.cwd();
|
|
159
196
|
const currentDirName = path.basename(cwd);
|
|
160
197
|
let userDir;
|
|
198
|
+
let isNewProject = false;
|
|
199
|
+
|
|
200
|
+
// Check if a .rad-coder.json exists in cwd (we're inside a project folder)
|
|
201
|
+
const cwdConfigPath = path.join(cwd, '.rad-coder.json');
|
|
202
|
+
const inProjectDir = fs.existsSync(cwdConfigPath) || currentDirName === creativeId;
|
|
161
203
|
|
|
162
|
-
if (
|
|
204
|
+
if (inProjectDir) {
|
|
163
205
|
// Already in the correct folder
|
|
164
206
|
userDir = cwd;
|
|
165
|
-
console.log(`Using existing
|
|
207
|
+
console.log(`Using existing project: ${userDir}`);
|
|
166
208
|
} else {
|
|
167
209
|
// Create or use a folder with the creative ID
|
|
168
210
|
userDir = path.join(cwd, creativeId);
|
|
169
211
|
if (!fs.existsSync(userDir)) {
|
|
170
212
|
fs.mkdirSync(userDir);
|
|
213
|
+
isNewProject = true;
|
|
171
214
|
console.log(`Created folder: ./${creativeId}`);
|
|
172
215
|
} else {
|
|
173
216
|
console.log(`Using existing folder: ./${creativeId}`);
|
|
174
217
|
}
|
|
175
218
|
}
|
|
176
219
|
|
|
220
|
+
// Handle --fresh: delete folder and recreate
|
|
221
|
+
if (freshFlag && fs.existsSync(userDir)) {
|
|
222
|
+
fs.rmSync(userDir, { recursive: true, force: true });
|
|
223
|
+
fs.mkdirSync(userDir);
|
|
224
|
+
isNewProject = true;
|
|
225
|
+
console.log(` Fresh start: deleted and recreated ./${creativeId}`);
|
|
226
|
+
}
|
|
227
|
+
|
|
177
228
|
// Set environment variables for the server
|
|
178
229
|
process.env.RAD_CODER_USER_DIR = userDir;
|
|
179
230
|
process.env.RAD_CODER_PACKAGE_DIR = packageRoot;
|
|
@@ -189,56 +240,40 @@ async function main() {
|
|
|
189
240
|
const hasCreativeCustomJs = config.customjs && config.customjs.trim().length > 0;
|
|
190
241
|
|
|
191
242
|
// Handle custom.js file creation/update
|
|
192
|
-
if (hasCreativeCustomJs) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
console.log(' Created custom.js (from template)');
|
|
213
|
-
}
|
|
214
|
-
}
|
|
243
|
+
if (resetFlag && hasCreativeCustomJs) {
|
|
244
|
+
// --reset: overwrite local custom.js with remote version
|
|
245
|
+
fs.writeFileSync(customJsPath, config.customjs, 'utf-8');
|
|
246
|
+
console.log(' Reset custom.js (from creative)');
|
|
247
|
+
} else if (customJsExists) {
|
|
248
|
+
// custom.js already exists — use it silently (zero-friction repeat run)
|
|
249
|
+
console.log(' Using existing custom.js');
|
|
250
|
+
} else if (hasCreativeCustomJs) {
|
|
251
|
+
// First run with remote customjs available — prompt
|
|
252
|
+
const choice = await promptUser(
|
|
253
|
+
'Found customJS in this creative. What would you like to use?',
|
|
254
|
+
[
|
|
255
|
+
'Use customJS from the creative (recommended)',
|
|
256
|
+
'Start with blank template'
|
|
257
|
+
]
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
if (choice === 0) {
|
|
261
|
+
fs.writeFileSync(customJsPath, config.customjs, 'utf-8');
|
|
262
|
+
console.log(' Created custom.js (from creative)');
|
|
215
263
|
} else {
|
|
216
|
-
// custom.js exists - ask user if they want to overwrite
|
|
217
|
-
const choice = await promptUser(
|
|
218
|
-
'Found customJS in this creative. Your custom.js already exists.',
|
|
219
|
-
[
|
|
220
|
-
'Keep existing custom.js',
|
|
221
|
-
'Overwrite with customJS from creative'
|
|
222
|
-
]
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
if (choice === 1) {
|
|
226
|
-
// Overwrite with creative's customjs
|
|
227
|
-
fs.writeFileSync(customJsPath, config.customjs, 'utf-8');
|
|
228
|
-
console.log(' Overwrote custom.js with creative\'s customJS');
|
|
229
|
-
} else {
|
|
230
|
-
console.log(' Keeping existing custom.js');
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
} else {
|
|
234
|
-
// No customjs in creative - use template if custom.js doesn't exist
|
|
235
|
-
if (!customJsExists) {
|
|
236
264
|
const templatePath = path.join(packageRoot, 'templates', 'custom.js');
|
|
237
265
|
if (fs.existsSync(templatePath)) {
|
|
238
266
|
fs.copyFileSync(templatePath, customJsPath);
|
|
239
267
|
console.log(' Created custom.js (from template)');
|
|
240
268
|
}
|
|
241
269
|
}
|
|
270
|
+
} else {
|
|
271
|
+
// No customjs in creative — use template if custom.js doesn't exist
|
|
272
|
+
const templatePath = path.join(packageRoot, 'templates', 'custom.js');
|
|
273
|
+
if (fs.existsSync(templatePath)) {
|
|
274
|
+
fs.copyFileSync(templatePath, customJsPath);
|
|
275
|
+
console.log(' Created custom.js (from template)');
|
|
276
|
+
}
|
|
242
277
|
}
|
|
243
278
|
|
|
244
279
|
// Copy AGENTS.md if it doesn't exist
|
|
@@ -251,6 +286,22 @@ async function main() {
|
|
|
251
286
|
}
|
|
252
287
|
}
|
|
253
288
|
|
|
289
|
+
// Save .rad-coder.json config for future no-arg runs
|
|
290
|
+
const radCoderConfigPath = path.join(userDir, '.rad-coder.json');
|
|
291
|
+
const savedConfig = {
|
|
292
|
+
creativeId: config.creativeId,
|
|
293
|
+
flowlineId: config.flowlineId,
|
|
294
|
+
flowlineName: config.flowlineName,
|
|
295
|
+
createdAt: fs.existsSync(radCoderConfigPath)
|
|
296
|
+
? JSON.parse(fs.readFileSync(radCoderConfigPath, 'utf-8')).createdAt
|
|
297
|
+
: new Date().toISOString(),
|
|
298
|
+
updatedAt: new Date().toISOString()
|
|
299
|
+
};
|
|
300
|
+
fs.writeFileSync(radCoderConfigPath, JSON.stringify(savedConfig, null, 2) + '\n', 'utf-8');
|
|
301
|
+
if (isNewProject) {
|
|
302
|
+
console.log(' Created .rad-coder.json');
|
|
303
|
+
}
|
|
304
|
+
|
|
254
305
|
// Start the server with pre-fetched config
|
|
255
306
|
await startServer(config);
|
|
256
307
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rad-coder",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Development environment for testing ResponsiveAds creative custom JS with hot-reload",
|
|
5
5
|
"bin": {
|
|
6
|
-
"rad-coder": "
|
|
6
|
+
"rad-coder": "bin/cli.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
9
|
"dev": "node bin/cli.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
],
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|
|
29
|
-
"url": "https://github.com/ResponsiveAds/rad-coder"
|
|
29
|
+
"url": "git+https://github.com/ResponsiveAds/rad-coder.git"
|
|
30
30
|
},
|
|
31
31
|
"author": "ResponsiveAds",
|
|
32
32
|
"license": "MIT",
|
package/public/test.html
CHANGED
|
@@ -87,23 +87,8 @@
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
.ad-container {
|
|
90
|
-
padding: 20px;
|
|
91
|
-
display: flex;
|
|
92
|
-
justify-content: center;
|
|
93
|
-
align-items: flex-start;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.ad-wrapper {
|
|
97
|
-
background: #fff;
|
|
98
|
-
border-radius: 8px;
|
|
99
|
-
overflow: hidden;
|
|
100
|
-
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
|
|
101
90
|
}
|
|
102
91
|
|
|
103
|
-
.inner-content {
|
|
104
|
-
width: 100%;
|
|
105
|
-
min-height: 300px;
|
|
106
|
-
}
|
|
107
92
|
|
|
108
93
|
/* Toast notification for reload */
|
|
109
94
|
.toast {
|
|
@@ -180,6 +165,8 @@
|
|
|
180
165
|
document.getElementById('creative-id').textContent = 'Loading...';
|
|
181
166
|
|
|
182
167
|
const config = await fetchConfigWithRetry();
|
|
168
|
+
//not sure why we get URL that does not work https://edit.responsiveads.com/flowlines
|
|
169
|
+
config.flSource = 'https://s3-eu-west-1.amazonaws.com/publish.responsiveads.com/flowlines/';
|
|
183
170
|
|
|
184
171
|
// Update UI
|
|
185
172
|
document.getElementById('creative-id').textContent = `ID: ${config.creativeId}`;
|
|
@@ -231,7 +218,7 @@
|
|
|
231
218
|
tracking: false,
|
|
232
219
|
isFluid: config.isFluid,
|
|
233
220
|
screenshot: false,
|
|
234
|
-
crossOrigin:
|
|
221
|
+
crossOrigin: false,
|
|
235
222
|
flSource: config.flSource,
|
|
236
223
|
config: {
|
|
237
224
|
_default: {
|
package/server/index.js
CHANGED
|
@@ -315,7 +315,15 @@ async function fetchCreativeConfig(creativeId) {
|
|
|
315
315
|
// ============================================================
|
|
316
316
|
|
|
317
317
|
const app = express();
|
|
318
|
-
const server = http.createServer(app);
|
|
318
|
+
const server = http.createServer({ maxHeaderSize: 65536 }, app);
|
|
319
|
+
|
|
320
|
+
// Track active HTTP sockets so shutdown can force-close stragglers.
|
|
321
|
+
const activeSockets = new Set();
|
|
322
|
+
server.on('connection', (socket) => {
|
|
323
|
+
activeSockets.add(socket);
|
|
324
|
+
socket.on('close', () => activeSockets.delete(socket));
|
|
325
|
+
});
|
|
326
|
+
let isShuttingDown = false;
|
|
319
327
|
|
|
320
328
|
// WebSocket server for hot-reload
|
|
321
329
|
const wss = new WebSocketServer({ server });
|
|
@@ -608,13 +616,53 @@ if (!isModule) {
|
|
|
608
616
|
|
|
609
617
|
// Graceful shutdown
|
|
610
618
|
function gracefulShutdown() {
|
|
619
|
+
if (isShuttingDown) {
|
|
620
|
+
console.log('\n Force exiting...\n');
|
|
621
|
+
process.exit(130);
|
|
622
|
+
}
|
|
623
|
+
isShuttingDown = true;
|
|
624
|
+
|
|
611
625
|
if (tui) {
|
|
612
626
|
tui.destroy();
|
|
613
627
|
}
|
|
614
628
|
console.log('\n Shutting down...');
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
629
|
+
console.log(' Press Ctrl+C again to force exit');
|
|
630
|
+
|
|
631
|
+
// Ensure websocket clients do not keep the process alive.
|
|
632
|
+
clients.forEach((client) => {
|
|
633
|
+
try {
|
|
634
|
+
client.terminate();
|
|
635
|
+
} catch (_) {
|
|
636
|
+
// Ignore client termination failures during shutdown.
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
if (typeof server.closeIdleConnections === 'function') {
|
|
641
|
+
server.closeIdleConnections();
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
if (typeof server.closeAllConnections === 'function') {
|
|
645
|
+
server.closeAllConnections();
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const forceTimer = setTimeout(() => {
|
|
649
|
+
activeSockets.forEach((socket) => {
|
|
650
|
+
try {
|
|
651
|
+
socket.destroy();
|
|
652
|
+
} catch (_) {
|
|
653
|
+
// Ignore socket destroy failures during forced shutdown.
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
console.log(' Forced shutdown: closed remaining connections');
|
|
657
|
+
process.exit(0);
|
|
658
|
+
}, 2000);
|
|
659
|
+
|
|
660
|
+
Promise.allSettled([
|
|
661
|
+
Promise.resolve().then(() => watcher.close()),
|
|
662
|
+
new Promise((resolve) => wss.close(resolve)),
|
|
663
|
+
new Promise((resolve) => server.close(resolve)),
|
|
664
|
+
]).finally(() => {
|
|
665
|
+
clearTimeout(forceTimer);
|
|
618
666
|
console.log(' Server stopped\n');
|
|
619
667
|
process.exit(0);
|
|
620
668
|
});
|
package/templates/AGENTS.md
CHANGED
|
@@ -4,6 +4,9 @@ You are an agent writing only JS for a responsive creative. The creative was bui
|
|
|
4
4
|
|
|
5
5
|
To do this you can only edit the `custom.js` file in this directory. When you edit and save this file the creative will be automatically loaded on the test page: http://localhost:3000/test.html. The code you wrote in `custom.js` will be applied to the creative.
|
|
6
6
|
|
|
7
|
+
Use the http://localhost:3000/test.html URL to open creative in the browser. Inspect the HTML dom so that you can use IDs from elements inside the custom.js code. Alo use the browser to test the code and make sure there are no console.log errors.
|
|
8
|
+
|
|
9
|
+
|
|
7
10
|
Use modern JS standards and code practices.
|
|
8
11
|
|
|
9
12
|
We can use custom in situations when we want to add extra interactivity to our responsive creative. Use the Radical API to access elements added from the editor, update their behavior, and add custom functionalities to your ad.
|
|
@@ -16,6 +19,32 @@ You can use all available JavaScript functions to manipulate element position an
|
|
|
16
19
|
|
|
17
20
|
This document outlines the specific implementation patterns and lifecycle hooks for the Radical API. Use this guide to programmatically control elements, manage dynamic data (DCO), and handle cross-window interactions in ResponsiveAds creatives.
|
|
18
21
|
|
|
22
|
+
Use this as a good starting point always call functions from `!rad.getMergedContent().inEditor` check to prevent messing up the editor code.
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
var rad = Radical.getAdByWindow(window);
|
|
26
|
+
var container = rad.getContainer();
|
|
27
|
+
|
|
28
|
+
var inScreenshot = window.location.href.indexOf('preview?screenshot=1') > -1 ? true : false;
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if (!rad.getMergedContent().inEditor) {
|
|
32
|
+
rad.onLoad(onAdLoaded);
|
|
33
|
+
rad.onBeforeRender(onBeforeRender);
|
|
34
|
+
rad.onRender(onAdRender);
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
function onAdLoaded() {}
|
|
38
|
+
function onBeforeRender(arg) {
|
|
39
|
+
console.log('onBeforeRender', arg);
|
|
40
|
+
}
|
|
41
|
+
function onAdRender() {
|
|
42
|
+
console.log('onAdRender');
|
|
43
|
+
const el = rad.getElementById('a2');
|
|
44
|
+
console.log(el);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
19
48
|
## 1. Initializing the Controller
|
|
20
49
|
|
|
21
50
|
Every script must first reference the ad instance and its container.
|