@teacharium/embed-sdk 0.1.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 +252 -0
- package/dist/main.d.ts +1 -0
- package/dist/teacharium-embed-sdk.es.js +127 -0
- package/dist/teacharium-embed-sdk.umd.js +1 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# Teacharium Embed SDK
|
|
2
|
+
|
|
3
|
+
A lightweight JavaScript SDK for embedding Teacharium lessons into any website using secure iframe-based embedding.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @teacharium/embed-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or use directly via CDN:
|
|
12
|
+
|
|
13
|
+
```html
|
|
14
|
+
<script src="https://unpkg.com/@teacharium/embed-sdk@latest/dist/teacharium-embed-sdk.umd.js"></script>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
### 1. Obtain a JWT Token
|
|
20
|
+
|
|
21
|
+
First, generate a signed token from your backend using the Teacharium API:
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
const response = await fetch('https://www.teacharium.io/api/public/sign-token', {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: {
|
|
27
|
+
'Authorization': `Bearer ${publicKey}.${secretKey}`,
|
|
28
|
+
'Content-Type': 'application/json'
|
|
29
|
+
},
|
|
30
|
+
body: JSON.stringify({
|
|
31
|
+
lessonId: 'your-lesson-id',
|
|
32
|
+
userAttributes: {
|
|
33
|
+
userId: 'user_12345',
|
|
34
|
+
organizationRole: 'student'
|
|
35
|
+
},
|
|
36
|
+
timeout: 3600
|
|
37
|
+
})
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const { token } = await response.json();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Important:** Do not include personally identifiable information (PII) such as email addresses, full names, or phone numbers in `userAttributes`. Use anonymous identifiers like user IDs or session IDs instead.
|
|
44
|
+
|
|
45
|
+
### 2. Embed the Lesson
|
|
46
|
+
|
|
47
|
+
```html
|
|
48
|
+
<div id="lesson-container"></div>
|
|
49
|
+
|
|
50
|
+
<script type="module">
|
|
51
|
+
import { embedLesson } from '@teacharium/embed-sdk';
|
|
52
|
+
|
|
53
|
+
const embed = embedLesson({
|
|
54
|
+
container: '#lesson-container',
|
|
55
|
+
token: 'your-jwt-token-here',
|
|
56
|
+
// baseUrl and lessonId are optional - defaults to www.teacharium.io and extracts lessonId from token
|
|
57
|
+
width: '100%',
|
|
58
|
+
height: '600px',
|
|
59
|
+
onLoad: () => console.log('Lesson loaded'),
|
|
60
|
+
onComplete: (data) => console.log('Lesson completed', data),
|
|
61
|
+
onProgress: (data) => console.log('Progress update', data),
|
|
62
|
+
onError: (error) => console.error('Error', error)
|
|
63
|
+
});
|
|
64
|
+
</script>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Using UMD (Browser Global)
|
|
68
|
+
|
|
69
|
+
```html
|
|
70
|
+
<div id="lesson-container"></div>
|
|
71
|
+
|
|
72
|
+
<script src="https://unpkg.com/@teacharium/embed-sdk@latest/dist/teacharium-embed-sdk.umd.js"></script>
|
|
73
|
+
<script>
|
|
74
|
+
const embed = TeachariumEmbed.embedLesson({
|
|
75
|
+
container: '#lesson-container',
|
|
76
|
+
token: 'your-jwt-token-here',
|
|
77
|
+
onComplete: (data) => {
|
|
78
|
+
console.log('Lesson completed!', data);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
</script>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## API Reference
|
|
85
|
+
|
|
86
|
+
### `embedLesson(options: EmbedOptions)`
|
|
87
|
+
|
|
88
|
+
Creates and embeds a lesson iframe.
|
|
89
|
+
|
|
90
|
+
**Options:**
|
|
91
|
+
|
|
92
|
+
- `container` (required): HTMLElement or CSS selector string for the container
|
|
93
|
+
- `token` (required): JWT token from `/api/public/sign-token` (lessonId is automatically extracted from this token)
|
|
94
|
+
- `baseUrl` (optional): Base URL of your Teacharium instance, default: "https://www.teacharium.io"
|
|
95
|
+
- `width` (optional): Iframe width, default: "100%"
|
|
96
|
+
- `height` (optional): Iframe height, default: "600px"
|
|
97
|
+
- `className` (optional): Additional CSS classes for the iframe
|
|
98
|
+
- `onLoad` (optional): Callback when lesson loads
|
|
99
|
+
- `onComplete` (optional): Callback when lesson is completed
|
|
100
|
+
- `onProgress` (optional): Callback for progress updates
|
|
101
|
+
- `onError` (optional): Callback for errors
|
|
102
|
+
|
|
103
|
+
**Returns:** `TeachariumEmbed` instance
|
|
104
|
+
|
|
105
|
+
### `TeachariumEmbed` Methods
|
|
106
|
+
|
|
107
|
+
#### `destroy()`
|
|
108
|
+
Removes the embedded lesson from the page.
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
embed.destroy();
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### `reload()`
|
|
115
|
+
Reloads the embedded lesson.
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
embed.reload();
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### `postMessage(type: string, payload?: any)`
|
|
122
|
+
Send a custom message to the embedded lesson.
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
embed.postMessage('custom:action', { data: 'value' });
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### `getIframe()`
|
|
129
|
+
Get the iframe element.
|
|
130
|
+
|
|
131
|
+
```javascript
|
|
132
|
+
const iframe = embed.getIframe();
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Events
|
|
136
|
+
|
|
137
|
+
### onLoad
|
|
138
|
+
Fired when the lesson iframe has loaded.
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
onLoad: () => {
|
|
142
|
+
console.log('Lesson is ready');
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### onComplete
|
|
147
|
+
Fired when the user completes the lesson.
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
onComplete: (data) => {
|
|
151
|
+
// data.lessonId - The lesson ID
|
|
152
|
+
// data.completedAt - ISO timestamp
|
|
153
|
+
// data.score - Optional score
|
|
154
|
+
// data.totalSteps - Total number of steps
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### onProgress
|
|
159
|
+
Fired when the user progresses through the lesson.
|
|
160
|
+
|
|
161
|
+
```javascript
|
|
162
|
+
onProgress: (data) => {
|
|
163
|
+
// data.currentStep - Current step number
|
|
164
|
+
// data.totalSteps - Total steps
|
|
165
|
+
// data.sectionIndex - Current section index
|
|
166
|
+
// data.stepIndex - Current step index
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### onError
|
|
171
|
+
Fired when an error occurs.
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
onError: (error) => {
|
|
175
|
+
console.error('Lesson error:', error.message);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Security
|
|
180
|
+
|
|
181
|
+
- Always generate tokens on your backend, never expose API keys in client-side code
|
|
182
|
+
- Tokens expire based on the `timeout` parameter (default 2 hours, max 24 hours)
|
|
183
|
+
- Tokens are tied to specific lessons and organizations
|
|
184
|
+
- User attributes in tokens cannot be modified without re-signing
|
|
185
|
+
|
|
186
|
+
## Examples
|
|
187
|
+
|
|
188
|
+
### Responsive Embed
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
embedLesson({
|
|
192
|
+
container: '#lesson',
|
|
193
|
+
token: token,
|
|
194
|
+
baseUrl: 'https://your-domain.com',
|
|
195
|
+
lessonId: lessonId,
|
|
196
|
+
width: '100%',
|
|
197
|
+
height: '80vh',
|
|
198
|
+
className: 'lesson-iframe'
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### With Progress Tracking
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
const embed = embedLesson({
|
|
206
|
+
container: '#lesson',
|
|
207
|
+
token: token,
|
|
208
|
+
baseUrl: 'https://your-domain.com',
|
|
209
|
+
lessonId: lessonId,
|
|
210
|
+
onProgress: (data) => {
|
|
211
|
+
const percent = (data.currentStep / data.totalSteps) * 100;
|
|
212
|
+
document.querySelector('#progress-bar').style.width = `${percent}%`;
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### React Example
|
|
218
|
+
|
|
219
|
+
```jsx
|
|
220
|
+
import { useEffect, useRef } from 'react';
|
|
221
|
+
import { embedLesson } from '@teacharium/embed-sdk';
|
|
222
|
+
|
|
223
|
+
function LessonEmbed({ token }) {
|
|
224
|
+
const containerRef = useRef(null);
|
|
225
|
+
const embedRef = useRef(null);
|
|
226
|
+
|
|
227
|
+
useEffect(() => {
|
|
228
|
+
if (containerRef.current && token) {
|
|
229
|
+
embedRef.current = embedLesson({
|
|
230
|
+
container: containerRef.current,
|
|
231
|
+
token: token,
|
|
232
|
+
// lessonId and baseUrl are automatically handled
|
|
233
|
+
onComplete: (data) => {
|
|
234
|
+
console.log('Lesson completed!', data);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return () => {
|
|
240
|
+
if (embedRef.current) {
|
|
241
|
+
embedRef.current.destroy();
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}, [token]);
|
|
245
|
+
|
|
246
|
+
return <div ref={containerRef} style={{ width: '100%', height: '600px' }} />;
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## License
|
|
251
|
+
|
|
252
|
+
MIT
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
function a(o) {
|
|
2
|
+
try {
|
|
3
|
+
const e = o.split(".");
|
|
4
|
+
if (e.length !== 3)
|
|
5
|
+
throw new Error("Invalid token format");
|
|
6
|
+
const t = e[1].replace(/-/g, "+").replace(/_/g, "/"), r = decodeURIComponent(
|
|
7
|
+
atob(t).split("").map((n) => "%" + ("00" + n.charCodeAt(0).toString(16)).slice(-2)).join("")
|
|
8
|
+
);
|
|
9
|
+
return JSON.parse(r);
|
|
10
|
+
} catch {
|
|
11
|
+
throw new Error("Failed to decode JWT token");
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
class i {
|
|
15
|
+
iframe = null;
|
|
16
|
+
container = null;
|
|
17
|
+
options;
|
|
18
|
+
constructor(e) {
|
|
19
|
+
let s;
|
|
20
|
+
try {
|
|
21
|
+
if (s = a(e.token).lessonId, !s)
|
|
22
|
+
throw new Error("Token does not contain lessonId");
|
|
23
|
+
} catch {
|
|
24
|
+
const r = new Error("Failed to extract lessonId from token. Ensure token is valid and contains lessonId.");
|
|
25
|
+
throw e.onError?.(r), r;
|
|
26
|
+
}
|
|
27
|
+
this.options = {
|
|
28
|
+
...e,
|
|
29
|
+
baseUrl: e.baseUrl || "https://www.teacharium.io",
|
|
30
|
+
lessonId: s,
|
|
31
|
+
width: e.width || "100%",
|
|
32
|
+
height: e.height || "600px",
|
|
33
|
+
className: e.className || ""
|
|
34
|
+
}, this.init();
|
|
35
|
+
}
|
|
36
|
+
init() {
|
|
37
|
+
if (typeof this.options.container == "string") {
|
|
38
|
+
const e = document.querySelector(this.options.container);
|
|
39
|
+
if (!e) {
|
|
40
|
+
const s = new Error(`Container element not found: ${this.options.container}`);
|
|
41
|
+
throw this.options.onError?.(s), s;
|
|
42
|
+
}
|
|
43
|
+
this.container = e;
|
|
44
|
+
} else
|
|
45
|
+
this.container = this.options.container;
|
|
46
|
+
this.createIframe(), this.setupMessageListener();
|
|
47
|
+
}
|
|
48
|
+
createIframe() {
|
|
49
|
+
if (!this.container)
|
|
50
|
+
throw new Error("Container not initialized");
|
|
51
|
+
this.iframe = document.createElement("iframe");
|
|
52
|
+
const e = new URL(
|
|
53
|
+
`/embed/lesson/${this.options.lessonId}`,
|
|
54
|
+
this.options.baseUrl
|
|
55
|
+
);
|
|
56
|
+
e.searchParams.set("token", this.options.token), this.iframe.src = e.toString(), this.iframe.width = this.options.width, this.iframe.height = this.options.height, this.iframe.style.border = "none", this.iframe.style.display = "block", this.iframe.setAttribute("allow", "autoplay; fullscreen"), this.iframe.setAttribute("allowfullscreen", "true"), this.options.className && (this.iframe.className = this.options.className), this.iframe.addEventListener("load", () => {
|
|
57
|
+
this.options.onLoad?.();
|
|
58
|
+
}), this.container.appendChild(this.iframe);
|
|
59
|
+
}
|
|
60
|
+
setupMessageListener() {
|
|
61
|
+
window.addEventListener("message", (e) => {
|
|
62
|
+
if (this.iframe && e.source !== this.iframe.contentWindow)
|
|
63
|
+
return;
|
|
64
|
+
const s = new URL(this.options.baseUrl).origin;
|
|
65
|
+
if (e.origin === s)
|
|
66
|
+
try {
|
|
67
|
+
const t = typeof e.data == "string" ? JSON.parse(e.data) : e.data;
|
|
68
|
+
switch (t.type) {
|
|
69
|
+
case "lesson:complete":
|
|
70
|
+
this.options.onComplete?.(t.payload);
|
|
71
|
+
break;
|
|
72
|
+
case "lesson:progress":
|
|
73
|
+
this.options.onProgress?.(t.payload);
|
|
74
|
+
break;
|
|
75
|
+
case "lesson:error":
|
|
76
|
+
this.options.onError?.(new Error(t.payload.message));
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
} catch (t) {
|
|
80
|
+
console.error("Error parsing message from iframe:", t);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Remove the embedded lesson from the page
|
|
86
|
+
*/
|
|
87
|
+
destroy() {
|
|
88
|
+
this.iframe && this.iframe.parentNode && (this.iframe.parentNode.removeChild(this.iframe), this.iframe = null);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Send a message to the embedded lesson
|
|
92
|
+
* @param type The message type
|
|
93
|
+
* @param payload The message payload
|
|
94
|
+
*/
|
|
95
|
+
postMessage(e, s) {
|
|
96
|
+
if (!this.iframe || !this.iframe.contentWindow) {
|
|
97
|
+
console.warn("Cannot post message: iframe not ready");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const t = { type: e, payload: s }, r = new URL(this.options.baseUrl).origin;
|
|
101
|
+
this.iframe.contentWindow.postMessage(t, r);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Reload the embedded lesson
|
|
105
|
+
*/
|
|
106
|
+
reload() {
|
|
107
|
+
this.iframe && (this.iframe.src = this.iframe.src);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get the iframe element
|
|
111
|
+
*/
|
|
112
|
+
getIframe() {
|
|
113
|
+
return this.iframe;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function h(o) {
|
|
117
|
+
return new i(o);
|
|
118
|
+
}
|
|
119
|
+
const l = {
|
|
120
|
+
TeachariumEmbed: i,
|
|
121
|
+
embedLesson: h
|
|
122
|
+
};
|
|
123
|
+
export {
|
|
124
|
+
i as TeachariumEmbed,
|
|
125
|
+
l as default,
|
|
126
|
+
h as embedLesson
|
|
127
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(i,r){typeof exports=="object"&&typeof module<"u"?r(exports):typeof define=="function"&&define.amd?define(["exports"],r):(i=typeof globalThis<"u"?globalThis:i||self,r(i.TeachariumEmbed={}))})(this,(function(i){"use strict";function r(n){try{const e=n.split(".");if(e.length!==3)throw new Error("Invalid token format");const t=e[1].replace(/-/g,"+").replace(/_/g,"/"),o=decodeURIComponent(atob(t).split("").map(c=>"%"+("00"+c.charCodeAt(0).toString(16)).slice(-2)).join(""));return JSON.parse(o)}catch{throw new Error("Failed to decode JWT token")}}class a{iframe=null;container=null;options;constructor(e){let s;try{if(s=r(e.token).lessonId,!s)throw new Error("Token does not contain lessonId")}catch{const o=new Error("Failed to extract lessonId from token. Ensure token is valid and contains lessonId.");throw e.onError?.(o),o}this.options={...e,baseUrl:e.baseUrl||"https://www.teacharium.io",lessonId:s,width:e.width||"100%",height:e.height||"600px",className:e.className||""},this.init()}init(){if(typeof this.options.container=="string"){const e=document.querySelector(this.options.container);if(!e){const s=new Error(`Container element not found: ${this.options.container}`);throw this.options.onError?.(s),s}this.container=e}else this.container=this.options.container;this.createIframe(),this.setupMessageListener()}createIframe(){if(!this.container)throw new Error("Container not initialized");this.iframe=document.createElement("iframe");const e=new URL(`/embed/lesson/${this.options.lessonId}`,this.options.baseUrl);e.searchParams.set("token",this.options.token),this.iframe.src=e.toString(),this.iframe.width=this.options.width,this.iframe.height=this.options.height,this.iframe.style.border="none",this.iframe.style.display="block",this.iframe.setAttribute("allow","autoplay; fullscreen"),this.iframe.setAttribute("allowfullscreen","true"),this.options.className&&(this.iframe.className=this.options.className),this.iframe.addEventListener("load",()=>{this.options.onLoad?.()}),this.container.appendChild(this.iframe)}setupMessageListener(){window.addEventListener("message",e=>{if(this.iframe&&e.source!==this.iframe.contentWindow)return;const s=new URL(this.options.baseUrl).origin;if(e.origin===s)try{const t=typeof e.data=="string"?JSON.parse(e.data):e.data;switch(t.type){case"lesson:complete":this.options.onComplete?.(t.payload);break;case"lesson:progress":this.options.onProgress?.(t.payload);break;case"lesson:error":this.options.onError?.(new Error(t.payload.message));break}}catch(t){console.error("Error parsing message from iframe:",t)}})}destroy(){this.iframe&&this.iframe.parentNode&&(this.iframe.parentNode.removeChild(this.iframe),this.iframe=null)}postMessage(e,s){if(!this.iframe||!this.iframe.contentWindow){console.warn("Cannot post message: iframe not ready");return}const t={type:e,payload:s},o=new URL(this.options.baseUrl).origin;this.iframe.contentWindow.postMessage(t,o)}reload(){this.iframe&&(this.iframe.src=this.iframe.src)}getIframe(){return this.iframe}}function h(n){return new a(n)}const l={TeachariumEmbed:a,embedLesson:h};i.TeachariumEmbed=a,i.default=l,i.embedLesson=h,Object.defineProperties(i,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@teacharium/embed-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "SDK for embedding Teacharium lessons via iframe",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"main": "dist/teacharium-embed-sdk.umd.js",
|
|
10
|
+
"module": "dist/teacharium-embed-sdk.es.js",
|
|
11
|
+
"types": "dist/main.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/main.d.ts",
|
|
15
|
+
"import": "./dist/teacharium-embed-sdk.es.js",
|
|
16
|
+
"require": "./dist/teacharium-embed-sdk.umd.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"teacharium",
|
|
21
|
+
"embed",
|
|
22
|
+
"iframe",
|
|
23
|
+
"lessons",
|
|
24
|
+
"elearning"
|
|
25
|
+
],
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@eslint/js": "^9.35.0",
|
|
28
|
+
"eslint": "^9.35.0",
|
|
29
|
+
"globals": "^16.4.0",
|
|
30
|
+
"typescript": "~5.8.3",
|
|
31
|
+
"typescript-eslint": "^8.43.0",
|
|
32
|
+
"vite": "^7.1.5",
|
|
33
|
+
"vite-plugin-dts": "^4.5.4"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"dev": "tsc -b && vite build --watch",
|
|
37
|
+
"build:watch": "tsc -b && vite build --watch",
|
|
38
|
+
"build": "tsc -b && vite build",
|
|
39
|
+
"type-check": "tsc --noEmit",
|
|
40
|
+
"lint": "eslint ."
|
|
41
|
+
}
|
|
42
|
+
}
|