@liascript/exporter 2.4.4--0.10.22 → 2.5.0--0.10.22
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 +153 -1
- package/dist/index.js +1 -13043
- package/package.json +10 -4
- package/src/export/helper.ts +4 -1
- package/src/export/ims.ts +2 -1
- package/src/export/pdf.ts +41 -26
- package/src/export/project.ts +474 -0
- package/src/export/scorm12.ts +2 -1
- package/src/export/scorm2004.ts +2 -1
- package/src/export/web.ts +8 -1
- package/src/index.ts +66 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liascript/exporter",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0--0.10.22",
|
|
4
4
|
"description": "A generic exporter for LiaScript",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"asset:scorm2004": "cd LiaScript && npm i && npm run build:scorm2004 && cp -r dist ../dist/assets/scorm2004",
|
|
29
29
|
"asset:indexeddb": "cd LiaScript && npm i && npm run build:indexeddb && cp -r dist ../dist/assets/indexeddb",
|
|
30
30
|
"asset:capacitor": "cd LiaScript && git checkout feat/capacitor && npm i && npm run build:android && cp -r dist ../dist/assets/capacitor && git checkout development",
|
|
31
|
-
"build": "npx parcel build --no-cache --no-source-maps
|
|
31
|
+
"build": "npx parcel build --no-cache --no-source-maps src/index.ts && npm run shebang",
|
|
32
32
|
"build:debug": "npx parcel build --target node --no-minify --log-level 5 src/index.ts",
|
|
33
33
|
"run:moodle": "cd docker/moodle && docker-compose up",
|
|
34
34
|
"run:ilias": "cd docker/ilias && docker-compose up",
|
|
@@ -49,9 +49,10 @@
|
|
|
49
49
|
"fs-extra": "^9.1.0",
|
|
50
50
|
"minimist": "^1.2.5",
|
|
51
51
|
"path": "^0.12.7",
|
|
52
|
-
"puppeteer": "^
|
|
52
|
+
"puppeteer": "^19.2.2",
|
|
53
53
|
"temp": "^0.9.4",
|
|
54
|
-
"xhr2": "^0.2.1"
|
|
54
|
+
"xhr2": "^0.2.1",
|
|
55
|
+
"yaml": "^2.1.3"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
58
|
"@parcel/transformer-elm": "^2.3.1",
|
|
@@ -63,5 +64,10 @@
|
|
|
63
64
|
},
|
|
64
65
|
"engines": {
|
|
65
66
|
"node": ">= 12"
|
|
67
|
+
},
|
|
68
|
+
"targets": {
|
|
69
|
+
"main": {
|
|
70
|
+
"optimize": true
|
|
71
|
+
}
|
|
66
72
|
}
|
|
67
73
|
}
|
package/src/export/helper.ts
CHANGED
|
@@ -58,6 +58,7 @@ export async function iframe(
|
|
|
58
58
|
tmpPath,
|
|
59
59
|
filename: string,
|
|
60
60
|
readme: string,
|
|
61
|
+
style?: string,
|
|
61
62
|
index?: string
|
|
62
63
|
) {
|
|
63
64
|
await writeFile(
|
|
@@ -69,7 +70,9 @@ export async function iframe(
|
|
|
69
70
|
</head>
|
|
70
71
|
<body style="height:100%">
|
|
71
72
|
|
|
72
|
-
<iframe id="lia-container" src="" style="
|
|
73
|
+
<iframe id="lia-container" src="" style="${
|
|
74
|
+
style || 'border: 0px; width: 100%; height: 100%'
|
|
75
|
+
}"></iframe>
|
|
73
76
|
|
|
74
77
|
<script>
|
|
75
78
|
let path = window.location.pathname.replace("start.html", "")
|
package/src/export/ims.ts
CHANGED
|
@@ -11,6 +11,7 @@ export async function exporter(
|
|
|
11
11
|
format: string
|
|
12
12
|
path: string
|
|
13
13
|
key?: string
|
|
14
|
+
style?: string
|
|
14
15
|
|
|
15
16
|
// special cases for IMS
|
|
16
17
|
'ims-indexeddb'?: boolean
|
|
@@ -64,7 +65,7 @@ export async function exporter(
|
|
|
64
65
|
await fs.move(old_, new_)
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
await helper.iframe(tmpPath, 'start.html', argument.readme)
|
|
68
|
+
await helper.iframe(tmpPath, 'start.html', argument.readme, argument.style)
|
|
68
69
|
|
|
69
70
|
helper.zip(tmpPath, argument.output)
|
|
70
71
|
}
|
package/src/export/pdf.ts
CHANGED
|
@@ -73,7 +73,9 @@ export async function exporter(
|
|
|
73
73
|
// remove timeout
|
|
74
74
|
timeout: 0,
|
|
75
75
|
})
|
|
76
|
-
} catch (e) {
|
|
76
|
+
} catch (e) {
|
|
77
|
+
console.warn('pdf generation failed:', e)
|
|
78
|
+
}
|
|
77
79
|
|
|
78
80
|
if (argument['pdf-stylesheet']) {
|
|
79
81
|
const href = path.resolve(__dirname + '/../', argument['pdf-stylesheet'])
|
|
@@ -127,29 +129,42 @@ export async function exporter(
|
|
|
127
129
|
|
|
128
130
|
console.warn(argument)
|
|
129
131
|
*/
|
|
130
|
-
if (!argument['pdf-preview'])
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
132
|
+
if (!argument['pdf-preview']) {
|
|
133
|
+
await sleep(argument['pdf-timeout'] || 30000)
|
|
134
|
+
await toPDF(argument, browser, page)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function sleep(ms: number) {
|
|
139
|
+
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function toPDF(argument: any, browser: any, page: any) {
|
|
143
|
+
try {
|
|
144
|
+
await page.emulateMediaType('screen')
|
|
145
|
+
await page.pdf({
|
|
146
|
+
path: argument.output + '.pdf',
|
|
147
|
+
format: argument['pdf-format'] || 'a4',
|
|
148
|
+
printBackground: argument['pdf-printBackground'] || true,
|
|
149
|
+
displayHeaderFooter: argument['pdf-displayHeaderFooter'] || false,
|
|
150
|
+
margin: {
|
|
151
|
+
top: argument['pdf-margin-top'] || 80,
|
|
152
|
+
bottom: argument['pdf-margin-bottom'] || 80,
|
|
153
|
+
left: argument['pdf-margin-left'] || 30,
|
|
154
|
+
right: argument['pdf-margin-right'] || 30,
|
|
155
|
+
},
|
|
156
|
+
scale: argument['pdf-scale'] || 1,
|
|
157
|
+
headerTemplate: argument['pdf-headerTemplate'],
|
|
158
|
+
footerTemplate: argument['pdf-footerTemplate'] || '',
|
|
159
|
+
landscape: argument['pdf-landscape'] || false,
|
|
160
|
+
width: argument['pdf-width'] || '',
|
|
161
|
+
height: argument['pdf-height'] || '',
|
|
162
|
+
//preferCSSPageSize: argument['pdf-preferCSSPageSize'] || '',
|
|
163
|
+
omitBackground: argument['pdf-omitBackground'] || false,
|
|
164
|
+
})
|
|
165
|
+
} catch (e) {
|
|
166
|
+
console.warn('failed to print to pdf', e)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
await browser.close()
|
|
155
170
|
}
|
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import * as helper from './helper'
|
|
2
|
+
import * as PDF from './pdf'
|
|
3
|
+
|
|
4
|
+
const fs = require('fs-extra')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
|
|
7
|
+
var Categories: Set<string> = new Set([])
|
|
8
|
+
|
|
9
|
+
export function getNext(collection: any): string | null {
|
|
10
|
+
if (collection['collection']) {
|
|
11
|
+
collection = collection['collection']
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (collection['url'] && collection['data'] === undefined) {
|
|
15
|
+
return collection['url']
|
|
16
|
+
} else {
|
|
17
|
+
for (let i = 0; i < collection.length; i++) {
|
|
18
|
+
let course = collection[i]
|
|
19
|
+
|
|
20
|
+
if (course.collection) {
|
|
21
|
+
let url = getNext(course)
|
|
22
|
+
|
|
23
|
+
if (url) {
|
|
24
|
+
return url
|
|
25
|
+
}
|
|
26
|
+
} else if (course.url && course.data === undefined) {
|
|
27
|
+
return course.url
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function storeNext(collection: any, data: any) {
|
|
35
|
+
if (collection['collection']) {
|
|
36
|
+
collection = collection['collection']
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < collection.length; i++) {
|
|
40
|
+
if (collection[i].collection) {
|
|
41
|
+
for (let j = 0; j < collection[i].collection.length; j++) {
|
|
42
|
+
if (
|
|
43
|
+
collection[i].collection[j].url &&
|
|
44
|
+
collection[i].collection[j].data === undefined
|
|
45
|
+
) {
|
|
46
|
+
collection[i].collection[j].data = data
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
} else if (collection[i].url && collection[i].data === undefined) {
|
|
51
|
+
collection[i].data = data
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function exporter(
|
|
60
|
+
argument: {
|
|
61
|
+
input: string
|
|
62
|
+
readme: string
|
|
63
|
+
output: string
|
|
64
|
+
format: string
|
|
65
|
+
path: string
|
|
66
|
+
key?: string
|
|
67
|
+
style?: string
|
|
68
|
+
|
|
69
|
+
// special project settings
|
|
70
|
+
'project-no-meta'?: boolean
|
|
71
|
+
'project-no-categories'?: boolean
|
|
72
|
+
'project-category-blur'?: boolean
|
|
73
|
+
'project-generate-pdf'?: boolean
|
|
74
|
+
|
|
75
|
+
// allow to pass pdf settings
|
|
76
|
+
'pdf-scale'?: number
|
|
77
|
+
'pdf-displayHeaderFooter'?: string
|
|
78
|
+
'pdf-headerTemplate'?: string
|
|
79
|
+
'pdf-footerTemplate'?: string
|
|
80
|
+
'pdf-printBackground'?: boolean
|
|
81
|
+
'pdf-landscape'?: boolean
|
|
82
|
+
'pdf-format'?: string
|
|
83
|
+
'pdf-width'?: string | number
|
|
84
|
+
'pdf-height'?: string | number
|
|
85
|
+
'pdf-margin-top'?: string | number
|
|
86
|
+
'pdf-margin-bottom'?: string | number
|
|
87
|
+
'pdf-margin-right'?: string | number
|
|
88
|
+
'pdf-margin-left'?: string | number
|
|
89
|
+
'pdf-preferCSSPageSize'?: boolean
|
|
90
|
+
'pdf-omitBackground'?: boolean
|
|
91
|
+
'pdf-timeout'?: number
|
|
92
|
+
|
|
93
|
+
'pdf-stylesheet'?: string
|
|
94
|
+
'pdf-theme'?: string
|
|
95
|
+
},
|
|
96
|
+
json
|
|
97
|
+
) {
|
|
98
|
+
// make temp folder
|
|
99
|
+
|
|
100
|
+
let cards = ''
|
|
101
|
+
const output = argument.output
|
|
102
|
+
|
|
103
|
+
for (let i = 0; i < json.collection.length; i++) {
|
|
104
|
+
let course = json.collection[i]
|
|
105
|
+
|
|
106
|
+
if (course.collection) {
|
|
107
|
+
let subCards = ''
|
|
108
|
+
|
|
109
|
+
for (let j = 0; j < course.collection.length; j++) {
|
|
110
|
+
subCards += `<div class='col-sm-6 col-md-4 col-lg-3 ${
|
|
111
|
+
course.grid ? 'mb-3' : ''
|
|
112
|
+
}'>
|
|
113
|
+
${await toCard(argument, course.collection[j], true)}
|
|
114
|
+
</div>`
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
cards += `
|
|
118
|
+
<div class="col-12">
|
|
119
|
+
<div class="card">
|
|
120
|
+
<div class="card-header">
|
|
121
|
+
${course.title}
|
|
122
|
+
</div>
|
|
123
|
+
<div class="card-body">
|
|
124
|
+
<p class="card-text">${course.comment}</p>
|
|
125
|
+
<div ${
|
|
126
|
+
course.grid
|
|
127
|
+
? 'class="row"'
|
|
128
|
+
: 'style="display: flex; scroll-snap-type: x mandatory; overflow-x: auto; overflow-y: hidden; padding-bottom: 10px"'
|
|
129
|
+
}>
|
|
130
|
+
${subCards}
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</div>`
|
|
135
|
+
} else if (course.html) {
|
|
136
|
+
cards += "<div class='col-12'>" + course.html + '</div>'
|
|
137
|
+
} else {
|
|
138
|
+
cards += "<div class='col'>" + (await toCard(argument, course)) + '</div>'
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const background = json.logo
|
|
143
|
+
? `style="background-size: cover; background-image: url('${json.logo}'); background-position: center center; background-repeat: no-repeat;"`
|
|
144
|
+
: ''
|
|
145
|
+
|
|
146
|
+
let options = ''
|
|
147
|
+
|
|
148
|
+
if (Categories.size > 0 && !argument['project-no-categories']) {
|
|
149
|
+
const opt = [...Categories].sort()
|
|
150
|
+
|
|
151
|
+
for (let i = 0; i < opt.length; i++) {
|
|
152
|
+
options += `<option value="${opt[i]}">${opt[i]}</option>`
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
options =
|
|
156
|
+
`<select class="form-select" aria-label="Default select example" onchange="window.blur(this.value)">
|
|
157
|
+
<option value="" selected>All categories</option>` +
|
|
158
|
+
options +
|
|
159
|
+
'</select>'
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let title = json.title || 'LiaScript Course Index'
|
|
163
|
+
|
|
164
|
+
if (json.title) {
|
|
165
|
+
title = title.replace(/<[^>]+>/g, '')
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const html = `<!DOCTYPE html>
|
|
169
|
+
<html>
|
|
170
|
+
<head>
|
|
171
|
+
<title>${title}</title>
|
|
172
|
+
|
|
173
|
+
${
|
|
174
|
+
json.icon
|
|
175
|
+
? '<link rel="icon" type="image/x-icon" href="' + json.icon + '">'
|
|
176
|
+
: ''
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
${argument['project-no-meta'] ? '' : meta(json)}
|
|
180
|
+
|
|
181
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
|
182
|
+
|
|
183
|
+
<script>
|
|
184
|
+
window.blur = function(category) {
|
|
185
|
+
const cards = document.querySelectorAll('div[data-category]')
|
|
186
|
+
|
|
187
|
+
for (card of cards) {
|
|
188
|
+
if(card.dataset.category.includes(category)) {
|
|
189
|
+
${
|
|
190
|
+
argument['project-category-blur']
|
|
191
|
+
? 'card.style.filter = ""'
|
|
192
|
+
: 'card.parentNode.style.display = "block"'
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
${
|
|
196
|
+
argument['project-category-blur']
|
|
197
|
+
? 'card.style.filter = "blur(1px) opacity(35%)"'
|
|
198
|
+
: 'card.parentNode.style.display = "none"'
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
</script>
|
|
204
|
+
</head>
|
|
205
|
+
<body>
|
|
206
|
+
|
|
207
|
+
<main>
|
|
208
|
+
<div class="container-fluid" ${background} >
|
|
209
|
+
<section class="py-5 text-center container">
|
|
210
|
+
<div class="row py-lg-5">
|
|
211
|
+
<div class="col-lg-6 col-md-8 mx-auto">
|
|
212
|
+
<h1 class="fw-light">${
|
|
213
|
+
json.title || 'LiaScript Course Index'
|
|
214
|
+
}</h1>
|
|
215
|
+
<p class="lead text-muted">${json.comment || ''}</p>
|
|
216
|
+
|
|
217
|
+
${options}
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
</section>
|
|
221
|
+
</div>
|
|
222
|
+
|
|
223
|
+
<div class="album py-5 bg-light">
|
|
224
|
+
<div class="container">
|
|
225
|
+
<div class="row row-cols-1 row-cols-sm-1 row-cols-md-2 row-cols-xl-3 g-3">
|
|
226
|
+
${cards}
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
|
|
231
|
+
</main>
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
<footer class="text-muted py-3">
|
|
235
|
+
<div class="container">
|
|
236
|
+
<p class="float-end">
|
|
237
|
+
<a href="#">Back to top</a>
|
|
238
|
+
</p>
|
|
239
|
+
<p>${
|
|
240
|
+
json.footer ||
|
|
241
|
+
'<a href="https://liascript.github.io" target="_blank">LiaScript</a> is a Markdown dialect made for education. For more information checkout out <a href="https://www.youtube.com/channel/UCyiTe2GkW_u05HSdvUblGYg" target="_blank">YouTube-Channel</a> or follow us on <a href="https://twitter.com/LiaScript" target="_blank">Twitter</a>.'
|
|
242
|
+
}</p>
|
|
243
|
+
</div>
|
|
244
|
+
</footer>
|
|
245
|
+
|
|
246
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
|
247
|
+
</body>
|
|
248
|
+
</html>
|
|
249
|
+
`
|
|
250
|
+
|
|
251
|
+
await helper.writeFile(output + '.html', html)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function moveFile(oldPath, newPath) {
|
|
255
|
+
// 1. Create the destination directory if it does not exist
|
|
256
|
+
// Set the `recursive` option to `true` to create all the subdirectories
|
|
257
|
+
await fs.mkdir(path.dirname(newPath), { recursive: true })
|
|
258
|
+
// 2. Rename the file (move it to the new directory)
|
|
259
|
+
// Return the promise
|
|
260
|
+
return fs.rename(oldPath, newPath)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function cleanHTML(html: string) {
|
|
264
|
+
return html.replace(/<[^>]+>/g, '')
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function meta(json: any) {
|
|
268
|
+
const title =
|
|
269
|
+
json.meta.title || cleanHTML(json.title) || 'LiaScript Course Index'
|
|
270
|
+
|
|
271
|
+
const description = json.meta.description || cleanHTML(json.comment)
|
|
272
|
+
|
|
273
|
+
const image = json.meta.image || json.logo
|
|
274
|
+
|
|
275
|
+
return `<meta property="og:type" content="website">
|
|
276
|
+
<meta property="og:title" content="${title}">
|
|
277
|
+
<meta property="og:description" content="${description}">
|
|
278
|
+
<meta property="og:image" content="${image}">
|
|
279
|
+
|
|
280
|
+
<meta name="twitter:title" content="${title}">
|
|
281
|
+
<meta name="twitter:description" content="${description}">
|
|
282
|
+
<meta name="twitter:image" content="${image}">
|
|
283
|
+
`
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function overwrite(check, defaultsTo) {
|
|
287
|
+
return check === null ? null : check || defaultsTo
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function hash(url: string) {
|
|
291
|
+
const value = url
|
|
292
|
+
.split('')
|
|
293
|
+
.map((v) => v.charCodeAt(0))
|
|
294
|
+
.reduce((a, v) => (a + ((a << 7) + (a << 3))) ^ v)
|
|
295
|
+
.toString(16)
|
|
296
|
+
|
|
297
|
+
return value.startsWith('-') ? '0' + value.slice(1) : value
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async function toCard(argument: any, course: any, small: boolean = false) {
|
|
301
|
+
let tags
|
|
302
|
+
try {
|
|
303
|
+
tags = course.data.definition.macro.tags
|
|
304
|
+
.split(',')
|
|
305
|
+
.map((e: string) => e.trim())
|
|
306
|
+
} catch (e) {
|
|
307
|
+
tags = []
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const tagList = course.tags || tags
|
|
311
|
+
for (let i = 0; i < tagList.length; i++) {
|
|
312
|
+
Categories.add(tagList[i].toLowerCase())
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
let downloads = {}
|
|
316
|
+
if (argument['project-generate-pdf']) {
|
|
317
|
+
argument.input = course.data.readme
|
|
318
|
+
argument.output = hash(course.data.readme)
|
|
319
|
+
const file = argument.output + '.pdf'
|
|
320
|
+
|
|
321
|
+
console.log('generate pdf of', argument.input, ' -> ', file)
|
|
322
|
+
|
|
323
|
+
await PDF.exporter(argument, {})
|
|
324
|
+
|
|
325
|
+
if (fs.existsSync(file)) {
|
|
326
|
+
moveFile(file, 'assets/pdf/' + file)
|
|
327
|
+
downloads['pdf'] = 'assets/pdf/' + file
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return card(
|
|
332
|
+
small,
|
|
333
|
+
course.data.readme,
|
|
334
|
+
overwrite(course.title, course.data.str_title),
|
|
335
|
+
overwrite(course.comment, course.data.comment),
|
|
336
|
+
tagList,
|
|
337
|
+
downloads,
|
|
338
|
+
overwrite(course.logo, course.data.definition.logo)
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function card(
|
|
343
|
+
small: boolean,
|
|
344
|
+
url: string,
|
|
345
|
+
title: string,
|
|
346
|
+
comment: string,
|
|
347
|
+
tags: string[],
|
|
348
|
+
download: {
|
|
349
|
+
pdf?: string
|
|
350
|
+
scorm12?: string
|
|
351
|
+
scorm2004?: string
|
|
352
|
+
ims?: string
|
|
353
|
+
apk?: string
|
|
354
|
+
},
|
|
355
|
+
img_url?: string
|
|
356
|
+
) {
|
|
357
|
+
let image = ''
|
|
358
|
+
|
|
359
|
+
if (img_url) {
|
|
360
|
+
if (!(img_url.startsWith('http:') || img_url.startsWith('https:'))) {
|
|
361
|
+
const fullImageUrl = new URL(img_url, url)
|
|
362
|
+
|
|
363
|
+
img_url = fullImageUrl.toString()
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
image =
|
|
367
|
+
//`<img src="${img_url}" class="card-img-top" alt="">`
|
|
368
|
+
`<div class="card-img-top" style="background-size: cover; height: 175px; background-image: url('${img_url}'); background-position: center center; background-repeat: no-repeat;"></div>`
|
|
369
|
+
}
|
|
370
|
+
/*
|
|
371
|
+
else {
|
|
372
|
+
image = `<svg class="bd-placeholder-img card-img-top" width="100%" height="175" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Placeholder: Thumbnail" preserveAspectRatio="xMidYMid slice" focusable="false"><title>Placeholder</title><rect width="100%" height="100%" fill="${stringToColor(
|
|
373
|
+
title
|
|
374
|
+
)}"></rect></svg>`
|
|
375
|
+
}
|
|
376
|
+
*/
|
|
377
|
+
|
|
378
|
+
let tag_list = ''
|
|
379
|
+
|
|
380
|
+
if (tags.length > 0) {
|
|
381
|
+
for (let i = 0; i < tags.length; i++) {
|
|
382
|
+
tag_list += `<span class="badge rounded-pill bg-light text-dark">${tags[i]}</span>`
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
tag_list = `<div class="d-flex align-items-center">${tag_list}</div>`
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (small && comment) {
|
|
389
|
+
comment = '<small>' + comment + '</small>'
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
let footer = ''
|
|
393
|
+
|
|
394
|
+
if (Object.keys(download).length > 0) {
|
|
395
|
+
footer = `<div class="card-footer">
|
|
396
|
+
<div class="dropdown">
|
|
397
|
+
<a class="btn btn-secondary btn-sm dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-bs-toggle="dropdown" aria-expanded="false">
|
|
398
|
+
Download as ...
|
|
399
|
+
</a>
|
|
400
|
+
|
|
401
|
+
<ul class="dropdown-menu" aria-labelledby="dropdownMenuLink" style="">
|
|
402
|
+
${
|
|
403
|
+
download.pdf
|
|
404
|
+
? '<li><a class="dropdown-item btn-sm" target="_blank" href="' +
|
|
405
|
+
download.pdf +
|
|
406
|
+
'">PDF</a></li>'
|
|
407
|
+
: ''
|
|
408
|
+
}
|
|
409
|
+
${
|
|
410
|
+
download.scorm12
|
|
411
|
+
? '<li><a class="dropdown-item btn-sm" href="' +
|
|
412
|
+
download.scorm12 +
|
|
413
|
+
'">SCORM 1.2</a></li>'
|
|
414
|
+
: ''
|
|
415
|
+
}
|
|
416
|
+
${
|
|
417
|
+
download.scorm2004
|
|
418
|
+
? '<li><a class="dropdown-item btn-sm" href="' +
|
|
419
|
+
download.scorm2004 +
|
|
420
|
+
'">SCORM 2004</a></li>'
|
|
421
|
+
: ''
|
|
422
|
+
}
|
|
423
|
+
${
|
|
424
|
+
download.ims
|
|
425
|
+
? '<li><a class="dropdown-item btn-sm" href="' +
|
|
426
|
+
download.ims +
|
|
427
|
+
'">IMS</a></li>'
|
|
428
|
+
: ''
|
|
429
|
+
}
|
|
430
|
+
${
|
|
431
|
+
download.apk
|
|
432
|
+
? '<li><a class="dropdown-item btn-sm" href="' +
|
|
433
|
+
download.apk +
|
|
434
|
+
'">Android APK</a></li>'
|
|
435
|
+
: ''
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
</ul>
|
|
442
|
+
</div>
|
|
443
|
+
</div>
|
|
444
|
+
`
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return `<div class="card shadow-sm m-1" style="height: 100%" data-category="${tags
|
|
448
|
+
.map((e) => e.toLowerCase())
|
|
449
|
+
.join('|')}">
|
|
450
|
+
${image}
|
|
451
|
+
<div class="card-body" style="transform: rotate(0);">
|
|
452
|
+
<a href="https://liascript.github.io/course/?${url}" target="_blank" class="link-dark stretched-link">
|
|
453
|
+
<h${small ? 6 : 5} class="card-title">${title}</h${small ? 6 : 5}>
|
|
454
|
+
</a>
|
|
455
|
+
<p class="card-text">${comment}</p>
|
|
456
|
+
${tag_list}
|
|
457
|
+
</div>
|
|
458
|
+
${footer}
|
|
459
|
+
</div>
|
|
460
|
+
`
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function stringToColor(str: string) {
|
|
464
|
+
var hash = 0
|
|
465
|
+
for (var i = 0; i < str.length; i++) {
|
|
466
|
+
hash = str.charCodeAt(i) + ((hash << 5) - hash)
|
|
467
|
+
}
|
|
468
|
+
var color = '#'
|
|
469
|
+
for (var i = 0; i < 3; i++) {
|
|
470
|
+
var value = (hash >> (i * 8)) & 0xff
|
|
471
|
+
color += ('00' + value.toString(16)).substr(-2)
|
|
472
|
+
}
|
|
473
|
+
return color
|
|
474
|
+
}
|
package/src/export/scorm12.ts
CHANGED
|
@@ -12,6 +12,7 @@ export async function exporter(
|
|
|
12
12
|
format: string
|
|
13
13
|
path: string
|
|
14
14
|
key?: string
|
|
15
|
+
style?: string
|
|
15
16
|
|
|
16
17
|
// special cases for SCORM
|
|
17
18
|
'scorm-organization'?: string
|
|
@@ -49,7 +50,7 @@ export async function exporter(
|
|
|
49
50
|
)
|
|
50
51
|
|
|
51
52
|
if (argument['scorm-iframe']) {
|
|
52
|
-
await helper.iframe(tmpPath, 'start.html', argument.readme)
|
|
53
|
+
await helper.iframe(tmpPath, 'start.html', argument.readme, argument.style)
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
try {
|
package/src/export/scorm2004.ts
CHANGED
|
@@ -12,6 +12,7 @@ export async function exporter(
|
|
|
12
12
|
format: string
|
|
13
13
|
path: string
|
|
14
14
|
key?: string
|
|
15
|
+
style?: string
|
|
15
16
|
|
|
16
17
|
// special cases for SCORM
|
|
17
18
|
'scorm-organization'?: string
|
|
@@ -49,7 +50,7 @@ export async function exporter(
|
|
|
49
50
|
)
|
|
50
51
|
|
|
51
52
|
if (argument['scorm-iframe']) {
|
|
52
|
-
await helper.iframe(tmpPath, 'start.html', argument.readme)
|
|
53
|
+
await helper.iframe(tmpPath, 'start.html', argument.readme, argument.style)
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
try {
|
package/src/export/web.ts
CHANGED
|
@@ -11,6 +11,7 @@ export async function exporter(
|
|
|
11
11
|
format: string
|
|
12
12
|
path: string
|
|
13
13
|
key?: string
|
|
14
|
+
style?: string
|
|
14
15
|
|
|
15
16
|
'web-iframe'?: boolean
|
|
16
17
|
'web-indexeddb'?: boolean
|
|
@@ -110,7 +111,13 @@ export async function exporter(
|
|
|
110
111
|
try {
|
|
111
112
|
if (argument['web-iframe']) {
|
|
112
113
|
await helper.writeFile(path.join(tmpPath, 'start.html'), index)
|
|
113
|
-
await helper.iframe(
|
|
114
|
+
await helper.iframe(
|
|
115
|
+
tmpPath,
|
|
116
|
+
'index.html',
|
|
117
|
+
argument.readme,
|
|
118
|
+
argument.style,
|
|
119
|
+
'start.html'
|
|
120
|
+
)
|
|
114
121
|
} else {
|
|
115
122
|
await helper.writeFile(path.join(tmpPath, 'index.html'), index)
|
|
116
123
|
}
|