@liascript/exporter 3.0.0--1.0.3 → 3.0.1--1.0.3

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 (72) hide show
  1. package/dist/assets/capacitor/{index.bfe7363b.js → index.a7f021f7.js} +1 -1
  2. package/dist/assets/capacitor/index.html +1 -1
  3. package/dist/assets/capacitor/{jszip.min.f6eda75b.js → jszip.min.43389eb1.js} +1 -1
  4. package/dist/assets/capacitor/{trystero-ipfs.min.b27a61d7.js → trystero-ipfs.min.f25fe3e7.js} +1 -1
  5. package/dist/assets/indexeddb/{index.599a57d6.js → index.4aceca2f.js} +1 -1
  6. package/dist/assets/indexeddb/index.html +1 -1
  7. package/dist/assets/indexeddb/{jszip.min.63142cc8.js → jszip.min.4fbcc13f.js} +1 -1
  8. package/dist/assets/scorm2004/{index.7a5820ab.js → index.33bec53a.js} +1 -1
  9. package/dist/assets/scorm2004/index.html +1 -1
  10. package/dist/assets/scorm2004/{jszip.min.63142cc8.js → jszip.min.4fbcc13f.js} +1 -1
  11. package/dist/assets/xapi/{index.018a032a.js → index.f2e89e49.js} +1 -1
  12. package/dist/assets/xapi/index.html +1 -1
  13. package/dist/assets/xapi/{jszip.min.eaecf580.js → jszip.min.19c66d77.js} +1 -1
  14. package/dist/index.js +47 -47
  15. package/dist/server/presets.json +94 -0
  16. package/dist/server/presets.yaml +120 -0
  17. package/dist/server/public/app.js +1 -0
  18. package/dist/server/public/assets/android.svg +38 -0
  19. package/dist/server/public/assets/cmi.svg +154 -0
  20. package/dist/server/public/assets/docx.svg +20 -0
  21. package/dist/server/public/assets/edX.svg +75 -0
  22. package/dist/server/public/assets/edx.svg +75 -0
  23. package/dist/server/public/assets/epub.svg +18 -0
  24. package/dist/server/public/assets/icon.svg +82 -0
  25. package/dist/server/public/assets/ilias.png +0 -0
  26. package/dist/server/public/assets/json.svg +4 -0
  27. package/dist/server/public/assets/learnworlds.png +0 -0
  28. package/dist/server/public/assets/moodle.svg +190 -0
  29. package/dist/server/public/assets/opal.png +0 -0
  30. package/dist/server/public/assets/openolat.png +0 -0
  31. package/dist/server/public/assets/pdf.svg +4 -0
  32. package/dist/server/public/assets/rdf.svg +4 -0
  33. package/dist/server/public/assets/scorm.png +0 -0
  34. package/dist/server/public/assets/web.png +0 -0
  35. package/dist/server/public/assets/xapi.png +0 -0
  36. package/dist/server/public/i18n.js +1 -0
  37. package/dist/server/public/index.html +1587 -0
  38. package/dist/server/public/locales/de.json +247 -0
  39. package/dist/server/public/locales/en.json +247 -0
  40. package/dist/server/public/status.html +251 -0
  41. package/dist/server/public/styles.css +712 -0
  42. package/package.json +5 -1
  43. package/.parcelrc +0 -3
  44. package/DESKTOP_APP_README.md +0 -58
  45. package/DOCKERHUB_DESCRIPTION.md +0 -52
  46. package/Dockerfile +0 -129
  47. package/PLAYSTORE_GUIDE.md +0 -172
  48. package/action.yml +0 -157
  49. package/custom.css +0 -10
  50. package/electron-builder.json +0 -149
  51. package/src/cli.ts +0 -69
  52. package/src/colorize.ts +0 -115
  53. package/src/export/android.ts +0 -419
  54. package/src/export/docx.ts +0 -1025
  55. package/src/export/epub.ts +0 -1306
  56. package/src/export/h5p.ts +0 -390
  57. package/src/export/helper.ts +0 -360
  58. package/src/export/ims.ts +0 -191
  59. package/src/export/pdf.ts +0 -406
  60. package/src/export/presets.ts +0 -220
  61. package/src/export/project.ts +0 -829
  62. package/src/export/rdf.ts +0 -551
  63. package/src/export/scorm12.ts +0 -167
  64. package/src/export/scorm2004.ts +0 -140
  65. package/src/export/web.ts +0 -306
  66. package/src/export/xapi.ts +0 -424
  67. package/src/exporter.ts +0 -296
  68. package/src/index.ts +0 -96
  69. package/src/parser.ts +0 -373
  70. package/src/presets.yaml +0 -219
  71. package/src/types.ts +0 -82
  72. package/tsconfig.json +0 -24
