├── .gitignore ├── .npmignore ├── bin ├── msg └── watch.js ├── docsrc ├── index.data.js ├── css │ ├── base │ │ ├── typography.css │ │ ├── svg-icon.css │ │ └── normalize.css │ ├── abstracts │ │ └── color.css │ └── main.css └── index.page.js ├── LICENSE ├── package.json ├── docs ├── main.css └── index.html └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | docs 2 | example2 3 | /bin/watch.js -------------------------------------------------------------------------------- /bin/msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../build'); -------------------------------------------------------------------------------- /docsrc/index.data.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | head: { 3 | title: "test title" 4 | }, 5 | body: "test content" 6 | } -------------------------------------------------------------------------------- /docsrc/css/base/typography.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | font-size: 2em; 3 | margin: 0; 4 | } 5 | 6 | .inline-code { 7 | padding: 4px 8px; 8 | border-radius: 4px; 9 | background: var(--grey); 10 | font-size: 16px; 11 | } -------------------------------------------------------------------------------- /docsrc/css/base/svg-icon.css: -------------------------------------------------------------------------------- 1 | .svg-icon { 2 | width: 3em; 3 | height: 3em; 4 | display: inline-block; 5 | vertical-align: middle; 6 | margin: 17px 20px 20px 23px; 7 | } 8 | 9 | .dev-icon { /* a svg */ 10 | text-decoration: none; 11 | width: 3em; 12 | height: 1em; 13 | display: inline-block; 14 | vertical-align: middle; 15 | margin: 0.5em; 16 | } -------------------------------------------------------------------------------- /docsrc/css/abstracts/color.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --black: #222222; 3 | --white: #ffffff; 4 | --yellow: #f7df1e; 5 | --blue: #12223f; 6 | --grey: #eae9e9; 7 | } 8 | 9 | .clr-yellow { color: var(--yellow); } 10 | .clr-white { color: var(--white); } 11 | .clr-black { color: var(--black); } 12 | .bg-yellow { background-color: var(--yellow); } 13 | .bg-white { background-color: var(--white); } 14 | .bg-black { background-color: var(--black); } 15 | .bg-blue { background-color: var(--blue); } 16 | .fill-yellow { fill: var(--yellow); } 17 | .fill-white { fill: var(--white); } 18 | .fill-black { fill: var(--black); } -------------------------------------------------------------------------------- /docsrc/css/main.css: -------------------------------------------------------------------------------- 1 | @import "abstracts/color.css"; 2 | @import "base/normalize.css"; 3 | @import "base/typography.css"; 4 | @import "base/svg-icon.css"; 5 | 6 | 7 | body { font-family: 'Bungee', cursive; overflow-x: hidden; } 8 | .screen-height { height: 100vh; } 9 | .text-center { text-align: center; } 10 | .text-left { text-align: left; } 11 | .v-center { vertical-align: middle; } 12 | span.v-center { display: inline-block; } 13 | .center-section { 14 | display: flex; 15 | align-items: center; 16 | justify-content: center; 17 | flex-direction: column; 18 | } 19 | .no-wrap { white-space: nowrap; } 20 | @media (max-width: 360px) { 21 | .hide-small { 22 | display: none; 23 | } 24 | } 25 | .fixed-bottom-right { 26 | position: fixed; 27 | bottom: 26px; 28 | right: 10px; 29 | } 30 | @media (min-width: 850px) { 31 | .md-flex-row { 32 | display: flex; 33 | justify-content: space-between; 34 | align-items: center; 35 | } 36 | } 37 | .z-1 { z-index: 1; } 38 | .z-2 { z-index: 2; } 39 | .position-relative { position: relative; } 40 | .padding-30 { padding: 30px; } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Iain J McCallum 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mini-site-generator", 3 | "version": "1.0.10", 4 | "description": "A marvelously minimal static site generator.", 5 | "keywords": [ 6 | "static", 7 | "file", 8 | "site", 9 | "website", 10 | "generator", 11 | "minimal", 12 | "simple", 13 | "javascript", 14 | "easy" 15 | ], 16 | "author": "Iain J McCallum", 17 | "main": "build.js", 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/ijmccallum/Mini-Site-Generator" 21 | }, 22 | "scripts": { 23 | "start": "run-p start:server start:watch", 24 | "start:server": "http-server ./docs/", 25 | "start:watch": "node bin/watch.js", 26 | "build:markup": "node build.js -io ./docsrc/ ./docs/", 27 | "build:css": "postcss --use postcss-import postcss-custom-media autoprefixer cssnano -o docs/main.css docsrc/css/main.css --no-map" 28 | }, 29 | "bin": { 30 | "msg": "./bin/msg" 31 | }, 32 | "dependencies": { 33 | "html-minifier": "^3.5.8" 34 | }, 35 | "devDependencies": { 36 | "cssnano": "^3.10.0", 37 | "http-server": "^0.11.1", 38 | "npm-run-all": "^4.1.2", 39 | "postcss": "^6.0.18", 40 | "postcss-cli": "^5.0.0", 41 | "postcss-cssnext": "^3.1.0", 42 | "postcss-custom-media": "^6.0.0", 43 | "postcss-import": "^11.1.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /bin/watch.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'), 2 | spawn = require('child_process').spawn, 3 | debounceMs = 100; 4 | 5 | //Takes the name of an npm script, and runs it 6 | function npmRun(script){ 7 | console.log('run ', script); 8 | 9 | //Defining the npm command - depends on the platform you're on (https://nodejs.org/api/process.html#process_process_platform) 10 | var cmd = 'npm'; 11 | if (process.platform === 'win32') { 12 | cmd = 'npm.cmd'; 13 | } 14 | 15 | const p = spawn('npm.cmd', ['run', script], { 16 | stdio: 'inherit' // pipe output to the console 17 | }); 18 | 19 | // Print something when the process completes 20 | p.on('close', code => { 21 | if (code === 1) { 22 | console.error(`✖ "npm run ${script}" failed.`); 23 | } else { 24 | console.log(`✔ "npm run ${script}" finished, watching for changes...`); 25 | } 26 | }); 27 | } 28 | 29 | //the davidwalsh debounce https://davidwalsh.name/javascript-debounce-function 30 | function debounce(func, wait, immediate) { 31 | var timeout; 32 | return function() { 33 | var context = this, args = arguments; 34 | var later = function() { 35 | timeout = null; 36 | if (!immediate) func.apply(context, args); 37 | }; 38 | var callNow = immediate && !timeout; 39 | clearTimeout(timeout); 40 | timeout = setTimeout(later, wait); 41 | if (callNow) func.apply(context, args); 42 | }; 43 | }; 44 | 45 | //debounced version of npmRun 46 | var npmRunD = debounce(npmRun, debounceMs); 47 | 48 | //watches the given dir and runs the given script 49 | fs.watch('docsrc', { 50 | recursive: true // watch everything in the directory 51 | }, (e, filename) => { 52 | // Use the extension of the file as the npm script name 53 | const fileExt = filename.split('.').pop(); 54 | switch (fileExt) { 55 | case 'js': 56 | npmRunD('build:markup'); 57 | break; 58 | case 'css': 59 | npmRunD('build:css'); 60 | break; 61 | default: 62 | console.log(`No watch commands written for .${fileExt} files`); 63 | } 64 | }); 65 | 66 | console.log('watching for changes...'); -------------------------------------------------------------------------------- /docs/main.css: -------------------------------------------------------------------------------- 1 | :root{--black:#222;--white:#fff;--yellow:#f7df1e;--blue:#12223f;--grey:#eae9e9}.clr-yellow{color:var(--yellow)}.clr-white{color:var(--white)}.clr-black{color:var(--black)}.bg-yellow{background-color:var(--yellow)}.bg-white{background-color:var(--white)}.bg-black{background-color:var(--black)}.bg-blue{background-color:var(--blue)}.fill-yellow{fill:var(--yellow)}.fill-white{fill:var(--white)}.fill-black{fill:var(--black)} 2 | 3 | /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,footer,header,main,menu,nav,section{display:block}a{background-color:transparent;-webkit-text-decoration-skip:objects}b,strong{font-weight:inherit;font-weight:bolder}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}h1{font-size:2em;margin:0}.inline-code{padding:4px 8px;border-radius:4px;background:var(--grey);font-size:16px}.svg-icon{height:3em;margin:17px 20px 20px 23px}.dev-icon,.svg-icon{width:3em;display:inline-block;vertical-align:middle}.dev-icon{text-decoration:none;height:1em;margin:.5em}body{font-family:Bungee,cursive;overflow-x:hidden}.screen-height{height:100vh}.text-center{text-align:center}.text-left{text-align:left}.v-center{vertical-align:middle}span.v-center{display:inline-block}.center-section{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.no-wrap{white-space:nowrap}@media (max-width:360px){.hide-small{display:none}}.fixed-bottom-right{position:fixed;bottom:26px;right:10px}@media (min-width:850px){.md-flex-row{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center}}.z-1{z-index:1}.z-2{z-index:2}.position-relative{position:relative}.padding-30{padding:30px} -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Mini Site Generator 2 | 3 | >Because I just wanted a really simple static site generator. 4 | 5 | ``` 6 | npm install mini-site-generator -D 7 | ``` 8 | 9 | or if you want to run it in your command line 10 | 11 | ``` 12 | npm i mini-site-generator -g 13 | ``` 14 | 15 | Make a file, `index.page.js`: 16 | 17 | ```js 18 | module.exports = function(){ 19 | return '

