├── .gitignore ├── CHANGELOG.md ├── README.md ├── img ├── new_homepage.png └── playground.png ├── models ├── session.js └── user.js ├── package-lock.json ├── package.json ├── public ├── 3d │ ├── OrbitControls.js │ ├── dat.gui.min.js │ ├── grasslight-big.jpg │ ├── main.css │ ├── three.js │ └── threejs.html ├── about.html ├── css │ ├── darcula.css │ ├── editor.css │ ├── icon │ │ ├── icon.css │ │ ├── materialicons-regular.ttf │ │ ├── materialicons-regular.woff │ │ └── materialicons-regular.woff2 │ ├── menu.css │ ├── normalize.css │ ├── skeleton.css │ ├── skin.css │ ├── spacecowboy.woff │ └── style.css ├── editor.html ├── editor.js ├── editor_old.html ├── filters.js ├── icon.png ├── img │ └── grammar.png ├── intro.html ├── lib │ ├── codemirror.css │ ├── codemirror.js │ ├── colorpicker │ │ ├── codemirror-colorpicker.css │ │ └── codemirror-colorpicker.js │ ├── marked.min.js │ ├── menu.js │ ├── mode │ │ ├── active-line.js │ │ ├── autorefresh.js │ │ ├── comment.js │ │ ├── continuecomment.js │ │ ├── gfm.js │ │ ├── javascript.js │ │ ├── markdown.js │ │ ├── overlay.js │ │ ├── pegjs.js │ │ ├── show-hint.css │ │ ├── show-hint.js │ │ └── simple.js │ ├── svg.filter.js │ └── svg.js ├── patterns.js ├── peg-0.10.0.js ├── quilting.html ├── target_lang.html ├── tutorial.html ├── variants.html └── vm.js └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .env 3 | node_modules -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## 2021 - 03 - 10 6 | ### General 7 | - Added changelog 8 | - Removed diary and knowledge documents (these can still be accessed in the v1 branch) 9 | - Changed all heading font to [spacecowboy](https://www.dafont.com/space-cowboy.font) 10 | - Added perfomances section to [about](https://www.barbara.graphics/about) page 11 | ### Barbara Playground 12 | - Added Ctrl-I and Ctrl-K commands for blend and pattern hints 13 | - Updated twitter link to use new url https://www.barbara.graphics/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Barbara 2 | 3 | Barbara is a novel live coding interface that enables users to design languages for visual geometric pattern generation. 4 | Inspired by algorithmic quilting patterns, Barbara helps users construct languages that mimic real-world quilting procedures 5 | which can be used for both live coding performance or digital quilt recreation. Users create languages and patterns in 6 | Barbara’s online coding environment with the Parsing Expression Grammar language formalism; the resulting patterns can 7 | be freely shared, remixed, and combined into new quilts. As a push towards the applications of language-oriented design, 8 | we created Barbara to broaden access to the power of computational media and the beauty of quilting for both programmers 9 | and quilters alike. Barbara is free and open-source software. 10 | 11 | ![Homepage](img/new_homepage.png) 12 | 13 | ![Playground](img/playground.png) 14 | 15 | ## Local Development Server 16 | 17 | In order to create a local development server, clone this repo. You will need to have a local instance of [MongoDB](https://docs.mongodb.com/manual/administration/install-community/) running. 18 | 19 | Then create a `.env` file in the root folder and add the connection string. If you have named the database barbara, this may look like `MONGODB_URI=mongodb://localhost:27017/barbara`. 20 | 21 | You can then run the server using `npm start`. 22 | 23 | ## Repo Structure 24 | 25 | - [changelog](CHANGELOG.md) barbara changelog 26 | - [public](public) directory contains the vm code 27 | - [models](models) directory contains the mongodb model schemas 28 | 29 | ## Acknowledgements 30 | 31 | Thank you to Charlie Roberts and Gillian Smith for their help and support. 32 | -------------------------------------------------------------------------------- /img/new_homepage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kitzeller/barbara/4c97e7228dcfa7766d7dec1d70a757505fe6f3bb/img/new_homepage.png -------------------------------------------------------------------------------- /img/playground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kitzeller/barbara/4c97e7228dcfa7766d7dec1d70a757505fe6f3bb/img/playground.png -------------------------------------------------------------------------------- /models/session.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var deepPopulate = require('mongoose-deep-populate')(mongoose); 3 | 4 | const sessionSchema = new mongoose.Schema({ 5 | name: String, 6 | user: {type: mongoose.Schema.Types.ObjectId, ref: 'User'}, 7 | svg: String, 8 | output: String, 9 | input: String, 10 | grammar: String, 11 | markdown: String, 12 | originalUrl: String, 13 | private: {type: Boolean, default: false}, 14 | height: Number, 15 | width: Number, 16 | parent: {type: mongoose.Schema.Types.ObjectId, ref: 'Session'}, 17 | children: [{type: mongoose.Schema.Types.ObjectId, ref: 'Session'}] 18 | }); 19 | 20 | var autoPopulateChildren = function(next) { 21 | this.populate('children'); 22 | next(); 23 | }; 24 | 25 | var autoPopulateParent = function(next) { 26 | this.populate('parent'); 27 | next(); 28 | }; 29 | 30 | sessionSchema 31 | .pre('findOne', autoPopulateChildren) 32 | .pre('find', autoPopulateChildren); 33 | 34 | sessionSchema.plugin(deepPopulate, { 35 | whitelist: [ 36 | // 'children', 37 | // 'parent' 38 | ] 39 | }); 40 | 41 | module.exports = mongoose.model('Session',sessionSchema); 42 | -------------------------------------------------------------------------------- /models/user.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var bcrypt = require('bcrypt'); 3 | 4 | const userSchema = new mongoose.Schema({ 5 | username: String, 6 | password: String, 7 | email: String, 8 | gender: String, 9 | address: String, 10 | sessions: [{type: mongoose.Schema.Types.ObjectId, ref: 'Session'}] 11 | }); 12 | 13 | // Before saving the user, hash the password 14 | userSchema.pre('save', function (next) { 15 | const user = this; 16 | if (!user.isModified('password')) { 17 | return next(); 18 | } 19 | bcrypt.genSalt(10, (err, salt) => { 20 | if (err) { 21 | return next(err); 22 | } 23 | bcrypt.hash(user.password, salt, (error, hash) => { 24 | if (error) { 25 | return next(error); 26 | } 27 | user.password = hash; 28 | next(); 29 | }); 30 | }); 31 | }); 32 | 33 | userSchema.methods.comparePassword = function (candidatePassword, callback) { 34 | bcrypt.compare(candidatePassword, this.password, (err, isMatch) => { 35 | if (err) { 36 | return callback(err); 37 | } 38 | callback(null, isMatch); 39 | }); 40 | }; 41 | 42 | // Omit the password when returning a user 43 | userSchema.set('toJSON', { 44 | transform: (doc, ret, options) => { 45 | delete ret.password; 46 | return ret; 47 | } 48 | }); 49 | 50 | module.exports = mongoose.model('User', userSchema); 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "barbara", 3 | "version": "1.0.0", 4 | "description": "livecoding languages for quilters", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/kitzeller/barbara.git" 13 | }, 14 | "author": "kit zellerbach", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/kitzeller/barbara/issues" 18 | }, 19 | "homepage": "https://github.com/kitzeller/barbara#readme", 20 | "dependencies": { 21 | "@svgdotjs/svg.filter.js": "^3.0.8", 22 | "@svgdotjs/svg.js": "^3.1.2", 23 | "babel-eslint": "^10.1.0", 24 | "bcrypt": "^5.1.0", 25 | "bcryptjs": "^2.4.3", 26 | "codemirror-colorpicker": "^1.9.80", 27 | "compression": "^1.7.4", 28 | "cookie-parser": "^1.4.6", 29 | "dotenv": "^8.6.0", 30 | "express": "^4.18.2", 31 | "express-session": "^1.17.3", 32 | "helmet": "^3.23.3", 33 | "mongoose": "^5.13.16", 34 | "mongoose-deep-populate": "^3.2.0", 35 | "morgan": "^1.10.0", 36 | "passport": "^0.6.0", 37 | "passport-local": "^1.0.0", 38 | "response-time": "^2.3.2", 39 | "serve-favicon": "^2.5.0", 40 | "socket.io": "^4.7.2", 41 | "timesync": "^1.0.11", 42 | "twitter-api-v2": "^1.15.2", 43 | "uuid": "^3.4.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /public/3d/grasslight-big.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kitzeller/barbara/4c97e7228dcfa7766d7dec1d70a757505fe6f3bb/public/3d/grasslight-big.jpg -------------------------------------------------------------------------------- /public/3d/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | background-color: #000; 4 | color: #fff; 5 | font-family: Monospace; 6 | font-size: 13px; 7 | line-height: 24px; 8 | overscroll-behavior: none; 9 | } 10 | 11 | a { 12 | color: #ff0; 13 | text-decoration: none; 14 | } 15 | 16 | a:hover { 17 | text-decoration: underline; 18 | } 19 | 20 | button { 21 | cursor: pointer; 22 | text-transform: uppercase; 23 | } 24 | 25 | canvas { 26 | display: block; 27 | } 28 | 29 | #info { 30 | position: absolute; 31 | top: 0px; 32 | width: 100%; 33 | padding: 10px; 34 | box-sizing: border-box; 35 | text-align: center; 36 | -moz-user-select: none; 37 | -webkit-user-select: none; 38 | -ms-user-select: none; 39 | user-select: none; 40 | pointer-events: none; 41 | z-index: 1; /* TODO Solve this in HTML */ 42 | } 43 | 44 | a, button, input, select { 45 | pointer-events: auto; 46 | } 47 | 48 | .dg.ac { 49 | -moz-user-select: none; 50 | -webkit-user-select: none; 51 | -ms-user-select: none; 52 | user-select: none; 53 | z-index: 2 !important; /* TODO Solve this in HTML */ 54 | } 55 | -------------------------------------------------------------------------------- /public/3d/threejs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | barbara 3D 5 | 6 | 7 | 8 | 18 | 19 | 20 | 21 |
3D Barbara Pattern 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 615 | 616 | 617 | -------------------------------------------------------------------------------- /public/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 22 | 23 | About 24 | 25 | 26 | 27 | 28 | 29 |
30 |

31 | Live Code a Graphics Language 32 |