@@ -1,360 +0,0 @@
1
- 'use strict'
2
-
3
- import fetch from 'node-fetch'
4
- import * as temp from 'temp'
5
- import * as fs from 'fs-extra'
6
- import * as path from 'path'
7
- const archiver = require('archiver')
8
- const beautify = require('simply-beautiful')
9
-
10
- // Constants
11
- const TEMP_DIR_PREFIX = 'lia'
12
- const DIST_DIR_RELATIVE_PATH = '../../dist'
13
- const ZIP_COMPRESSION_LEVEL = 9
14
- const RANDOM_STRING_LENGTH = 16
15
- const RANDOM_CHARS =
16
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
17
- const BEAUTIFY_OPTIONS = {
18
- indent_size: 4,
19
- space_before_conditional: true,
20
- jslint_happy: true,
21
- max_char: 0,
22
- }
23
-
24
- /**
25
- * Creates a temporary directory with a unique name.
26
- * @returns Promise that resolves to the path of the created temporary directory
27
- */
28
- export function tmpDir(): Promise<string> {
29
- return new Promise((resolve, reject) => {
30
- temp.mkdir(TEMP_DIR_PREFIX, function (err: Error | null, tmpPath: string) {
31
- if (err) reject(err)
32
- else resolve(tmpPath)
33
- })
34
- })
35
- }
36
-
37
- /**
38
- * Returns the absolute path to the distribution directory.
39
- * @returns The path to the dist directory (where index.js is located)
40
- */
41
- export function dirname(): string {
42
- // Get the directory where the main entry point (index.js) is located
43
- // This works both in Docker and locally because it's relative to the actual running file
44
- const mainFile = require.main?.filename || process.argv[1]
45
-
46
- return path.dirname(mainFile)
47
- }
48
-
49
- /**
50
- * Delays execution for a specified number of milliseconds.
51
- * @param ms - Number of milliseconds to sleep
52
- * @returns Promise that resolves after the specified delay
53
- */
54
- export function sleep(ms: number): Promise<void> {
55
- return new Promise((resolve) => setTimeout(resolve, ms))
56
- }
57
-
58
- /**
59
- * Writes content to a file.
60
- * @param filename - Path to the file to write
61
- * @param content - Content to write to the file
62
- * @returns Promise that resolves when the file is written
63
- */
64
- export function writeFile(filename: string, content: string): Promise<string> {
65
- return new Promise((resolve, reject) => {
66
- fs.writeFile(filename, content, function (err: Error | null) {
67
- if (err) reject(err)
68
- else resolve('ok')
69
- })
70
- })
71
- }
72
-
73
- /**
74
- * Creates a filter function to exclude hidden folders and node_modules during copy operations.
75
- * @param sourceDir - The source directory being copied
76
- * @returns A filter function that returns false for hidden folders and node_modules
77
- */
78
- export function filterHidden(
79
- sourceDir: string,
80
- ): (src: string, dest: string) => boolean {
81
- return function (src: string, dest: string): boolean {
82
- // Get the relative path of the source folder being copied
83
- const relPath = path.relative(path.resolve(sourceDir), src)
84
-
85
- // Split the relative path into its components
86
- const components = relPath.split(path.sep)
87
-
88
- // Check each component for hidden folders
89
- for (const component of components) {
90
- // Check if the component starts with a dot (i.e., hidden folder)
91
- if (component.startsWith('.')) {
92
- return false // Exclude the folder from the copy
93
- }
94
-
95
- // Check if the current folder being copied is the "node_modules" folder
96
- if (component === 'node_modules') {
97
- return false // Exclude the folder from the copy
98
- }
99
- }
100
-
101
- return true // Include the folder in the copy
102
- }
103
- }
104
-
105
- /**
106
- * Injects ResponsiveVoice script tag into an HTML string.
107
- * @param key - ResponsiveVoice API key
108
- * @param into - HTML content to inject the script into
109
- * @returns Modified HTML content with the ResponsiveVoice script
110
- */
111
- export function injectResponsivevoice(key: string, into: string): string {
112
- return inject(
113
- `<script src="https://code.responsivevoice.org/responsivevoice.js?key=${key}"></script>`,
114
- into,
115
- )
116
- }
117
-
118
- /**
119
- * Inject an arbitrary tag into HTML content.
120
- * @param element - new tag to be added
121
- * @param into - old index.html content
122
- * @param tag - Tag to inject at. Can be:
123
- * - boolean: true for after `<head>`, false for before `</head>` (backward compatibility)
124
- * - string: opening tag like `<body>` (injects after) or closing tag like `</body>` (injects before)
125
- * @returns - new index.html content
126
- */
127
- export function inject(
128
- element: string,
129
- into: string,
130
- tag: string | boolean = false,
131
- ): string {
132
- // Backward compatibility: handle boolean parameter
133
- if (typeof tag === 'boolean') {
134
- return tag
135
- ? into.replace('<head>', '<head>' + element)
136
- : into.replace('</head>', element + '</head>')
137
- }
138
-
139
- // New string-based tag injection
140
- if (tag.startsWith('</')) {
141
- // Closing tag: inject before it
142
- return into.replace(tag, element + tag)
143
- } else {
144
- // Opening tag: inject after it
145
- return into.replace(tag, tag + element)
146
- }
147
- }
148
-
149
- /**
150
- * Checks if a string is a valid URL (http, https, or file protocol).
151
- * @param uri - The string to check
152
- * @returns True if the string is a URL, false otherwise
153
- */
154
- export function isURL(uri: string): boolean {
155
- return (
156
- uri.startsWith('http://') ||
157
- uri.startsWith('https://') ||
158
- uri.startsWith('file://')
159
- )
160
- }
161
-
162
- /**
163
- * Creates an HTML iframe wrapper file for embedding LiaScript content.
164
- * @param tmpPath - Path to the temporary directory
165
- * @param filename - Name of the file to create
166
- * @param readme - Path to the README file
167
- * @param jsonLD - JSON-LD metadata to inject
168
- * @param style - Optional custom CSS styles for the iframe
169
- * @param index - Optional custom index.html filename
170
- * @returns Promise that resolves when the file is written
171
- */
172
- export async function iframe(
173
- tmpPath: string,
174
- filename: string,
175
- readme: string,
176
- jsonLD: string,
177
- style?: string,
178
- index?: string,
179
- ): Promise<string> {
180
- await writeFile(
181
- path.join(tmpPath, filename),
182
- prettify(`<!DOCTYPE html>
183
- <html style="height:100%; overflow: hidden">
184
- <head>
185
- ${jsonLD}
186
- </head>
187
- <body style="height:100%; margin: 0px">
188
-
189
- <iframe id="lia-container" src="" style="${
190
- style || 'border: 0px; width: 100%; height: 100%'
191
- }"></iframe>
192
-
193
- <script>
194
- let path = window.location.pathname.replace("start.html", "")
195
- let iframe = document.getElementById("lia-container")
196
-
197
- if (iframe) {
198
- const src = path + "${
199
- index || 'index.html'
200
- }?" + path + "${readme.replace('./', '')}"
201
- iframe.src = src
202
- }
203
- </script>
204
-
205
- </body>
206
- </html>
207
- `),
208
- )
209
-
210
- return 'ok'
211
- }
212
-
213
- /**
214
- * Creates a ZIP archive from a directory.
215
- * @param dir - Directory to archive
216
- * @param filename - Base filename for the ZIP archive (without .zip extension)
217
- * @returns Promise that resolves when the archive is finalized
218
- */
219
- export async function zip(dir: string, filename: string): Promise<void> {
220
- return new Promise((resolve, reject) => {
221
- const output = fs.createWriteStream(
222
- path.dirname(filename) + '/' + path.basename(filename + '.zip'),
223
- )
224
-
225
- const archive = archiver('zip', {
226
- zlib: { level: ZIP_COMPRESSION_LEVEL }, // Sets the compression level.
227
- })
228
-
229
- // listen for all archive data to be written
230
- // 'close' event is fired only when a file descriptor is involved
231
- output.on('close', function () {
232
- console.log(archive.pointer() + ' total bytes')
233
- console.log(
234
- 'archiver has been finalized and the output file descriptor has closed.',
235
- )
236
- resolve()
237
- })
238
-
239
- // This event is fired when the data source is drained no matter what was the data source.
240
- // It is not part of this library but rather from the NodeJS Stream API.
241
- // @see: https://nodejs.org/api/stream.html#stream_event_end
242
- output.on('end', function () {
243
- console.log('Data has been drained')
244
- })
245
-
246
- // good practice to catch warnings (ie stat failures and other non-blocking errors)
247
- archive.on('warning', function (err: any) {
248
- if (err.code === 'ENOENT') {
249
- console.warn('Archive warning:', err)
250
- } else {
251
- reject(err)
252
- }
253
- })
254
-
255
- // good practice to catch this error explicitly
256
- archive.on('error', function (err: Error) {
257
- reject(err)
258
- })
259
-
260
- // pipe archive data to the file
261
- archive.pipe(output)
262
-
263
- archive.directory(dir, false)
264
- archive.finalize()
265
- })
266
- }
267
-
268
- /**
269
- * Generates a random alphanumeric string.
270
- * Note: Uses Math.random() - not cryptographically secure.
271
- * @param length - Length of the random string to generate (default: 16)
272
- * @returns Random alphanumeric string
273
- */
274
- export function random(length: number = RANDOM_STRING_LENGTH): string {
275
- // Pick characters randomly
276
- let str = ''
277
- for (let i = 0; i < length; i++) {
278
- str += RANDOM_CHARS.charAt(Math.floor(Math.random() * RANDOM_CHARS.length))
279
- }
280
-
281
- return str
282
- }
283
-
284
- /**
285
- * Parses a raw GitHub URL and extracts repository information.
286
- * Supports URLs in format: raw.githubusercontent.com/org/repo/branch/path or raw.githubusercontent.com/org/repo/refs/heads/branch/path
287
- * @param raw_url - Raw GitHub URL to parse
288
- * @returns Object with repository information or null if URL doesn't match
289
- */
290
- export function getRepository(raw_url: string): {
291
- url: string
292
- branch: string
293
- path: string
294
- cmd: string
295
- } | null {
296
- const match = raw_url.match(
297
- /raw\.githubusercontent\.com\/([^\/]+)\/([^\/]+)\/(?:refs\/heads\/)?([^\/]+)\/(.*)/i,
298
- )
299
-
300
- if (match?.length === 5) {
301
- const [_, organization, repository, branch, filePath] = match
302
-
303
- const url = `https://github.com/${organization}/${repository}`
304
-
305
- return {
306
- url,
307
- branch,
308
- path: filePath,
309
- cmd: `git clone --branch ${branch} ${url} tmp`,
310
- }
311
- }
312
-
313
- return null
314
- }
315
-
316
- /**
317
- * Checks if a LICENSE file exists at the given base URL.
318
- * @param baseURL - Base URL to check for LICENSE file
319
- * @returns Promise that resolves to true if LICENSE exists, false otherwise
320
- */
321
- export async function checkLicense(baseURL: string): Promise<boolean> {
322
- const url = new URL('LICENSE', baseURL).href
323
-
324
- return await checkFileExists(url)
325
- }
326
-
327
- /**
328
- * Extracts the base URL by removing the last path segment.
329
- * @param url - URL to extract base from
330
- * @returns Base URL without the last path segment
331
- */
332
- export function baseURL(url: string): string {
333
- const pathSegments = url.split('/')
334
- pathSegments.pop()
335
- return pathSegments.join('/')
336
- }
337
-
338
- /**
339
- * Checks if a file exists at the given URL using a HEAD request.
340
- * @param fileUrl - URL of the file to check
341
- * @returns Promise that resolves to true if file exists (200 status), false otherwise
342
- */
343
- async function checkFileExists(fileUrl: string): Promise<boolean> {
344
- try {
345
- const response = await fetch(fileUrl, { method: 'HEAD' })
346
- return response.status === 200
347
- } catch (error) {
348
- console.warn(`Error checking if file exists: ${error}`)
349
- return false
350
- }
351
- }
352
-
353
- /**
354
- * Prettifies HTML content using beautification options.
355
- * @param html - HTML string to prettify
356
- * @returns Prettified HTML string
357
- */
358
- export function prettify(html: string): string {
359
- return beautify.html(html, BEAUTIFY_OPTIONS)
360
- }
package/src/export/ims.ts DELETED
@@ -1,191 +0,0 @@
1
- import * as helper from './helper'
2
- import * as RDF from './rdf'
3
- import * as COLOR from '../colorize'
4
-
5
- const path = require('path')
6
- const fs = require('fs-extra')
7
-
8
- export function help() {
9
- console.log('')
10
- console.log(COLOR.heading('IMS settings:'), '\n')
11
-
12
- COLOR.info(
13
- 'IMS (Instructional Management Systems) Content Package is an interoperable standard format for packaging learning content between different LMSes.',
14
- )
15
- console.log(
16
- '\nLearn more: https://www.imsglobal.org/content/packaging/index.html\n',
17
- )
18
-
19
- COLOR.command(
20
- null,
21
- '--ims-indexeddb',
22
- ' Use IndexedDB to store data persistently',
23
- )
24
-
25
- console.log('')
26
- console.log(COLOR.heading('WEB settings:'), '\n')
27
-
28
- COLOR.info(
29
- 'Pack the project into a self contained web project that can be hosted everywhere.',
30
- )
31
-
32
- console.log('')
33
-
34
- COLOR.command(
35
- null,
36
- '--web-iframe',
37
- ' Use an iframed version to hide the course URL.',
38
- )
39
- COLOR.command(
40
- null,
41
- '--web-indexeddb',
42
- ' This will allow to store data within the browser using indexeddb, you can optionally pass a unique key (by default one is generated randomly).',
43
- )
44
- COLOR.command(
45
- null,
46
- '--web-zip',
47
- ' By default the result is not zipped, you can change this with this parameter.',
48
- )
49
- }
50
-
51
- export interface ImsExportArguments {
52
- input: string
53
- readme: string
54
- output: string
55
- format: string
56
- path: string
57
- key?: string
58
- style?: string
59
- 'ims-indexeddb'?: boolean
60
- }
61
-
62
- export const format = 'ims'
63
-
64
- export async function exporter(argument: ImsExportArguments, json: any) {
65
- if (typeof json === 'string') {
66
- json = JSON.parse(json)
67
- }
68
-
69
- const dirname = helper.dirname()
70
-
71
- // make temp folder
72
- let tmp = await helper.tmpDir()
73
-
74
- let tmpPath = path.join(tmp, 'pro')
75
-
76
- // copy assets to temp
77
- await fs.copy(
78
- path.join(
79
- dirname,
80
- argument['ims-indexeddb'] ? './assets/indexeddb' : './assets/web',
81
- ),
82
- tmpPath,
83
- )
84
- await fs.copy(path.join(dirname, './assets/common'), tmpPath)
85
-
86
- let index = fs.readFileSync(path.join(tmpPath, 'index.html'), 'utf8')
87
-
88
- // change responsive key
89
- if (argument.key) {
90
- index = helper.injectResponsivevoice(argument.key, index)
91
- }
92
-
93
- try {
94
- await helper.writeFile(path.join(tmpPath, 'index.html'), index)
95
- } catch (e) {
96
- console.warn(e)
97
- return
98
- }
99
-
100
- await manifest(tmpPath, json.lia)
101
-
102
- // copy base path or readme-directory into temp
103
- await fs.copy(argument.path, tmpPath, {
104
- filter: helper.filterHidden(argument.path),
105
- })
106
-
107
- if (argument['ims-indexeddb']) {
108
- let newReadme = helper.random(20) + '.md'
109
-
110
- let old_ = path.join(tmpPath, argument.readme)
111
- let new_ = path.join(path.dirname(old_), newReadme)
112
-
113
- argument.readme = argument.readme.replace(
114
- path.basename(argument.readme),
115
- newReadme,
116
- )
117
-
118
- await fs.move(old_, new_)
119
- }
120
-
121
- const jsonLD = await RDF.script(argument, json)
122
-
123
- await helper.iframe(
124
- tmpPath,
125
- 'start.html',
126
- argument.readme,
127
- jsonLD,
128
- argument.style,
129
- )
130
-
131
- await helper.zip(tmpPath, argument.output)
132
- }
133
-
134
- async function manifest(tmpPath: any, meta: any) {
135
- let keywords = ''
136
-
137
- try {
138
- const tags = meta.definition.macro.tags
139
- .split(',')
140
- .map((e: string) => e.trim())
141
-
142
- for (let i = 0; i < tags.length; i++) {
143
- keywords += `<imsmd:langstring xml:lang="${meta.definition.language}">${tags[i]}</imsmd:langstring>`
144
- }
145
- } catch (e) {}
146
-
147
- await helper.writeFile(
148
- path.join(tmpPath, 'imsmanifest.xml'),
149
- `<manifest xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2"
150
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
151
- xsi:schemaLocation="http://www.imsglobal.org/xsd/imscp_v1p1 http://www.imsglobal.org/xsd/imscp_v1p1.xsd http://www.imsglobal.org/xsd/imsmd_v1p2 http://www.imsglobal.org/xsd/imsmd_v1p2.xsd "
152
- identifier="Manifest5-CEC3D3-3201-DF8E-8F42-3CEED12F4197" version="IMS CP 1.1.4">
153
- <metadata>
154
- <schema>IMS Content</schema>
155
- <schemaversion>1.1.4</schemaversion>
156
- <imsmd:lom>
157
- <imsmd:general>
158
- <imsmd:title>
159
- <imsmd:langstring xml:lang="${meta.definition.language}">${meta.str_title}</imsmd:langstring>
160
- </imsmd:title>
161
- <imsmd:language>${meta.definition.language}</imsmd:language>
162
- <imsmd:description>
163
- <imsmd:langstring xml:lang="${meta.definition.language}">${meta.comment}</imsmd:langstring>
164
- </imsmd:description>
165
- <imsmd:keyword>
166
- ${keywords}
167
- </imsmd:keyword>
168
- </imsmd:general>
169
- <imsmd:lifecycle>
170
- <imsmd:version>
171
- <imsmd:langstring xml:lang="${meta.definition.language}">${meta.definition.version}</imsmd:langstring>
172
- </imsmd:version>
173
- </imsmd:lifecycle>
174
- </imsmd:lom>
175
- </metadata>
176
- <organizations default="TOC1">
177
- <organization identifier="TOC1" structure="hierarchical">
178
- <title>All Lessons</title>
179
- <item identifier="ITEM1" identifierref="LIASCRIPT">
180
- <title>LiaScript - Course</title>
181
- </item>
182
- </organization>
183
- </organizations>
184
- <resources>
185
- <resource identifier="LIASCRIPT" type="webcontent" href="start.html">
186
- <file href="start.html" />
187
- </resource>
188
- </resources>
189
- </manifest>`,
190
- )
191
- }