tada!

'; 20 | } 21 | ``` 22 | 23 | Now run the build, `msg`: 24 | 25 | `index.html`: 26 | ```html 27 |

tada!

28 | ``` 29 | 30 | Simple! The page.js file exports a function that returns a string. That string is then saved into a .html file of the same name. Because the page file is just JavaScript, you can do pretty much anything you want in there! 31 | 32 | ## Defining src & dist folders 33 | 34 | Pass in the relative paths of your source and distribution folders with the `-io` flag, "in" first "out" second: 35 | 36 | ``` 37 | msg -io ./src/ ./dist/ 38 | ``` 39 | 40 | Before: 41 | 42 | ``` 43 | ./dist/ 44 | ./src/ 45 | ├──index.page.js 46 | ├──about.page.js 47 | ├──/funFolder/ 48 | | ├──fun.page.js 49 | ``` 50 | 51 | After: 52 | 53 | ``` 54 | ./dist/ 55 | ├──index.html 56 | ├──about.html 57 | ├──/funFolder/ 58 | | ├──fun.html 59 | ./src/ 60 | ├──index.page.js 61 | ├──about.page.js 62 | ├──/funFolder/ 63 | | ├──fun.page.js 64 | ``` 65 | 66 | Alternativly you may wish to keep your source and distribution files together. Just pass the same value twice: 67 | 68 | ``` 69 | msg -io ./docs/ ./docs/ 70 | ``` 71 | 72 | Before: 73 | 74 | ``` 75 | ./docs/ 76 | ├──index.page.js 77 | ├──about.page.js 78 | ├──/funFolder/ 79 | | ├──fun.page.js 80 | ``` 81 | 82 | After: 83 | 84 | ``` 85 | ./docs/ 86 | ├──index.html - Hello! 87 | ├──index.page.js 88 | ├──about.html - hello 89 | ├──about.page.js 90 | ├──/funFolder/ 91 | | ├──fun.html - HELLO!! 92 | | ├──fun.page.js 93 | ``` 94 | 95 | You can even run both if you want to watch the world burn: `msg -io ./src/ ./dist/ -io ./docs/ ./docs/` 96 | 97 | --- 98 | 99 | ## Syntax Highlighting 100 | Different editors do this in different ways. VS Code requires [a plugin](https://marketplace.visualstudio.com/items?itemName=bierner.lit-html), Atom does not (according to a quick google). However it happens, in most cases you will need to add the `html` tag: 101 | 102 | ```js 103 | module.exports = function(){ 104 | return html` 105 |

Me Syntax be Higlighted!

106 | `; 107 | } 108 | ``` 109 | 110 | _Note that tags have to be defined functions. To make life a little easier I've added a global defenition for html so it won't throw undefined errors at you._ 111 | 112 | --- 113 | 114 | ## How does it work? 115 | 116 | I'm glad you asked! Time for sudocode. 117 | 118 | ```js 119 | //pass it a directory 120 | goIntoFolder(folder){ 121 | 122 | //get all the .page.js files in this directory 123 | pages = getAllPagesFrom(folder) 124 | 125 | pages.forEach(page){ 126 | //run the "page" 127 | markup = page() 128 | //save the result 129 | saveToDist(markup) 130 | } 131 | 132 | //now get all the folders in this folder 133 | folderChildren = getAllFoldersFrom(folder) 134 | 135 | folderChildren.forEach(childFolder){ 136 | //go into each of them and do the same thing 137 | goIntoFolder(childFolder) 138 | } 139 | } 140 | ``` 141 | 142 | _The real code is a little different, but that's the essence of it. [Have a read if you wish!](https://github.com/ijmccallum/Mini-Site-Generator/blob/master/build.js)_ 143 | 144 | --- 145 | -------------------------------------------------------------------------------- /docsrc/css/base/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in 9 | * IE on Windows Phone and in iOS. 10 | */ 11 | 12 | html { 13 | line-height: 1.15; /* 1 */ 14 | -ms-text-size-adjust: 100%; /* 2 */ 15 | -webkit-text-size-adjust: 100%; /* 2 */ 16 | } 17 | 18 | /* Sections 19 | ========================================================================== */ 20 | 21 | /** 22 | * Remove the margin in all browsers (opinionated). 23 | */ 24 | 25 | body { 26 | margin: 0; 27 | } 28 | 29 | /** 30 | * Add the correct display in IE 9-. 31 | */ 32 | 33 | article, 34 | aside, 35 | footer, 36 | header, 37 | nav, 38 | section, 39 | main, 40 | details, 41 | menu { 42 | display: block; 43 | } 44 | 45 | 46 | 47 | /* Text-level semantics 48 | ========================================================================== */ 49 | 50 | /** 51 | * 1. Remove the gray background on active links in IE 10. 52 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. 53 | */ 54 | 55 | a { 56 | background-color: transparent; /* 1 */ 57 | -webkit-text-decoration-skip: objects; /* 2 */ 58 | } 59 | 60 | 61 | 62 | /** 63 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6. 64 | */ 65 | 66 | b, 67 | strong { 68 | font-weight: inherit; 69 | font-weight: bolder; 70 | 71 | } 72 | /** 73 | * Add the correct font size in all browsers. 74 | */ 75 | 76 | small { 77 | font-size: 80%; 78 | } 79 | 80 | /** 81 | * Prevent `sub` and `sup` elements from affecting the line height in 82 | * all browsers. 83 | */ 84 | 85 | sub, 86 | sup { 87 | font-size: 75%; 88 | line-height: 0; 89 | position: relative; 90 | vertical-align: baseline; 91 | } 92 | 93 | sub { 94 | bottom: -0.25em; 95 | } 96 | 97 | sup { 98 | top: -0.5em; 99 | } 100 | 101 | 102 | 103 | 104 | /** 105 | * Remove the border on images inside links in IE 10-. 106 | */ 107 | 108 | img { 109 | border-style: none; 110 | } 111 | 112 | /** 113 | * Hide the overflow in IE. 114 | */ 115 | 116 | svg:not(:root) { 117 | overflow: hidden; 118 | } 119 | 120 | /* Forms 121 | ========================================================================== */ 122 | 123 | /** 124 | * 1. Change the font styles in all browsers (opinionated). 125 | * 2. Remove the margin in Firefox and Safari. 126 | */ 127 | 128 | button, 129 | input, 130 | optgroup, 131 | select, 132 | textarea { 133 | font-family: sans-serif; /* 1 */ 134 | font-size: 100%; /* 1 */ 135 | line-height: 1.15; /* 1 */ 136 | margin: 0; /* 2 */ 137 | } 138 | 139 | /** 140 | * Show the overflow in IE. 141 | * 1. Show the overflow in Edge. 142 | */ 143 | 144 | button, 145 | input { /* 1 */ 146 | overflow: visible; 147 | } 148 | 149 | /** 150 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 151 | * 1. Remove the inheritance of text transform in Firefox. 152 | */ 153 | 154 | button, 155 | select { /* 1 */ 156 | text-transform: none; 157 | } 158 | 159 | /** 160 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` 161 | * controls in Android 4. 162 | * 2. Correct the inability to style clickable types in iOS and Safari. 163 | */ 164 | 165 | button, 166 | html [type="button"], /* 1 */ 167 | [type="reset"], 168 | [type="submit"] { 169 | -webkit-appearance: button; /* 2 */ 170 | } 171 | 172 | /** 173 | * Remove the inner border and padding in Firefox. 174 | */ 175 | 176 | button::-moz-focus-inner, 177 | [type="button"]::-moz-focus-inner, 178 | [type="reset"]::-moz-focus-inner, 179 | [type="submit"]::-moz-focus-inner { 180 | border-style: none; 181 | padding: 0; 182 | } 183 | 184 | /** 185 | * Restore the focus styles unset by the previous rule. 186 | */ 187 | 188 | button:-moz-focusring, 189 | [type="button"]:-moz-focusring, 190 | [type="reset"]:-moz-focusring, 191 | [type="submit"]:-moz-focusring { 192 | outline: 1px dotted ButtonText; 193 | } 194 | 195 | 196 | /** 197 | * Remove the default vertical scrollbar in IE. 198 | */ 199 | 200 | textarea { 201 | overflow: auto; 202 | } 203 | 204 | /** 205 | * 1. Add the correct box sizing in IE 10-. 206 | * 2. Remove the padding in IE 10-. 207 | */ 208 | 209 | [type="checkbox"], 210 | [type="radio"] { 211 | box-sizing: border-box; /* 1 */ 212 | padding: 0; /* 2 */ 213 | } 214 | 215 | /** 216 | * Correct the cursor style of increment and decrement buttons in Chrome. 217 | */ 218 | 219 | [type="number"]::-webkit-inner-spin-button, 220 | [type="number"]::-webkit-outer-spin-button { 221 | height: auto; 222 | } 223 | 224 | /** 225 | * 1. Correct the odd appearance in Chrome and Safari. 226 | * 2. Correct the outline style in Safari. 227 | */ 228 | 229 | [type="search"] { 230 | -webkit-appearance: textfield; /* 1 */ 231 | outline-offset: -2px; /* 2 */ 232 | } 233 | 234 | /** 235 | * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. 236 | */ 237 | 238 | [type="search"]::-webkit-search-cancel-button, 239 | [type="search"]::-webkit-search-decoration { 240 | -webkit-appearance: none; 241 | } 242 | 243 | 244 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | Mini Site Generator

Mini Site Generator

It's Just JavaScript

____.page.js is transformed into ____.html


2 | module.exports = function(){
3 |     return html`
4 |         <p>It's just JavaScript!</p>
5 |     `
6 | }
7 | 
npm install mini-site-generator
-------------------------------------------------------------------------------- /docsrc/index.page.js: -------------------------------------------------------------------------------- 1 | let data = require('./index.data.js'); 2 | //index file in the root for github pages 3 | 4 | module.exports = function(){ 5 | return html` 6 | 7 | 8 | 9 | 10 | 11 | 12 | Mini Site Generator 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |

Mini Site Generator

21 |

It's Just JavaScript

22 |
23 | 24 |
25 |

26 | ____.page.js 27 | 28 | is transformed into 29 | 30 | 31 | ____.html 32 |

33 |
34 | 35 |
36 |

37 | module.exports = function(){
38 |     return html`
39 |         <p>It's just JavaScript!</p>
40 |     `
41 | }
42 | 
43 |
44 | 45 |
46 |
47 |
48 | npm install mini-site-generator 49 |
50 |
51 | 52 | 53 | 54 |
55 | 64 | 74 |
75 |
76 | 77 | 78 | 79 | `; 80 | } --------------------------------------------------------------------------------