33 | 34 | 35 | 36 | 37 |
38 | 39 |
40 |
41 |
42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 80 | 81 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /public/css/darcula.css: -------------------------------------------------------------------------------- 1 | /** 2 | Name: IntelliJ IDEA darcula theme 3 | From IntelliJ IDEA by JetBrains 4 | */ 5 | 6 | .cm-s-darcula { font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif;} 7 | .cm-s-darcula.CodeMirror { background: #2B2B2B; color: #A9B7C6; } 8 | 9 | .cm-s-darcula span.cm-meta { color: #BBB529; } 10 | .cm-s-darcula span.cm-number { color: #6897BB; } 11 | .cm-s-darcula span.cm-keyword { color: #CC7832; line-height: 1em; font-weight: bold; } 12 | .cm-s-darcula span.cm-def { color: #A9B7C6; font-style: italic; } 13 | .cm-s-darcula span.cm-variable { color: #A9B7C6; } 14 | .cm-s-darcula span.cm-variable-2 { color: #A9B7C6; } 15 | .cm-s-darcula span.cm-variable-3 { color: #9876AA; } 16 | .cm-s-darcula span.cm-type { color: #AABBCC; font-weight: bold; } 17 | .cm-s-darcula span.cm-property { color: #FFC66D; } 18 | .cm-s-darcula span.cm-operator { color: #A9B7C6; } 19 | .cm-s-darcula span.cm-string { color: #6A8759; } 20 | .cm-s-darcula span.cm-string-2 { color: #6A8759; } 21 | .cm-s-darcula span.cm-comment { color: #61A151; font-style: italic; } 22 | .cm-s-darcula span.cm-link { color: #CC7832; } 23 | .cm-s-darcula span.cm-atom { color: #CC7832; } 24 | .cm-s-darcula span.cm-error { color: #BC3F3C; } 25 | .cm-s-darcula span.cm-tag { color: #629755; font-weight: bold; font-style: italic; text-decoration: underline; } 26 | .cm-s-darcula span.cm-attribute { color: #6897bb; } 27 | .cm-s-darcula span.cm-qualifier { color: #6A8759; } 28 | .cm-s-darcula span.cm-bracket { color: #A9B7C6; } 29 | .cm-s-darcula span.cm-builtin { color: #FF9E59; } 30 | .cm-s-darcula span.cm-special { color: #FF9E59; } 31 | .cm-s-darcula span.cm-matchhighlight { color: #FFFFFF; background-color: rgba(50, 89, 48, .7); font-weight: normal;} 32 | .cm-s-darcula span.cm-searching { color: #FFFFFF; background-color: rgba(61, 115, 59, .7); font-weight: normal;} 33 | 34 | .cm-s-darcula .CodeMirror-cursor { border-left: 1px solid #A9B7C6; } 35 | .cm-s-darcula .CodeMirror-activeline-background { background: #323232; } 36 | .cm-s-darcula .CodeMirror-gutters { background: #313335; border-right: 1px solid #313335; } 37 | .cm-s-darcula .CodeMirror-guttermarker { color: #FFEE80; } 38 | .cm-s-darcula .CodeMirror-guttermarker-subtle { color: #D0D0D0; } 39 | .cm-s-darcula .CodeMirrir-linenumber { color: #606366; } 40 | .cm-s-darcula .CodeMirror-matchingbracket { background-color: #3B514D; color: #FFEF28 !important; font-weight: bold; } 41 | 42 | .cm-s-darcula div.CodeMirror-selected { background: #214283; } 43 | 44 | .CodeMirror-hints.darcula { 45 | font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; 46 | color: #9C9E9E; 47 | background-color: #3B3E3F !important; 48 | } 49 | 50 | .CodeMirror-hints.darcula .CodeMirror-hint-active { 51 | background-color: #494D4E !important; 52 | color: #9C9E9E !important; 53 | } 54 | -------------------------------------------------------------------------------- /public/css/editor.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: spacecowboy; 3 | src: url(spacecowboy.woff); 4 | } 5 | 6 | body { 7 | background-color: #222; 8 | color: white; 9 | margin: 0; 10 | padding-top: 40px; 11 | } 12 | 13 | h1, h2, h3 { 14 | font-family: spacecowboy !important; 15 | } 16 | 17 | code { 18 | /*Darkmode*/ 19 | color: #222; 20 | } 21 | 22 | .row { 23 | display: flex; 24 | } 25 | 26 | .column { 27 | width: 50%; 28 | } 29 | 30 | #output { 31 | color: black; 32 | } 33 | 34 | input { 35 | z-index: 1000; 36 | } 37 | 38 | .live-code-I { 39 | position: absolute; 40 | top: 50%; 41 | left: 70%; 42 | transform: translate(-50%, -50%); 43 | z-index: 149; 44 | } 45 | 46 | .live-code-II { 47 | position: absolute; 48 | top: 10%; 49 | left: 5%; 50 | z-index: 150; 51 | background: rgba(0, 0, 0, 0) !important; 52 | height: 80%; 53 | width: 35%; 54 | } 55 | 56 | /* The Modal (background) */ 57 | .modal { 58 | display: none; /* Hidden by default */ 59 | position: fixed; /* Stay in place */ 60 | z-index: 200; /* Sit on top */ 61 | padding-top: 100px; /* Location of the box */ 62 | left: 0; 63 | top: 0; 64 | width: 100%; /* Full width */ 65 | height: 100%; /* Full height */ 66 | overflow: auto; /* Enable scroll if needed */ 67 | background-color: rgb(0, 0, 0); /* Fallback color */ 68 | background-color: rgba(0, 0, 0, 0.4); /* Black w/ opacity */ 69 | } 70 | 71 | /* Modal Content */ 72 | .modal-content { 73 | background-color: rgba(0, 0, 0, 0.8); 74 | margin: auto; 75 | padding: 20px; 76 | border: 1px solid #888; 77 | width: 80%; 78 | } 79 | 80 | /* The Close Button */ 81 | .close { 82 | color: #aaaaaa; 83 | float: right; 84 | font-size: 28px; 85 | font-weight: bold; 86 | } 87 | 88 | .close:hover, 89 | .close:focus { 90 | color: #000; 91 | text-decoration: none; 92 | cursor: pointer; 93 | } 94 | 95 | #converter { 96 | display: none; 97 | } 98 | 99 | .content { 100 | margin: 10px; 101 | } 102 | 103 | .sidenav { 104 | height: 100%; 105 | width: 0; 106 | position: fixed; 107 | z-index: 200; 108 | top: 0; 109 | right: 0; 110 | background-color: rgba(0, 0, 0, 0.8); 111 | overflow-x: hidden; 112 | transition: 0.5s; 113 | padding-top: 60px; 114 | } 115 | 116 | .sidenav a { 117 | padding: 8px 8px 8px 32px; 118 | text-decoration: none; 119 | font-size: 25px; 120 | color: #818181; 121 | display: block; 122 | transition: 0.3s; 123 | } 124 | 125 | .sidenav h5:hover { 126 | color: #7c7c7c; 127 | } 128 | 129 | .sidenav .closebtn { 130 | position: absolute; 131 | top: 0; 132 | right: 25px; 133 | font-size: 36px; 134 | margin-left: 50px; 135 | } 136 | 137 | @media screen and (max-height: 450px) { 138 | .sidenav { 139 | padding-top: 15px; 140 | } 141 | 142 | .sidenav a { 143 | font-size: 18px; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /public/css/icon/icon.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "materialicons"; 3 | src: url(materialicons-regular.woff) format("woff"); 4 | } 5 | 6 | .Icon, *[data-icon]:before { 7 | display: inline-block; 8 | position: relative; 9 | width: 40px; 10 | height: 40px; 11 | line-height: 40px; 12 | 13 | font-family: "materialicons"; 14 | font-size: 24px; 15 | text-align: center; 16 | 17 | content: attr(data-icon); 18 | 19 | /* This is important for IE, which has it off by default */ 20 | font-feature-settings: "liga" 1; 21 | } 22 | 23 | .Icon.-small { 24 | font-size: 18px; 25 | } 26 | -------------------------------------------------------------------------------- /public/css/icon/materialicons-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kitzeller/barbara/4c97e7228dcfa7766d7dec1d70a757505fe6f3bb/public/css/icon/materialicons-regular.ttf -------------------------------------------------------------------------------- /public/css/icon/materialicons-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kitzeller/barbara/4c97e7228dcfa7766d7dec1d70a757505fe6f3bb/public/css/icon/materialicons-regular.woff -------------------------------------------------------------------------------- /public/css/icon/materialicons-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kitzeller/barbara/4c97e7228dcfa7766d7dec1d70a757505fe6f3bb/public/css/icon/materialicons-regular.woff2 -------------------------------------------------------------------------------- /public/css/menu.css: -------------------------------------------------------------------------------- 1 | /* 2 | Naming convention: 3 | Block 4 | element 5 | -modifier 6 | */ 7 | 8 | .Menu { 9 | display: none; 10 | position: relative; 11 | } 12 | 13 | /* Three basic menu layouts */ 14 | /* This also enforces that menu is a ul, and that 15 | * layout modifier is specified 16 | */ 17 | ul.Menu.-horizontal, 18 | ul.Menu.-vertical { 19 | display: inline-block; 20 | } 21 | 22 | ul.Menu.-floating { 23 | display: block; 24 | position: absolute; 25 | } 26 | 27 | /* Menu and menu-item layout */ 28 | .Menu, 29 | .Menu li, 30 | .Menu li > ul { 31 | list-style: none; 32 | padding: 0px; 33 | margin: 0px; 34 | } 35 | 36 | .Menu li { 37 | display: block; 38 | position: relative; 39 | white-space: nowrap; 40 | word-break: keep-all; 41 | } 42 | 43 | .Menu.-horizontal > li { 44 | display: inline-block; 45 | float: left; 46 | } 47 | 48 | .Menu li > * { 49 | display: block; 50 | position: relative; 51 | } 52 | 53 | .Menu li > ul { 54 | position: absolute; 55 | min-width: 100%; 56 | /*min-width: 100px;*/ 57 | top: 0px; 58 | left: 100%; 59 | } 60 | 61 | .Menu.-horizontal.-alignRight li > ul { 62 | left: auto; 63 | right: 100%; 64 | } 65 | 66 | .Menu.-horizontal.-alignRight > li > ul { 67 | right: 0px; 68 | } 69 | 70 | .Menu.-horizontal > li > ul { 71 | top: auto; 72 | left: auto; 73 | } 74 | 75 | /* Menu behaviour */ 76 | .Menu li > ul, 77 | .Menu.-floating { 78 | display: none; 79 | } 80 | 81 | .Menu li > ul.-visible, 82 | ul.Menu.-floating.-visible { 83 | display: block; 84 | } 85 | 86 | /* Menu animation */ 87 | .Menu li > ul, 88 | .Menu.-horizontal.-alignRight li > ul, 89 | .Menu.-floating { 90 | opacity: 1; 91 | transform: scale(1) translateY(0px); 92 | transform-origin: left top; 93 | } 94 | 95 | .Menu.-alignRight li > ul, 96 | .Menu.-floating.-alignRight { 97 | transform-origin: right top; 98 | } 99 | 100 | .Menu li > ul.-animating, 101 | .Menu.-floating.-animating { 102 | opacity: 0 !important; 103 | transform: scale(0.96) translateX(-16px); 104 | } 105 | 106 | .Menu li > ul.-animating { 107 | z-index: -1 !important; 108 | } 109 | 110 | .Menu.-horizontal > li > ul.-animating { 111 | transform: scale(0.96) translateY(-16px); 112 | } 113 | 114 | .Menu.-alignRight li > ul.-animating, 115 | .Menu.-floating.-alignRight.-animating { 116 | transform: scale(0.96) translateX(16px); 117 | } 118 | 119 | .Menu.-horizontal.-alignRight > li > ul.-animating { 120 | transform: scale(0.96) translateY(-16px); 121 | } 122 | 123 | /* Menu item icons */ 124 | .Menu *[data-icon]:before { 125 | position: absolute; 126 | left: 0px; 127 | top: 0px; 128 | bottom: 0px; 129 | margin: auto 0px; 130 | } 131 | 132 | .Menu .Icon, 133 | .Menu *[data-icon]:before { 134 | line-height: inherit; 135 | } 136 | 137 | .Menu .Icon { 138 | padding: 0px; 139 | } 140 | 141 | .Menu *:empty[data-icon] { 142 | padding-left: 0px !important; 143 | padding-right: 0px !important; 144 | } 145 | 146 | /* Submenu chevrons */ 147 | .Menu li.-hasSubmenu > a:after { 148 | display: block; 149 | position: absolute; 150 | 151 | width: 8px; 152 | height: 8px; 153 | right: 8px; 154 | bottom: 0px; 155 | top: 0px; 156 | margin: auto 0px; 157 | 158 | transform: rotate(45deg); 159 | border-width: 1px; 160 | border-color: black; 161 | border-style: solid solid none none; 162 | 163 | content: ""; 164 | } 165 | 166 | .Menu.-horizontal > li.-hasSubmenu > a:after { 167 | width: 4px; 168 | height: 4px; 169 | /*bottom: 4px;*/ 170 | /*top: auto;*/ 171 | /*left: 0px;*/ 172 | /*right: -5px;*/ 173 | /*margin-left: 10px;*/ 174 | 175 | border-style: none solid solid none; 176 | } 177 | 178 | .Menu li.-hasSubmenu.-noChevron > a:after { 179 | display: none; 180 | } 181 | 182 | /* 183 | ** Configurable values 184 | */ 185 | /* Height of navbar, and menu items */ 186 | /* All of these must be of equal value */ 187 | .Menu { 188 | line-height: 40px; 189 | } 190 | 191 | .Menu.-horizontal, 192 | .Menu li, 193 | .Menu li > :first-child { 194 | height: 40px; 195 | } 196 | 197 | .Menu *[data-icon]:before, 198 | .Menu .Icon { 199 | width: 40px; 200 | height: 40px; 201 | } 202 | 203 | .Menu *[data-icon] { 204 | min-width: 40px; 205 | min-height: 40px; 206 | padding-left: 40px; 207 | } 208 | 209 | /* Default font settings for menu */ 210 | .Menu { 211 | font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; 212 | /*font-family: Arial, Helvetica, Sans;*/ 213 | font-size: 16px; 214 | } 215 | 216 | /* Icon font sizes */ 217 | .Menu *[data-icon]:before, 218 | .Menu .Icon { 219 | font-size: 24px; 220 | } 221 | 222 | /* For submenus */ 223 | .Menu > li ul .Icon, 224 | .Menu.-floating .Icon, 225 | .Menu > li ul *[data-icon]:before, 226 | .Menu.-floating *[data-icon]:before 227 | { 228 | font-size: 18px; 229 | } 230 | 231 | /* Colouring of menus */ 232 | /* Foreground */ 233 | .Menu, 234 | .Menu li.-hasSubmenu > a:after { 235 | color: black; 236 | border-color: black; /* chevron colour */ 237 | } 238 | 239 | /* Background */ 240 | .Menu ul, 241 | .Menu.-floating { 242 | background: white; 243 | } 244 | 245 | /* Padding for each menu item */ 246 | .Menu li > * { 247 | padding: 0px 20px; 248 | } 249 | 250 | /* Indent of chevron */ 251 | .Menu li li.-hasSubmenu > a:after, 252 | .Menu:not(.-horizontal) > li.-hasSubmenu > a:after { 253 | right: 12px; 254 | } 255 | 256 | .Menu li li.-hasSubmenu > a, 257 | .Menu:not(.-horizontal) > li.-hasSubmenu > a { 258 | padding-right: 28px; 259 | } 260 | 261 | /* Minimum width of dropdown menus */ 262 | .Menu.-horizontal li > ul, 263 | .Menu.-floating { 264 | min-width: 200px; 265 | } 266 | 267 | /* Animation speed of dropdown menus */ 268 | .Menu li > ul, 269 | .Menu.-floating { 270 | transition: width 0.1s, height 0.1s, transform 0.1s, opacity 0.1s; 271 | } 272 | 273 | /* Styling of hyperlink text */ 274 | .Menu li > a { 275 | text-decoration: none; 276 | color: inherit; 277 | } 278 | 279 | /* Animation speed of :hover shading */ 280 | .Menu li > a:first-child { 281 | transition: background-color 0.2s; 282 | } 283 | 284 | /* Colour of :hover shading */ 285 | .Menu li:hover > a:first-child, 286 | .Menu li.-active > a:first-child { 287 | background-color: rgba(0, 0, 0, 0.1); 288 | } 289 | 290 | /* 291 | ** End configurable values 292 | */ 293 | -------------------------------------------------------------------------------- /public/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability when focused and also mouse hovered in all browsers. 95 | */ 96 | 97 | a:active, 98 | a:hover { 99 | outline: 0; 100 | } 101 | 102 | /* Text-level semantics 103 | ========================================================================== */ 104 | 105 | /** 106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 107 | */ 108 | 109 | abbr[title] { 110 | border-bottom: 1px dotted; 111 | } 112 | 113 | /** 114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 115 | */ 116 | 117 | b, 118 | strong { 119 | font-weight: bold; 120 | } 121 | 122 | /** 123 | * Address styling not present in Safari and Chrome. 124 | */ 125 | 126 | dfn { 127 | font-style: italic; 128 | } 129 | 130 | /** 131 | * Address variable `h1` font-size and margin within `section` and `article` 132 | * contexts in Firefox 4+, Safari, and Chrome. 133 | */ 134 | 135 | h1 { 136 | font-size: 2em; 137 | margin: 0.67em 0; 138 | } 139 | 140 | /** 141 | * Address styling not present in IE 8/9. 142 | */ 143 | 144 | mark { 145 | background: #ff0; 146 | color: #000; 147 | } 148 | 149 | /** 150 | * Address inconsistent and variable font size in all browsers. 151 | */ 152 | 153 | small { 154 | font-size: 80%; 155 | } 156 | 157 | /** 158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 159 | */ 160 | 161 | sub, 162 | sup { 163 | font-size: 75%; 164 | line-height: 0; 165 | position: relative; 166 | vertical-align: baseline; 167 | } 168 | 169 | sup { 170 | top: -0.5em; 171 | } 172 | 173 | sub { 174 | bottom: -0.25em; 175 | } 176 | 177 | /* Embedded content 178 | ========================================================================== */ 179 | 180 | /** 181 | * Remove border when inside `a` element in IE 8/9/10. 182 | */ 183 | 184 | img { 185 | border: 0; 186 | } 187 | 188 | /** 189 | * Correct overflow not hidden in IE 9/10/11. 190 | */ 191 | 192 | svg:not(:root) { 193 | overflow: hidden; 194 | } 195 | 196 | /* Grouping content 197 | ========================================================================== */ 198 | 199 | /** 200 | * Address margin not present in IE 8/9 and Safari. 201 | */ 202 | 203 | figure { 204 | margin: 1em 40px; 205 | } 206 | 207 | /** 208 | * Address differences between Firefox and other browsers. 209 | */ 210 | 211 | hr { 212 | -moz-box-sizing: content-box; 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 354 | * (include `-moz` to future-proof). 355 | */ 356 | 357 | input[type="search"] { 358 | -webkit-appearance: textfield; /* 1 */ 359 | -moz-box-sizing: content-box; 360 | -webkit-box-sizing: content-box; /* 2 */ 361 | box-sizing: content-box; 362 | } 363 | 364 | /** 365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 366 | * Safari (but not Chrome) clips the cancel button when the search input has 367 | * padding (and `textfield` appearance). 368 | */ 369 | 370 | input[type="search"]::-webkit-search-cancel-button, 371 | input[type="search"]::-webkit-search-decoration { 372 | -webkit-appearance: none; 373 | } 374 | 375 | /** 376 | * Define consistent border, margin, and padding. 377 | */ 378 | 379 | fieldset { 380 | border: 1px solid #c0c0c0; 381 | margin: 0 2px; 382 | padding: 0.35em 0.625em 0.75em; 383 | } 384 | 385 | /** 386 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 388 | */ 389 | 390 | legend { 391 | border: 0; /* 1 */ 392 | padding: 0; /* 2 */ 393 | } 394 | 395 | /** 396 | * Remove default vertical scrollbar in IE 8/9/10/11. 397 | */ 398 | 399 | textarea { 400 | overflow: auto; 401 | } 402 | 403 | /** 404 | * Don't inherit the `font-weight` (applied by a rule above). 405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 406 | */ 407 | 408 | optgroup { 409 | font-weight: bold; 410 | } 411 | 412 | /* Tables 413 | ========================================================================== */ 414 | 415 | /** 416 | * Remove most spacing between table cells. 417 | */ 418 | 419 | table { 420 | border-collapse: collapse; 421 | border-spacing: 0; 422 | } 423 | 424 | td, 425 | th { 426 | padding: 0; 427 | } -------------------------------------------------------------------------------- /public/css/skeleton.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Skeleton V2.0.4 3 | * Copyright 2014, Dave Gamache 4 | * www.getskeleton.com 5 | * Free to use under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * 12/29/2014 8 | */ 9 | 10 | 11 | /* Table of contents 12 | –––––––––––––––––––––––––––––––––––––––––––––––––– 13 | - Grid 14 | - Base Styles 15 | - Typography 16 | - Links 17 | - Buttons 18 | - Forms 19 | - Lists 20 | - Code 21 | - Tables 22 | - Spacing 23 | - Utilities 24 | - Clearing 25 | - Media Queries 26 | */ 27 | 28 | 29 | /* Grid 30 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 31 | .container { 32 | position: relative; 33 | width: 100%; 34 | max-width: 960px; 35 | margin: 0 auto; 36 | padding: 0 20px; 37 | box-sizing: border-box; } 38 | .column, 39 | .columns { 40 | width: 100%; 41 | float: left; 42 | box-sizing: border-box; } 43 | 44 | /* For devices larger than 400px */ 45 | @media (min-width: 400px) { 46 | .container { 47 | width: 85%; 48 | padding: 0; } 49 | } 50 | 51 | /* For devices larger than 550px */ 52 | @media (min-width: 550px) { 53 | .container { 54 | width: 80%; } 55 | .column, 56 | .columns { 57 | margin-left: 4%; } 58 | .column:first-child, 59 | .columns:first-child { 60 | margin-left: 0; } 61 | 62 | .one.column, 63 | .one.columns { width: 4.66666666667%; } 64 | .two.columns { width: 13.3333333333%; } 65 | .three.columns { width: 22%; } 66 | .four.columns { width: 30.6666666667%; } 67 | .five.columns { width: 39.3333333333%; } 68 | .six.columns { width: 48%; } 69 | .seven.columns { width: 56.6666666667%; } 70 | .eight.columns { width: 65.3333333333%; } 71 | .nine.columns { width: 74.0%; } 72 | .ten.columns { width: 82.6666666667%; } 73 | .eleven.columns { width: 91.3333333333%; } 74 | .twelve.columns { width: 100%; margin-left: 0; } 75 | 76 | .one-third.column { width: 30.6666666667%; } 77 | .two-thirds.column { width: 65.3333333333%; } 78 | 79 | .one-half.column { width: 48%; } 80 | 81 | /* Offsets */ 82 | .offset-by-one.column, 83 | .offset-by-one.columns { margin-left: 8.66666666667%; } 84 | .offset-by-two.column, 85 | .offset-by-two.columns { margin-left: 17.3333333333%; } 86 | .offset-by-three.column, 87 | .offset-by-three.columns { margin-left: 26%; } 88 | .offset-by-four.column, 89 | .offset-by-four.columns { margin-left: 34.6666666667%; } 90 | .offset-by-five.column, 91 | .offset-by-five.columns { margin-left: 43.3333333333%; } 92 | .offset-by-six.column, 93 | .offset-by-six.columns { margin-left: 52%; } 94 | .offset-by-seven.column, 95 | .offset-by-seven.columns { margin-left: 60.6666666667%; } 96 | .offset-by-eight.column, 97 | .offset-by-eight.columns { margin-left: 69.3333333333%; } 98 | .offset-by-nine.column, 99 | .offset-by-nine.columns { margin-left: 78.0%; } 100 | .offset-by-ten.column, 101 | .offset-by-ten.columns { margin-left: 86.6666666667%; } 102 | .offset-by-eleven.column, 103 | .offset-by-eleven.columns { margin-left: 95.3333333333%; } 104 | 105 | .offset-by-one-third.column, 106 | .offset-by-one-third.columns { margin-left: 34.6666666667%; } 107 | .offset-by-two-thirds.column, 108 | .offset-by-two-thirds.columns { margin-left: 69.3333333333%; } 109 | 110 | .offset-by-one-half.column, 111 | .offset-by-one-half.columns { margin-left: 52%; } 112 | 113 | } 114 | 115 | 116 | /* Base Styles 117 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 118 | /* NOTE 119 | html is set to 62.5% so that all the REM measurements throughout Skeleton 120 | are based on 10px sizing. So basically 1.5rem = 15px :) */ 121 | html { 122 | font-size: 62.5%; } 123 | body { 124 | font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */ 125 | line-height: 1.6; 126 | font-weight: 400; 127 | font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; 128 | color: #222; } 129 | 130 | 131 | /* Typography 132 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 133 | h1, h2, h3, h4, h5, h6 { 134 | margin-top: 0; 135 | margin-bottom: 2rem; 136 | font-weight: 300; } 137 | h1 { font-size: 4.0rem; line-height: 1.2; letter-spacing: -.1rem;} 138 | h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; } 139 | h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; } 140 | h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; } 141 | h5 { font-size: 1.8rem; line-height: 1.5; letter-spacing: -.05rem; } 142 | h6 { font-size: 1.5rem; line-height: 1.6; letter-spacing: 0; } 143 | 144 | /* Larger than phablet */ 145 | @media (min-width: 550px) { 146 | h1 { font-size: 5.0rem; } 147 | h2 { font-size: 4.2rem; } 148 | h3 { font-size: 3.6rem; } 149 | h4 { font-size: 3.0rem; } 150 | h5 { font-size: 2.4rem; } 151 | h6 { font-size: 1.5rem; } 152 | } 153 | 154 | p { 155 | margin-top: 0; } 156 | 157 | 158 | /* Links 159 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 160 | a { 161 | color: #1EAEDB; } 162 | a:hover { 163 | color: #0FA0CE; } 164 | 165 | 166 | /* Buttons 167 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 168 | .button, 169 | button, 170 | input[type="submit"], 171 | input[type="reset"], 172 | input[type="button"] { 173 | display: inline-block; 174 | height: 38px; 175 | padding: 0 30px; 176 | /*Darkmode*/ 177 | color: #ffffff; 178 | text-align: center; 179 | font-size: 11px; 180 | font-weight: 600; 181 | line-height: 38px; 182 | letter-spacing: .1rem; 183 | text-transform: uppercase; 184 | text-decoration: none; 185 | white-space: nowrap; 186 | background-color: transparent; 187 | border-radius: 4px; 188 | border: 1px solid #bbb; 189 | cursor: pointer; 190 | box-sizing: border-box; } 191 | .button:hover, 192 | button:hover, 193 | input[type="submit"]:hover, 194 | input[type="reset"]:hover, 195 | input[type="button"]:hover, 196 | .button:focus, 197 | button:focus, 198 | input[type="submit"]:focus, 199 | input[type="reset"]:focus, 200 | input[type="button"]:focus { 201 | color: #333; 202 | border-color: #888; 203 | outline: 0; } 204 | .button.button-primary, 205 | button.button-primary, 206 | input[type="submit"].button-primary, 207 | input[type="reset"].button-primary, 208 | input[type="button"].button-primary { 209 | color: #FFF; 210 | background-color: #33C3F0; 211 | border-color: #33C3F0; } 212 | .button.button-primary:hover, 213 | button.button-primary:hover, 214 | input[type="submit"].button-primary:hover, 215 | input[type="reset"].button-primary:hover, 216 | input[type="button"].button-primary:hover, 217 | .button.button-primary:focus, 218 | button.button-primary:focus, 219 | input[type="submit"].button-primary:focus, 220 | input[type="reset"].button-primary:focus, 221 | input[type="button"].button-primary:focus { 222 | color: #FFF; 223 | background-color: #1EAEDB; 224 | border-color: #1EAEDB; } 225 | 226 | 227 | /* Forms 228 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 229 | input[type="email"], 230 | input[type="number"], 231 | input[type="search"], 232 | input[type="text"], 233 | input[type="tel"], 234 | input[type="url"], 235 | input[type="password"], 236 | textarea, 237 | select { 238 | height: 38px; 239 | padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ 240 | background-color: #fff; 241 | border: 1px solid #D1D1D1; 242 | border-radius: 4px; 243 | box-shadow: none; 244 | box-sizing: border-box; } 245 | /* Removes awkward default styles on some inputs for iOS */ 246 | input[type="email"], 247 | input[type="number"], 248 | input[type="search"], 249 | input[type="text"], 250 | input[type="tel"], 251 | input[type="url"], 252 | input[type="password"], 253 | textarea { 254 | -webkit-appearance: none; 255 | -moz-appearance: none; 256 | appearance: none; } 257 | textarea { 258 | min-height: 65px; 259 | padding-top: 6px; 260 | padding-bottom: 6px; } 261 | input[type="email"]:focus, 262 | input[type="number"]:focus, 263 | input[type="search"]:focus, 264 | input[type="text"]:focus, 265 | input[type="tel"]:focus, 266 | input[type="url"]:focus, 267 | input[type="password"]:focus, 268 | textarea:focus, 269 | select:focus { 270 | border: 1px solid #33C3F0; 271 | outline: 0; } 272 | label, 273 | legend { 274 | display: block; 275 | margin-bottom: .5rem; 276 | font-weight: 600; } 277 | fieldset { 278 | padding: 0; 279 | border-width: 0; } 280 | input[type="checkbox"], 281 | input[type="radio"] { 282 | display: inline; } 283 | label > .label-body { 284 | display: inline-block; 285 | margin-left: .5rem; 286 | font-weight: normal; } 287 | 288 | 289 | /* Lists 290 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 291 | ul { 292 | list-style: circle inside; } 293 | ol { 294 | list-style: decimal inside; } 295 | ol, ul { 296 | padding-left: 0; 297 | margin-top: 0; } 298 | ul ul, 299 | ul ol, 300 | ol ol, 301 | ol ul { 302 | margin: 1.5rem 0 1.5rem 3rem; 303 | font-size: 90%; } 304 | li { 305 | margin-bottom: 1rem; } 306 | 307 | 308 | /* Code 309 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 310 | code { 311 | padding: .2rem .5rem; 312 | margin: 0 .2rem; 313 | font-size: 90%; 314 | white-space: nowrap; 315 | background: #F1F1F1; 316 | border: 1px solid #E1E1E1; 317 | border-radius: 4px; } 318 | pre > code { 319 | display: block; 320 | padding: 1rem 1.5rem; 321 | white-space: pre; } 322 | 323 | 324 | /* Tables 325 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 326 | th, 327 | td { 328 | padding: 12px 15px; 329 | text-align: left; 330 | border-bottom: 1px solid #E1E1E1; } 331 | th:first-child, 332 | td:first-child { 333 | padding-left: 0; } 334 | th:last-child, 335 | td:last-child { 336 | padding-right: 0; } 337 | 338 | 339 | /* Spacing 340 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 341 | button, 342 | .button { 343 | margin-bottom: 1rem; } 344 | input, 345 | textarea, 346 | select, 347 | fieldset { 348 | margin-bottom: 1.5rem; } 349 | pre, 350 | blockquote, 351 | dl, 352 | figure, 353 | table, 354 | p, 355 | ul, 356 | ol, 357 | form { 358 | margin-bottom: 2.5rem; } 359 | 360 | 361 | /* Utilities 362 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 363 | .u-full-width { 364 | width: 100%; 365 | box-sizing: border-box; } 366 | .u-max-full-width { 367 | max-width: 100%; 368 | box-sizing: border-box; } 369 | .u-pull-right { 370 | float: right; } 371 | .u-pull-left { 372 | float: left; } 373 | 374 | 375 | /* Misc 376 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 377 | hr { 378 | margin-top: 3rem; 379 | margin-bottom: 3.5rem; 380 | border-width: 0; 381 | border-top: 1px solid #E1E1E1; } 382 | 383 | 384 | /* Clearing 385 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 386 | 387 | /* Self Clearing Goodness */ 388 | .container:after, 389 | .row:after, 390 | .u-cf { 391 | content: ""; 392 | display: table; 393 | clear: both; } 394 | 395 | 396 | /* Media Queries 397 | –––––––––––––––––––––––––––––––––––––––––––––––––– */ 398 | /* 399 | Note: The best way to structure the use of media queries is to create the queries 400 | near the relevant code. For example, if you wanted to change the styles for buttons 401 | on small devices, paste the mobile query code up in the buttons section and style it 402 | there. 403 | */ 404 | 405 | 406 | /* Larger than mobile */ 407 | @media (min-width: 400px) {} 408 | 409 | /* Larger than phablet (also point when grid becomes active) */ 410 | @media (min-width: 550px) {} 411 | 412 | /* Larger than tablet */ 413 | @media (min-width: 750px) {} 414 | 415 | /* Larger than desktop */ 416 | @media (min-width: 1000px) {} 417 | 418 | /* Larger than Desktop HD */ 419 | @media (min-width: 1200px) {} 420 | -------------------------------------------------------------------------------- /public/css/skin.css: -------------------------------------------------------------------------------- 1 | .Menu { 2 | color: black; 3 | cursor: pointer; 4 | } 5 | 6 | /* Foreground */ 7 | .Menu, 8 | .Menu li.-hasSubmenu > a:after { 9 | color: black; 10 | border-color: black; /* chevron colour */ 11 | } 12 | 13 | /* Background */ 14 | .Menu, 15 | .Menu li ul { 16 | background: white; 17 | } 18 | 19 | /* Border */ 20 | .Menu, 21 | .Menu li ul { 22 | box-shadow: none; 23 | } 24 | 25 | .Menu li ul, 26 | .Menu.-floating { 27 | border-radius: 3px; 28 | } 29 | 30 | .Menu.-horizontal > li > ul { 31 | border-top-left-radius: 0px; 32 | border-top-right-radius: 0px; 33 | } 34 | 35 | .Menu:not(.-alignRight) li > ul, 36 | .Menu.-floating:not(.-alignRight) { 37 | border-top-left-radius: 0px; 38 | } 39 | 40 | .Menu.-alignRight li > ul, 41 | .Menu.-floating.-alignRight { 42 | border-top-right-radius: 0px; 43 | } 44 | 45 | /* Drop shadow on Menus and header */ 46 | .Header, 47 | .Menu li ul, 48 | .Menu.-floating { 49 | box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.2), 50 | 0px 3px 1px 0px rgba(0, 0, 0, 0.2), 51 | 0px 6px 12px 0px rgba(0, 0, 0, 0.15); 52 | } 53 | 54 | .Header { 55 | position: fixed; 56 | display: block; 57 | z-index: 100; 58 | top: 0px; 59 | left: 0px; 60 | right: 0px; 61 | } 62 | 63 | /* Header and Menu sizing */ 64 | .Header .Menu { 65 | line-height: 40px; 66 | } 67 | 68 | .Header, 69 | .Header .Menu.-horizontal, 70 | .Header .Menu li, 71 | .Header .Menu li > :first-child { 72 | height: 40px; 73 | } 74 | 75 | .Header .Menu *[data-icon]:before, 76 | .Header .Menu .Icon { 77 | width: 40px; 78 | height: 40px; 79 | } 80 | 81 | .Header .Menu *[data-icon] { 82 | min-width: 40px; 83 | min-height: 40px; 84 | padding-left: 40px; 85 | } 86 | 87 | /* Menu positioning in Header */ 88 | .Header .Menu.-horizontal { 89 | position: absolute; 90 | top: 0px; 91 | left: 0px; 92 | } 93 | 94 | .Header .Menu.-horizontal.-alignRight { 95 | left: auto; 96 | right: 0px; 97 | } 98 | 99 | /* Colouring/styling of Header and its Menus */ 100 | .Header { 101 | background: rgba(0, 0, 0, 0.8); 102 | } 103 | 104 | .Header .Menu { 105 | background: none; 106 | } 107 | 108 | .Header .Menu, 109 | .Header .Menu > li.-hasSubmenu > a:after { 110 | color: white; 111 | border-color: white; 112 | } 113 | 114 | .Header .Menu li ul { 115 | color: black; 116 | } 117 | 118 | .Header .Menu > li:hover > a:first-child, 119 | .Header .Menu > li.-active > a:first-child { 120 | background-color: white; 121 | color: black; 122 | /*background-color: rgba(255,255,255,0.2);*/ 123 | } 124 | -------------------------------------------------------------------------------- /public/css/spacecowboy.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kitzeller/barbara/4c97e7228dcfa7766d7dec1d70a757505fe6f3bb/public/css/spacecowboy.woff -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: spacecowboy; 3 | src: url(spacecowboy.woff); 4 | } 5 | 6 | .container-m10 { 7 | margin: 10px; 8 | font-size: 14px; 9 | } 10 | 11 | h1, 12 | h2, 13 | h3 { 14 | font-family: spacecowboy !important; 15 | } 16 | 17 | body { 18 | background-color: #222; 19 | color: white; 20 | margin: 0; 21 | } 22 | 23 | code { 24 | /*Darkmode*/ 25 | color: #222; 26 | } 27 | 28 | .row { 29 | display: flex; 30 | } 31 | 32 | .column { 33 | /*flex: 50%;*/ 34 | width: 50%; 35 | } 36 | 37 | .sidenav { 38 | height: 100%; 39 | width: 0; 40 | position: fixed; 41 | z-index: 200; 42 | top: 0; 43 | right: 0; 44 | background-color: rgba(0, 0, 0, 0.8); 45 | overflow-x: hidden; 46 | transition: 0.5s; 47 | padding-top: 60px; 48 | } 49 | 50 | .sidenav a { 51 | padding: 8px 8px 8px 32px; 52 | text-decoration: none; 53 | font-size: 25px; 54 | color: #818181; 55 | display: block; 56 | transition: 0.3s; 57 | } 58 | 59 | .sidenav h5:hover { 60 | color: #7c7c7c; 61 | } 62 | 63 | .sidenav .closebtn { 64 | position: absolute; 65 | top: 0; 66 | right: 25px; 67 | font-size: 36px; 68 | margin-left: 50px; 69 | } 70 | 71 | @media screen and (max-height: 450px) { 72 | .sidenav { 73 | padding-top: 15px; 74 | } 75 | 76 | .sidenav a { 77 | font-size: 18px; 78 | } 79 | } 80 | 81 | .fixed-header { 82 | width: 100%; 83 | pointer-events: none; 84 | z-index: 200; 85 | position: fixed; 86 | transform: translateY(35%); 87 | text-align: center; 88 | } 89 | 90 | .fixed-header h1 { 91 | opacity: 0.8; 92 | color: rgba(255, 255, 255, 1); 93 | font-size: 18.5vw; 94 | letter-spacing: 6.5px; 95 | text-shadow: -0.5px -0.5px 10px #000, 96 | 0.5px -0.5px 10px #000, 97 | -0.5px 0.5px 10px #000, 98 | 0.5px 0.5px 10px #000; 99 | } 100 | 101 | .fixed-header h3 { 102 | text-shadow: -0.5px -0.5px 10px #000, 103 | 0.5px -0.5px 10px #000, 104 | -0.5px 0.5px 10px #000, 105 | 0.5px 0.5px 10px #000; 106 | } 107 | 108 | 109 | #panel { 110 | position: fixed; 111 | z-index: 100; 112 | top: 0; 113 | left: 0; 114 | width: 25%; 115 | height: 100%; 116 | overflow: auto; 117 | display: flex; 118 | flex-direction: column; 119 | } 120 | 121 | /*Ghost Button*/ 122 | .ghost-button { 123 | display: inline-block; 124 | width: 150px; 125 | font-weight: bold; 126 | padding: 8px; 127 | color: white; 128 | border: 3px solid white; 129 | text-align: center; 130 | outline: none; 131 | text-decoration: none; 132 | background-color: transparent; 133 | transition: background-color 0.2s ease-out, 134 | color 0.2s ease-out; 135 | } 136 | 137 | .ghost-button:hover, 138 | .ghost-button:active { 139 | background-color: white; 140 | color: #000; 141 | transition: background-color 0.3s ease-in, 142 | color 0.3s ease-in; 143 | } 144 | 145 | .ghost-active { 146 | background-color: white; 147 | color: #000; 148 | transition: background-color 0.3s ease-in, 149 | color 0.3s ease-in; 150 | } 151 | 152 | 153 | .top-bar { 154 | position: fixed; 155 | top: 0; 156 | z-index: 200; 157 | width: 100%; 158 | text-align: center; 159 | background-color: rgba(0, 0, 0, 0.5); 160 | } 161 | 162 | .overlay { 163 | transition: .5s ease; 164 | opacity: 0; 165 | position: absolute; 166 | top: 50%; 167 | left: 50%; 168 | transform: translate(-50%, -50%); 169 | -ms-transform: translate(-50%, -50%); 170 | text-align: center; 171 | } 172 | 173 | .svg-container svg, 174 | .overlay { 175 | will-change: opacity; 176 | } 177 | 178 | .svg-container:hover .overlay { 179 | opacity: 1; 180 | } 181 | 182 | .svg-container:hover svg { 183 | opacity: 0.5; 184 | } 185 | 186 | .svg-container:hover button { 187 | margin: 5px; 188 | color: white; 189 | font-size: 16px; 190 | } -------------------------------------------------------------------------------- /public/editor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Barbara Playground 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 90 | 118 |
119 | 120 | 121 |
122 |

Grammar

123 | Shift+Enter to check the parser 124 |
125 | 208 |
209 |

Output

210 | 211 |
212 | 213 | 214 | 240 | 241 | 242 | 256 | 257 | 258 | 277 | 278 | 279 | 289 | 290 | 291 |
292 | × 293 |
294 |

Your Account

295 |
296 |
297 |
298 | 302 | 306 | 307 |
308 | 309 |
310 |
311 |
312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | -------------------------------------------------------------------------------- /public/filters.js: -------------------------------------------------------------------------------- 1 | var filters = { 2 | "layerednet": "\n" + 3 | "\t\n" + 4 | "\t\n" + 8 | "\t\n" + 9 | "\t\n" + 10 | "\t\n" + 11 | "", 12 | "dust": "\n" + 13 | "\t\n" + 14 | "\t\n" + 18 | "\t\n" + 19 | "\t\n" + 20 | "\t\n" + 21 | "", 22 | "smoke": "\n" + 23 | "\t\n" + 24 | "\t\n" + 25 | "\t\n" + 26 | "\t\n" + 27 | "", 28 | "shrooms": "\n" + 29 | "\t\n" + 30 | "\t\n" + 31 | "\t\n" + 32 | " \t\t\n" + 33 | "\t\t\n" + 34 | " \t\n" + 35 | "\t\n" + 36 | "\t\n" + 40 | "\t\n" + 41 | "\t\n" + 42 | "", 43 | "roughpaper": "\n" + 44 | "\n" + 45 | " \n" + 46 | " \n" + 47 | "\n" + 48 | "", 49 | "neon_glow": "\n" + 50 | " \n" + 51 | " ", 52 | "neon_blur": "\n" + 53 | "\n" + 54 | "" 55 | }; 56 | 57 | window.filters = filters; 58 | 59 | -------------------------------------------------------------------------------- /public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kitzeller/barbara/4c97e7228dcfa7766d7dec1d70a757505fe6f3bb/public/icon.png -------------------------------------------------------------------------------- /public/img/grammar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kitzeller/barbara/4c97e7228dcfa7766d7dec1d70a757505fe6f3bb/public/img/grammar.png -------------------------------------------------------------------------------- /public/intro.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 77 | Barbara 78 | 79 | 80 | 81 | 82 |
83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 |
91 | 92 |
93 |

BARBARA

94 |

Live coding languages for quilters.

95 |
96 | 97 |
98 | 99 | 100 |
101 | × 102 |
103 |

Your Account

104 |
105 |
106 |
107 |

Login

108 | 112 | 116 | 117 |
118 | 119 |
120 |
121 |
122 | 123 | 124 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /public/lib/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 100%; 7 | color: black; 8 | direction: ltr; 9 | } 10 | 11 | /* PADDING */ 12 | 13 | .CodeMirror-lines { 14 | padding: 4px 0; /* Vertical padding around content */ 15 | } 16 | .CodeMirror pre.CodeMirror-line, 17 | .CodeMirror pre.CodeMirror-line-like { 18 | padding: 0 4px; /* Horizontal padding of content */ 19 | } 20 | 21 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 22 | background-color: white; /* The little square between H and V scrollbars */ 23 | } 24 | 25 | /* GUTTER */ 26 | 27 | .CodeMirror-gutters { 28 | border-right: 1px solid #ddd; 29 | background-color: #f7f7f7; 30 | white-space: nowrap; 31 | } 32 | .CodeMirror-linenumbers {} 33 | .CodeMirror-linenumber { 34 | padding: 0 3px 0 5px; 35 | min-width: 20px; 36 | text-align: right; 37 | color: #999; 38 | white-space: nowrap; 39 | } 40 | 41 | .CodeMirror-guttermarker { color: black; } 42 | .CodeMirror-guttermarker-subtle { color: #999; } 43 | 44 | /* CURSOR */ 45 | 46 | .CodeMirror-cursor { 47 | border-left: 1px solid black; 48 | border-right: none; 49 | width: 0; 50 | } 51 | /* Shown when moving in bi-directional text */ 52 | .CodeMirror div.CodeMirror-secondarycursor { 53 | border-left: 1px solid silver; 54 | } 55 | .cm-fat-cursor .CodeMirror-cursor { 56 | width: auto; 57 | border: 0 !important; 58 | background: #7e7; 59 | } 60 | .cm-fat-cursor div.CodeMirror-cursors { 61 | z-index: 1; 62 | } 63 | .cm-fat-cursor-mark { 64 | background-color: rgba(20, 255, 20, 0.5); 65 | -webkit-animation: blink 1.06s steps(1) infinite; 66 | -moz-animation: blink 1.06s steps(1) infinite; 67 | animation: blink 1.06s steps(1) infinite; 68 | } 69 | .cm-animate-fat-cursor { 70 | width: auto; 71 | border: 0; 72 | -webkit-animation: blink 1.06s steps(1) infinite; 73 | -moz-animation: blink 1.06s steps(1) infinite; 74 | animation: blink 1.06s steps(1) infinite; 75 | background-color: #7e7; 76 | } 77 | @-moz-keyframes blink { 78 | 0% {} 79 | 50% { background-color: transparent; } 80 | 100% {} 81 | } 82 | @-webkit-keyframes blink { 83 | 0% {} 84 | 50% { background-color: transparent; } 85 | 100% {} 86 | } 87 | @keyframes blink { 88 | 0% {} 89 | 50% { background-color: transparent; } 90 | 100% {} 91 | } 92 | 93 | /* Can style cursor different in overwrite (non-insert) mode */ 94 | .CodeMirror-overwrite .CodeMirror-cursor {} 95 | 96 | .cm-tab { display: inline-block; text-decoration: inherit; } 97 | 98 | .CodeMirror-rulers { 99 | position: absolute; 100 | left: 0; right: 0; top: -50px; bottom: 0; 101 | overflow: hidden; 102 | } 103 | .CodeMirror-ruler { 104 | border-left: 1px solid #ccc; 105 | top: 0; bottom: 0; 106 | position: absolute; 107 | } 108 | 109 | /* DEFAULT THEME */ 110 | 111 | .cm-s-default .cm-header {color: blue;} 112 | .cm-s-default .cm-quote {color: #090;} 113 | .cm-negative {color: #d44;} 114 | .cm-positive {color: #292;} 115 | .cm-header, .cm-strong {font-weight: bold;} 116 | .cm-em {font-style: italic;} 117 | .cm-link {text-decoration: underline;} 118 | .cm-strikethrough {text-decoration: line-through;} 119 | 120 | .cm-s-default .cm-keyword {color: #708;} 121 | .cm-s-default .cm-atom {color: #219;} 122 | .cm-s-default .cm-number {color: #164;} 123 | .cm-s-default .cm-def {color: #00f;} 124 | .cm-s-default .cm-variable, 125 | .cm-s-default .cm-punctuation, 126 | .cm-s-default .cm-property, 127 | .cm-s-default .cm-operator {} 128 | .cm-s-default .cm-variable-2 {color: #05a;} 129 | .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} 130 | .cm-s-default .cm-comment {color: #a50;} 131 | .cm-s-default .cm-string {color: #a11;} 132 | .cm-s-default .cm-string-2 {color: #f50;} 133 | .cm-s-default .cm-meta {color: #555;} 134 | .cm-s-default .cm-qualifier {color: #555;} 135 | .cm-s-default .cm-builtin {color: #30a;} 136 | .cm-s-default .cm-bracket {color: #997;} 137 | .cm-s-default .cm-tag {color: #170;} 138 | .cm-s-default .cm-attribute {color: #00c;} 139 | .cm-s-default .cm-hr {color: #999;} 140 | .cm-s-default .cm-link {color: #00c;} 141 | 142 | .cm-s-default .cm-error {color: #f00;} 143 | .cm-invalidchar {color: #f00;} 144 | 145 | .CodeMirror-composing { border-bottom: 2px solid; } 146 | 147 | /* Default styles for common addons */ 148 | 149 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} 150 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} 151 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 152 | .CodeMirror-activeline-background {background: #e8f2ff;} 153 | 154 | /* STOP */ 155 | 156 | /* The rest of this file contains styles related to the mechanics of 157 | the editor. You probably shouldn't touch them. */ 158 | 159 | .CodeMirror { 160 | position: relative; 161 | overflow: hidden; 162 | background: white; 163 | } 164 | 165 | .CodeMirror-scroll { 166 | overflow: scroll !important; /* Things will break if this is overridden */ 167 | /* 30px is the magic margin used to hide the element's real scrollbars */ 168 | /* See overflow: hidden in .CodeMirror */ 169 | margin-bottom: -30px; margin-right: -30px; 170 | padding-bottom: 30px; 171 | height: 100%; 172 | outline: none; /* Prevent dragging from highlighting the element */ 173 | position: relative; 174 | } 175 | .CodeMirror-sizer { 176 | position: relative; 177 | border-right: 30px solid transparent; 178 | } 179 | 180 | /* The fake, visible scrollbars. Used to force redraw during scrolling 181 | before actual scrolling happens, thus preventing shaking and 182 | flickering artifacts. */ 183 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 184 | position: absolute; 185 | z-index: 6; 186 | display: none; 187 | } 188 | .CodeMirror-vscrollbar { 189 | right: 0; top: 0; 190 | overflow-x: hidden; 191 | overflow-y: scroll; 192 | } 193 | .CodeMirror-hscrollbar { 194 | bottom: 0; left: 0; 195 | overflow-y: hidden; 196 | overflow-x: scroll; 197 | } 198 | .CodeMirror-scrollbar-filler { 199 | right: 0; bottom: 0; 200 | } 201 | .CodeMirror-gutter-filler { 202 | left: 0; bottom: 0; 203 | } 204 | 205 | .CodeMirror-gutters { 206 | position: absolute; left: 0; top: 0; 207 | min-height: 100%; 208 | z-index: 3; 209 | } 210 | .CodeMirror-gutter { 211 | white-space: normal; 212 | height: 100%; 213 | display: inline-block; 214 | vertical-align: top; 215 | margin-bottom: -30px; 216 | } 217 | .CodeMirror-gutter-wrapper { 218 | position: absolute; 219 | z-index: 4; 220 | background: none !important; 221 | border: none !important; 222 | } 223 | .CodeMirror-gutter-background { 224 | position: absolute; 225 | top: 0; bottom: 0; 226 | z-index: 4; 227 | } 228 | .CodeMirror-gutter-elt { 229 | position: absolute; 230 | cursor: default; 231 | z-index: 4; 232 | } 233 | .CodeMirror-gutter-wrapper ::selection { background-color: transparent } 234 | .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } 235 | 236 | .CodeMirror-lines { 237 | cursor: text; 238 | min-height: 1px; /* prevents collapsing before first draw */ 239 | } 240 | .CodeMirror pre.CodeMirror-line, 241 | .CodeMirror pre.CodeMirror-line-like { 242 | /* Reset some styles that the rest of the page might have set */ 243 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 244 | border-width: 0; 245 | background: transparent; 246 | font-family: inherit; 247 | /*Kit changed this*/ 248 | font-size: 12px; 249 | margin: 0; 250 | white-space: pre; 251 | word-wrap: normal; 252 | line-height: inherit; 253 | color: inherit; 254 | z-index: 2; 255 | position: relative; 256 | overflow: visible; 257 | -webkit-tap-highlight-color: transparent; 258 | -webkit-font-variant-ligatures: contextual; 259 | font-variant-ligatures: contextual; 260 | } 261 | .CodeMirror-wrap pre.CodeMirror-line, 262 | .CodeMirror-wrap pre.CodeMirror-line-like { 263 | word-wrap: break-word; 264 | white-space: pre-wrap; 265 | word-break: normal; 266 | } 267 | 268 | .CodeMirror-linebackground { 269 | position: absolute; 270 | left: 0; right: 0; top: 0; bottom: 0; 271 | z-index: 0; 272 | } 273 | 274 | .CodeMirror-linewidget { 275 | position: relative; 276 | z-index: 2; 277 | padding: 0.1px; /* Force widget margins to stay inside of the container */ 278 | } 279 | 280 | .CodeMirror-widget {} 281 | 282 | .CodeMirror-rtl pre { direction: rtl; } 283 | 284 | .CodeMirror-code { 285 | outline: none; 286 | } 287 | 288 | /* Force content-box sizing for the elements where we expect it */ 289 | .CodeMirror-scroll, 290 | .CodeMirror-sizer, 291 | .CodeMirror-gutter, 292 | .CodeMirror-gutters, 293 | .CodeMirror-linenumber { 294 | -moz-box-sizing: content-box; 295 | box-sizing: content-box; 296 | } 297 | 298 | .CodeMirror-measure { 299 | position: absolute; 300 | width: 100%; 301 | height: 0; 302 | overflow: hidden; 303 | visibility: hidden; 304 | } 305 | 306 | .CodeMirror-cursor { 307 | position: absolute; 308 | pointer-events: none; 309 | } 310 | .CodeMirror-measure pre { position: static; } 311 | 312 | div.CodeMirror-cursors { 313 | visibility: hidden; 314 | position: relative; 315 | z-index: 3; 316 | } 317 | div.CodeMirror-dragcursors { 318 | visibility: visible; 319 | } 320 | 321 | .CodeMirror-focused div.CodeMirror-cursors { 322 | visibility: visible; 323 | } 324 | 325 | .CodeMirror-selected { background: #d9d9d9; } 326 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 327 | .CodeMirror-crosshair { cursor: crosshair; } 328 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 329 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 330 | 331 | .cm-searching { 332 | background-color: #ffa; 333 | background-color: rgba(255, 255, 0, .4); 334 | } 335 | 336 | /* Used to force a border model for a node */ 337 | .cm-force-border { padding-right: .1px; } 338 | 339 | @media print { 340 | /* Hide the cursor when printing */ 341 | .CodeMirror div.CodeMirror-cursors { 342 | visibility: hidden; 343 | } 344 | } 345 | 346 | /* See issue #2901 */ 347 | .cm-tab-wrap-hack:after { content: ''; } 348 | 349 | /* Help users use markselection to safely style text background */ 350 | span.CodeMirror-selectedtext { background: none; } 351 | -------------------------------------------------------------------------------- /public/lib/menu.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | function $(selector, context){ 3 | context = context || document; 4 | return context["querySelectorAll"](selector); 5 | } 6 | 7 | function forEach(collection, iterator){ 8 | for(var key in Object.keys(collection)){ 9 | iterator(collection[key]); 10 | } 11 | } 12 | 13 | function showMenu(menu){ 14 | var menu = this; 15 | var ul = $("ul", menu)[0]; 16 | 17 | if(!ul || ul.classList.contains("-visible")) return; 18 | 19 | menu.classList.add("-active"); 20 | ul.classList.add("-animating"); 21 | ul.classList.add("-visible"); 22 | setTimeout(function(){ 23 | ul.classList.remove("-animating") 24 | }, 25); 25 | } 26 | 27 | function hideMenu(menu){ 28 | var menu = this; 29 | var ul = $("ul", menu)[0]; 30 | 31 | if(!ul || !ul.classList.contains("-visible")) return; 32 | 33 | menu.classList.remove("-active"); 34 | ul.classList.add("-animating"); 35 | setTimeout(function(){ 36 | ul.classList.remove("-visible"); 37 | ul.classList.remove("-animating"); 38 | }, 300); 39 | } 40 | 41 | function hideAllInactiveMenus(menu){ 42 | var menu = this; 43 | forEach( 44 | $("li.-hasSubmenu.-active:not(:hover)", menu.parent), 45 | function(e){ 46 | e.hideMenu && e.hideMenu(); 47 | } 48 | ); 49 | } 50 | 51 | window.addEventListener("load", function(){ 52 | forEach($(".Menu li.-hasSubmenu"), function(e){ 53 | e.showMenu = showMenu; 54 | e.hideMenu = hideMenu; 55 | }); 56 | 57 | forEach($(".Menu > li.-hasSubmenu"), function(e){ 58 | e.addEventListener("mouseenter", hideAllInactiveMenus); 59 | e.addEventListener("mouseenter", showMenu); 60 | }); 61 | 62 | forEach($(".Menu > li"), function(e){ 63 | e.addEventListener("mouseenter", hideAllInactiveMenus); 64 | }); 65 | 66 | forEach($(".Menu > li.-hasSubmenu li"), function(e){ 67 | e.addEventListener("mouseenter", hideAllInactiveMenus); 68 | }); 69 | 70 | forEach($(".Menu > li.-hasSubmenu li.-hasSubmenu"), function(e){ 71 | e.addEventListener("mouseenter", showMenu); 72 | }); 73 | 74 | document.addEventListener("click", hideAllInactiveMenus); 75 | }); 76 | })(); 77 | -------------------------------------------------------------------------------- /public/lib/mode/active-line.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | var WRAP_CLASS = "CodeMirror-activeline"; 14 | var BACK_CLASS = "CodeMirror-activeline-background"; 15 | var GUTT_CLASS = "CodeMirror-activeline-gutter"; 16 | 17 | CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { 18 | var prev = old == CodeMirror.Init ? false : old; 19 | if (val == prev) return 20 | if (prev) { 21 | cm.off("beforeSelectionChange", selectionChange); 22 | clearActiveLines(cm); 23 | delete cm.state.activeLines; 24 | } 25 | if (val) { 26 | cm.state.activeLines = []; 27 | updateActiveLines(cm, cm.listSelections()); 28 | cm.on("beforeSelectionChange", selectionChange); 29 | } 30 | }); 31 | 32 | function clearActiveLines(cm) { 33 | for (var i = 0; i < cm.state.activeLines.length; i++) { 34 | cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS); 35 | cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS); 36 | cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS); 37 | } 38 | } 39 | 40 | function sameArray(a, b) { 41 | if (a.length != b.length) return false; 42 | for (var i = 0; i < a.length; i++) 43 | if (a[i] != b[i]) return false; 44 | return true; 45 | } 46 | 47 | function updateActiveLines(cm, ranges) { 48 | var active = []; 49 | for (var i = 0; i < ranges.length; i++) { 50 | var range = ranges[i]; 51 | var option = cm.getOption("styleActiveLine"); 52 | if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty()) 53 | continue 54 | var line = cm.getLineHandleVisualStart(range.head.line); 55 | if (active[active.length - 1] != line) active.push(line); 56 | } 57 | if (sameArray(cm.state.activeLines, active)) return; 58 | cm.operation(function() { 59 | clearActiveLines(cm); 60 | for (var i = 0; i < active.length; i++) { 61 | cm.addLineClass(active[i], "wrap", WRAP_CLASS); 62 | cm.addLineClass(active[i], "background", BACK_CLASS); 63 | cm.addLineClass(active[i], "gutter", GUTT_CLASS); 64 | } 65 | cm.state.activeLines = active; 66 | }); 67 | } 68 | 69 | function selectionChange(cm, sel) { 70 | updateActiveLines(cm, sel.ranges); 71 | } 72 | }); 73 | -------------------------------------------------------------------------------- /public/lib/mode/autorefresh.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")) 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod) 9 | else // Plain browser env 10 | mod(CodeMirror) 11 | })(function(CodeMirror) { 12 | "use strict" 13 | 14 | CodeMirror.defineOption("autoRefresh", false, function(cm, val) { 15 | if (cm.state.autoRefresh) { 16 | stopListening(cm, cm.state.autoRefresh) 17 | cm.state.autoRefresh = null 18 | } 19 | if (val && cm.display.wrapper.offsetHeight == 0) 20 | startListening(cm, cm.state.autoRefresh = {delay: val.delay || 250}) 21 | }) 22 | 23 | function startListening(cm, state) { 24 | function check() { 25 | if (cm.display.wrapper.offsetHeight) { 26 | stopListening(cm, state) 27 | if (cm.display.lastWrapHeight != cm.display.wrapper.clientHeight) 28 | cm.refresh() 29 | } else { 30 | state.timeout = setTimeout(check, state.delay) 31 | } 32 | } 33 | state.timeout = setTimeout(check, state.delay) 34 | state.hurry = function() { 35 | clearTimeout(state.timeout) 36 | state.timeout = setTimeout(check, 50) 37 | } 38 | CodeMirror.on(window, "mouseup", state.hurry) 39 | CodeMirror.on(window, "keyup", state.hurry) 40 | } 41 | 42 | function stopListening(_cm, state) { 43 | clearTimeout(state.timeout) 44 | CodeMirror.off(window, "mouseup", state.hurry) 45 | CodeMirror.off(window, "keyup", state.hurry) 46 | } 47 | }); 48 | -------------------------------------------------------------------------------- /public/lib/mode/comment.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var noOptions = {}; 15 | var nonWS = /[^\s\u00a0]/; 16 | var Pos = CodeMirror.Pos; 17 | 18 | function firstNonWS(str) { 19 | var found = str.search(nonWS); 20 | return found == -1 ? 0 : found; 21 | } 22 | 23 | CodeMirror.commands.toggleComment = function(cm) { 24 | cm.toggleComment(); 25 | }; 26 | 27 | CodeMirror.defineExtension("toggleComment", function(options) { 28 | if (!options) options = noOptions; 29 | var cm = this; 30 | var minLine = Infinity, ranges = this.listSelections(), mode = null; 31 | for (var i = ranges.length - 1; i >= 0; i--) { 32 | var from = ranges[i].from(), to = ranges[i].to(); 33 | if (from.line >= minLine) continue; 34 | if (to.line >= minLine) to = Pos(minLine, 0); 35 | minLine = from.line; 36 | if (mode == null) { 37 | if (cm.uncomment(from, to, options)) mode = "un"; 38 | else { cm.lineComment(from, to, options); mode = "line"; } 39 | } else if (mode == "un") { 40 | cm.uncomment(from, to, options); 41 | } else { 42 | cm.lineComment(from, to, options); 43 | } 44 | } 45 | }); 46 | 47 | // Rough heuristic to try and detect lines that are part of multi-line string 48 | function probablyInsideString(cm, pos, line) { 49 | return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"\`]/.test(line) 50 | } 51 | 52 | function getMode(cm, pos) { 53 | var mode = cm.getMode() 54 | return mode.useInnerComments === false || !mode.innerMode ? mode : cm.getModeAt(pos) 55 | } 56 | 57 | CodeMirror.defineExtension("lineComment", function(from, to, options) { 58 | if (!options) options = noOptions; 59 | var self = this, mode = getMode(self, from); 60 | var firstLine = self.getLine(from.line); 61 | if (firstLine == null || probablyInsideString(self, from, firstLine)) return; 62 | 63 | var commentString = options.lineComment || mode.lineComment; 64 | if (!commentString) { 65 | if (options.blockCommentStart || mode.blockCommentStart) { 66 | options.fullLines = true; 67 | self.blockComment(from, to, options); 68 | } 69 | return; 70 | } 71 | 72 | var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1); 73 | var pad = options.padding == null ? " " : options.padding; 74 | var blankLines = options.commentBlankLines || from.line == to.line; 75 | 76 | self.operation(function() { 77 | if (options.indent) { 78 | var baseString = null; 79 | for (var i = from.line; i < end; ++i) { 80 | var line = self.getLine(i); 81 | var whitespace = line.slice(0, firstNonWS(line)); 82 | if (baseString == null || baseString.length > whitespace.length) { 83 | baseString = whitespace; 84 | } 85 | } 86 | for (var i = from.line; i < end; ++i) { 87 | var line = self.getLine(i), cut = baseString.length; 88 | if (!blankLines && !nonWS.test(line)) continue; 89 | if (line.slice(0, cut) != baseString) cut = firstNonWS(line); 90 | self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut)); 91 | } 92 | } else { 93 | for (var i = from.line; i < end; ++i) { 94 | if (blankLines || nonWS.test(self.getLine(i))) 95 | self.replaceRange(commentString + pad, Pos(i, 0)); 96 | } 97 | } 98 | }); 99 | }); 100 | 101 | CodeMirror.defineExtension("blockComment", function(from, to, options) { 102 | if (!options) options = noOptions; 103 | var self = this, mode = getMode(self, from); 104 | var startString = options.blockCommentStart || mode.blockCommentStart; 105 | var endString = options.blockCommentEnd || mode.blockCommentEnd; 106 | if (!startString || !endString) { 107 | if ((options.lineComment || mode.lineComment) && options.fullLines != false) 108 | self.lineComment(from, to, options); 109 | return; 110 | } 111 | if (/\bcomment\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return 112 | 113 | var end = Math.min(to.line, self.lastLine()); 114 | if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end; 115 | 116 | var pad = options.padding == null ? " " : options.padding; 117 | if (from.line > end) return; 118 | 119 | self.operation(function() { 120 | if (options.fullLines != false) { 121 | var lastLineHasText = nonWS.test(self.getLine(end)); 122 | self.replaceRange(pad + endString, Pos(end)); 123 | self.replaceRange(startString + pad, Pos(from.line, 0)); 124 | var lead = options.blockCommentLead || mode.blockCommentLead; 125 | if (lead != null) for (var i = from.line + 1; i <= end; ++i) 126 | if (i != end || lastLineHasText) 127 | self.replaceRange(lead + pad, Pos(i, 0)); 128 | } else { 129 | self.replaceRange(endString, to); 130 | self.replaceRange(startString, from); 131 | } 132 | }); 133 | }); 134 | 135 | CodeMirror.defineExtension("uncomment", function(from, to, options) { 136 | if (!options) options = noOptions; 137 | var self = this, mode = getMode(self, from); 138 | var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end); 139 | 140 | // Try finding line comments 141 | var lineString = options.lineComment || mode.lineComment, lines = []; 142 | var pad = options.padding == null ? " " : options.padding, didSomething; 143 | lineComment: { 144 | if (!lineString) break lineComment; 145 | for (var i = start; i <= end; ++i) { 146 | var line = self.getLine(i); 147 | var found = line.indexOf(lineString); 148 | if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1; 149 | if (found == -1 && nonWS.test(line)) break lineComment; 150 | if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment; 151 | lines.push(line); 152 | } 153 | self.operation(function() { 154 | for (var i = start; i <= end; ++i) { 155 | var line = lines[i - start]; 156 | var pos = line.indexOf(lineString), endPos = pos + lineString.length; 157 | if (pos < 0) continue; 158 | if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length; 159 | didSomething = true; 160 | self.replaceRange("", Pos(i, pos), Pos(i, endPos)); 161 | } 162 | }); 163 | if (didSomething) return true; 164 | } 165 | 166 | // Try block comments 167 | var startString = options.blockCommentStart || mode.blockCommentStart; 168 | var endString = options.blockCommentEnd || mode.blockCommentEnd; 169 | if (!startString || !endString) return false; 170 | var lead = options.blockCommentLead || mode.blockCommentLead; 171 | var startLine = self.getLine(start), open = startLine.indexOf(startString) 172 | if (open == -1) return false 173 | var endLine = end == start ? startLine : self.getLine(end) 174 | var close = endLine.indexOf(endString, end == start ? open + startString.length : 0); 175 | var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1) 176 | if (close == -1 || 177 | !/comment/.test(self.getTokenTypeAt(insideStart)) || 178 | !/comment/.test(self.getTokenTypeAt(insideEnd)) || 179 | self.getRange(insideStart, insideEnd, "\n").indexOf(endString) > -1) 180 | return false; 181 | 182 | // Avoid killing block comments completely outside the selection. 183 | // Positions of the last startString before the start of the selection, and the first endString after it. 184 | var lastStart = startLine.lastIndexOf(startString, from.ch); 185 | var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length); 186 | if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false; 187 | // Positions of the first endString after the end of the selection, and the last startString before it. 188 | firstEnd = endLine.indexOf(endString, to.ch); 189 | var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch); 190 | lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart; 191 | if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false; 192 | 193 | self.operation(function() { 194 | self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)), 195 | Pos(end, close + endString.length)); 196 | var openEnd = open + startString.length; 197 | if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length; 198 | self.replaceRange("", Pos(start, open), Pos(start, openEnd)); 199 | if (lead) for (var i = start + 1; i <= end; ++i) { 200 | var line = self.getLine(i), found = line.indexOf(lead); 201 | if (found == -1 || nonWS.test(line.slice(0, found))) continue; 202 | var foundEnd = found + lead.length; 203 | if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length; 204 | self.replaceRange("", Pos(i, found), Pos(i, foundEnd)); 205 | } 206 | }); 207 | return true; 208 | }); 209 | }); 210 | -------------------------------------------------------------------------------- /public/lib/mode/continuecomment.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | var nonspace = /\S/g; 13 | var repeat = String.prototype.repeat || function (n) { return Array(n + 1).join(this); }; 14 | function continueComment(cm) { 15 | if (cm.getOption("disableInput")) return CodeMirror.Pass; 16 | var ranges = cm.listSelections(), mode, inserts = []; 17 | for (var i = 0; i < ranges.length; i++) { 18 | var pos = ranges[i].head 19 | if (!/\bcomment\b/.test(cm.getTokenTypeAt(pos))) return CodeMirror.Pass; 20 | var modeHere = cm.getModeAt(pos) 21 | if (!mode) mode = modeHere; 22 | else if (mode != modeHere) return CodeMirror.Pass; 23 | 24 | var insert = null, line, found; 25 | var blockStart = mode.blockCommentStart, lineCmt = mode.lineComment; 26 | if (blockStart && mode.blockCommentContinue) { 27 | line = cm.getLine(pos.line); 28 | var end = line.lastIndexOf(mode.blockCommentEnd, pos.ch - mode.blockCommentEnd.length); 29 | // 1. if this block comment ended 30 | // 2. if this is actually inside a line comment 31 | if (end != -1 && end == pos.ch - mode.blockCommentEnd.length || 32 | lineCmt && (found = line.lastIndexOf(lineCmt, pos.ch - 1)) > -1 && 33 | /\bcomment\b/.test(cm.getTokenTypeAt({line: pos.line, ch: found + 1}))) { 34 | // ...then don't continue it 35 | } else if (pos.ch >= blockStart.length && 36 | (found = line.lastIndexOf(blockStart, pos.ch - blockStart.length)) > -1 && 37 | found > end) { 38 | // reuse the existing leading spaces/tabs/mixed 39 | // or build the correct indent using CM's tab/indent options 40 | if (nonspaceAfter(0, line) >= found) { 41 | insert = line.slice(0, found); 42 | } else { 43 | var tabSize = cm.options.tabSize, numTabs; 44 | found = CodeMirror.countColumn(line, found, tabSize); 45 | insert = !cm.options.indentWithTabs ? repeat.call(" ", found) : 46 | repeat.call("\t", (numTabs = Math.floor(found / tabSize))) + 47 | repeat.call(" ", found - tabSize * numTabs); 48 | } 49 | } else if ((found = line.indexOf(mode.blockCommentContinue)) > -1 && 50 | found <= pos.ch && 51 | found <= nonspaceAfter(0, line)) { 52 | insert = line.slice(0, found); 53 | } 54 | if (insert != null) insert += mode.blockCommentContinue 55 | } 56 | if (insert == null && lineCmt && continueLineCommentEnabled(cm)) { 57 | if (line == null) line = cm.getLine(pos.line); 58 | found = line.indexOf(lineCmt); 59 | // cursor at pos 0, line comment also at pos 0 => shift it down, don't continue 60 | if (!pos.ch && !found) insert = ""; 61 | // continue only if the line starts with an optional space + line comment 62 | else if (found > -1 && nonspaceAfter(0, line) >= found) { 63 | // don't continue if there's only space(s) after cursor or the end of the line 64 | insert = nonspaceAfter(pos.ch, line) > -1; 65 | // but always continue if the next line starts with a line comment too 66 | if (!insert) { 67 | var next = cm.getLine(pos.line + 1) || '', 68 | nextFound = next.indexOf(lineCmt); 69 | insert = nextFound > -1 && nonspaceAfter(0, next) >= nextFound || null; 70 | } 71 | if (insert) { 72 | insert = line.slice(0, found) + lineCmt + 73 | line.slice(found + lineCmt.length).match(/^\s*/)[0]; 74 | } 75 | } 76 | } 77 | if (insert == null) return CodeMirror.Pass; 78 | inserts[i] = "\n" + insert; 79 | } 80 | 81 | cm.operation(function() { 82 | for (var i = ranges.length - 1; i >= 0; i--) 83 | cm.replaceRange(inserts[i], ranges[i].from(), ranges[i].to(), "+insert"); 84 | }); 85 | } 86 | 87 | function nonspaceAfter(ch, str) { 88 | nonspace.lastIndex = ch; 89 | var m = nonspace.exec(str); 90 | return m ? m.index : -1; 91 | } 92 | 93 | function continueLineCommentEnabled(cm) { 94 | var opt = cm.getOption("continueComments"); 95 | if (opt && typeof opt == "object") 96 | return opt.continueLineComment !== false; 97 | return true; 98 | } 99 | 100 | CodeMirror.defineOption("continueComments", null, function(cm, val, prev) { 101 | if (prev && prev != CodeMirror.Init) 102 | cm.removeKeyMap("continueComment"); 103 | if (val) { 104 | var key = "Enter"; 105 | if (typeof val == "string") 106 | key = val; 107 | else if (typeof val == "object" && val.key) 108 | key = val.key; 109 | var map = {name: "continueComment"}; 110 | map[key] = continueComment; 111 | cm.addKeyMap(map); 112 | } 113 | }); 114 | }); 115 | -------------------------------------------------------------------------------- /public/lib/mode/gfm.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), require("../markdown/markdown"), require("../../addon/mode/overlay")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror", "../markdown/markdown", "../../addon/mode/overlay"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var urlRE = /^((?:(?:aaas?|about|acap|adiumxtra|af[ps]|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|cap|chrome(?:-extension)?|cid|coap|com-eventbrite-attendee|content|crid|cvs|data|dav|dict|dlna-(?:playcontainer|playsingle)|dns|doi|dtn|dvb|ed2k|facetime|feed|file|finger|fish|ftp|geo|gg|git|gizmoproject|go|gopher|gtalk|h323|hcp|https?|iax|icap|icon|im|imap|info|ipn|ipp|irc[6s]?|iris(?:\.beep|\.lwz|\.xpc|\.xpcs)?|itms|jar|javascript|jms|keyparc|lastfm|ldaps?|magnet|mailto|maps|market|message|mid|mms|ms-help|msnim|msrps?|mtqp|mumble|mupdate|mvn|news|nfs|nih?|nntp|notes|oid|opaquelocktoken|palm|paparazzi|platform|pop|pres|proxy|psyc|query|res(?:ource)?|rmi|rsync|rtmp|rtsp|secondlife|service|session|sftp|sgn|shttp|sieve|sips?|skype|sm[bs]|snmp|soap\.beeps?|soldat|spotify|ssh|steam|svn|tag|teamspeak|tel(?:net)?|tftp|things|thismessage|tip|tn3270|tv|udp|unreal|urn|ut2004|vemmi|ventrilo|view-source|webcal|wss?|wtai|wyciwyg|xcon(?:-userid)?|xfire|xmlrpc\.beeps?|xmpp|xri|ymsgr|z39\.50[rs]?):(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`*!()\[\]{};:'".,<>?«»“”‘’]))/i 15 | 16 | CodeMirror.defineMode("gfm", function(config, modeConfig) { 17 | var codeDepth = 0; 18 | function blankLine(state) { 19 | state.code = false; 20 | return null; 21 | } 22 | var gfmOverlay = { 23 | startState: function() { 24 | return { 25 | code: false, 26 | codeBlock: false, 27 | ateSpace: false 28 | }; 29 | }, 30 | copyState: function(s) { 31 | return { 32 | code: s.code, 33 | codeBlock: s.codeBlock, 34 | ateSpace: s.ateSpace 35 | }; 36 | }, 37 | token: function(stream, state) { 38 | state.combineTokens = null; 39 | 40 | // Hack to prevent formatting override inside code blocks (block and inline) 41 | if (state.codeBlock) { 42 | if (stream.match(/^```+/)) { 43 | state.codeBlock = false; 44 | return null; 45 | } 46 | stream.skipToEnd(); 47 | return null; 48 | } 49 | if (stream.sol()) { 50 | state.code = false; 51 | } 52 | if (stream.sol() && stream.match(/^```+/)) { 53 | stream.skipToEnd(); 54 | state.codeBlock = true; 55 | return null; 56 | } 57 | // If this block is changed, it may need to be updated in Markdown mode 58 | if (stream.peek() === '`') { 59 | stream.next(); 60 | var before = stream.pos; 61 | stream.eatWhile('`'); 62 | var difference = 1 + stream.pos - before; 63 | if (!state.code) { 64 | codeDepth = difference; 65 | state.code = true; 66 | } else { 67 | if (difference === codeDepth) { // Must be exact 68 | state.code = false; 69 | } 70 | } 71 | return null; 72 | } else if (state.code) { 73 | stream.next(); 74 | return null; 75 | } 76 | // Check if space. If so, links can be formatted later on 77 | if (stream.eatSpace()) { 78 | state.ateSpace = true; 79 | return null; 80 | } 81 | if (stream.sol() || state.ateSpace) { 82 | state.ateSpace = false; 83 | if (modeConfig.gitHubSpice !== false) { 84 | if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?=.{0,6}\d)(?:[a-f0-9]{7,40}\b)/)) { 85 | // User/Project@SHA 86 | // User@SHA 87 | // SHA 88 | state.combineTokens = true; 89 | return "link"; 90 | } else if (stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+)?#[0-9]+\b/)) { 91 | // User/Project#Num 92 | // User#Num 93 | // #Num 94 | state.combineTokens = true; 95 | return "link"; 96 | } 97 | } 98 | } 99 | if (stream.match(urlRE) && 100 | stream.string.slice(stream.start - 2, stream.start) != "](" && 101 | (stream.start == 0 || /\W/.test(stream.string.charAt(stream.start - 1)))) { 102 | // URLs 103 | // Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls 104 | // And then (issue #1160) simplified to make it not crash the Chrome Regexp engine 105 | // And then limited url schemes to the CommonMark list, so foo:bar isn't matched as a URL 106 | state.combineTokens = true; 107 | return "link"; 108 | } 109 | stream.next(); 110 | return null; 111 | }, 112 | blankLine: blankLine 113 | }; 114 | 115 | var markdownConfig = { 116 | taskLists: true, 117 | strikethrough: true, 118 | emoji: true 119 | }; 120 | for (var attr in modeConfig) { 121 | markdownConfig[attr] = modeConfig[attr]; 122 | } 123 | markdownConfig.name = "markdown"; 124 | return CodeMirror.overlayMode(CodeMirror.getMode(config, markdownConfig), gfmOverlay); 125 | 126 | }, "markdown"); 127 | 128 | CodeMirror.defineMIME("text/x-gfm", "gfm"); 129 | }); 130 | -------------------------------------------------------------------------------- /public/lib/mode/overlay.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | // Utility function that allows modes to be combined. The mode given 5 | // as the base argument takes care of most of the normal mode 6 | // functionality, but a second (typically simple) mode is used, which 7 | // can override the style of text. Both modes get to parse all of the 8 | // text, but when both assign a non-null style to a piece of code, the 9 | // overlay wins, unless the combine argument was true and not overridden, 10 | // or state.overlay.combineTokens was true, in which case the styles are 11 | // combined. 12 | 13 | (function(mod) { 14 | if (typeof exports == "object" && typeof module == "object") // CommonJS 15 | mod(require("../../lib/codemirror")); 16 | else if (typeof define == "function" && define.amd) // AMD 17 | define(["../../lib/codemirror"], mod); 18 | else // Plain browser env 19 | mod(CodeMirror); 20 | })(function(CodeMirror) { 21 | "use strict"; 22 | 23 | CodeMirror.overlayMode = function(base, overlay, combine) { 24 | return { 25 | startState: function() { 26 | return { 27 | base: CodeMirror.startState(base), 28 | overlay: CodeMirror.startState(overlay), 29 | basePos: 0, baseCur: null, 30 | overlayPos: 0, overlayCur: null, 31 | streamSeen: null 32 | }; 33 | }, 34 | copyState: function(state) { 35 | return { 36 | base: CodeMirror.copyState(base, state.base), 37 | overlay: CodeMirror.copyState(overlay, state.overlay), 38 | basePos: state.basePos, baseCur: null, 39 | overlayPos: state.overlayPos, overlayCur: null 40 | }; 41 | }, 42 | 43 | token: function(stream, state) { 44 | if (stream != state.streamSeen || 45 | Math.min(state.basePos, state.overlayPos) < stream.start) { 46 | state.streamSeen = stream; 47 | state.basePos = state.overlayPos = stream.start; 48 | } 49 | 50 | if (stream.start == state.basePos) { 51 | state.baseCur = base.token(stream, state.base); 52 | state.basePos = stream.pos; 53 | } 54 | if (stream.start == state.overlayPos) { 55 | stream.pos = stream.start; 56 | state.overlayCur = overlay.token(stream, state.overlay); 57 | state.overlayPos = stream.pos; 58 | } 59 | stream.pos = Math.min(state.basePos, state.overlayPos); 60 | 61 | // state.overlay.combineTokens always takes precedence over combine, 62 | // unless set to null 63 | if (state.overlayCur == null) return state.baseCur; 64 | else if (state.baseCur != null && 65 | state.overlay.combineTokens || 66 | combine && state.overlay.combineTokens == null) 67 | return state.baseCur + " " + state.overlayCur; 68 | else return state.overlayCur; 69 | }, 70 | 71 | indent: base.indent && function(state, textAfter, line) { 72 | return base.indent(state.base, textAfter, line); 73 | }, 74 | electricChars: base.electricChars, 75 | 76 | innerMode: function(state) { return {state: state.base, mode: base}; }, 77 | 78 | blankLine: function(state) { 79 | var baseToken, overlayToken; 80 | if (base.blankLine) baseToken = base.blankLine(state.base); 81 | if (overlay.blankLine) overlayToken = overlay.blankLine(state.overlay); 82 | 83 | return overlayToken == null ? 84 | baseToken : 85 | (combine && baseToken != null ? baseToken + " " + overlayToken : overlayToken); 86 | } 87 | }; 88 | }; 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /public/lib/mode/pegjs.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function (mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), require("../javascript/javascript")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror", "../javascript/javascript"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function (CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineMode("pegjs", function (config) { 15 | var jsMode = CodeMirror.getMode(config, "javascript"); 16 | 17 | function identifier(stream) { 18 | return stream.match(/^[a-zA-Z_][a-zA-Z0-9_]*/); 19 | } 20 | 21 | return { 22 | startState: function () { 23 | return { 24 | inString: false, 25 | stringType: null, 26 | inComment: false, 27 | inCharacterClass: false, 28 | braced: 0, 29 | lhs: true, 30 | localState: null 31 | }; 32 | }, 33 | token: function (stream, state) { 34 | if (stream) 35 | 36 | //check for state changes 37 | if (!state.inString && !state.inComment && ((stream.peek() == '"') || (stream.peek() == "'"))) { 38 | state.stringType = stream.peek(); 39 | stream.next(); // Skip quote 40 | state.inString = true; // Update state 41 | } 42 | if (!state.inString && !state.inComment && stream.match(/^\/\*/)) { 43 | state.inComment = true; 44 | } 45 | 46 | //return state 47 | if (state.inString) { 48 | while (state.inString && !stream.eol()) { 49 | if (stream.peek() === state.stringType) { 50 | stream.next(); // Skip quote 51 | state.inString = false; // Clear flag 52 | } else if (stream.peek() === '\\') { 53 | stream.next(); 54 | stream.next(); 55 | } else { 56 | stream.match(/^.[^\\\"\']*/); 57 | } 58 | } 59 | return state.lhs ? "property string" : "string"; // Token style 60 | } else if (state.inComment) { 61 | while (state.inComment && !stream.eol()) { 62 | if (stream.match(/\*\//)) { 63 | state.inComment = false; // Clear flag 64 | } else { 65 | stream.match(/^.[^\*]*/); 66 | } 67 | } 68 | return "comment"; 69 | } else if (state.inCharacterClass) { 70 | while (state.inCharacterClass && !stream.eol()) { 71 | if (!(stream.match(/^[^\]\\]+/) || stream.match(/^\\./))) { 72 | state.inCharacterClass = false; 73 | } 74 | } 75 | } else if (stream.peek() === '[') { 76 | stream.next(); 77 | state.inCharacterClass = true; 78 | return 'bracket'; 79 | } else if (stream.match(/^\/\//)) { 80 | stream.skipToEnd(); 81 | return "comment"; 82 | } else if (state.braced || stream.peek() === '{') { 83 | if (state.localState === null) { 84 | state.localState = CodeMirror.startState(jsMode); 85 | } 86 | var token = jsMode.token(stream, state.localState); 87 | var text = stream.current(); 88 | if (!token) { 89 | for (var i = 0; i < text.length; i++) { 90 | if (text[i] === '{') { 91 | state.braced++; 92 | } else if (text[i] === '}') { 93 | state.braced--; 94 | } 95 | } 96 | ; 97 | } 98 | return token; 99 | } else if (identifier(stream)) { 100 | if (stream.peek() === ':') { 101 | return 'variable'; 102 | } 103 | return 'variable-2'; 104 | } else if (['[', ']', '(', ')'].indexOf(stream.peek()) != -1) { 105 | stream.next(); 106 | return 'bracket'; 107 | } else if (!stream.eatSpace()) { 108 | stream.next(); 109 | } 110 | return null; 111 | }, 112 | blockCommentStart: "/*", 113 | blockCommentEnd: "*/", 114 | blockCommentContinue: " * ", 115 | lineComment: "//", 116 | }; 117 | }, "javascript"); 118 | 119 | }); 120 | -------------------------------------------------------------------------------- /public/lib/mode/show-hint.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-hints { 2 | position: absolute; 3 | z-index: 10; 4 | overflow: hidden; 5 | list-style: none; 6 | 7 | margin: 0; 8 | padding: 2px; 9 | 10 | -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 11 | -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 12 | box-shadow: 2px 3px 5px rgba(0,0,0,.2); 13 | border-radius: 3px; 14 | border: 1px solid silver; 15 | 16 | background: white; 17 | font-size: 90%; 18 | font-family: monospace; 19 | 20 | max-height: 20em; 21 | overflow-y: auto; 22 | } 23 | 24 | .CodeMirror-hint { 25 | margin: 0; 26 | padding: 0 4px; 27 | border-radius: 2px; 28 | white-space: pre; 29 | color: black; 30 | cursor: pointer; 31 | } 32 | 33 | li.CodeMirror-hint-active { 34 | background: #08f; 35 | color: white; 36 | } 37 | -------------------------------------------------------------------------------- /public/lib/mode/show-hint.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var HINT_ELEMENT_CLASS = "CodeMirror-hint"; 15 | var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active"; 16 | 17 | // This is the old interface, kept around for now to stay 18 | // backwards-compatible. 19 | CodeMirror.showHint = function(cm, getHints, options) { 20 | if (!getHints) return cm.showHint(options); 21 | if (options && options.async) getHints.async = true; 22 | var newOpts = {hint: getHints}; 23 | if (options) for (var prop in options) newOpts[prop] = options[prop]; 24 | return cm.showHint(newOpts); 25 | }; 26 | 27 | CodeMirror.defineExtension("showHint", function(options) { 28 | options = parseOptions(this, this.getCursor("start"), options); 29 | var selections = this.listSelections() 30 | if (selections.length > 1) return; 31 | // By default, don't allow completion when something is selected. 32 | // A hint function can have a `supportsSelection` property to 33 | // indicate that it can handle selections. 34 | if (this.somethingSelected()) { 35 | if (!options.hint.supportsSelection) return; 36 | // Don't try with cross-line selections 37 | for (var i = 0; i < selections.length; i++) 38 | if (selections[i].head.line != selections[i].anchor.line) return; 39 | } 40 | 41 | if (this.state.completionActive) this.state.completionActive.close(); 42 | var completion = this.state.completionActive = new Completion(this, options); 43 | if (!completion.options.hint) return; 44 | 45 | CodeMirror.signal(this, "startCompletion", this); 46 | completion.update(true); 47 | }); 48 | 49 | CodeMirror.defineExtension("closeHint", function() { 50 | if (this.state.completionActive) this.state.completionActive.close() 51 | }) 52 | 53 | function Completion(cm, options) { 54 | this.cm = cm; 55 | this.options = options; 56 | this.widget = null; 57 | this.debounce = 0; 58 | this.tick = 0; 59 | this.startPos = this.cm.getCursor("start"); 60 | this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length; 61 | 62 | var self = this; 63 | cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); }); 64 | } 65 | 66 | var requestAnimationFrame = window.requestAnimationFrame || function(fn) { 67 | return setTimeout(fn, 1000/60); 68 | }; 69 | var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout; 70 | 71 | Completion.prototype = { 72 | close: function() { 73 | if (!this.active()) return; 74 | this.cm.state.completionActive = null; 75 | this.tick = null; 76 | this.cm.off("cursorActivity", this.activityFunc); 77 | 78 | if (this.widget && this.data) CodeMirror.signal(this.data, "close"); 79 | if (this.widget) this.widget.close(); 80 | CodeMirror.signal(this.cm, "endCompletion", this.cm); 81 | }, 82 | 83 | active: function() { 84 | return this.cm.state.completionActive == this; 85 | }, 86 | 87 | pick: function(data, i) { 88 | var completion = data.list[i]; 89 | if (completion.hint) completion.hint(this.cm, data, completion); 90 | else this.cm.replaceRange(getText(completion), completion.from || data.from, 91 | completion.to || data.to, "complete"); 92 | CodeMirror.signal(data, "pick", completion); 93 | this.close(); 94 | }, 95 | 96 | cursorActivity: function() { 97 | if (this.debounce) { 98 | cancelAnimationFrame(this.debounce); 99 | this.debounce = 0; 100 | } 101 | 102 | var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line); 103 | if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch || 104 | pos.ch < this.startPos.ch || this.cm.somethingSelected() || 105 | (!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) { 106 | this.close(); 107 | } else { 108 | var self = this; 109 | this.debounce = requestAnimationFrame(function() {self.update();}); 110 | if (this.widget) this.widget.disable(); 111 | } 112 | }, 113 | 114 | update: function(first) { 115 | if (this.tick == null) return 116 | var self = this, myTick = ++this.tick 117 | fetchHints(this.options.hint, this.cm, this.options, function(data) { 118 | if (self.tick == myTick) self.finishUpdate(data, first) 119 | }) 120 | }, 121 | 122 | finishUpdate: function(data, first) { 123 | if (this.data) CodeMirror.signal(this.data, "update"); 124 | 125 | var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle); 126 | if (this.widget) this.widget.close(); 127 | 128 | this.data = data; 129 | 130 | if (data && data.list.length) { 131 | if (picked && data.list.length == 1) { 132 | this.pick(data, 0); 133 | } else { 134 | this.widget = new Widget(this, data); 135 | CodeMirror.signal(data, "shown"); 136 | } 137 | } 138 | } 139 | }; 140 | 141 | function parseOptions(cm, pos, options) { 142 | var editor = cm.options.hintOptions; 143 | var out = {}; 144 | for (var prop in defaultOptions) out[prop] = defaultOptions[prop]; 145 | if (editor) for (var prop in editor) 146 | if (editor[prop] !== undefined) out[prop] = editor[prop]; 147 | if (options) for (var prop in options) 148 | if (options[prop] !== undefined) out[prop] = options[prop]; 149 | if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos) 150 | return out; 151 | } 152 | 153 | function getText(completion) { 154 | if (typeof completion == "string") return completion; 155 | else return completion.text; 156 | } 157 | 158 | function buildKeyMap(completion, handle) { 159 | var baseMap = { 160 | Up: function() {handle.moveFocus(-1);}, 161 | Down: function() {handle.moveFocus(1);}, 162 | PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);}, 163 | PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);}, 164 | Home: function() {handle.setFocus(0);}, 165 | End: function() {handle.setFocus(handle.length - 1);}, 166 | Enter: handle.pick, 167 | Tab: handle.pick, 168 | Esc: handle.close 169 | }; 170 | 171 | var mac = /Mac/.test(navigator.platform); 172 | 173 | if (mac) { 174 | baseMap["Ctrl-P"] = function() {handle.moveFocus(-1);}; 175 | baseMap["Ctrl-N"] = function() {handle.moveFocus(1);}; 176 | } 177 | 178 | var custom = completion.options.customKeys; 179 | var ourMap = custom ? {} : baseMap; 180 | function addBinding(key, val) { 181 | var bound; 182 | if (typeof val != "string") 183 | bound = function(cm) { return val(cm, handle); }; 184 | // This mechanism is deprecated 185 | else if (baseMap.hasOwnProperty(val)) 186 | bound = baseMap[val]; 187 | else 188 | bound = val; 189 | ourMap[key] = bound; 190 | } 191 | if (custom) 192 | for (var key in custom) if (custom.hasOwnProperty(key)) 193 | addBinding(key, custom[key]); 194 | var extra = completion.options.extraKeys; 195 | if (extra) 196 | for (var key in extra) if (extra.hasOwnProperty(key)) 197 | addBinding(key, extra[key]); 198 | return ourMap; 199 | } 200 | 201 | function getHintElement(hintsElement, el) { 202 | while (el && el != hintsElement) { 203 | if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el; 204 | el = el.parentNode; 205 | } 206 | } 207 | 208 | function Widget(completion, data) { 209 | this.completion = completion; 210 | this.data = data; 211 | this.picked = false; 212 | var widget = this, cm = completion.cm; 213 | var ownerDocument = cm.getInputField().ownerDocument; 214 | var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow; 215 | 216 | var hints = this.hints = ownerDocument.createElement("ul"); 217 | var theme = completion.cm.options.theme; 218 | hints.className = "CodeMirror-hints " + theme; 219 | this.selectedHint = data.selectedHint || 0; 220 | 221 | var completions = data.list; 222 | for (var i = 0; i < completions.length; ++i) { 223 | var elt = hints.appendChild(ownerDocument.createElement("li")), cur = completions[i]; 224 | var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS); 225 | if (cur.className != null) className = cur.className + " " + className; 226 | elt.className = className; 227 | if (cur.render) cur.render(elt, data, cur); 228 | else elt.appendChild(ownerDocument.createTextNode(cur.displayText || getText(cur))); 229 | elt.hintId = i; 230 | } 231 | 232 | var container = completion.options.container || ownerDocument.body; 233 | var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null); 234 | var left = pos.left, top = pos.bottom, below = true; 235 | var offsetLeft = 0, offsetTop = 0; 236 | if (container !== ownerDocument.body) { 237 | // We offset the cursor position because left and top are relative to the offsetParent's top left corner. 238 | var isContainerPositioned = ['absolute', 'relative', 'fixed'].indexOf(parentWindow.getComputedStyle(container).position) !== -1; 239 | var offsetParent = isContainerPositioned ? container : container.offsetParent; 240 | var offsetParentPosition = offsetParent.getBoundingClientRect(); 241 | var bodyPosition = ownerDocument.body.getBoundingClientRect(); 242 | offsetLeft = (offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft); 243 | offsetTop = (offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop); 244 | } 245 | hints.style.left = (left - offsetLeft) + "px"; 246 | hints.style.top = (top - offsetTop) + "px"; 247 | 248 | // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. 249 | var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth); 250 | var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight); 251 | container.appendChild(hints); 252 | var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH; 253 | var scrolls = hints.scrollHeight > hints.clientHeight + 1 254 | var startScroll = cm.getScrollInfo(); 255 | 256 | if (overlapY > 0) { 257 | var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top); 258 | if (curTop - height > 0) { // Fits above cursor 259 | hints.style.top = (top = pos.top - height - offsetTop) + "px"; 260 | below = false; 261 | } else if (height > winH) { 262 | hints.style.height = (winH - 5) + "px"; 263 | hints.style.top = (top = pos.bottom - box.top - offsetTop) + "px"; 264 | var cursor = cm.getCursor(); 265 | if (data.from.ch != cursor.ch) { 266 | pos = cm.cursorCoords(cursor); 267 | hints.style.left = (left = pos.left - offsetLeft) + "px"; 268 | box = hints.getBoundingClientRect(); 269 | } 270 | } 271 | } 272 | var overlapX = box.right - winW; 273 | if (overlapX > 0) { 274 | if (box.right - box.left > winW) { 275 | hints.style.width = (winW - 5) + "px"; 276 | overlapX -= (box.right - box.left) - winW; 277 | } 278 | hints.style.left = (left = pos.left - overlapX - offsetLeft) + "px"; 279 | } 280 | if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling) 281 | node.style.paddingRight = cm.display.nativeBarWidth + "px" 282 | 283 | cm.addKeyMap(this.keyMap = buildKeyMap(completion, { 284 | moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); }, 285 | setFocus: function(n) { widget.changeActive(n); }, 286 | menuSize: function() { return widget.screenAmount(); }, 287 | length: completions.length, 288 | close: function() { completion.close(); }, 289 | pick: function() { widget.pick(); }, 290 | data: data 291 | })); 292 | 293 | if (completion.options.closeOnUnfocus) { 294 | var closingOnBlur; 295 | cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); }); 296 | cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); }); 297 | } 298 | 299 | cm.on("scroll", this.onScroll = function() { 300 | var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect(); 301 | var newTop = top + startScroll.top - curScroll.top; 302 | var point = newTop - (parentWindow.pageYOffset || (ownerDocument.documentElement || ownerDocument.body).scrollTop); 303 | if (!below) point += hints.offsetHeight; 304 | if (point <= editor.top || point >= editor.bottom) return completion.close(); 305 | hints.style.top = newTop + "px"; 306 | hints.style.left = (left + startScroll.left - curScroll.left) + "px"; 307 | }); 308 | 309 | CodeMirror.on(hints, "dblclick", function(e) { 310 | var t = getHintElement(hints, e.target || e.srcElement); 311 | if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();} 312 | }); 313 | 314 | CodeMirror.on(hints, "click", function(e) { 315 | var t = getHintElement(hints, e.target || e.srcElement); 316 | if (t && t.hintId != null) { 317 | widget.changeActive(t.hintId); 318 | if (completion.options.completeOnSingleClick) widget.pick(); 319 | } 320 | }); 321 | 322 | CodeMirror.on(hints, "mousedown", function() { 323 | setTimeout(function(){cm.focus();}, 20); 324 | }); 325 | 326 | CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]); 327 | return true; 328 | } 329 | 330 | Widget.prototype = { 331 | close: function() { 332 | if (this.completion.widget != this) return; 333 | this.completion.widget = null; 334 | this.hints.parentNode.removeChild(this.hints); 335 | this.completion.cm.removeKeyMap(this.keyMap); 336 | 337 | var cm = this.completion.cm; 338 | if (this.completion.options.closeOnUnfocus) { 339 | cm.off("blur", this.onBlur); 340 | cm.off("focus", this.onFocus); 341 | } 342 | cm.off("scroll", this.onScroll); 343 | }, 344 | 345 | disable: function() { 346 | this.completion.cm.removeKeyMap(this.keyMap); 347 | var widget = this; 348 | this.keyMap = {Enter: function() { widget.picked = true; }}; 349 | this.completion.cm.addKeyMap(this.keyMap); 350 | }, 351 | 352 | pick: function() { 353 | this.completion.pick(this.data, this.selectedHint); 354 | }, 355 | 356 | changeActive: function(i, avoidWrap) { 357 | if (i >= this.data.list.length) 358 | i = avoidWrap ? this.data.list.length - 1 : 0; 359 | else if (i < 0) 360 | i = avoidWrap ? 0 : this.data.list.length - 1; 361 | if (this.selectedHint == i) return; 362 | var node = this.hints.childNodes[this.selectedHint]; 363 | if (node) node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, ""); 364 | node = this.hints.childNodes[this.selectedHint = i]; 365 | node.className += " " + ACTIVE_HINT_ELEMENT_CLASS; 366 | if (node.offsetTop < this.hints.scrollTop) 367 | this.hints.scrollTop = node.offsetTop - 3; 368 | else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight) 369 | this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3; 370 | CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node); 371 | }, 372 | 373 | screenAmount: function() { 374 | return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1; 375 | } 376 | }; 377 | 378 | function applicableHelpers(cm, helpers) { 379 | if (!cm.somethingSelected()) return helpers 380 | var result = [] 381 | for (var i = 0; i < helpers.length; i++) 382 | if (helpers[i].supportsSelection) result.push(helpers[i]) 383 | return result 384 | } 385 | 386 | function fetchHints(hint, cm, options, callback) { 387 | if (hint.async) { 388 | hint(cm, callback, options) 389 | } else { 390 | var result = hint(cm, options) 391 | if (result && result.then) result.then(callback) 392 | else callback(result) 393 | } 394 | } 395 | 396 | function resolveAutoHints(cm, pos) { 397 | var helpers = cm.getHelpers(pos, "hint"), words 398 | if (helpers.length) { 399 | var resolved = function(cm, callback, options) { 400 | var app = applicableHelpers(cm, helpers); 401 | function run(i) { 402 | if (i == app.length) return callback(null) 403 | fetchHints(app[i], cm, options, function(result) { 404 | if (result && result.list.length > 0) callback(result) 405 | else run(i + 1) 406 | }) 407 | } 408 | run(0) 409 | } 410 | resolved.async = true 411 | resolved.supportsSelection = true 412 | return resolved 413 | } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) { 414 | return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) } 415 | } else if (CodeMirror.hint.anyword) { 416 | return function(cm, options) { return CodeMirror.hint.anyword(cm, options) } 417 | } else { 418 | return function() {} 419 | } 420 | } 421 | 422 | CodeMirror.registerHelper("hint", "auto", { 423 | resolve: resolveAutoHints 424 | }); 425 | 426 | CodeMirror.registerHelper("hint", "fromList", function(cm, options) { 427 | var cur = cm.getCursor(), token = cm.getTokenAt(cur) 428 | var term, from = CodeMirror.Pos(cur.line, token.start), to = cur 429 | if (token.start < cur.ch && /\w/.test(token.string.charAt(cur.ch - token.start - 1))) { 430 | term = token.string.substr(0, cur.ch - token.start) 431 | } else { 432 | term = "" 433 | from = cur 434 | } 435 | var found = []; 436 | for (var i = 0; i < options.words.length; i++) { 437 | var word = options.words[i]; 438 | if (word.slice(0, term.length) == term) 439 | found.push(word); 440 | } 441 | 442 | if (found.length) return {list: found, from: from, to: to}; 443 | }); 444 | 445 | CodeMirror.commands.autocomplete = CodeMirror.showHint; 446 | 447 | var defaultOptions = { 448 | hint: CodeMirror.hint.auto, 449 | completeSingle: true, 450 | alignWithWord: true, 451 | closeCharacters: /[\s()\[\]{};:>,]/, 452 | closeOnUnfocus: true, 453 | completeOnSingleClick: true, 454 | container: null, 455 | customKeys: null, 456 | extraKeys: null 457 | }; 458 | 459 | CodeMirror.defineOption("hintOptions", null); 460 | }); 461 | -------------------------------------------------------------------------------- /public/lib/mode/simple.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: https://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.defineSimpleMode = function(name, states) { 15 | CodeMirror.defineMode(name, function(config) { 16 | return CodeMirror.simpleMode(config, states); 17 | }); 18 | }; 19 | 20 | CodeMirror.simpleMode = function(config, states) { 21 | ensureState(states, "start"); 22 | var states_ = {}, meta = states.meta || {}, hasIndentation = false; 23 | for (var state in states) if (state != meta && states.hasOwnProperty(state)) { 24 | var list = states_[state] = [], orig = states[state]; 25 | for (var i = 0; i < orig.length; i++) { 26 | var data = orig[i]; 27 | list.push(new Rule(data, states)); 28 | if (data.indent || data.dedent) hasIndentation = true; 29 | } 30 | } 31 | var mode = { 32 | startState: function() { 33 | return {state: "start", pending: null, 34 | local: null, localState: null, 35 | indent: hasIndentation ? [] : null}; 36 | }, 37 | copyState: function(state) { 38 | var s = {state: state.state, pending: state.pending, 39 | local: state.local, localState: null, 40 | indent: state.indent && state.indent.slice(0)}; 41 | if (state.localState) 42 | s.localState = CodeMirror.copyState(state.local.mode, state.localState); 43 | if (state.stack) 44 | s.stack = state.stack.slice(0); 45 | for (var pers = state.persistentStates; pers; pers = pers.next) 46 | s.persistentStates = {mode: pers.mode, 47 | spec: pers.spec, 48 | state: pers.state == state.localState ? s.localState : CodeMirror.copyState(pers.mode, pers.state), 49 | next: s.persistentStates}; 50 | return s; 51 | }, 52 | token: tokenFunction(states_, config), 53 | innerMode: function(state) { return state.local && {mode: state.local.mode, state: state.localState}; }, 54 | indent: indentFunction(states_, meta) 55 | }; 56 | if (meta) for (var prop in meta) if (meta.hasOwnProperty(prop)) 57 | mode[prop] = meta[prop]; 58 | return mode; 59 | }; 60 | 61 | function ensureState(states, name) { 62 | if (!states.hasOwnProperty(name)) 63 | throw new Error("Undefined state " + name + " in simple mode"); 64 | } 65 | 66 | function toRegex(val, caret) { 67 | if (!val) return /(?:)/; 68 | var flags = ""; 69 | if (val instanceof RegExp) { 70 | if (val.ignoreCase) flags = "i"; 71 | val = val.source; 72 | } else { 73 | val = String(val); 74 | } 75 | return new RegExp((caret === false ? "" : "^") + "(?:" + val + ")", flags); 76 | } 77 | 78 | function asToken(val) { 79 | if (!val) return null; 80 | if (val.apply) return val 81 | if (typeof val == "string") return val.replace(/\./g, " "); 82 | var result = []; 83 | for (var i = 0; i < val.length; i++) 84 | result.push(val[i] && val[i].replace(/\./g, " ")); 85 | return result; 86 | } 87 | 88 | function Rule(data, states) { 89 | if (data.next || data.push) ensureState(states, data.next || data.push); 90 | this.regex = toRegex(data.regex); 91 | this.token = asToken(data.token); 92 | this.data = data; 93 | } 94 | 95 | function tokenFunction(states, config) { 96 | return function(stream, state) { 97 | if (state.pending) { 98 | var pend = state.pending.shift(); 99 | if (state.pending.length == 0) state.pending = null; 100 | stream.pos += pend.text.length; 101 | return pend.token; 102 | } 103 | 104 | if (state.local) { 105 | if (state.local.end && stream.match(state.local.end)) { 106 | var tok = state.local.endToken || null; 107 | state.local = state.localState = null; 108 | return tok; 109 | } else { 110 | var tok = state.local.mode.token(stream, state.localState), m; 111 | if (state.local.endScan && (m = state.local.endScan.exec(stream.current()))) 112 | stream.pos = stream.start + m.index; 113 | return tok; 114 | } 115 | } 116 | 117 | var curState = states[state.state]; 118 | for (var i = 0; i < curState.length; i++) { 119 | var rule = curState[i]; 120 | var matches = (!rule.data.sol || stream.sol()) && stream.match(rule.regex); 121 | if (matches) { 122 | if (rule.data.next) { 123 | state.state = rule.data.next; 124 | } else if (rule.data.push) { 125 | (state.stack || (state.stack = [])).push(state.state); 126 | state.state = rule.data.push; 127 | } else if (rule.data.pop && state.stack && state.stack.length) { 128 | state.state = state.stack.pop(); 129 | } 130 | 131 | if (rule.data.mode) 132 | enterLocalMode(config, state, rule.data.mode, rule.token); 133 | if (rule.data.indent) 134 | state.indent.push(stream.indentation() + config.indentUnit); 135 | if (rule.data.dedent) 136 | state.indent.pop(); 137 | var token = rule.token 138 | if (token && token.apply) token = token(matches) 139 | if (matches.length > 2 && rule.token && typeof rule.token != "string") { 140 | state.pending = []; 141 | for (var j = 2; j < matches.length; j++) 142 | if (matches[j]) 143 | state.pending.push({text: matches[j], token: rule.token[j - 1]}); 144 | stream.backUp(matches[0].length - (matches[1] ? matches[1].length : 0)); 145 | return token[0]; 146 | } else if (token && token.join) { 147 | return token[0]; 148 | } else { 149 | return token; 150 | } 151 | } 152 | } 153 | stream.next(); 154 | return null; 155 | }; 156 | } 157 | 158 | function cmp(a, b) { 159 | if (a === b) return true; 160 | if (!a || typeof a != "object" || !b || typeof b != "object") return false; 161 | var props = 0; 162 | for (var prop in a) if (a.hasOwnProperty(prop)) { 163 | if (!b.hasOwnProperty(prop) || !cmp(a[prop], b[prop])) return false; 164 | props++; 165 | } 166 | for (var prop in b) if (b.hasOwnProperty(prop)) props--; 167 | return props == 0; 168 | } 169 | 170 | function enterLocalMode(config, state, spec, token) { 171 | var pers; 172 | if (spec.persistent) for (var p = state.persistentStates; p && !pers; p = p.next) 173 | if (spec.spec ? cmp(spec.spec, p.spec) : spec.mode == p.mode) pers = p; 174 | var mode = pers ? pers.mode : spec.mode || CodeMirror.getMode(config, spec.spec); 175 | var lState = pers ? pers.state : CodeMirror.startState(mode); 176 | if (spec.persistent && !pers) 177 | state.persistentStates = {mode: mode, spec: spec.spec, state: lState, next: state.persistentStates}; 178 | 179 | state.localState = lState; 180 | state.local = {mode: mode, 181 | end: spec.end && toRegex(spec.end), 182 | endScan: spec.end && spec.forceEnd !== false && toRegex(spec.end, false), 183 | endToken: token && token.join ? token[token.length - 1] : token}; 184 | } 185 | 186 | function indexOf(val, arr) { 187 | for (var i = 0; i < arr.length; i++) if (arr[i] === val) return true; 188 | } 189 | 190 | function indentFunction(states, meta) { 191 | return function(state, textAfter, line) { 192 | if (state.local && state.local.mode.indent) 193 | return state.local.mode.indent(state.localState, textAfter, line); 194 | if (state.indent == null || state.local || meta.dontIndentStates && indexOf(state.state, meta.dontIndentStates) > -1) 195 | return CodeMirror.Pass; 196 | 197 | var pos = state.indent.length - 1, rules = states[state.state]; 198 | scan: for (;;) { 199 | for (var i = 0; i < rules.length; i++) { 200 | var rule = rules[i]; 201 | if (rule.data.dedent && rule.data.dedentIfLineStart !== false) { 202 | var m = rule.regex.exec(textAfter); 203 | if (m && m[0]) { 204 | pos--; 205 | if (rule.next || rule.push) rules = states[rule.next || rule.push]; 206 | textAfter = textAfter.slice(m[0].length); 207 | continue scan; 208 | } 209 | } 210 | } 211 | break; 212 | } 213 | return pos < 0 ? 0 : state.indent[pos]; 214 | }; 215 | } 216 | }); 217 | -------------------------------------------------------------------------------- /public/patterns.js: -------------------------------------------------------------------------------- 1 | // Patterns from https://philiprogers.com/svgpatterns/ 2 | // Patterns from https://iros.github.io/patternfills/sample_svg.html 3 | var patterns = { 4 | "anchors": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 80 80' width='80' height='80'%3E%3Cpath fill='%239C92AC' fill-opacity='0.4' d='M14 16H9v-2h5V9.87a4 4 0 1 1 2 0V14h5v2h-5v15.95A10 10 0 0 0 23.66 27l-3.46-2 8.2-2.2-2.9 5a12 12 0 0 1-21 0l-2.89-5 8.2 2.2-3.47 2A10 10 0 0 0 14 31.95V16zm40 40h-5v-2h5v-4.13a4 4 0 1 1 2 0V54h5v2h-5v15.95A10 10 0 0 0 63.66 67l-3.47-2 8.2-2.2-2.88 5a12 12 0 0 1-21.02 0l-2.88-5 8.2 2.2-3.47 2A10 10 0 0 0 54 71.95V56zm-39 6a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm40-40a2 2 0 1 1 0-4 2 2 0 0 1 0 4zM15 8a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm40 40a2 2 0 1 0 0-4 2 2 0 0 0 0 4z'%3E%3C/path%3E%3C/svg%3E", 5 | "prisonbars": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSd3aGl0ZScgLz4KICA8cmVjdCB4PScwJyB5PScwJyB3aWR0aD0nNicgaGVpZ2h0PScxMCcgZmlsbD0nYmxhY2snIC8+Cjwvc3ZnPg==", 6 | "crosshatch": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPSc4JyBoZWlnaHQ9JzgnPgogIDxyZWN0IHdpZHRoPSc4JyBoZWlnaHQ9JzgnIGZpbGw9JyNmZmYnLz4KICA8cGF0aCBkPSdNMCAwTDggOFpNOCAwTDAgOFonIHN0cm9rZS13aWR0aD0nMC41JyBzdHJva2U9JyNhYWEnLz4KPC9zdmc+Cg==", 7 | "whitecarbon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB3aWR0aD0nNicgaGVpZ2h0PSc2Jz4KICA8cmVjdCB3aWR0aD0nNicgaGVpZ2h0PSc2JyBmaWxsPScjZWVlZWVlJy8+CiAgPGcgaWQ9J2MnPgogICAgPHJlY3Qgd2lkdGg9JzMnIGhlaWdodD0nMycgZmlsbD0nI2U2ZTZlNicvPgogICAgPHJlY3QgeT0nMScgd2lkdGg9JzMnIGhlaWdodD0nMicgZmlsbD0nI2Q4ZDhkOCcvPgogIDwvZz4KICA8dXNlIHhsaW5rOmhyZWY9JyNjJyB4PSczJyB5PSczJy8+Cjwvc3ZnPg==", 8 | "honeycomb": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1NiIgaGVpZ2h0PSIxMDAiPgo8cmVjdCB3aWR0aD0iNTYiIGhlaWdodD0iMTAwIiBmaWxsPSIjZjhkMjAzIj48L3JlY3Q+CjxwYXRoIGQ9Ik0yOCA2NkwwIDUwTDAgMTZMMjggMEw1NiAxNkw1NiA1MEwyOCA2NkwyOCAxMDAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZjYyOSIgc3Ryb2tlLXdpZHRoPSIyIj48L3BhdGg+CjxwYXRoIGQ9Ik0yOCAwTDI4IDM0TDAgNTBMMCA4NEwyOCAxMDBMNTYgODRMNTYgNTBMMjggMzQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZTUwMyIgc3Ryb2tlLXdpZHRoPSIyIj48L3BhdGg+Cjwvc3ZnPg==", 9 | "blueprint": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj4KPHJlY3Qgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxMDAiIGZpbGw9IiMyNjkiPjwvcmVjdD4KPGcgZmlsbD0iIzY0OTRiNyI+CjxyZWN0IHdpZHRoPSIxMDAiIGhlaWdodD0iMSIgeT0iMjAiPjwvcmVjdD4KPHJlY3Qgd2lkdGg9IjEwMCIgaGVpZ2h0PSIxIiB5PSI0MCI+PC9yZWN0Pgo8cmVjdCB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEiIHk9IjYwIj48L3JlY3Q+CjxyZWN0IHdpZHRoPSIxMDAiIGhlaWdodD0iMSIgeT0iODAiPjwvcmVjdD4KPHJlY3Qgd2lkdGg9IjEiIGhlaWdodD0iMTAwIiB4PSIyMCI+PC9yZWN0Pgo8cmVjdCB3aWR0aD0iMSIgaGVpZ2h0PSIxMDAiIHg9IjQwIj48L3JlY3Q+CjxyZWN0IHdpZHRoPSIxIiBoZWlnaHQ9IjEwMCIgeD0iNjAiPjwvcmVjdD4KPHJlY3Qgd2lkdGg9IjEiIGhlaWdodD0iMTAwIiB4PSI4MCI+PC9yZWN0Pgo8L2c+CjxyZWN0IHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIiBmaWxsPSJub25lIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZT0iI2ZmZiI+PC9yZWN0Pgo8L3N2Zz4=", 10 | "pink-circles-2": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPGNpcmNsZSBjeD0nMS41JyBjeT0nMS41JyByPScxLjUnIGZpbGw9JyNmZjAwNDgnLz4KPC9zdmc+Cg==", 11 | "pink-circles-1": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSJoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCkiIC8+CiAgPGNpcmNsZSBjeD0iMSIgY3k9IjEiIHI9IjEiIGZpbGw9IiNmYjVkNjciLz4KPC9zdmc+", 12 | "pink-circles-3": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPGNpcmNsZSBjeD0nMicgY3k9JzInIHI9JzInIGZpbGw9JyNmYjVkNjcnLz4KPC9zdmc+", 13 | "pink-circles-5": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPGNpcmNsZSBjeD0nMycgY3k9JzMnIHI9JzMnIGZpbGw9JyNmYjVkNjcnLz4KPC9zdmc+Cg==", 14 | "pink-circles-7": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPGNpcmNsZSBjeD0nNCcgY3k9JzQnIHI9JzQnIGZpbGw9JyNmYjVkNjcnLz4KPC9zdmc+", 15 | "pink-circles-9": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPGNpcmNsZSBjeD0nNC41JyBjeT0nNC41JyByPSc0LjUnIGZpbGw9JyNmYjVkNjcnLz4KPC9zdmc+", 16 | "pink-stripe-3": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPHJlY3QgeD0nMCcgeT0nMCcgd2lkdGg9JzEwJyBoZWlnaHQ9JzMnIGZpbGw9JyNmZjAwNDgnIC8+Cjwvc3ZnPg==", 17 | "blue-circles-4": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPGNpcmNsZSBjeD0nMi41JyBjeT0nMi41JyByPScyLjUnIGZpbGw9JyMwMDU0YTgnLz4KPC9zdmc+", 18 | "blue-stripe-4": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPHJlY3QgeD0nMCcgeT0nMCcgd2lkdGg9JzEwJyBoZWlnaHQ9JzQnIGZpbGw9JyMwMDU0YTgnIC8+Cjwvc3ZnPg==", 19 | "navy-stripe-3": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdyZ2JhKDI1NSwgMjU1LCAyNTUsIDApJyAvPgogIDxyZWN0IHg9JzAnIHk9JzAnIHdpZHRoPScxMCcgaGVpZ2h0PSczJyBmaWxsPScjMDAxNzUyJyAvPgo8L3N2Zz4=", 20 | "navy-circles-4": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdyZ2JhKDI1NSwgMjU1LCAyNTUsIDApJyAvPgogIDxjaXJjbGUgY3g9JzIuNScgY3k9JzIuNScgcj0nMi41JyBmaWxsPScjMDAxNzUyJy8+Cjwvc3ZnPg==", 21 | "yellow-circles-3": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPGNpcmNsZSBjeD0nMicgY3k9JzInIHI9JzInIGZpbGw9JyNmZmY3MGYnLz4KPC9zdmc+", 22 | "yellow-stripe-3": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPHJlY3QgeD0nMCcgeT0nMCcgd2lkdGg9JzEwJyBoZWlnaHQ9JzMnIGZpbGw9JyNmZmY3MGYnIC8+Cjwvc3ZnPg==", 23 | "gray-circles-3": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPGNpcmNsZSBjeD0nMicgY3k9JzInIHI9JzInIGZpbGw9JyM0YTRmNTQnLz4KPC9zdmc+", 24 | "gray-circles-7": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPGNpcmNsZSBjeD0nNCcgY3k9JzQnIHI9JzQnIGZpbGw9JyM0YTRmNTQnLz4KPC9zdmc+", 25 | "gray-stripe-4": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPHJlY3QgeD0nMCcgeT0nMCcgd2lkdGg9JzEwJyBoZWlnaHQ9JzQnIGZpbGw9JyM0YTRmNTQnIC8+Cjwvc3ZnPg==", 26 | "gray-subtle-patch": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPSc1JyBoZWlnaHQ9JzUnPgogIDxyZWN0IHdpZHRoPSc1JyBoZWlnaHQ9JzUnIGZpbGw9J2hzbGEoMzYwLCAxMDAlLCAxMDAlLCAwKScgLz4KICA8cmVjdCB4PScyJyB5PScyJyB3aWR0aD0nMScgaGVpZ2h0PScxJyBmaWxsPScjNGE0ZjU0JyAvPgo8L3N2Zz4=", 27 | "gray-circles-9": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPGNpcmNsZSBjeD0nNScgY3k9JzUnIHI9JzUnIGZpbGw9JyM0YTRmNTQnLz4KPC9zdmc+", 28 | "white-circles-3": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDI0MCwgNiUsIDkwJSwgMCknIC8+CiAgPGNpcmNsZSBjeD0nMicgY3k9JzInIHI9JzInIGZpbGw9J2hzbGEoMjQwLCA2JSwgOTAlLCAxKScvPgo8L3N2Zz4=", 29 | "white-circles-9": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDI0MCwgNiUsIDkwJSwgMCknIC8+CiAgPGNpcmNsZSBjeD0nNScgY3k9JzUnIHI9JzUnIGZpbGw9J2hzbGEoMjQwLCA2JSwgOTAlLCAxKScvPgo8L3N2Zz4=", 30 | "white-stripe-4": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDI0MCwgNiUsIDkwJSwgMCknIC8+CiAgPHJlY3QgeD0nMCcgeT0nMCcgd2lkdGg9JzQnIGhlaWdodD0nMTAnIGZpbGw9J2hzbGEoMjQwLCA2JSwgOTAlLCAxKScgLz4KPC9zdmc+", 31 | "mint-circles-4": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPGNpcmNsZSBjeD0nMi41JyBjeT0nMi41JyByPScyLjUnIGZpbGw9JyMwMGJhYTknLz4KPC9zdmc+", 32 | "mint-stripe-3": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPHJlY3QgeD0nMCcgeT0nMCcgd2lkdGg9JzEwJyBoZWlnaHQ9JzMnIGZpbGw9JyMwMGJhYTknIC8+Cjwvc3ZnPg==", 33 | "orange-circles-3": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPGNpcmNsZSBjeD0nMicgY3k9JzInIHI9JzInIGZpbGw9JyNmZmFhMzcnLz4KPC9zdmc+", 34 | "orange-stripe-3": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPHJlY3QgeD0nMCcgeT0nMCcgd2lkdGg9JzEwJyBoZWlnaHQ9JzMnIGZpbGw9JyNmZmFhMzcnIC8+Cjwvc3ZnPg==", 35 | "br-orange-circles-3": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPGNpcmNsZSBjeD0nMicgY3k9JzInIHI9JzInIGZpbGw9JyNmODAnLz4KPC9zdmc+", 36 | "br-orange-stripe-3": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSdoc2xhKDM2MCwgMTAwJSwgMTAwJSwgMCknIC8+CiAgPHJlY3QgeD0nMCcgeT0nMCcgd2lkdGg9JzMnIGhlaWdodD0nMTAnIGZpbGw9JyNmODAnIC8+Cjwvc3ZnPg==" 37 | }; 38 | 39 | window.patterns = patterns; 40 | -------------------------------------------------------------------------------- /public/target_lang.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | Target Language 19 | 20 | 21 | 22 | 23 | 24 |
25 |

26 | Live Code a Graphics Language 27 |

28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 | 36 |
37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 348 | 349 | 402 | 403 | 404 | -------------------------------------------------------------------------------- /public/variants.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Variants 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 28 | 29 | 30 | 31 |
32 |

33 | Live Code a Graphics Language 34 |

35 | 36 | 37 | 38 | 39 | 40 | 41 |
42 | 43 |
44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 148 | 149 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Code for Barbara VM Server 3 | * @author Kit Zellerbach 4 | */ 5 | 6 | const express = require('express'); 7 | const passport = require('passport'); 8 | const LocalStrategy = require('passport-local').Strategy; 9 | const path = require('path'); 10 | const compression = require('compression'); 11 | const favicon = require('serve-favicon'); 12 | const bodyParser = require('body-parser'); 13 | const cookieParser = require('cookie-parser'); 14 | const responseTime = require('response-time'); 15 | const helmet = require('helmet'); 16 | const dotenv = require('dotenv'); 17 | const { TwitterApi } = require('twitter-api-v2'); 18 | dotenv.config(); 19 | 20 | const client = new TwitterApi({ 21 | appKey: process.env.CONSUMER_KEY, 22 | appSecret: process.env.CONSUMER_SECRET, 23 | accessToken: process.env.ACCESS_KEY, 24 | accessSecret: process.env.ACCESS_SECRET 25 | }); 26 | // Enable read and write permissions 27 | const rwClient = client.readWrite; 28 | 29 | // Mongo 30 | var mongoose = require('mongoose'); 31 | mongoose.connect(process.env.MONGODB_URI); 32 | 33 | // CHANGE from 1.x: need to pass in mongoose instance 34 | var deepPopulate = require('mongoose-deep-populate')(mongoose); 35 | 36 | var User = require('./models/user'); 37 | var Session = require('./models/session'); 38 | 39 | const app = express(); 40 | var port = process.env.PORT || 3000; 41 | 42 | var http = require('http').Server(app); 43 | http.listen(port, function () { 44 | console.log('listening on *:' + port); 45 | }); 46 | 47 | // Middleware 48 | app.use(express.static('public', {})); 49 | app.use(favicon(path.join(__dirname, 'public', 'icon.png'))); 50 | app.use(bodyParser.urlencoded({ 51 | extended: true, 52 | limit: '50mb' 53 | })); 54 | app.use(bodyParser.json()); 55 | // app.use(bodyParser({limit: '50mb'})); 56 | app.use(cookieParser()); 57 | app.use(require('express-session')({ secret: 'keyboard cat', resave: false, saveUninitialized: false })); 58 | app.use(responseTime()); 59 | app.use(helmet()); 60 | app.use(compression()); 61 | 62 | // Initialize Passport and restore authentication state, if any, from the session. 63 | app.use(passport.initialize()); 64 | app.use(passport.session()); 65 | app.set('view engine', 'html'); 66 | 67 | /** 68 | * Login 69 | */ 70 | passport.use('login', new LocalStrategy( 71 | function (username, password, done) { 72 | // check in mongo if a user with username exists or not 73 | User.findOne({ 'username': username }, 74 | function (err, user) { 75 | // In case of any error, return using the done method 76 | if (err) 77 | return done(err); 78 | // Username does not exist, log the error and redirect back 79 | if (!user) { 80 | console.log('User Not Found with username ' + username); 81 | return done(null, false, { message: 'No user found' }); 82 | } 83 | 84 | user.comparePassword(password, (error, isMatch) => { 85 | if (!isMatch) { 86 | console.log("Wrong password") 87 | return done(null, false, { message: 'Invalid username & password.' }); 88 | } 89 | return done(null, user); 90 | }); 91 | } 92 | ); 93 | } 94 | )); 95 | 96 | /** 97 | * Signup 98 | */ 99 | passport.use('signup', new LocalStrategy( 100 | { 101 | passReqToCallback: true // allows us to pass back the entire request to the callback 102 | }, 103 | function (req, username, password, done) { 104 | findOrCreateUser = function () { 105 | // find a user in Mongo with provided username 106 | User.findOne({ 'username': username }, function (err, user) { 107 | // In case of any error, return using the done method 108 | if (err) { 109 | console.log('Error in SignUp: ' + err); 110 | return done(err); 111 | } 112 | // already exists 113 | if (user) { 114 | console.log('User already exists with username: ' + username); 115 | return done(null, false, { message: 'user exists.' }); 116 | } else { 117 | // if there is no user with that email create the user 118 | var newUser = new User(); 119 | 120 | // set the user's local credentials 121 | newUser.username = username; 122 | newUser.password = password; 123 | newUser.email = req.param('email'); 124 | newUser.firstName = req.param('firstName'); 125 | newUser.lastName = req.param('lastName'); 126 | 127 | // save the user 128 | newUser.save(function (err) { 129 | if (err) { 130 | console.log('Error in Saving user: ' + err); 131 | throw err; 132 | } 133 | console.log('User Registration succesful'); 134 | return done(null, newUser); 135 | }); 136 | } 137 | }); 138 | }; 139 | // Delay the execution of findOrCreateUser and execute the method in the next tick of the event loop 140 | process.nextTick(findOrCreateUser); 141 | } 142 | )); 143 | 144 | // only the user ID is serialized and added to the session 145 | passport.serializeUser(function (user, done) { 146 | done(null, user.id); 147 | }); 148 | 149 | // for every request, the id is used to find the user, which will be restored 150 | // to req.user. 151 | passport.deserializeUser(function (id, done) { 152 | User.findById(id, function (err, user) { 153 | done(err, user); 154 | }); 155 | }); 156 | 157 | 158 | /** 159 | * Routes 160 | */ 161 | 162 | app.get('/', 163 | function (req, res) { 164 | console.log("/ called"); 165 | res.sendFile(path.join(__dirname + '/public/intro.html')); 166 | }); 167 | 168 | app.get('/about', 169 | function (req, res) { 170 | res.sendFile(path.join(__dirname + '/public/about.html')); 171 | }); 172 | 173 | 174 | app.get('/playground', 175 | function (req, res) { 176 | res.sendFile(path.join(__dirname + '/public/editor.html')); 177 | }); 178 | 179 | app.get('/editor', 180 | function (req, res) { 181 | res.sendFile(path.join(__dirname + '/public/editor.html')); 182 | }); 183 | 184 | app.get('/target_lang', 185 | function (req, res) { 186 | res.sendFile(path.join(__dirname + '/public/target_lang.html')); 187 | }); 188 | 189 | app.get('/tutorial', 190 | function (req, res) { 191 | res.sendFile(path.join(__dirname + '/public/tutorial.html')); 192 | }); 193 | 194 | app.get('/quilting', 195 | function (req, res) { 196 | res.sendFile(path.join(__dirname + '/public/quilting.html')); 197 | }); 198 | 199 | app.get('/variants', 200 | function (req, res) { 201 | res.sendFile(path.join(__dirname + '/public/variants.html')); 202 | }); 203 | 204 | /** 205 | * Login & Sign-up 206 | */ 207 | 208 | app.get('/login', 209 | function (req, res) { 210 | res.sendFile(path.join(__dirname + '/public/intro.html')); 211 | }); 212 | 213 | app.post('/login', 214 | passport.authenticate('login', { failureRedirect: '/' }), 215 | function (req, res) { 216 | res.redirect('back'); 217 | }); 218 | 219 | app.post('/signup', 220 | passport.authenticate('signup', { failureRedirect: '/' }), 221 | function (req, res) { 222 | res.status(200).json({ status: "ok" }); 223 | }); 224 | 225 | app.get('/logout', 226 | function (req, res) { 227 | req.logout(function (err) { 228 | if (err) { return next(err); } 229 | res.redirect('back'); 230 | }); 231 | }); 232 | 233 | /** 234 | * Twitter 235 | */ 236 | 237 | app.post('/tweet', async function (req, res) { 238 | try { 239 | // Split the base64 image string from the 'img' field in the request body 240 | const base64Data = req.body.img.split(",")[1]; // Get the data part after the comma 241 | 242 | // Upload the media to Twitter 243 | const mediaId = await rwClient.v1.uploadMedia(Buffer.from(base64Data, 'base64'), { type: 'png' }); 244 | 245 | // If media upload is successful, post a tweet with the media 246 | const tweetText = "A new pattern by " + req.body.name + " at https://www.barbara.graphics/playground?id=" + req.body.id; 247 | const tweet = await rwClient.v2.tweet(tweetText, { media: { media_ids: [mediaId] } }); 248 | 249 | console.log("Successfully tweeted an image!"); 250 | // Return tweet id to give user tweet url 251 | res.status(200).json({ id: tweet.data.id }); 252 | } catch (error) { 253 | console.error("Failed to post tweet: ", error); 254 | } 255 | }); 256 | 257 | /** 258 | * Save Session 259 | * TOOD: Refactor this mess 260 | */ 261 | app.post('/savesession', 262 | function (req, res) { 263 | var session = new Session(req.body); 264 | console.log(req.body); 265 | if (req.body.parent) { 266 | Session.findOne({ _id: req.body.parent }, function (err, parentSession) { 267 | session.parent = parentSession; 268 | parentSession.children.push(session); 269 | parentSession.save().then(() => { 270 | session.save().then(() => { 271 | console.log("saved session w/ parent & children"); 272 | if (req.body.user) { 273 | User.findOne({ _id: req.body.user }, function (err, user) { 274 | if (err) { 275 | console.log('Error: ' + err); 276 | res.status(400); 277 | throw err; 278 | } 279 | // Save to user 280 | console.log(user); 281 | user.sessions.push(session._id); 282 | user.save(function (err) { 283 | if (err) { 284 | console.log('Error: ' + err); 285 | res.status(400); 286 | throw err; 287 | } 288 | // Seems like sending the session back exceeds the stack size 289 | res.status(200).json(session._id); 290 | } 291 | ) 292 | }); 293 | } else { 294 | res.status(200).json(session._id); 295 | } 296 | }).catch(error => console.log(error)); 297 | }).catch(error => console.log(error)); 298 | }); 299 | } else { 300 | session.save(function (err) { 301 | if (err) { 302 | res.status(400); 303 | throw err; 304 | } 305 | console.log("saved session w/out parent & children"); 306 | if (req.body.user) { 307 | User.findOne({ _id: req.body.user }, function (err, user) { 308 | if (err) { 309 | console.log('Error: ' + err); 310 | res.status(400); 311 | throw err; 312 | } 313 | // Save to user 314 | console.log(user); 315 | user.sessions.push(session._id); 316 | user.save(function (err) { 317 | if (err) { 318 | console.log('Error: ' + err); 319 | res.status(400); 320 | throw err; 321 | } 322 | res.status(200).json(session._id); 323 | } 324 | ) 325 | }); 326 | } else { 327 | res.status(200).json(session._id); 328 | } 329 | }); 330 | } 331 | }); 332 | 333 | 334 | /** 335 | * Delete 336 | */ 337 | app.delete('/session/:id', function (req, res) { 338 | console.log("DELETE"); 339 | console.log(req.params.id); 340 | 341 | Session.findOneAndDelete({ "_id": req.params.id }, function (err, session) { 342 | if (err) { 343 | console.log('Error: ' + err); 344 | res.status(400); 345 | throw err; 346 | } 347 | 348 | // TODO: Delete from children/parent? 349 | // Delete from user 350 | User.findOne({ _id: session.user }, function (err, user) { 351 | if (err) { 352 | console.log('Error: ' + err); 353 | res.status(400); 354 | throw err; 355 | } 356 | user.sessions.pull(session._id); 357 | user.save(function (err) { 358 | if (err) { 359 | console.log('Error: ' + err); 360 | res.status(400); 361 | throw err; 362 | } 363 | res.status(200).json({ status: "ok" }); 364 | } 365 | ) 366 | }); 367 | }); 368 | }); 369 | 370 | /** 371 | * Get Sessions 372 | */ 373 | app.get('/sessions', 374 | function (req, res) { 375 | Session.find({}, { _id: 1, svg: 1, name: 1 }, function (err, data) { 376 | data.map(function (item) { 377 | item.children = undefined; 378 | return item; 379 | }); 380 | res.send(data); 381 | // 15 most recent 382 | // TODO: Add pagination (mongoose-paginate?) 383 | }).sort({ _id: -1 }).limit(15); 384 | }); 385 | 386 | // Session by session id 387 | app.get('/sessions/:id', 388 | function (req, res) { 389 | Session.findOne({ _id: req.params.id }, function (err, data) { 390 | if (err) { 391 | console.log('Error: ' + err); 392 | res.status(400); 393 | throw err; 394 | } 395 | if (!data) { 396 | res.status(200).json({ "status": "no" }); 397 | } else { 398 | res.send(data); 399 | } 400 | }); 401 | }); 402 | 403 | // Session by user id 404 | app.get('/sessions/user/:id', 405 | function (req, res) { 406 | User.findOne({ _id: req.params.id }) 407 | .populate('sessions') // <== 408 | .exec(function (err, sessions) { 409 | if (err) { 410 | console.log('Error: ' + err); 411 | res.status(400); 412 | throw err; 413 | } 414 | res.status(200).json(sessions); 415 | }); 416 | }); 417 | 418 | 419 | // Session children 420 | app.get('/sessions/children/:id', 421 | function (req, res) { 422 | Session.findOne({ _id: req.params.id }, function (err, data) { 423 | res.send(data); 424 | }); 425 | }); 426 | 427 | 428 | // TODO: Sessions by parent id 429 | // app.get('/sessions/parents/:id', 430 | // function (req, res) { 431 | // Session.findOne({_id: req.params.id}, function (err, data) { 432 | // // res.send(data); 433 | // data.deepPopulate('parent', function (err, _data) { 434 | // // console.log(_data); 435 | // res.send(_data) 436 | // }); 437 | // }); 438 | // }); 439 | 440 | app.get('/loggeduser', function (req, res) { 441 | if (req.user === undefined) { 442 | // The user is not logged in 443 | res.json({}); 444 | } else { 445 | res.json({ 446 | username: req.user 447 | }); 448 | } 449 | }); 450 | 451 | 452 | /** 453 | * Custom middleware 454 | * @param req 455 | * @param res 456 | * @param next 457 | */ 458 | function loggedIn(req, res, next) { 459 | console.log(req.user); 460 | if (req.user) { 461 | next(); 462 | } else { 463 | res.redirect('/login'); 464 | } 465 | } 466 | --------------------------------------------------------------------------------