├── .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 | 
12 |
13 | 
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 |
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 |
119 |
120 |
121 |
122 |
Grammar
123 |
Shift+Enter
to check the parser
124 |
125 |
208 |
209 |
Output
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
Input
218 |
Shift+Enter
to run
219 |
220 |
230 |
231 |
Output
232 |
233 |
234 |
235 |
238 |
239 |
240 |
241 |
242 |
243 |
Information
244 |
245 |
246 |
249 |
250 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
×
263 |
Help
264 |
You can use Cmd + Enter
to parse
265 |
You can use Shift + Enter
to run
266 |
You can use Alt + Enter
to export your drawing
267 |
You can use Cmd + E
to look at pattern suggestions
268 |
You can use Cmd + I
to look at filter suggestions
269 |
You can use Shift + Backspace
to switch between Code and Grammar views
270 |
You can click View -> Live Code Mode to toggle Live Coding Mode
271 |
You can click Run -> Start Loop to re-draw the canvas every second.
272 |
In order to load and delete your patterns, make sure you are always logged in by clicking User on the top
273 | right.
274 |
275 |
276 |
277 |
278 |
279 |
289 |
290 |
291 |
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 |
96 |
97 |
98 |
99 |
100 |
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 |
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 |
--------------------------------------------------------------------------------