@jseeio/jsee 0.3.2 → 0.3.4

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 CHANGED
@@ -109,8 +109,56 @@ Extra blocks can be provided for further customization
109
109
  - `action` or `button` - Button (its `name` will be passed as a `caller` to the model)
110
110
  - `default` - Default value
111
111
  - `outputs` - Outputs definition
112
+ - `name`* - Name of the output
113
+ - `type`* - Type. Possible types:
114
+ - `object` - JavaScript Object
115
+ - `html` or `svg` - SVG element
116
+ - `code` - Code block
117
+ - `function` - Render function. Rather than returning a value, a model returns a function that JSEE will call passing the container element.
112
118
  - `examples` - List of examples
113
119
  - `autorun` (boolean, default: `false`) - Defines if the script should be evaluated on each input change event
114
120
  - `interval` (number, default: `0`) - Defines the interval between script evaluations (in milliseconds). If set to `0`, the script is evaluated only once.
115
121
 
116
122
  JSEE is a reactive branch of [StatSim](https://statsim.com)'s [Port](https://github.com/statsim/port). It's still work in progress. Expect API changes.
123
+
124
+ # Changelog
125
+
126
+ ## 0.3.4
127
+ ### JSEE:
128
+ - [x] Add `columns` parameter to the `inputs`, `outputs` blocks (making it possible to create multi-column layouts, like simple dashboards)
129
+ - [x] Add `function` output type (for custom renderers which take a container element as an argument)
130
+ - [x] Add `dom-to-image` library for exporting dynamic output blocks to PNG
131
+ - [x] Support for inputs to be set with url parameters (e.g. `?input1=1&input2=2`)
132
+ ### HTML Generator:
133
+ - [x] Add `latex` and table output in the markdown renderer
134
+ - [x] Cache `import` scripts to avoid multiple loads when `--fetch` is used
135
+ - [x] Infer `description` from the markdown and update html `<head>` with it
136
+ - [x] Update `social`, `org`, `ga` blocks
137
+ - [x] Small layout fixes
138
+
139
+ ## 0.3.1
140
+ - [x] Add `download` method to jsee object
141
+ - [x] Add `bin` folder with `cmd.js` for easier project building
142
+
143
+ ## 0.2.9
144
+ - [x] Add examples
145
+ - [x] Add imports
146
+ - [x] Add `caller` field to the model input (can be: `run`, `autorun` or a button name)
147
+ - [x] Add `title` field (for buttons rn)
148
+ - [x] If `display` field is `false` the input is not shown
149
+ - [x] If `autorun` is true, then actually autorun the model initially
150
+
151
+ ## 0.2.8
152
+ - [x] Fix no input case
153
+
154
+ ## 0.2.7
155
+ - [x] Show output when result is `0`
156
+ - [x] Updated style for buttons and inputs
157
+
158
+ ## 0.2.6
159
+ - [x] Tests
160
+ - [x] Load schema from query (loader)
161
+ - [x] Reset button appears only after data change
162
+ - [x] Default input type (`string`)
163
+ - [x] Directly load code when running in a window (not code to text)
164
+ - [x] Passing code directly
package/bin/jsee CHANGED
@@ -1,11 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const minimist = require('minimist')
4
3
  const fs = require('fs')
5
4
  const path = require('path')
5
+ const os = require('os')
6
+ const crypto = require('crypto')
7
+
8
+ const minimist = require('minimist')
6
9
  const jsdoc2md = require('jsdoc-to-markdown')
7
10
  const showdown = require('showdown')
8
- const converter = new showdown.Converter()
11
+ const showdownKatex = require('showdown-katex')
12
+ const converter = new showdown.Converter({
13
+ extensions: [
14
+ showdownKatex({
15
+ throwOnError: true,
16
+ displayMode: true,
17
+ errorColor: '#1500ff',
18
+ output: 'mathml'
19
+ }),
20
+ ],
21
+ tables: true
22
+ })
23
+ showdown.setFlavor('github')
9
24
 
10
25
  // left padding of multiple lines
11
26
  function pad (str, len, start=0) {
@@ -38,19 +53,19 @@ if (argv._.length > 0) {
38
53
  argv.inputs = argv._[0]
39
54
  }
40
55
 
41
- console.log(argv)
42
-
43
56
  gen(argv)
44
57
 
45
58
  // Adding async here breaks express. TODO: investigate
46
59
  async function gen (argv, returnHtml=false) {
60
+ console.log('argv:', argv)
47
61
  let cwd = process.cwd()
48
62
  let inputs = argv.inputs
49
63
  let outputs = argv.outputs
50
- let imports = argv.imports
51
64
  let description = argv.description
65
+ let version = argv.version
52
66
  let ga = argv.ga
53
67
  let schema
68
+ let descriptionTxt = ''
54
69
  let descriptionHtml = ''
55
70
  let jsdocMarkdown = ''
56
71
 
@@ -90,38 +105,47 @@ async function gen (argv, returnHtml=false) {
90
105
  if (description) {
91
106
  const descriptionMd = fs.readFileSync(path.join(cwd, description), 'utf8')
92
107
  descriptionHtml = converter.makeHtml(descriptionMd)
108
+
109
+ if (descriptionMd.includes('---')) {
110
+ descriptionTxt = descriptionMd
111
+ .split('---')[0]
112
+ .replace(/\n/g, ' ')
113
+ .replace(/\s+/g, ' ')
114
+ .replace(/#/g, '')
115
+ .replace(/\*/g, '')
116
+ .trim()
117
+ }
93
118
  }
94
119
 
95
120
  descriptionHtml += genHtmlFromSchema(schema)
96
121
 
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
- ` : ``
122
+
108
123
 
109
124
  // Generate jsee code
110
125
  let jseeHtml = ''
111
126
  let hiddenElementHtml = ''
112
127
  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>`
128
+ // Fetch jsee code from the CDN or local server
129
+ let jseeCode
130
+ if (argv.version === 'dev') {
131
+ jseeCode = fs.readFileSync(path.join(__dirname, '..', 'dist', 'jsee.js'), 'utf8')
132
+ } else if (argv.version === 'latest') {
133
+ jseeCode = fs.readFileSync(path.join(__dirname, '..', 'dist', 'jsee.runtime.js'), 'utf8')
117
134
  } else {
118
135
  // 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`)
136
+ jseeCode = await fetch(`https://cdn.jsdelivr.net/npm/@jseeio/jsee@${argv.version}/dist/jsee.runtime.js`)
120
137
  jseeCode = await jseeCode.text()
121
- jseeHtml = `<script>${jseeCode}</script>`
122
138
  }
139
+ jseeHtml = `<script>${jseeCode}</script>`
123
140
  // Fetch model files and store them in hidden elements
124
141
  hiddenElementHtml += '<div id="hidden-storage" style="display: none;">'
142
+ if (!schema.model) {
143
+ console.error('No model found in schema')
144
+ process.exit(1)
145
+ }
146
+ if (!Array.isArray(schema.model)) {
147
+ schema.model = [schema.model]
148
+ }
125
149
  for (let m of schema.model) {
126
150
  if (m.url) {
127
151
  const modelCode = fs.readFileSync(path.join(cwd, m.url), 'utf8')
@@ -130,8 +154,42 @@ async function gen (argv, returnHtml=false) {
130
154
  if (m.imports) {
131
155
  for (let i of m.imports) {
132
156
  const importUrl = i.includes('.js') ? i : `https://cdn.jsdelivr.net/npm/${i}`
133
- let importCode = await fetch(importUrl)
134
- importCode = await importCode.text()
157
+
158
+ // Create cache directory if it doesn't exist
159
+ const cacheDir = path.join(os.homedir(), '.cache', 'jsee')
160
+ fs.mkdirSync(cacheDir, { recursive: true })
161
+
162
+ // Create a hash of the importUrl
163
+ const hash = crypto.createHash('sha256').update(importUrl).digest('hex')
164
+ const cacheFilePath = path.join(cacheDir, `${hash}.js`)
165
+
166
+ let importCode
167
+ let useCache = false
168
+
169
+ // Check if cache file exists and is less than 1 day old
170
+ if (fs.existsSync(cacheFilePath)) {
171
+ const stats = fs.statSync(cacheFilePath)
172
+ const mtime = new Date(stats.mtime)
173
+ const now = new Date()
174
+ const ageInDays = (now - mtime) / (1000 * 60 * 60 * 24)
175
+
176
+ if (ageInDays < 1) {
177
+ console.log('Using cached import:', importUrl)
178
+ importCode = fs.readFileSync(cacheFilePath, 'utf8');
179
+ useCache = true;
180
+ }
181
+ }
182
+
183
+ if (!useCache) {
184
+ const response = await fetch(importUrl);
185
+ if (!response.ok) {
186
+ console.error(`Failed to fetch ${importUrl}: ${response.statusText}`);
187
+ process.exit(1);
188
+ }
189
+ importCode = await response.text()
190
+ fs.writeFileSync(cacheFilePath, importCode, 'utf8')
191
+ console.log('Fetched and stored to cache:', importUrl)
192
+ }
135
193
  hiddenElementHtml += `<script type="text/plain" style="display: none;" data-src="${importUrl}">${importCode}</script>`
136
194
  }
137
195
  }
@@ -143,11 +201,74 @@ async function gen (argv, returnHtml=false) {
143
201
  : `<script src="http://localhost:${argv.port}/dist/jsee.runtime.js"></script>`
144
202
  }
145
203
 
204
+ let socialHtml = ''
205
+ let gaHtml = ''
206
+ let orgHtml = ''
207
+
208
+ if (schema.page) {
209
+ if (schema.page.title) {
210
+ title = schema.page.title
211
+ }
212
+ if (schema.page.ga) {
213
+ gaHtml = `
214
+ <script id="ga-src" async src="https://www.googletagmanager.com/gtag/js?id=${schema.page.ga}"></script>
215
+ <script id="ga-body">
216
+ window['ga-disable-${schema.page.ga}'] = window.doNotTrack === "1" || navigator.doNotTrack === "1" || navigator.doNotTrack === "yes" || navigator.msDoNotTrack === "1";
217
+ window.dataLayer = window.dataLayer || [];
218
+ function gtag(){dataLayer.push(arguments);}
219
+ gtag('js', new Date());
220
+ gtag('config', '${schema.page.ga}');
221
+ </script>
222
+ `
223
+ }
224
+
225
+ // Social media links
226
+ if (schema.page.social) {
227
+ // iterate over dict with k, v pairs
228
+ for (let [name, url] of Object.entries(schema.page.social)) {
229
+ switch (name) {
230
+ case 'twitter':
231
+ socialHtml += `<li><a rel="me" href="https://twitter.com/${url}">Twitter</a></li>`
232
+ break
233
+ case 'github':
234
+ socialHtml += `<li><a rel="me" href="https://github.com/${url}">GitHub</a></li>`
235
+ break
236
+ case 'facebook':
237
+ socialHtml += `<li><a rel="me" href="https://www.facebook.com/${url}">Facebook</a></li>`
238
+ break
239
+ case 'linkedin':
240
+ socialHtml += `<li><a rel="me" href="https://www.linkedin.com/company/${url}">LinkedIn</a></li>`
241
+ break
242
+ case 'instagram':
243
+ socialHtml += `<li><a rel="me" href="https://www.instagram.com/${url}">Instagram</a></li>`
244
+ break
245
+ case 'youtube':
246
+ socialHtml += `<li><a rel="me" href="https://www.youtube.com/${url}">YouTube</a></li>`
247
+ break
248
+ default:
249
+ socialHtml += `<li><a rel="me" href="${s.url}">${s.name}</a></li>`
250
+ }
251
+ }
252
+ }
253
+
254
+ if (schema.page.org) {
255
+ orgHtml = `<div class="footer-org"><h4 class="footer-heading"><a href="${schema.page.org.url}">${schema.page.org.name}</a></h4>`
256
+ if (schema.page.org.description) {
257
+ orgHtml += `<p>${schema.page.org.description}</p>`
258
+ }
259
+ orgHtml += '</div>'
260
+ }
261
+
262
+ }
263
+
146
264
  const html = template(schema, {
147
265
  descriptionHtml: pad(descriptionHtml, 8, 1),
266
+ descriptionTxt: descriptionTxt,
148
267
  gaHtml: pad(gaHtml, 2, 1),
149
268
  jseeHtml: jseeHtml,
150
- hiddenElementHtml: hiddenElementHtml
269
+ hiddenElementHtml: hiddenElementHtml,
270
+ socialHtml: pad(socialHtml, 2, 1),
271
+ orgHtml: pad(orgHtml, 2, 1),
151
272
  })
152
273
 
153
274
  if (returnHtml) {
@@ -174,16 +295,16 @@ async function gen (argv, returnHtml=false) {
174
295
  const express = require('express')
175
296
  const app = express()
176
297
  app.get('/', async (req, res) => {
177
- console.log('Serving the html')
298
+ console.log('Serving index.html')
178
299
  res.send(await gen(argv, true))
179
300
  })
180
301
  app.get('/dist/jsee.runtime.js', (req, res) => {
181
- console.log('Serving jsee.runtime.js from:', path.join(__dirname, '..', 'dist', 'jsee.runtime.js'))
302
+ console.log('Serving jsee.runtime.js')
182
303
  res.sendFile(path.join(__dirname, '..', 'dist', 'jsee.runtime.js'))
183
304
  })
184
305
  app.use(express.static(cwd))
185
306
  app.listen(argv.port, () => {
186
- console.log(`JSEE app is running at http://localhost:${argv.port}`)
307
+ console.log(`JSEE app is running: http://localhost:${argv.port}`)
187
308
  })
188
309
  }
189
310
  }
@@ -361,8 +482,11 @@ function genMarkdownFromSchema(schema) {
361
482
 
362
483
  function template(schema, blocks) {
363
484
  let title = 'jsee'
485
+ let url = schema.page.url ? schema.page.url : ''
364
486
  if (schema.title) {
365
487
  title = schema.title
488
+ } else if (schema.page && schema.page.title) {
489
+ title = schema.page.title
366
490
  } else if (schema.model) {
367
491
  if (Array.isArray(schema.model)) {
368
492
  title = schema.model[0].name
@@ -372,28 +496,50 @@ function template(schema, blocks) {
372
496
  }
373
497
 
374
498
  return `<!DOCTYPE html>
499
+
500
+ <!-- Generated by JSEE (https://jsee.org) -->
501
+ <!-- Do not edit this file directly. Edit the source files and run jsee to generate this file. -->
502
+ <!-- Source: ${argv.inputs} -->
503
+ <!-- License: MIT (https://opensource.org/licenses/MIT) -->
504
+
375
505
  <html lang="en">
376
506
  <head>
377
507
  <meta charset="utf-8">
378
508
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
379
509
  <meta name="viewport" content="width=device-width, initial-scale=1">
380
510
  <title>${title}</title>
381
- <meta name="generator" content="Jekyll v4.3.3" />
382
- <meta property="og:title" content="hashr" />
511
+ <meta name="description" content="${blocks.descriptionTxt}">
512
+
513
+ <!-- Open Graph -->
514
+ <meta property="og:title" content="${title}" />
515
+ <meta property="og:description" content="${blocks.descriptionTxt}" />
383
516
  <meta property="og:locale" content="en_US" />
384
- <meta property="og:site_name" content="hashr" />
517
+ <meta property="og:url" content="${url}" />
518
+ <meta property="og:site_name" content="${title}" />
385
519
  <meta property="og:type" content="website" />
520
+
521
+ <!-- Twitter Card -->
386
522
  <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>
523
+ <meta name="twitter:title" content="${title}" />
524
+ <meta name="twitter:description" content="${blocks.descriptionTxt}" />
525
+
526
+ <!-- Structured Data -->
527
+ <script type="application/ld+json">{"@context":"https://schema.org","@type":"WebSite","headline":"${title}","name":"${title}","url":"${url}", "description":"${blocks.descriptionTxt}"}</script>
528
+
529
+ <!-- Canonical Link -->
530
+ <link rel="canonical" href="${url}" />
531
+
532
+ <!-- Favicon -->
389
533
  <link href="data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAEAAAAAAAAAD9/f0AAAAAAPj4+AAMDAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAAAAAAABERAAAAAAAAEREAAAAAAAAREQABERESABERAAETMzAAEREAARAAAAAREQABEAAAABERAAEQARAAEREAARABEAAREQABEAAAABERAAEQAAAAEREAAREREAAREQABEREQABERAAAAAAAAEREAAAAAAAAREQAAAAAAABHAAwAAwAMAAMADAADAAwAAwAMAAMADAADAAwAAwAMAAMADAADAAwAAwAMAAMADAADAAwAAwAMAAMADAADAAwAA" rel="icon" type="image/x-icon" />
534
+
535
+ <!-- Styles -->
390
536
  <style>
391
537
  /** Main */
392
538
  html { font-size: 16px; }
393
539
  body, h1, h2, h3, h4, h5, h6, p, blockquote, pre, hr, dl, dd, ol, ul, figure { margin: 0; padding: 0; }
394
540
  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
541
  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; }
542
+ hr { margin-top: 30px; margin-bottom: 30px; border: 0; border-top: 1px solid #ececec; }
397
543
  main { display: block; /* Default value of display of main element is 'inline' in IE 11. */ }
398
544
  img { max-width: 100%; vertical-align: middle; }
399
545
  figure > img { display: block; }
@@ -440,7 +586,10 @@ function template(schema, blocks) {
440
586
  .site-nav .page-link:not(:last-child) { margin-right: 0; }
441
587
  @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
588
  .site-footer { border-top: 1px solid #e8e8e8; padding: 30px 0; }
443
- .footer-heading { font-size: 1.125rem; margin-bottom: 15px; }
589
+ .footer-heading { font-size: 1.7rem; line-height: 1.7rem; font-weight: 200; margin-bottom: 5px;}
590
+ .footer-heading a { color: #a2a2a2; text-decoration: none; }
591
+ .footer-heading a:hover { color: #828282; }
592
+ .footer-org p { font-size: 0.65rem; color: #828282; }
444
593
  .feed-subscribe .svg-icon { padding: 5px 5px 2px 0; }
445
594
  .contact-list, .social-media-list, .pagination { list-style: none; margin-left: 0; }
446
595
  .footer-col-wrapper, .social-links { font-size: 0.9375rem; color: #828282; }
@@ -504,7 +653,7 @@ function template(schema, blocks) {
504
653
  <header class="site-header">
505
654
  <div class="wrapper">
506
655
  <span class="site-title">${title}</span>
507
- <button id="download-btn" title="Bundled HTML without external dependencies">Download HTML</button>
656
+ <button id="download-btn" title="Download bundled HTML file without external dependencies to use offline">Download bundle (html)</button>
508
657
  </div>
509
658
  </header>
510
659
  <div class="page-content app-container">
@@ -526,32 +675,12 @@ function template(schema, blocks) {
526
675
  <div class="wrapper">
527
676
  <div class="footer-col-wrapper">
528
677
  <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>
678
+ ${blocks.orgHtml}
548
679
  </div>
549
680
  <div class="footer-col">
550
681
  <div class="social-links">
551
682
  <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>
683
+ ${blocks.socialHtml}
555
684
  </ul>
556
685
  </div>
557
686
  </div>