@jseeio/jsee 0.3.0 → 0.3.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.1
4
+ - [x] Add `download` method to jsee object
5
+ - [x] Add `bin` folder with `cmd.js` for easier project building
6
+
3
7
  ## 0.2.9
4
8
  - [x] Add examples
5
9
  - [x] Add imports
package/bin/jsee ADDED
@@ -0,0 +1,575 @@
1
+ #!/usr/bin/env node
2
+
3
+ const minimist = require('minimist')
4
+ const fs = require('fs')
5
+ const path = require('path')
6
+ const jsdoc2md = require('jsdoc-to-markdown')
7
+ const showdown = require('showdown')
8
+ const converter = new showdown.Converter()
9
+
10
+ // left padding of multiple lines
11
+ function pad (str, len, start=0) {
12
+ return str.split('\n').map((s, i) => i >= start ? ' '.repeat(len) + s : s).join('\n')
13
+ }
14
+
15
+ function depad (str, len) {
16
+ return str.split('\n').map(s => s.slice(len)).join('\n')
17
+ }
18
+
19
+ const argv = minimist(process.argv.slice(2), {
20
+ alias: {
21
+ inputs: 'i',
22
+ outputs: 'o',
23
+ description: 'd',
24
+ ga: 'g',
25
+ port: 'p',
26
+ version: 'v',
27
+ fetch: 'f',
28
+ },
29
+ default: {
30
+ inputs: '',
31
+ fetch: false,
32
+ port: 3000,
33
+ version: 'latest'
34
+ }
35
+ })
36
+ // Set argv.inputs to the first non-option argument if it exists
37
+ if (argv._.length > 0) {
38
+ argv.inputs = argv._[0]
39
+ }
40
+
41
+ console.log(argv)
42
+
43
+ gen(argv)
44
+
45
+ // Adding async here breaks express. TODO: investigate
46
+ async function gen (argv, returnHtml=false) {
47
+ let cwd = process.cwd()
48
+ let inputs = argv.inputs
49
+ let outputs = argv.outputs
50
+ let imports = argv.imports
51
+ let description = argv.description
52
+ let ga = argv.ga
53
+ let schema
54
+ let descriptionHtml = ''
55
+ let jsdocMarkdown = ''
56
+
57
+ // if inputs is a string with js file names, split it into an array
58
+ if (typeof inputs === 'string') {
59
+ if (inputs.includes('.js')) {
60
+ inputs = inputs.split(',')
61
+ }
62
+ }
63
+
64
+ // if outputs is a string with js file names, split it into an array
65
+ if (typeof outputs === 'string') {
66
+ outputs = outputs.split(',')
67
+ }
68
+
69
+ if (inputs.length === 0) {
70
+ console.error('No inputs provided')
71
+ process.exit(1)
72
+ } else if ((inputs.length === 1) && (inputs[0].includes('.json'))) {
73
+ // Input is json schema
74
+ // Curren working directory if not provided
75
+ schema = require(path.join(cwd, inputs[0]))
76
+ } else {
77
+ // Array of js files
78
+ // Generate schema
79
+ let jsdocData = jsdoc2md.getTemplateDataSync({ files: inputs.map(f => path.join(cwd, f)) })
80
+ schema = genSchema(jsdocData)
81
+ // jsdocMarkdown = jsdoc2md.renderSync({
82
+ // data: jsdocData,
83
+ // 'param-list-format': 'list',
84
+ // })
85
+ }
86
+
87
+ // console.log('Schema:', schema)
88
+
89
+ // Generate description block
90
+ if (description) {
91
+ const descriptionMd = fs.readFileSync(path.join(cwd, description), 'utf8')
92
+ descriptionHtml = converter.makeHtml(descriptionMd)
93
+ }
94
+
95
+ descriptionHtml += genHtmlFromSchema(schema)
96
+
97
+ // Generate google analytics code
98
+ let gaHtml = ga ? `
99
+ <script id="ga-src" async src="https://www.googletagmanager.com/gtag/js?id=${blocks.ga}"></script>
100
+ <script id="ga-body">
101
+ window['ga-disable-${ga}'] = window.doNotTrack === "1" || navigator.doNotTrack === "1" || navigator.doNotTrack === "yes" || navigator.msDoNotTrack === "1";
102
+ window.dataLayer = window.dataLayer || [];
103
+ function gtag(){dataLayer.push(arguments);}
104
+ gtag('js', new Date());
105
+ gtag('config', '${ga}');
106
+ </script>
107
+ ` : ``
108
+
109
+ // Generate jsee code
110
+ let jseeHtml = ''
111
+ let hiddenElementHtml = ''
112
+ if (argv.fetch) {
113
+ // Fetch the jsee runtime from the CDN or local server
114
+ if (argv.version === 'latest') {
115
+ const jseeCode = fs.readFileSync(path.join(__dirname, '..', 'dist', 'jsee.runtime.js'), 'utf8')
116
+ jseeHtml = `<script>${jseeCode}</script>`
117
+ } else {
118
+ // Pre-fetch the jsee runtime from the CDN https://cdn.jsdelivr.net/npm/@jseeio/jsee@${argv.version}/dist/jsee.runtime.js
119
+ let jseeCode = await fetch(`https://cdn.jsdelivr.net/npm/@jseeio/jsee@${argv.version}/dist/jsee.runtime.js`)
120
+ jseeCode = await jseeCode.text()
121
+ jseeHtml = `<script>${jseeCode}</script>`
122
+ }
123
+ // Fetch model files and store them in hidden elements
124
+ hiddenElementHtml += '<div id="hidden-storage" style="display: none;">'
125
+ for (let m of schema.model) {
126
+ if (m.url) {
127
+ const modelCode = fs.readFileSync(path.join(cwd, m.url), 'utf8')
128
+ hiddenElementHtml += `<script type="text/plain" style="display: none;" data-src="${m.url}">${modelCode}</script>`
129
+ }
130
+ if (m.imports) {
131
+ for (let i of m.imports) {
132
+ const importUrl = i.includes('.js') ? i : `https://cdn.jsdelivr.net/npm/${i}`
133
+ let importCode = await fetch(importUrl)
134
+ importCode = await importCode.text()
135
+ hiddenElementHtml += `<script type="text/plain" style="display: none;" data-src="${importUrl}">${importCode}</script>`
136
+ }
137
+ }
138
+ }
139
+ hiddenElementHtml += '</div>'
140
+ } else {
141
+ jseeHtml = outputs
142
+ ? `<script src="https://cdn.jsdelivr.net/npm/@jseeio/jsee@${argv.version}/dist/jsee.runtime.js"></script>`
143
+ : `<script src="http://localhost:${argv.port}/dist/jsee.runtime.js"></script>`
144
+ }
145
+
146
+ const html = template(schema, {
147
+ descriptionHtml: pad(descriptionHtml, 8, 1),
148
+ gaHtml: pad(gaHtml, 2, 1),
149
+ jseeHtml: jseeHtml,
150
+ hiddenElementHtml: hiddenElementHtml
151
+ })
152
+
153
+ if (returnHtml) {
154
+ // Return the html as a string
155
+ return html
156
+ } else if (outputs) {
157
+ // Store the html in the output file
158
+ for (let o of outputs) {
159
+ if (o === 'stdout') {
160
+ console.log(html)
161
+ } else if (o.includes('.html')) {
162
+ fs.writeFileSync(path.join(cwd, o), html)
163
+ } else if (o.includes('.json')) {
164
+ fs.writeFileSync(path.join(cwd, o), JSON.stringify(schema, null, 2))
165
+ } else if (o.includes('.md')) {
166
+ fs.writeFileSync(path.join(cwd, o), genMarkdownFromSchema(schema))
167
+ } else {
168
+ console.error('Invalid output file:', o)
169
+ }
170
+ }
171
+ fs.writeFileSync(path.join(cwd, outputs[0]), html)
172
+ } else {
173
+ // Serve the html
174
+ const express = require('express')
175
+ const app = express()
176
+ app.get('/', async (req, res) => {
177
+ console.log('Serving the html')
178
+ res.send(await gen(argv, true))
179
+ })
180
+ app.get('/dist/jsee.runtime.js', (req, res) => {
181
+ console.log('Serving jsee.runtime.js from:', path.join(__dirname, '..', 'dist', 'jsee.runtime.js'))
182
+ res.sendFile(path.join(__dirname, '..', 'dist', 'jsee.runtime.js'))
183
+ })
184
+ app.use(express.static(cwd))
185
+ app.listen(argv.port, () => {
186
+ console.log(`JSEE app is running at http://localhost:${argv.port}`)
187
+ })
188
+ }
189
+ }
190
+
191
+ function genSchema (jsdocData) {
192
+ let schema = {
193
+ model: [],
194
+ inputs: [],
195
+ outputs: [],
196
+ }
197
+ for (let d of jsdocData) {
198
+ const model = {
199
+ name: d.name ? d.name : d.meta.filename.split('.')[0],
200
+ description: d.description ? d.description : '',
201
+ type: d.kind,
202
+ container: 'args',
203
+ url: path.relative(process.cwd(), path.join(d.meta.path, d.meta.filename)),
204
+ worker: false
205
+ }
206
+ if (d.requires) {
207
+ model.imports = d.requires.map(r => r.replace('module:', ''))
208
+ }
209
+ if (d.params) {
210
+ // Check if all params have the same name before '.'
211
+ const names = new Set(d.params.map(p => p.name.split('.')[0]))
212
+ if ((d.params.length > 1) && (names.size === 1)) {
213
+ // Object
214
+ model.container = 'object'
215
+ d.params.slice(1).forEach(p => {
216
+ const inp = {
217
+ name: p.name.split('.')[1],
218
+ type: p.type.names[0],
219
+ description: p.description,
220
+ }
221
+ if (p.defaultvalue) {
222
+ inp.default = p.defaultvalue
223
+ }
224
+ schema.inputs.push(inp)
225
+ })
226
+ } else {
227
+ // Array
228
+ model.container = 'args'
229
+ d.params.forEach(p => {
230
+ const inp = {
231
+ name: p.name,
232
+ type: p.type.names[0],
233
+ description: p.description,
234
+ }
235
+ if (p.defaultvalue) {
236
+ inp.default = p.defaultvalue
237
+ }
238
+ schema.inputs.push(inp)
239
+ })
240
+ }
241
+ }
242
+ if (d.returns) {
243
+ d.returns.forEach(r => {
244
+ r.name = r.name ? r.name : r.description.split('-')[0].trim()
245
+ r.description = r.description.split('-').slice(1).join('-').trim()
246
+ })
247
+ const names = new Set(d.returns.map(r => r.name.split('.')[0]))
248
+ if ((d.returns.length > 1) && (names.size === 1)) {
249
+ // Object
250
+ d.returns.slice(1).forEach(p => {
251
+ const out = {
252
+ name: p.name.split('.')[1],
253
+ type: p.type.names[0],
254
+ description: p.description,
255
+ }
256
+ schema.outputs.push(out)
257
+ })
258
+ } else {
259
+ // Array
260
+ d.returns.forEach(p => {
261
+ const out = {
262
+ name: p.name,
263
+ type: p.type.names[0],
264
+ description: p.description,
265
+ }
266
+ schema.outputs.push(out)
267
+ })
268
+ }
269
+ }
270
+ if (d.customTags) {
271
+ d.customTags.forEach(t => {
272
+ if (t.tag === 'worker') {
273
+ model.worker = true
274
+ }
275
+ })
276
+ }
277
+ schema.model.push(model)
278
+ }
279
+ return schema
280
+ }
281
+
282
+ function genHtmlFromSchema(schema) {
283
+ let htmlDescription = '<br><div class="schema-description">';
284
+
285
+ // Process the model section
286
+ if (schema.model && schema.model.length > 0) {
287
+ schema.model.forEach(model => {
288
+ htmlDescription += `<h3><strong>${model.name}</strong></h3>`
289
+ if (model.description) {
290
+ htmlDescription += `<p>${model.description}</p>`
291
+ }
292
+ })
293
+ }
294
+
295
+ // Process the inputs section
296
+ if (schema.inputs && schema.inputs.length > 0) {
297
+ htmlDescription += '<h4>Inputs</h4><ul>';
298
+ schema.inputs.forEach(input => {
299
+ htmlDescription += `<li><strong>${input.name}</strong> (${input.type})`
300
+ if (input.description) {
301
+ htmlDescription += ` - ${input.description}`
302
+ }
303
+ })
304
+ htmlDescription += '</ul>';
305
+ }
306
+
307
+ // Process the outputs section
308
+ if (schema.outputs && schema.outputs.length > 0) {
309
+ htmlDescription += '<h4>Outputs</h4><ul>';
310
+ schema.outputs.forEach(output => {
311
+ htmlDescription += `<li><strong>${output.name}</strong> (${output.type})`
312
+ if (output.description) {
313
+ htmlDescription += ` - ${output.description}`
314
+ }
315
+ })
316
+ htmlDescription += '</ul>';
317
+ }
318
+ htmlDescription += '</div>';
319
+ return htmlDescription;
320
+ }
321
+
322
+ function genMarkdownFromSchema(schema) {
323
+ let markdownDescription = '';
324
+
325
+ // Process the model section
326
+ if (schema.model && schema.model.length > 0) {
327
+ schema.model.forEach(model => {
328
+ markdownDescription += `### **${model.name}**\n`;
329
+ if (model.description) {
330
+ markdownDescription += `${model.description}\n\n`;
331
+ }
332
+ });
333
+ }
334
+
335
+ // Process the inputs section
336
+ if (schema.inputs && schema.inputs.length > 0) {
337
+ markdownDescription += '#### Inputs\n';
338
+ schema.inputs.forEach(input => {
339
+ markdownDescription += `- **${input.name}** (${input.type})`;
340
+ if (input.description) {
341
+ markdownDescription += ` - ${input.description}`;
342
+ }
343
+ markdownDescription += '\n';
344
+ });
345
+ }
346
+
347
+ // Process the outputs section
348
+ if (schema.outputs && schema.outputs.length > 0) {
349
+ markdownDescription += '#### Outputs\n';
350
+ schema.outputs.forEach(output => {
351
+ markdownDescription += `- **${output.name}** (${output.type})`;
352
+ if (output.description) {
353
+ markdownDescription += ` - ${output.description}`;
354
+ }
355
+ markdownDescription += '\n';
356
+ });
357
+ }
358
+
359
+ return markdownDescription;
360
+ }
361
+
362
+ function template(schema, blocks) {
363
+ let title = 'jsee'
364
+ if (schema.title) {
365
+ title = schema.title
366
+ } else if (schema.model) {
367
+ if (Array.isArray(schema.model)) {
368
+ title = schema.model[0].name
369
+ } else {
370
+ title = schema.model.name
371
+ }
372
+ }
373
+
374
+ return `<!DOCTYPE html>
375
+ <html lang="en">
376
+ <head>
377
+ <meta charset="utf-8">
378
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
379
+ <meta name="viewport" content="width=device-width, initial-scale=1">
380
+ <title>${title}</title>
381
+ <meta name="generator" content="Jekyll v4.3.3" />
382
+ <meta property="og:title" content="hashr" />
383
+ <meta property="og:locale" content="en_US" />
384
+ <meta property="og:site_name" content="hashr" />
385
+ <meta property="og:type" content="website" />
386
+ <meta name="twitter:card" content="summary" />
387
+ <meta property="twitter:title" content="hashr" />
388
+ <script type="application/ld+json">{"@context":"https://schema.org","@type":"WebSite","headline":"${title}","name":"${title}","url":"/"}</script>
389
+ <link href="data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAEAAAAAAAAAD9/f0AAAAAAPj4+AAMDAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAABERAAAAAAAAEREAAAAAAAAREQABERESABERAAETMzAAEREAARAAAAAREQABEAAAABERAAEQARAAEREAARABEAAREQABEAAAABERAAEQAAAAEREAAREREAAREQABEREQABERAAAAAAAAEREAAAAAAAAREQAAAAAAABHAAwAAwAMAAMADAADAAwAAwAMAAMADAADAAwAAwAMAAMADAADAAwAAwAMAAMADAADAAwAAwAMAAMADAADAAwAA" rel="icon" type="image/x-icon" />
390
+ <style>
391
+ /** Main */
392
+ html { font-size: 16px; }
393
+ body, h1, h2, h3, h4, h5, h6, p, blockquote, pre, hr, dl, dd, ol, ul, figure { margin: 0; padding: 0; }
394
+ body { font: 400 16px/1.5 -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Segoe UI Symbol", "Segoe UI Emoji", "Apple Color Emoji", Roboto, Helvetica, Arial, sans-serif; color: #111111; background-color: #fdfdfd; -webkit-text-size-adjust: 100%; -webkit-font-feature-settings: "kern" 1; -moz-font-feature-settings: "kern" 1; -o-font-feature-settings: "kern" 1; font-feature-settings: "kern" 1; font-kerning: normal; display: flex; min-height: 100vh; flex-direction: column; overflow-wrap: break-word; }
395
+ h1, h2, h3, h4, h5, h6, p, blockquote, pre, ul, ol, dl, figure, .highlight { margin-bottom: 15px; }
396
+ hr { margin-top: 30px; margin-bottom: 30px; }
397
+ main { display: block; /* Default value of display of main element is 'inline' in IE 11. */ }
398
+ img { max-width: 100%; vertical-align: middle; }
399
+ figure > img { display: block; }
400
+ figcaption { font-size: 14px; }
401
+ ul, ol { margin-left: 30px; }
402
+ li > ul, li > ol { margin-bottom: 0; }
403
+ h1, h2, h3, h4, h5, h6 { font-weight: 400; }
404
+ a { color: #2a7ae2; text-decoration: none; }
405
+ a:visited { color: #1756a9; }
406
+ a:hover { color: #111111; text-decoration: underline; }
407
+ .social-media-list a:hover, .pagination a:hover { text-decoration: none; }
408
+ .social-media-list a:hover .username, .pagination a:hover .username { text-decoration: underline; }
409
+ blockquote { color: #828282; border-left: 4px solid #e8e8e8; padding-left: 15px; font-size: 1.125rem; font-style: italic; }
410
+ blockquote > :last-child { margin-bottom: 0; }
411
+ blockquote i, blockquote em { font-style: normal; }
412
+ pre, code { font-family: "Menlo", "Inconsolata", "Consolas", "Roboto Mono", "Ubuntu Mono", "Liberation Mono", "Courier New", monospace; font-size: 0.9375em; border: 1px solid #e8e8e8; border-radius: 3px; background-color: #eeeeff; }
413
+ code { padding: 1px 5px; }
414
+ pre { padding: 8px 12px; overflow-x: auto; }
415
+ pre > code { border: 0; padding-right: 0; padding-left: 0; }
416
+ .wrapper { max-width: calc(800px - (30px)); margin-right: auto; margin-left: auto; padding-right: 15px; padding-left: 15px; }
417
+ @media screen and (min-width: 800px) { .wrapper { max-width: calc(800px - (30px * 2)); padding-right: 30px; padding-left: 30px; } }
418
+ .wrapper:after { content: ""; display: table; clear: both; }
419
+ .orange { color: #f66a0a; }
420
+ .grey { color: #828282; }
421
+ .svg-icon { width: 16px; height: 16px; display: inline-block; fill: currentColor; padding: 5px 3px 2px 5px; vertical-align: text-bottom; }
422
+ table { margin-bottom: 30px; width: 100%; text-align: left; color: #3f3f3f; border-collapse: collapse; border: 1px solid #e8e8e8; }
423
+ table tr:nth-child(even) { background-color: #f7f7f7; }
424
+ table th, table td { padding: 10px 15px; }
425
+ table th { background-color: #f0f0f0; border: 1px solid #e0e0e0; }
426
+ table td { border: 1px solid #e8e8e8; }
427
+ @media screen and (max-width: 800px) { table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; -ms-overflow-style: -ms-autohiding-scrollbar; } }
428
+ .site-header { border-bottom: 1px solid #e8e8e8; min-height: 55.95px; line-height: 54px; position: relative; }
429
+ .site-title { font-size: 1.625rem; font-weight: 800; letter-spacing: -1px; margin-bottom: 0; float: left; }
430
+ @media screen and (max-width: 600px) { .site-title { padding-right: 45px; } }
431
+ .site-title, .site-title:visited { color: #424242; }
432
+ .site-nav { position: absolute; top: 9px; right: 15px; background-color: #fdfdfd; border: 1px solid #e8e8e8; border-radius: 5px; text-align: right; }
433
+ .site-nav .nav-trigger { display: none; }
434
+ .site-nav .menu-icon { float: right; width: 36px; height: 26px; line-height: 0; padding-top: 10px; text-align: center; }
435
+ .site-nav .menu-icon > svg path { fill: #424242; }
436
+ .site-nav label[for="nav-trigger"] { display: block; float: right; width: 36px; height: 36px; z-index: 2; cursor: pointer; }
437
+ .site-nav input ~ .trigger { clear: both; display: none; }
438
+ .site-nav input:checked ~ .trigger { display: block; padding-bottom: 5px; }
439
+ .site-nav .page-link { color: #111111; line-height: 1.5; display: block; padding: 5px 10px; margin-left: 20px; }
440
+ .site-nav .page-link:not(:last-child) { margin-right: 0; }
441
+ @media screen and (min-width: 600px) { .site-nav { position: static; float: right; border: none; background-color: inherit; } .site-nav label[for="nav-trigger"] { display: none; } .site-nav .menu-icon { display: none; } .site-nav input ~ .trigger { display: block; } .site-nav .page-link { display: inline; padding: 0; margin-left: auto; } .site-nav .page-link:not(:last-child) { margin-right: 20px; } }
442
+ .site-footer { border-top: 1px solid #e8e8e8; padding: 30px 0; }
443
+ .footer-heading { font-size: 1.125rem; margin-bottom: 15px; }
444
+ .feed-subscribe .svg-icon { padding: 5px 5px 2px 0; }
445
+ .contact-list, .social-media-list, .pagination { list-style: none; margin-left: 0; }
446
+ .footer-col-wrapper, .social-links { font-size: 0.9375rem; color: #828282; }
447
+ .footer-col { margin-bottom: 15px; }
448
+ .footer-col-1, .footer-col-2 { width: calc(50% - (30px / 2)); }
449
+ .footer-col-3 { width: calc(100% - (30px / 2)); }
450
+ @media screen and (min-width: 800px) { .footer-col-1 { width: calc(35% - (30px / 2)); } .footer-col-2 { width: calc(20% - (30px / 2)); } .footer-col-3 { width: calc(45% - (30px / 2)); } }
451
+ @media screen and (min-width: 600px) { .footer-col-wrapper { display: flex; } .footer-col { width: calc(100% - (30px / 2)); padding: 0 15px; } .footer-col:first-child { padding-right: 15px; padding-left: 0; } .footer-col:last-child { padding-right: 0; padding-left: 15px; } }
452
+ /** Page content */
453
+ .page-content { padding: 30px 0; flex: 1 0 auto; }
454
+ .page-heading { font-size: 2rem; }
455
+ .post-list-heading { font-size: 1.75rem; }
456
+ .post-list { margin-left: 0; list-style: none; }
457
+ .post-list > li { margin-bottom: 30px; }
458
+ .post-meta { font-size: 14px; color: #828282; }
459
+ .post-link { display: block; font-size: 1.5rem; }
460
+ /** Posts */
461
+ .post-header { margin-bottom: 30px; }
462
+ .post-title, .post-content h1 { font-size: 2.625rem; letter-spacing: -1px; line-height: 1.15; }
463
+ @media screen and (min-width: 800px) { .post-title, .post-content h1 { font-size: 2.625rem; } }
464
+ .post-content { margin-bottom: 30px; }
465
+ .post-content h1, .post-content h2, .post-content h3 { margin-top: 60px; }
466
+ .post-content h4, .post-content h5, .post-content h6 { margin-top: 30px; }
467
+ .post-content h2 { font-size: 1.75rem; }
468
+ @media screen and (min-width: 800px) { .post-content h2 { font-size: 2rem; } }
469
+ .post-content h3 { font-size: 1.375rem; }
470
+ @media screen and (min-width: 800px) { .post-content h3 { font-size: 1.625rem; } }
471
+ .post-content h4 { font-size: 1.25rem; }
472
+ .post-content h5 { font-size: 1.125rem; }
473
+ .post-content h6 { font-size: 1.0625rem; }
474
+ .social-media-list, .pagination { display: table; margin: 0 auto; }
475
+ .social-media-list li, .pagination li { float: left; margin: 5px 10px 5px 0; }
476
+ .social-media-list li:last-of-type, .pagination li:last-of-type { margin-right: 0; }
477
+ .social-media-list li a, .pagination li a { display: block; padding: 7.5px; border: 1px solid #e8e8e8; }
478
+ .social-media-list li a:hover, .pagination li a:hover { border-color: #dbdbdb; }
479
+ /** Pagination navbar */
480
+ .pagination { margin-bottom: 30px; }
481
+ .pagination li a, .pagination li div { min-width: 41px; text-align: center; box-sizing: border-box; }
482
+ .pagination li div { display: block; padding: 7.5px; border: 1px solid transparent; }
483
+ .pagination li div.pager-edge { color: #e8e8e8; border: 1px dashed; }
484
+ /** Grid helpers */
485
+ @media screen and (min-width: 800px) { .one-half { width: calc(50% - (30px / 2)); } }
486
+ /** Jsee elements */
487
+ .app-container { background-color: #F0F1F4; border-bottom: 1px solid #e8e8e8; padding-bottom: 55px }
488
+ #download-btn { float: right; margin-top: 10px; padding: 10px; background-color: white; border: none; cursor: pointer; }
489
+ #download-btn:hover { background-color: #f0f0f0; }
490
+ .schema-description { background-color: #f8f8fa; padding: 20px; margin-top: 20px; border-radius: 10px; border: 1px solid #e8e8e8; }
491
+ .schema-description h2, .schema-description h3, .schema-description h4 { margin-top: 10px; }
492
+ /** Logos */
493
+ .logo_footer { margin-left: -3px; }
494
+ .logo_footer svg { opacity: 0.35; }
495
+ .logo_footer:hover svg { opacity: 1; }
496
+ .social-links { display: flex; justify-content: right; }
497
+ .social-links .social-media-list, .social-links .pagination { margin: 0; }
498
+ </style>
499
+ <link type="application/atom+xml" rel="alternate" href="/feed.xml" title="hashr" />
500
+ ${blocks.gaHtml}
501
+ </head>
502
+ <body>
503
+ ${blocks.hiddenElementHtml}
504
+ <header class="site-header">
505
+ <div class="wrapper">
506
+ <span class="site-title">${title}</span>
507
+ <button id="download-btn" title="Bundled HTML without external dependencies">Download HTML</button>
508
+ </div>
509
+ </header>
510
+ <div class="page-content app-container">
511
+ <div class="wrapper">
512
+ <div id="jsee-container"></div>
513
+ </div>
514
+ </div>
515
+ <main class="page-content" aria-label="Content">
516
+ <div class="wrapper">
517
+ <article class="post">
518
+ <div class="post-content">
519
+ ${blocks.descriptionHtml}
520
+ </div>
521
+ </article>
522
+ </div>
523
+ </main>
524
+ <footer class="site-footer h-card">
525
+ <data class="u-url" href="/"></data>
526
+ <div class="wrapper">
527
+ <div class="footer-col-wrapper">
528
+ <div class="footer-col">
529
+ <p>
530
+ <a href="https://jsee.io/" class="logo_footer">
531
+ <svg width="29mm" height="13mm" viewBox="0 0 29 13" version="1.1" id="svg1274" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" style="width: 50px;">
532
+ <defs id="defs1271"/>
533
+ <g id="layer1">
534
+ <path id="path53650-9-7-8-7-5" style="fill:#000000;fill-opacity:1;stroke-width:0.033443" d="m 15.085048,2.2627646 v 1.7268189 6.9073135 h 1.726792 1.726873 1.726871 V 9.1700919 H 18.538713 16.81184 V 3.9895835 h 1.726873 1.726871 V 2.2627646 H 18.538713 16.81184 Z m 3.453665,3.453624 v 1.7268716 h 1.726871 V 5.7163886 Z"/>
535
+ <path id="path53647-5-5-3-6-1" style="fill:#000000;fill-opacity:1;stroke-width:0.033443" d="m 22.001709,2.2627517 v 1.7268184 6.9073269 h 1.726793 1.726871 1.726872 V 9.1700919 H 25.455373 23.728502 V 3.9895701 h 1.726871 1.726872 V 2.2627517 h -1.726872 -1.726871 z m 3.453664,3.4536369 v 1.7268716 h 1.726872 V 5.7163886 Z"/>
536
+ <g aria-label="JS" transform="matrix(0.05545656,0,0,0.05597005,-218.22149,96.848185)" id="text9866-2-7-20-0-9-1-4-1" style="font-size:241.925px;fill:#000000;stroke:#0024ee;stroke-width:3.31154">
537
+ <g id="path52502-6-4-6-8-3-0" style="fill:#000000">
538
+ <path id="path53644-8-2-9-0-8" style="color:#000000;-inkscape-font-specification:'Ubuntu Mono Bold';fill:#000000;stroke:none;stroke-width:3.31155;-inkscape-stroke:none" d="m 4057.5092,-1689.778 -33.3113,-0.027 v 99.6732 c 0,10.8886 -2.3949,18.0118 -6.6855,21.7303 -4.3348,3.7352 -10.0924,5.6444 -17.5448,5.6444 -4.9004,0 -9.717,-1.1428 -14.514,-3.4628 h -0.011 c -4.7842,-2.393 -9.1636,-4.8607 -13.1345,-7.4021 l -1.5765,-1.0103 -12.7499,26.5883 1.0351,0.822 c 4.8358,3.8352 10.7133,7.0544 17.6154,9.6838 7.0314,2.6785 15.6913,3.9804 25.9924,3.9804 9.8334,0 18.2685,-1.3898 25.2967,-4.2345 l 0.016,-0.01 0.016,-0.01 c 6.955,-2.9809 12.6568,-6.9906 17.029,-12.0274 l 0.011,-0.01 c 4.5181,-5.0224 7.7113,-10.908 9.5488,-17.5823 1.98,-6.6083 2.9669,-13.6189 2.9669,-21.0153 z"/>
539
+ </g>
540
+ <g id="path52504-2-7-1-6-3-5" transform="translate(95.989089,-84.950886)" style="fill:#000000">
541
+ <path id="path53638-1-7-4-0-0" style="color:#000000;-inkscape-font-specification:'Ubuntu Mono Bold';fill:#000000;stroke:none;stroke-width:12.5161;-inkscape-stroke:none" d="m 14820.902,-7165.5098 c -61.098,0 -110.108,15.3477 -146.047,46.5274 l -0.04,0.039 h -0.04 c -35.354,31.2211 -53.095,75.0334 -53.095,129.6602 0,27.7074 4.995,51.4357 15.298,70.9336 l 0.04,0.039 0.04,0.045 c 10.052,18.3884 22.693,34.038 37.871,46.8027 l 0.05,0.037 0.05,0.047 c 15.605,12.5992 32.778,22.9927 51.471,31.168 18.277,7.9934 35.956,15.0668 53.053,21.2324 12.58,4.837 25.165,10.284 37.761,16.3379 l 0.116,0.053 0.109,0.053 c 12.38,5.3549 23.522,11.5723 33.449,18.6446 l 0.102,0.076 0.101,0.072 c 9.746,6.3641 17.645,13.7908 23.844,22.3223 5.933,8.165 8.811,17.0897 8.811,27.5117 0,8.4874 -1.396,16.9808 -4.233,25.5703 l -0.05,0.127 -0.04,0.1328 c -2.09,7.3855 -6.277,14.3394 -12.873,21.0312 -6.408,5.904 -15.549,11.0389 -27.597,15.1055 -11.28,3.4051 -26.177,5.2285 -44.551,5.2285 -32.227,0 -59.425,-4.2332 -81.584,-12.4726 -22.161,-8.4662 -41.245,-17.504 -57.227,-27.0625 l -6.488,-3.877 -37.041,103.8457 4.35,2.6699 c 14.574,8.9537 35.758,18.5092 63.875,28.9961 l 0.06,0.037 0.06,0.039 c 28.93,10.1558 66.843,15.0469 113.996,15.0469 70.627,0 124.053,-14.4999 159.64,-44.6446 36.005,-30.6002 54.096,-74.1656 54.096,-128.8164 0,-31.9205 -5.252,-58.6401 -16.158,-80.0176 -10.645,-21.4715 -24.527,-39.3432 -41.582,-53.3886 -16.251,-14.4867 -34.421,-25.8642 -54.418,-34.0684 0,0 -0.04,9e-4 -0.04,-0.037 -18.835,-8.58 -37.086,-16.2605 -54.742,-23.0195 h -0.04 -0.04 c -11.438,-4.253 -23.183,-8.8126 -35.232,-13.6758 -11.18,-4.7508 -21.427,-10.0803 -30.781,-15.9668 -8.553,-6.3562 -15.604,-13.2064 -21.213,-20.541 -5.304,-7.5192 -7.906,-15.8372 -7.906,-25.668 0,-20.2937 6.46,-34.1264 19.728,-43.8281 13.269,-9.7017 34.264,-15.1055 63.244,-15.1055 23.752,0 44.321,3.0022 61.737,8.8614 h 0.04 0.04 c 18.467,6.0121 35.07,13.2006 49.834,21.5449 l 6.312,3.5664 37.239,-99.1895 -4.733,-2.6328 c -18.112,-10.0852 -40.336,-19.0958 -66.734,-27.1504 -26.237,-8.1912 -56.862,-12.2129 -91.906,-12.2129 z" transform="matrix(0.26580544,0,0,0.26336684,98.529211,278.95254)"/>
542
+ </g>
543
+ </g>
544
+ </g>
545
+ </svg>
546
+ </a>
547
+ </p>
548
+ </div>
549
+ <div class="footer-col">
550
+ <div class="social-links">
551
+ <ul class="social-media-list">
552
+ <li><a rel="me" href="https://github.com/jseeio/hash" title="jseeio/hash">GitHub</a></li>
553
+ <li><a rel="me" href="https://twitter.com/jseeio" title="jseeio">Twitter</a></li>
554
+ <li><a rel="me" href="https://www.facebook.com/jseeio" title="jseeio">Facebook</a></li>
555
+ </ul>
556
+ </div>
557
+ </div>
558
+ </div>
559
+ </div>
560
+ </footer>
561
+ ${blocks.jseeHtml}
562
+ <script>
563
+ const schema = ${JSON.stringify(schema, null, 2)}
564
+ const title = "${title}"
565
+ var env = new JSEE({
566
+ container: document.getElementById('jsee-container'),
567
+ schema
568
+ })
569
+ document.getElementById('download-btn').addEventListener('click', async () => {
570
+ env.download(title)
571
+ })
572
+ </script>
573
+ </body>
574
+ </html>`
575
+ }