├── README.md
├── css
├── index.css
├── normalize.css
└── style.css
├── images
├── css-grid-app1.png
├── css-grid-app2.png
└── portfolio_grid_app1.jpg
├── index.html
├── script
├── css-constants.js
├── mce_ta.js
└── pages.js
└── static_pages
├── a-grid1.html
├── a-grid10.html
├── a-grid11.html
├── a-grid12.html
├── a-grid13.html
├── a-grid14.html
├── a-grid2.html
├── a-grid3.html
├── a-grid4.html
├── a-grid5.html
├── a-grid6.html
├── a-grid7.html
├── a-grid8.html
├── a-grid9.html
├── css
└── style.css
├── lib
└── css-constants.js
└── script
└── mce_ta.js
/README.md:
--------------------------------------------------------------------------------
1 | # CSS Grid Practice Environment
2 |
3 | This is a CSS Grid learning and practice environment. It provides a series of follow-along lessons and was inspired by [Grid Critters](https://gridcritters.com) learning game.
4 |
5 | 
6 |
7 | This proof-of-concept was written from the ground up using vanilla JavaScript. It provides the following:
8 |
9 | - Dynamic in-page code editor
10 | - Syntax highlighting
11 | - CSS environment to learn CSS Grid Layout
12 |
13 | You can test it at the following link.
14 |
15 | - Netlify - [https://css-grid.netlify.app/](https://css-grid.netlify.app/)
16 | - Practice Environment - [https://james-priest.github.io/grid-critters-code/](https://james-priest.github.io/grid-critters-code/)
17 |
18 | See below for [screenshots](#screenshots).
19 |
20 | ## Installation
21 |
22 | Clone the repository and change directories.
23 |
24 | ```bash
25 | $ git https://github.com/james-priest/grid-critters-code.git
26 | $ cd grid-critters-code
27 | ```
28 |
29 | No build environment is necessary. This can be run from the file system but ideally it should be run from any local http server.
30 |
31 | Here are a couple links showing how to do this.
32 |
33 | - [Simple HTTP Server](http://jasonwatmore.com/post/2016/06/22/nodejs-setup-simple-http-server-local-web-server) - Runs on Node.js
34 | - [How to run things locally](https://threejs.org/docs/#manual/en/introduction/How-to-run-things-locally) - Instructions for , Node.js, Python, & Ruby
35 |
36 |
37 | ## Usage
38 |
39 | The app has two main JavaScript pages.
40 |
41 | - mce_ta.js - This is responsible for the code editor and all event handling within the app. Any new functionality would be added here.
42 | - pages.js - This acts as the data source. Any new pages, lessons, or quizzes would be added here.
43 | - constants.js - provides a lists of html elements, css constants, and css functions used for syntax highlighting within the editor.
44 |
45 | ## Screenshots
46 |
47 | 
48 |
49 | 
50 |
--------------------------------------------------------------------------------
/css/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
3 | display: flex;
4 | flex-direction: column;
5 | }
6 | html, body, main {
7 | height:100%
8 | }
9 |
10 | header, footer {
11 | /* background-color: hsl(99, 63%, 67%); */
12 | background-color: lightgrey;
13 | text-align: center;
14 | }
15 | header {
16 | border-bottom: 1px solid #999;
17 | }
18 | header h1 {
19 | margin: .67em 0 0 0;
20 | }
21 | header p { margin: 0 0 20px; }
22 | footer {
23 | border-top: 1px solid #999;
24 | }
25 |
26 | main {
27 | margin: 0 20px;
28 | display: flex;
29 | align-items: flex-start;
30 | }
31 | nav {
32 | /* flex: 0 1 270px; */
33 | /* flex-basis: 270px; */
34 | /* height:96%; */
35 | height:100%;
36 | display: inline-block;
37 | /* overflow-y: scroll; */
38 | }
39 | nav ol { padding: 0 40px 0 30px;}
40 | nav a {
41 | text-decoration: none;
42 | color: #333;
43 | /* transition: all .2s ease-in-out; */
44 | }
45 | nav a:hover {
46 | /* text-decoration: underline; */
47 | color: green;
48 | font-weight: bold;
49 | }
50 | nav a.active {
51 | /* color: #c00; */
52 | font-weight: bold;
53 | color: green;
54 | /* text-decoration: none; */
55 | }
56 | .content-container {
57 | flex-grow: 1;
58 | align-self: stretch;
59 | width: 100%;
60 | }
61 | .content-container h2 {
62 | margin: 10px 0 0 10px;;
63 | height: 1.4em;
64 | }
65 | .content-container iframe {
66 | width: 100%;
67 | /* height: 92%; */
68 | height: 100%;
69 | border: none;
70 | }
71 |
--------------------------------------------------------------------------------
/css/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
2 |
3 | /* Document
4 | ========================================================================== */
5 |
6 | /**
7 | * 1. Correct the line height in all browsers.
8 | * 2. Prevent adjustments of font size after orientation changes in
9 | * IE on Windows Phone and in iOS.
10 | */
11 |
12 | html {
13 | line-height: 1.15; /* 1 */
14 | -ms-text-size-adjust: 100%; /* 2 */
15 | -webkit-text-size-adjust: 100%; /* 2 */
16 | }
17 |
18 | /* Sections
19 | ========================================================================== */
20 |
21 | /**
22 | * Remove the margin in all browsers (opinionated).
23 | */
24 |
25 | body {
26 | margin: 0;
27 | }
28 |
29 | /**
30 | * Add the correct display in IE 9-.
31 | */
32 |
33 | article,
34 | aside,
35 | footer,
36 | header,
37 | nav,
38 | section {
39 | display: block;
40 | }
41 |
42 | /**
43 | * Correct the font size and margin on `h1` elements within `section` and
44 | * `article` contexts in Chrome, Firefox, and Safari.
45 | */
46 |
47 | h1 {
48 | font-size: 2em;
49 | margin: 0.67em 0;
50 | }
51 |
52 | /* Grouping content
53 | ========================================================================== */
54 |
55 | /**
56 | * Add the correct display in IE 9-.
57 | * 1. Add the correct display in IE.
58 | */
59 |
60 | figcaption,
61 | figure,
62 | main { /* 1 */
63 | display: block;
64 | }
65 |
66 | /**
67 | * Add the correct margin in IE 8.
68 | */
69 |
70 | figure {
71 | margin: 1em 40px;
72 | }
73 |
74 | /**
75 | * 1. Add the correct box sizing in Firefox.
76 | * 2. Show the overflow in Edge and IE.
77 | */
78 |
79 | hr {
80 | box-sizing: content-box; /* 1 */
81 | height: 0; /* 1 */
82 | overflow: visible; /* 2 */
83 | }
84 |
85 | /**
86 | * 1. Correct the inheritance and scaling of font size in all browsers.
87 | * 2. Correct the odd `em` font sizing in all browsers.
88 | */
89 |
90 | pre {
91 | font-family: monospace, monospace; /* 1 */
92 | font-size: 1em; /* 2 */
93 | }
94 |
95 | /* Text-level semantics
96 | ========================================================================== */
97 |
98 | /**
99 | * 1. Remove the gray background on active links in IE 10.
100 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
101 | */
102 |
103 | a {
104 | background-color: transparent; /* 1 */
105 | -webkit-text-decoration-skip: objects; /* 2 */
106 | }
107 |
108 | /**
109 | * 1. Remove the bottom border in Chrome 57- and Firefox 39-.
110 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
111 | */
112 |
113 | abbr[title] {
114 | border-bottom: none; /* 1 */
115 | text-decoration: underline; /* 2 */
116 | text-decoration: underline dotted; /* 2 */
117 | }
118 |
119 | /**
120 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6.
121 | */
122 |
123 | b,
124 | strong {
125 | font-weight: inherit;
126 | }
127 |
128 | /**
129 | * Add the correct font weight in Chrome, Edge, and Safari.
130 | */
131 |
132 | b,
133 | strong {
134 | font-weight: bolder;
135 | }
136 |
137 | /**
138 | * 1. Correct the inheritance and scaling of font size in all browsers.
139 | * 2. Correct the odd `em` font sizing in all browsers.
140 | */
141 |
142 | code,
143 | kbd,
144 | samp {
145 | font-family: monospace, monospace; /* 1 */
146 | font-size: 1em; /* 2 */
147 | }
148 |
149 | /**
150 | * Add the correct font style in Android 4.3-.
151 | */
152 |
153 | dfn {
154 | font-style: italic;
155 | }
156 |
157 | /**
158 | * Add the correct background and color in IE 9-.
159 | */
160 |
161 | mark {
162 | background-color: #ff0;
163 | color: #000;
164 | }
165 |
166 | /**
167 | * Add the correct font size in all browsers.
168 | */
169 |
170 | small {
171 | font-size: 80%;
172 | }
173 |
174 | /**
175 | * Prevent `sub` and `sup` elements from affecting the line height in
176 | * all browsers.
177 | */
178 |
179 | sub,
180 | sup {
181 | font-size: 75%;
182 | line-height: 0;
183 | position: relative;
184 | vertical-align: baseline;
185 | }
186 |
187 | sub {
188 | bottom: -0.25em;
189 | }
190 |
191 | sup {
192 | top: -0.5em;
193 | }
194 |
195 | /* Embedded content
196 | ========================================================================== */
197 |
198 | /**
199 | * Add the correct display in IE 9-.
200 | */
201 |
202 | audio,
203 | video {
204 | display: inline-block;
205 | }
206 |
207 | /**
208 | * Add the correct display in iOS 4-7.
209 | */
210 |
211 | audio:not([controls]) {
212 | display: none;
213 | height: 0;
214 | }
215 |
216 | /**
217 | * Remove the border on images inside links in IE 10-.
218 | */
219 |
220 | img {
221 | border-style: none;
222 | }
223 |
224 | /**
225 | * Hide the overflow in IE.
226 | */
227 |
228 | svg:not(:root) {
229 | overflow: hidden;
230 | }
231 |
232 | /* Forms
233 | ========================================================================== */
234 |
235 | /**
236 | * 1. Change the font styles in all browsers (opinionated).
237 | * 2. Remove the margin in Firefox and Safari.
238 | */
239 |
240 | button,
241 | input,
242 | optgroup,
243 | select,
244 | textarea {
245 | font-family: sans-serif; /* 1 */
246 | font-size: 100%; /* 1 */
247 | line-height: 1.15; /* 1 */
248 | margin: 0; /* 2 */
249 | }
250 |
251 | /**
252 | * Show the overflow in IE.
253 | * 1. Show the overflow in Edge.
254 | */
255 |
256 | button,
257 | input { /* 1 */
258 | overflow: visible;
259 | }
260 |
261 | /**
262 | * Remove the inheritance of text transform in Edge, Firefox, and IE.
263 | * 1. Remove the inheritance of text transform in Firefox.
264 | */
265 |
266 | button,
267 | select { /* 1 */
268 | text-transform: none;
269 | }
270 |
271 | /**
272 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
273 | * controls in Android 4.
274 | * 2. Correct the inability to style clickable types in iOS and Safari.
275 | */
276 |
277 | button,
278 | html [type="button"], /* 1 */
279 | [type="reset"],
280 | [type="submit"] {
281 | -webkit-appearance: button; /* 2 */
282 | }
283 |
284 | /**
285 | * Remove the inner border and padding in Firefox.
286 | */
287 |
288 | button::-moz-focus-inner,
289 | [type="button"]::-moz-focus-inner,
290 | [type="reset"]::-moz-focus-inner,
291 | [type="submit"]::-moz-focus-inner {
292 | border-style: none;
293 | padding: 0;
294 | }
295 |
296 | /**
297 | * Restore the focus styles unset by the previous rule.
298 | */
299 |
300 | button:-moz-focusring,
301 | [type="button"]:-moz-focusring,
302 | [type="reset"]:-moz-focusring,
303 | [type="submit"]:-moz-focusring {
304 | outline: 1px dotted ButtonText;
305 | }
306 |
307 | /**
308 | * Correct the padding in Firefox.
309 | */
310 |
311 | fieldset {
312 | padding: 0.35em 0.75em 0.625em;
313 | }
314 |
315 | /**
316 | * 1. Correct the text wrapping in Edge and IE.
317 | * 2. Correct the color inheritance from `fieldset` elements in IE.
318 | * 3. Remove the padding so developers are not caught out when they zero out
319 | * `fieldset` elements in all browsers.
320 | */
321 |
322 | legend {
323 | box-sizing: border-box; /* 1 */
324 | color: inherit; /* 2 */
325 | display: table; /* 1 */
326 | max-width: 100%; /* 1 */
327 | padding: 0; /* 3 */
328 | white-space: normal; /* 1 */
329 | }
330 |
331 | /**
332 | * 1. Add the correct display in IE 9-.
333 | * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
334 | */
335 |
336 | progress {
337 | display: inline-block; /* 1 */
338 | vertical-align: baseline; /* 2 */
339 | }
340 |
341 | /**
342 | * Remove the default vertical scrollbar in IE.
343 | */
344 |
345 | textarea {
346 | overflow: auto;
347 | }
348 |
349 | /**
350 | * 1. Add the correct box sizing in IE 10-.
351 | * 2. Remove the padding in IE 10-.
352 | */
353 |
354 | [type="checkbox"],
355 | [type="radio"] {
356 | box-sizing: border-box; /* 1 */
357 | padding: 0; /* 2 */
358 | }
359 |
360 | /**
361 | * Correct the cursor style of increment and decrement buttons in Chrome.
362 | */
363 |
364 | [type="number"]::-webkit-inner-spin-button,
365 | [type="number"]::-webkit-outer-spin-button {
366 | height: auto;
367 | }
368 |
369 | /**
370 | * 1. Correct the odd appearance in Chrome and Safari.
371 | * 2. Correct the outline style in Safari.
372 | */
373 |
374 | [type="search"] {
375 | -webkit-appearance: textfield; /* 1 */
376 | outline-offset: -2px; /* 2 */
377 | }
378 |
379 | /**
380 | * Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
381 | */
382 |
383 | [type="search"]::-webkit-search-cancel-button,
384 | [type="search"]::-webkit-search-decoration {
385 | -webkit-appearance: none;
386 | }
387 |
388 | /**
389 | * 1. Correct the inability to style clickable types in iOS and Safari.
390 | * 2. Change font properties to `inherit` in Safari.
391 | */
392 |
393 | ::-webkit-file-upload-button {
394 | -webkit-appearance: button; /* 1 */
395 | font: inherit; /* 2 */
396 | }
397 |
398 | /* Interactive
399 | ========================================================================== */
400 |
401 | /*
402 | * Add the correct display in IE 9-.
403 | * 1. Add the correct display in Edge, IE, and Firefox.
404 | */
405 |
406 | details, /* 1 */
407 | menu {
408 | display: block;
409 | }
410 |
411 | /*
412 | * Add the correct display in all browsers.
413 | */
414 |
415 | summary {
416 | display: list-item;
417 | }
418 |
419 | /* Scripting
420 | ========================================================================== */
421 |
422 | /**
423 | * Add the correct display in IE 9-.
424 | */
425 |
426 | canvas {
427 | display: inline-block;
428 | }
429 |
430 | /**
431 | * Add the correct display in IE.
432 | */
433 |
434 | template {
435 | display: none;
436 | }
437 |
438 | /* Hidden
439 | ========================================================================== */
440 |
441 | /**
442 | * Add the correct display in IE 10-.
443 | */
444 |
445 | [hidden] {
446 | display: none;
447 | }
--------------------------------------------------------------------------------
/css/style.css:
--------------------------------------------------------------------------------
1 | html { font-size: 19px; height: 90%; min-height: 580px; height: 100%; }
2 | body * { box-sizing: border-box; }
3 | a { transition: all .15s ease-in-out;}
4 | div { margin: 0; padding: 0; }
5 | body {
6 | font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
7 | font-size: 1em;
8 | background-color: #666;
9 | color: #cccccc;
10 | /* margin: 1em; */
11 | margin: 0;
12 | padding: 0;
13 | display: flex;
14 | flex-direction: column;
15 | height: 100%;
16 | transition: all .3s ease-in-out;
17 | position: relative;
18 | overflow-x: hidden;
19 | }
20 | h1 {
21 | /* margin: 10px 0; */
22 | margin: 0;
23 | padding: 16px 0 10px;
24 | }
25 | h2 {
26 | margin: 0 0 14px;
27 | font-size: 1.25em; /* 20px/16px */
28 | }
29 | code { font-family: Consolas, monospace; }
30 | ::-webkit-scrollbar {
31 | -webkit-appearance: none;
32 | width: 7px;
33 | height: 7px;
34 | background: none;
35 | }
36 | ::-webkit-scrollbar-thumb {
37 | background-color: #ccc;
38 | border: 2px solid #aaa;
39 | border-radius: 4px;
40 | box-shadow: 0 0 1px rgba(255,255,255,.5);
41 | }
42 | .splash-btn a {
43 | border-radius: 15px;
44 | border: 1px solid #ccc;
45 | }
46 | .splash-container {
47 | position: absolute;
48 | z-index: 10;
49 | display: flex;
50 | /* left: 5em;
51 | right: 5em;
52 | top: 5em;
53 | bottom: 5em; */
54 | width: 0vw;
55 | height: 0vh;
56 | left: 5vw;
57 | /* left: calc(50% - (90vw / 2)); */
58 | top: 5vh;
59 | /* top: calc(50% - (90vh / 2));; */
60 | /* border: 10px solid rgb(249, 44, 0); */
61 | /* border: 10px solid #999; */
62 | border: 1px solid #666;
63 | border-radius: 10px;
64 | /* display: none; */
65 | opacity: 0;
66 | transition: all .3s ease-out;
67 | overflow: hidden;
68 |
69 | }
70 | .splash-container.show {
71 | display: flex;
72 | opacity: 1;
73 | width: 90vw;
74 | height: 90vh;
75 | }
76 | .splash {
77 | /* margin: 10px; */
78 | width: 100%;
79 | /* height: 100%; */
80 | background-color: #eee;
81 | padding: 20px 26px;
82 | position: relative;
83 | color: #333;
84 | font-size: .8125em; /* 13px/16px */
85 | overflow-y: auto;
86 | }
87 | .splash .splash-btn-close {
88 | position: absolute;
89 | top: 9px;
90 | right: 7px;
91 | }
92 | .splash h2 {
93 | font-size: 1.384615em; /* 18px/13px */
94 | }
95 | .splash h3 {
96 | font-size: 1.230769em; /* 16px/13px */
97 | border-bottom: 1px solid #ccc;
98 | }
99 |
100 | .toc {
101 | position: fixed;
102 | width: 300px;
103 | height: 100vh;
104 | /* background-color: rgba(250,50,50,.5); */
105 | background-color: #eee;
106 | z-index: 5;
107 | /* margin-top: -1em; */
108 | margin-left: -320px;
109 | transition: margin-left .20s ease-out;
110 | /* padding: 1em; */
111 | color: #333;
112 | /* overflow: scroll; */
113 | }
114 | .toc.show {
115 | margin-left: 0;
116 | }
117 | .toc .toc-btn-close {
118 | position: absolute;
119 | top: 6px;
120 | right: 4px;
121 | }
122 | .toc h2 {
123 | margin: 0;
124 | padding: 16px 10px;
125 | background-color:rgb(177, 221, 247);
126 | }
127 | .toc-wrapper {
128 | overflow: scroll;
129 | height: 100vh;
130 | }
131 | .toc ul.section-list {
132 | margin: 0;
133 | padding: 0;
134 | }
135 | .toc ul.section-list .section-item {
136 | /* border-bottom: 1px solid rgba(255, 49, 108, 0.1) !important; */
137 | border-bottom: 1px solid #ddd;
138 | margin: 0;
139 | padding: 0;
140 | list-style-type: none;
141 | }
142 | .toc a.item {
143 | display: block;
144 | color: #333;
145 | /* padding: 8px 14px; */
146 | /* padding: 11px 16px; */
147 | text-decoration: none;
148 | }
149 | .toc a.item:hover {
150 | color: rgb(112, 203, 255);
151 | background-color: rgba(211,211,211,.6);
152 | }
153 | .toc a.item.selected {
154 | color: rgb(80, 191, 255);
155 | background-color: rgba(211,211,211,.6);
156 | }
157 | .toc ul.section-list .section-item .title-container {
158 | padding: 10px 0 10px 30px;
159 | }
160 |
161 | header, main {
162 | /* margin: 1em; */
163 | margin: 0;
164 | padding: 0 1em;
165 |
166 | transition: all .3s ease-in-out;
167 | }
168 | header {
169 | display: flex;
170 | justify-content: space-between;
171 | align-items: center;
172 | }
173 | main {
174 | /* margin:auto; */
175 | /* height: 75vh; */
176 | /* width: 90vw; */
177 | /* max-width: 700px; */
178 | /* height: 96%; */
179 | /* height: 94%; */
180 | display: flex;
181 | justify-content: space-between;
182 | padding-bottom: 1em;
183 | flex-grow: 1;
184 | }
185 | .sandbox-wrapper {
186 | flex-basis: 58%;
187 | width: 100%;
188 | /* height: 100%; */
189 | display: flex;
190 | flex-direction: column;
191 | justify-content: space-between;
192 | flex-grow: 1;
193 | padding-bottom: 1px;
194 | }
195 | #sandbox {
196 | box-sizing: border-box;
197 | flex-basis: 95%;
198 | margin-bottom: .4em;
199 | }
200 | .page-nav {
201 | /* border: 1px solid gray; */
202 | text-align: right;
203 | height: 28px;
204 | }
205 | .page-nav ul {
206 | list-style-type: none;
207 | margin: 0;
208 | padding: 0;
209 | overflow: hidden;
210 | font-size: .875em; /* 16px/14px */
211 | }
212 | .page-nav .right {
213 | float: right;
214 | margin-left: 6px;
215 | }
216 | .page-nav .left {
217 | float: left;
218 | margin-right: 6px;
219 | }
220 | .page-nav ul li {
221 | display: inline-block;
222 | /* background-color: #333; */
223 | padding: 6px 0px;
224 | }
225 | .page-nav ul li.hide {
226 | display: none;
227 | }
228 | /* .page-nav ul li a { */
229 | .page-nav a.nav-btn {
230 | padding: 8px 14px;
231 | color: darkgrey;
232 | text-align: center;
233 | text-decoration: none;
234 | font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
235 | background-color: #333;
236 | /* border-radius: 10px; */
237 | }
238 | /* .page-nav ul li a:hover { */
239 | .page-nav a.nav-btn:hover {
240 | color: #eee;
241 | background-color: #111;
242 | }
243 | .page-nav a.nav-btn.disabled {
244 | color: #555;
245 | }
246 | .page-nav a.nav-btn.disabled:hover {
247 | color: #555;
248 | background-color: #333;
249 | cursor: default;
250 | }
251 | .page-nav a.toggle-code-btn {
252 | color: #eee;
253 | background-color: #888;
254 | border: 1px solid #777;
255 | }
256 | .page-nav a.toggle-code-btn:hover {
257 | color: #ccc;
258 | background-color: #888;
259 | }
260 | .page-nav a.toggle-code-btn.show {
261 | color: darkgrey;
262 | background-color: #333;
263 | }
264 | .page-nav a.toggle-code-btn.show:hover {
265 | color: #ccc;
266 | background-color: #333;
267 | }
268 |
269 | section.grid-container {
270 | position: relative;
271 | box-sizing: border-box;
272 | border: 8px solid #555;
273 | height: 100%;
274 | width: 100%;
275 | transition: height 1.4s ease-out, width 1.4s ease-out, border .1s .2s;
276 | }
277 | .grid-container.solved {
278 | border: 10px solid #388E3C;
279 | }
280 | .guide, .guidelines {
281 | position: absolute;
282 | z-index: 1;
283 | box-sizing: border-box;
284 | /* min-height: 100%;
285 | min-width: 100%; */
286 | height: 100%;
287 | width: 100%;
288 | display: grid;
289 | }
290 | .guide>* {
291 | border: 8px dashed #bbb;
292 | }
293 | .guide>.dunes {
294 | border: 8px dashed #F99;
295 | }
296 | .guide>.rocky {
297 | border: 8px dashed aquamarine;
298 | }
299 | .guide>.cloudy {
300 | /* border: 8px dashed rgb(146, 188, 204); */
301 | border: 8px dashed rgb(146, 164, 204);
302 | }
303 | .guide>.lava {
304 | border: 8px dashed rgb(160, 64, 64);
305 | }
306 | .guidelines>div {
307 | border: 1px solid #eee;
308 | }
309 | .grid {
310 | /* position: relative; */
311 | height: 100%;
312 | width: 100%;
313 | }
314 | .target {
315 | /* width: 100px; */
316 | /* height: 100px; */
317 | color: #333;
318 | font-size: 1.25em; /* 20px/16px */
319 | font-weight: bold;
320 | /* padding: 1px; */
321 | border-bottom: 2px dotted gray;
322 |
323 | display: flex;
324 | justify-content: center;
325 | align-items: center;
326 | }
327 | .target:nth-of-type(1) {
328 | background-color: #7f7f7f;
329 | /* background-color: #777; */
330 | }
331 | .target:nth-of-type(2) {
332 | background-color: #999;
333 | /* background-color: #858585; */
334 | /* background-color: #8f8f8f; */
335 | }
336 | .target.dunes {
337 | background-color: lightcoral;
338 | }
339 | .target.rocky {
340 | background-color: rgb(100, 201, 165);
341 | }
342 | .target.cloudy {
343 | background-color: rgb(100, 107, 201);
344 | }
345 | .target.lava {
346 | background-color: darkred;
347 | }
348 | .control-container {
349 | flex-basis: 40%;
350 | min-width: 380px;
351 | max-width: 420px;
352 | display: flex;
353 | flex-direction: column;
354 | justify-content: space-between;
355 | /* height: 100%; */
356 | margin-right: 16px;
357 | }
358 | .ta-code-editor, .div-code-display, .instructions {
359 | /* flex-basis: 32%; */
360 | border: 1px solid darkgray;
361 | margin: 0;
362 | padding: 0.8em 1em;
363 | background-color: darkslategrey
364 | }
365 | .ta-code-editor, .div-code-display {
366 | flex-basis: 24%;
367 | font-family: Consolas, monospace;
368 | font-size: .8125em; /* 13px/16px */
369 | font-weight: 400;
370 | background-color: #333;
371 | tab-size: 2;
372 | -moz-tab-size: 2;
373 | -o-tab-size: 2;
374 | min-height: 98px;
375 | }
376 | .hide { display: none; }
377 | .instructions {
378 | /* flex-basis: 50%; */
379 | flex-grow: 1;
380 | font-size: .875em; /* 14px/16px */
381 | position: relative;
382 | margin-bottom: .4em;
383 | }
384 | .instructions h2 {
385 | font-size: 1.428571em; /* 20px/14px */
386 | /* text-decoration: underline; */
387 | border-bottom: 1px solid;
388 | }
389 | .instructions .toc-btn {
390 | position: absolute;
391 | top: 0.8em;
392 | right: 1em;
393 | }
394 | .instructions em {
395 | color: lightcoral;
396 | color: #ae81ff;
397 | color: #A6E22E;
398 | font-style: normal;
399 | }
400 | .instructions code {
401 | color: #66D9EF;
402 | font-size: .928571em; /* 13px/14px */
403 | }
404 | .instructions .code {
405 | color: lightcoral;
406 | font-size: .923077em; /* 12px/13px */
407 | /* margin: 20px; */
408 | background-color: #333;
409 | padding: .5em .5em .8em;
410 | border: 1px solid grey;
411 | }
412 | .instructions .code code {
413 | /* color: darkgrey; */
414 | color: palegoldenrod;
415 | white-space: pre-wrap;
416 | tab-size: 2;
417 | -moz-tab-size: 2;
418 | -o-tab-size: 2;
419 | }
420 | .instructions .code code em {
421 | color: lightcoral;
422 | }
423 | .instructions .code .answer code {
424 | color: #66D9EF;
425 | color: #dadb6e;
426 | color: #A6E22E;
427 | }
428 | .inst-btn {
429 | color: #fff;
430 | background-color: #999;
431 | border: 1px solid #777;
432 | padding: 3px 10px;
433 | text-decoration: none;
434 | font-size: .857143em; /* 12px/14px */
435 |
436 | }
437 | .inst-btn:hover, .inst-btn:active { color: #ccc; background-color: #666;}
438 | .inst-btn.disabled {color: #ccc; background-color: #666; cursor: default; }
439 | .instructions .right { text-align: right; /*margin-right: 20px;*/ }
440 | .left { text-align: left; }
441 | .answer {
442 | transition: visibility .3s ease-out, opacity .3s ease-out;
443 | }
444 | .answer.hide { visibility: hidden; opacity: 0; display: unset; }
445 | .answer.show { visibility: visible; opacity: 1; }
446 | .answer code {
447 | white-space: pre-wrap;
448 | tab-size: 2;
449 | -moz-tab-size: 2;
450 | -o-tab-size: 2;
451 | }
452 | .ta-code-editor {
453 | color: #66D9EF;
454 | resize: none;
455 | color: lightgrey;
456 | background-color: rgb(20, 20, 20);
457 | margin-bottom: .4em;
458 | }
459 | .div-code-display {
460 | color: #FFF;
461 | word-wrap: normal;
462 | white-space: pre-wrap;
463 | /* cursor: not-allowed; */
464 | cursor: default;
465 | }
466 | .mce-line { color: #fff; }
467 | .mce-selector { color: #A6E22E; }
468 | .mce-element { color: #F92672; }
469 | .mce-property { color: #66D9EF; font-style: italic; }
470 | .mce-constant { color: #66D9EF; }
471 | .mce-keyword { color: #F92672; }
472 | .mce-number { color: #ae81ff; }
473 | .mce-quotes { color: #dadb6e; }
474 | .mce-function { color: #FD971F;}
475 | /* .mce-comment { color: rgb(255, 118, 248); } */
476 | .mce-comment { color: gray !important; }
477 |
478 | @media screen and (max-width: 768px) {
479 | html { height: initial; }
480 | main { display: block; height: 100%; }
481 | .control-container { min-width: 270px; max-width: 100%; margin-right: 0; height: 100%;}
482 | .sandbox-wrapper { margin-top: .4em; height: 70%; margin-bottom: .4em;}
483 | #sandbox { height: 450px; flex-basis: unset; }
484 | }
485 |
486 | @media screen and (max-width: 480px) {
487 | /* html { font-size: 14px; } */
488 | /* body { margin: 8px; } */
489 | /* main, header { margin: 0; } */
490 | .toc { width: 260px; margin-left: -280px; }
491 | /* .toc.show { margin-left: -8px; } */
492 | .page-nav a.nav-btn { padding: 8px 10px; }
493 | }
--------------------------------------------------------------------------------
/images/css-grid-app1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/james-priest/css-grid-code-editor/f703305436fce99dab9dcc7032532c021a5ac73d/images/css-grid-app1.png
--------------------------------------------------------------------------------
/images/css-grid-app2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/james-priest/css-grid-code-editor/f703305436fce99dab9dcc7032532c021a5ac73d/images/css-grid-app2.png
--------------------------------------------------------------------------------
/images/portfolio_grid_app1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/james-priest/css-grid-code-editor/f703305436fce99dab9dcc7032532c021a5ac73d/images/portfolio_grid_app1.jpg
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
26 |
27 |
Answer
28 |
33 |
34 | Show
35 | Paste
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
60 |
61 |
62 |
Table of Contents
63 |
64 |
67 |
68 |
69 |
70 |
CSS Grid Proof-of-concept App
71 |
72 |
What is this?
73 |
74 | This is a project created as a programming exercise and built as a Single Page Application (SPA). It was inspired by Dave Geddes' (@geddski ) awesome learning app Grid Critters .
75 |
76 |
Background
77 |
78 | I had such a good time going through Dave's Flexbox Zombies course that when I started Grid Critters I wanted to not just code along with the exercises but also build a browser-based environment where I could replicate the lessons of the game. This was strictly for my own learning purposes and would maybe be used as portfolio piece if I got it working.
79 |
80 |
81 | Beyond Dave's very cool concept of turning learning & memorization into game play, what I was completely floored by was how cool the in-browser code editor was and wanted to see if I could replicate it. I loved the interaction between the code I typed and the instant results I saw on the screen.
82 |
83 |
84 | Now at the time, I thought Dave had written all of this from scratch. So beyond him being a bad-ass god of code in my mind, I figured I could learn something by attempting this myself. Heck, if he could do it, maybe I could too...
85 |
86 |
87 | So, I set to work creating an input element that applied the css I typed as a style rule on a sand-boxed area of the DOM. That was pretty straight-forward and I got it working in a day. The hard part was building a fully-fledged css editor with code indentation , shortcut keys , auto-completion , cut and paste , undo history , syntax highlighting , etc.
88 |
89 |
90 | I attempted to do this by analyzing Dave's code editor through DevTools. It was incredibly layered and complex, using a content editable <div>
for display with a one character wide invisible <textarea>
element that received the input and moved on screen as you typed. It is what produced the blinking cursor and provided the illusion that you were working in a single integrated editor environment. Meanwhile, content in the underlying div could be styled using complex regex to produce proper syntax highlighting.
91 |
92 |
93 | So, I got all the pieces working separately and went down that path before finally settling on a middle ground by using a <textarea>
for input and a separate <div>
below that for display. Maybe one day I'll merge the two but not today. Sometimes good enough is good enough!
94 |
95 |
96 | I later found out the editor was a well established, giant of a product called Ace - a high performance code editor for the web that has 8 years of industry development behind it and is actively maintained By Ajax.org and Mozilla. It's used by Cloud9 IDE and GitHub.
97 |
98 |
99 | No wonder it was so cool! It could do just about anything - and when I typed, it just worked! I didn't know this at the time and ignorance is bliss so I set off to build it myself and ended up with what you see here.
100 |
101 |
Technology
102 |
103 | This SPA was built using pure HTML5, CSS3, and JavaScript. JSON was used for page data which is loaded synchronously because of the tiny file size. That means it was built with no dependencies on:
104 |
105 |
106 | Pre-built JavaScript libraries
107 | CSS frameworks
108 | Font & icon toolkits
109 |
110 |
111 | While relying on the above can help speed development, I wanted to try my hand at an app that relied solely on the holy trinity of front end development - HTML, CSS, JS. For this reason the app and all its resources is super lightweight and comes in under 90 KB in total size with no compression or optimization.
112 |
113 |
Acknowledgement & Inspiration
114 |
115 | As mention previously, this was inspired by these two awesome learning applications:
116 |
117 |
121 |
122 | If you are at all interested in learning Flexbox or CSS Grid I would strongly urge you to check out these two courses. They are the reason I can use and understand these technologies today and are also responsible for the inspiration to build this proof-of-concept site.
123 |
124 |
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/script/css-constants.js:
--------------------------------------------------------------------------------
1 | // This format only allows for one module export.
2 | var getFunctions = function() {
3 | return 'url|translateZ|translateY|translateX|translate3d|translate|steps|skewY|skewX|skew|sepia|scaleZ|scaleY|scaleX|scale3d|scale|saturate|rotateZ|rotateY|rotateX|rotate3d|rotate|repeating-radial-gradient|repeating-linear-gradient|repeat|radial-gradient|perspective|opacity|matrix3d|matrix|linear-gradient|invert|hue-rotate|grayscale|drop-shadow|cubic-bezier|contrast|calc|brightness|blur|attr';
4 | };
5 |
6 | var getHtmlElements = function() {
7 | return 'a|abbr|address|area|article|aside|audio|b|base|bdi|bdo|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|datalist|dd|del|details|dfn|dialog|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|head|header|hr|html|i|iframe|img|input|ins|kbd|label|legend|li|link|main|map|mark|menu|menuitem|meta|meter|nav|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|source|span|strong|style|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|u|ul|var|video|wbr';
8 | };
9 |
10 | var getConstants = function() {
11 | return 'absolute|alias|aliceblue|all|all-scroll|allow-end|alternate|alternate-reverse|always|antiquewhite|aqua|aqua|aquamarine|armenian|auto|avoid|azure|backwards|balance|baseline|beige|bidi-override|bisque|black|blanchedalmond|block|blue|blueviolet|border-box|both|bottom|break-all|break-word|brown|burlywood|cadetblue|capitalize|caption|cell|center|chartreuse|chocolate|circle|cjk-ideographic|clip|clone|close-quote|col-resize|collapse|color-dodge|column|column-reverse|condensed|contain|content-box|context-menu|copy|coral|cornflowerblue|cornsilk|counter|cover|crimson|crosshair|cursive|cyan|darkblue|darkcyan|darken|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|dashed|decimal|decimal-leading-zero|deeppink|deepskyblue|default|dimgray|dimgrey|disc|distribute|dodgerblue|dotted|double|e-resize|ease|ease-in|ease-in-out|ease-out|ellipsis|embed|end|ew-resize|expanded|extra-condensed|extra-expanded|fantasy|fill|firebrick|first|fixed|flat|flex|flex-end|flex-start|floralwhite|force-end|forestgreen|forwards|fuchsia|fuchsia|gainsboro|georgian|ghostwhite|gold|goldenrod|grab|grabbing|gray|green|greenyellow|grey|grid|groove|hebrew|help|hidden|hide|hiragana|hiragana-iroha|honeydew|horizontal|hotpink|icon|indianred|indigo|infinite|inherit|initial|inline|inline-block|inline-flex|inline-table|inset|inside|inter-cluster|inter-ideograph|inter-word|invert|italic|ivory|justify|kashida|katakana|katakana-iroha|keep-all|khaki|large|larger|last|lavender|lavenderblush|lawngreen|left|lemonchiffon|lightblue|lightcoral|lightcyan|lighten|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|line-through|linear|linen|list-item|local|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|ltr|luminosity|magenta|maroon|max-content|medium|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|menu|message-box|middle|midnightblue|min-content|mintcream|mistyrose|moccasin|monospace|move|multiply|n-resize|navajowhite|navy|ne-resize|nesw-resize|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|ns-resize|nw-resize|nwse-resize|oblique|oldlace|olive|olivedrab|open-quote|orange|orangered|orchid|outset|outside|overlay|overline|padding-box|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|paused|peachpuff|peru|pink|plum|pointer|powderblue|pre|pre-line|pre-wrap|preserve-3d|progress|purple|red|relative|repeat|repeat-x|repeat-y|reverse|ridge|right|rosybrown|round|row|row dense|row-line|row-resize|row-reverse|royalblue|rtl|run-in|running|s-resize|saddlebrown|salmon|sandybrown|sans-serif|saturation|scale-down|screen|scroll|se-resize|seagreen|seashell|semi-condensed|semi-expanded|separate|serif|show|sienna|silver|skyblue|slateblue|slategray|slategrey|slice|small|small-caps|small-caption|smaller|snow|solid|space|space-around|space-between|span|springgreen|square|start|static|status-bar|steelblue|step-end|step-start|sticky|stretch|sub|super|sw-resize|system-ui|table|table-caption|table-cell|table-column|table-column-group|table-footer-group|table-header-group|table-row|table-row-group|tan|teal|text|text-bottom|text-top|thick|thin|thistle|tomato|top|transparent|trim|turquoise|ultra-condensed|ultra-expanded|underline|unset|upper-alpha|upper-greek|upper-latin|upper-roman|uppercase|vertical|vertical-text|violet|visible|w-resize|wait|wavy|wheat|white|whitesmoke|wrap|wrap-reverse|x-large|x-small|xx-large|xx-small|yellow|yellowgreen|zoom-in|zoom-out';
12 | // return 'solid|dashed|dotted|grid';
13 | };
14 |
15 | if ( typeof module !== 'undefined' ) {
16 | module.exports = getConstants;
17 | }
18 |
19 | // export function message( msg ) { alert( msg ); };
--------------------------------------------------------------------------------
/script/mce_ta.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | // Create a namespace using the Module Pattern.
3 | var CSSVALUES = (function() {
4 | var cssProperties = 'align-content|align-items|align-self|all|animation|animation-delay|animation-direction|animation-duration|animation-fill-mode|animation-iteration-count|animation-name|animation-play-state|animation-timing-function|backface-visibility|background|background-attachment|background-blend-mode|background-clip|background-color|background-image|background-origin|background-position|background-repeat|background-size|border|border-bottom|border-bottom-color|border-bottom-left-radius|border-bottom-right-radius|border-bottom-style|border-bottom-width|border-collapse|border-color|border-image|border-image-outset|border-image-repeat|border-image-slice|border-image-source|border-image-width|border-left|border-left-color|border-left-style|border-left-width|border-radius|border-right|border-right-color|border-right-style|border-right-width|border-spacing|border-style|border-top|border-top-color|border-top-left-radius|border-top-right-radius|border-top-style|border-top-width|border-width|bottom|box-decoration-break|box-shadow|box-sizing|caption-side|caret-color|clear|clip|color|column-count|column-fill|column-gap|column-rule|column-rule-color|column-rule-style|column-rule-width|column-span|column-width|columns|content|counter-increment|counter-reset|cursor|direction|display|empty-cells|filter|flex|flex-basis|flex-direction|flex-flow|flex-grow|flex-shrink|flex-wrap|float|font|font-family|font-kerning|font-size|font-size-adjust|font-stretch|font-style|font-variant|font-weight|grid|grid-area|grid-auto-columns|grid-auto-flow|grid-auto-rows|grid-column|grid-column-end|grid-column-gap|grid-column-start|grid-gap|grid-row|grid-row-end|grid-row-gap|grid-row-start|grid-template|grid-template-areas|grid-template-columns|grid-template-rows|hanging-punctuation|height|justify-content|left|letter-spacing|line-height|list-style|list-style-image|list-style-position|list-style-type|margin|margin-bottom|margin-left|margin-right|margin-top|max-height|max-width|min-height|min-width|object-fit|opacity|order|outline|outline-color|outline-offset|outline-style|outline-width|overflow|overflow-x|overflow-y|padding|padding-bottom|padding-left|padding-right|padding-top|page-break-after|page-break-before|page-break-inside|perspective|perspective-origin|position|quotes|resize|right|tab-size|table-layout|text-align|text-align-last|text-decoration|text-decoration-color|text-decoration-line|text-decoration-style|text-indent|text-justify|text-overflow|text-shadow|text-transform|top|transform|transform-origin|transform-style|transition|transition-delay|transition-duration|transition-property|transition-timing-function|unicode-bidi|user-select|vertical-align|visibility|white-space|width|word-break|word-spacing|word-wrap|z-index';
5 | var cssFunctions = 'url|translateZ|translateY|translateX|translate3d|translate|steps|skewY|skewX|skew|sepia|scaleZ|scaleY|scaleX|scale3d|scale|saturate|rotateZ|rotateY|rotateX|rotate3d|rotate|rgba|rgb|repeating-radial-gradient|repeating-linear-gradient|repeat|radial-gradient|perspective|opacity|matrix3d|matrix|linear-gradient|invert|hue-rotate|hsla|hsl|grayscale|drop-shadow|cubic-bezier|contrast|calc|brightness|blur|attr';
6 | var cssHtmlElements = 'a|abbr|address|area|article|aside|audio|b|base|bdi|bdo|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|datalist|dd|del|details|dfn|dialog|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|head|header|hr|html|i|iframe|img|input|ins|kbd|label|legend|li|link|main|map|mark|menu|menuitem|meta|meter|nav|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|source|span|strong|style|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|u|ul|var|video|wbr';
7 | var cssFonts = 'cursive|fantasy|monospace|sans-serif|serif|system-ui';
8 | var cssConstants = 'absolute|alias|all|all-scroll|allow-end|alternate|alternate-reverse|always|armenian|auto|avoid|backwards|balance|baseline|bidi-override|block|border-box|both|bottom|break-all|break-word|capitalize|caption|cell|center|circle|cjk-ideographic|clip|clone|close-quote|col-resize|collapse|color-dodge|column|column-reverse|condensed|contain|content-box|context-menu|copy|counter|cover|crosshair|darken|dashed|decimal|decimal-leading-zero|default|disc|distribute|dotted|double|e-resize|ease|ease-in|ease-in-out|ease-out|ellipsis|embed|end|ew-resize|expanded|extra-condensed|extra-expanded|fill|first|fixed|flat|flex|flex-end|flex-start|force-end|forwards|georgian|grab|grabbing|grid|groove|hebrew|help|hidden|hide|hiragana|hiragana-iroha|horizontal|icon|infinite|inherit|initial|inline|inline-block|inline-flex|inline-table|inset|inside|inter-cluster|inter-ideograph|inter-word|invert|italic|justify|kashida|katakana|katakana-iroha|keep-all|large|larger|last|left|lighten|line-through|linear|list-item|local|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|ltr|luminosity|max-content|medium|menu|message-box|middle|min-content|move|multiply|n-resize|ne-resize|nesw-resize|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|ns-resize|nw-resize|nwse-resize|oblique|open-quote|outset|outside|overlay|overline|padding-box|paused|pointer|pre|pre-line|pre-wrap|preserve-3d|progress|relative|repeat|repeat-x|repeat-y|reverse|ridge|right|round|row|row-line|row-resize|row-reverse|rowdense|rtl|run-in|running|s-resize|saturation|scale-down|screen|scroll|se-resize|semi-condensed|semi-expanded|separate|show|slice|small|small-caps|small-caption|smaller|solid|space|space-around|space-between|span|square|start|static|status-bar|step-end|step-start|sticky|stretch|sub|super|sw-resize|table|table-caption|table-cell|table-column|table-column-group|table-footer-group|table-header-group|table-row|table-row-group|text|text-bottom|text-top|thick|thin|top|transparent|trim|ultra-condensed|ultra-expanded|underline|unset|upper-alpha|upper-greek|upper-latin|upper-roman|uppercase|vertical|vertical-text|visible|w-resize|wait|wavy|wrap|wrap-reverse|x-large|x-small|xx-large|xx-small|zoom-in|zoom-out';
9 | var cssColors = 'aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen';
10 | var cssUnits = 'ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vmin|vmax|vw|%';
11 |
12 | return {
13 | getProperties: function() { return cssProperties; },
14 | getFunctions: function() { return cssFunctions; },
15 | getHtmlElements: function() { return cssHtmlElements; },
16 | getFonts: function() { return cssFonts; },
17 | getConstants: function() { return cssConstants; },
18 | getColors: function() { return cssColors; },
19 | getUnits: function() { return cssUnits; }
20 | };
21 | } )();
22 | // Private properties are created in the local scope of the function expression.
23 | // Public properties are built within the object which is returned to become the namespace.
24 | // Access to private data is made possible only because of closure within the larger module.
25 | // Parens at end of the IIFE invokes function and immediately provides access to ns object.
26 |
27 | var myTACodeEditor = {
28 | gridContainer: document.querySelector( '.grid-container' ),
29 | divFollow: document.querySelector( '.follow code'),
30 | divSolution: document.querySelector( '.answer code' ),
31 | taCodeEditor: document.querySelector( '.ta-code-editor' ),
32 | divCodeDisplay: document.querySelector( '.div-code-display' ),
33 | toc: document.querySelector( '.toc' ),
34 | splashContainer: document.querySelector( '.splash-container' ),
35 | backBtn: document.querySelector( '.back-btn '),
36 | nextBtn: document.querySelector( '.next-btn' ),
37 |
38 | rxSelectors: /^[\w .\-@#[\]'"=:()>^~*+,|$]+(?={)/gm,
39 | rxHtmlElements: new RegExp( '\\/\\*.*|<.*?>|\\b(' + CSSVALUES.getHtmlElements() + ')\\b(?=.*{)', 'gm' ),
40 | rxConstants: new RegExp('^[\\s\\w-]+|.*?{|\\w+\\(.*\\)|\\/\\*.*|<.*?>|-\\w+|(\\b(' + CSSVALUES.getConstants() + '|' + CSSVALUES.getColors() + '|' + CSSVALUES.getFonts() + ')(?![\\w-\\()]))', 'gm'),
41 | rxKeywords: new RegExp('^[\\s\\w-]+:|\\/\\*.*|\\(.*\\)|([\\d.]+)(' + CSSVALUES.getUnits() + ')(?=\\W)', 'gm'),
42 | // rxNumbers: /^[\s\w-]+:|.*?{|[a-z]\d+|\/\*.*|\(.*\)|([^:>("'/_-]\d*\.?\d+|#[0-9A-Fa-f]{3,6})/gm,
43 | rxNumbers: /^[\s\w-]+:|.*?{|[a-z]\d+|\/\*.*|\(.*\)|([^:>("'/_-]\d*\.?\d+|(#[0-9A-Fa-f]{6}|#[0-9A-Fa-f]{3})\b)/gm,
44 | // rxProperties: /^[^{][ \t\w-]+(?=:)/gm,
45 | rxProperties: new RegExp('^[ \\t\\w-]*(' + CSSVALUES.getProperties() + ')(?=:)','gm'),
46 | rxFunctions: new RegExp( '\\/\\*.*|((' + CSSVALUES.getFunctions() + ')\\([\\w\\d `~!@#$%^&*()\\-_=+[\\]{}\\\\|:;' + String.fromCharCode(39) + '",.\\/?]*\\))', 'gm' ),
47 | rxQuotes: /^[/*].*\*\/|("[\w\s-]*"(?!>)|'[\w\s-]*'(?!>))/gm,
48 | rxRules: /(.+)\s*(?={)|^[\t\s]*(.+;)/gm,
49 | rxTextToWrap: /[\w\d\-[\] {}.:;#,>+'"=()/~^$*]+$/gm,
50 | rxBlockToWrap: /[\w\d\-[\] {}.:;#,>+'"=()/~^$*%\t]+$/gm,
51 | rxFindComment: /\/\*|\*\//,
52 | rxReplaceComment: /\/\*|\*\//gm,
53 | rxComments: /\/\*.*\*\//gm,
54 |
55 | curPage: 0,
56 | pages: [],
57 | preFill: '',
58 | curPos: 0,
59 | answerCode: '',
60 | styleRuleIdx: 0,
61 | browser: { chrome: 0, ie: 0, firefox: 0, safari: 0, opera: 0, edge: 0 },
62 | debug: false,
63 |
64 | init: function() {
65 | var mceObj = this;
66 | mceObj.sniffBrowser();
67 | mceObj.taCodeEditor.onkeydown = function( evt ) {
68 | return mceObj.captureKeyDown( evt );
69 | };
70 | mceObj.taCodeEditor.oninput = function( evt ) {
71 | mceObj.highlightSyntax( mceObj.taCodeEditor.value );
72 | mceObj.processRules( evt );
73 | };
74 | mceObj.taCodeEditor.onkeyup = function( evt ) {
75 | return mceObj.captureKeyUp( evt );
76 | };
77 | mceObj.backBtn.onclick = function( evt ) {
78 | return mceObj.clickBack( evt );
79 | };
80 | mceObj.nextBtn.onclick = function( evt ) {
81 | return mceObj.clickNext( evt );
82 | };
83 | mceObj.pages = getPages();
84 | mceObj.curPage = mceObj.getPage();
85 | mceObj.buildToc( mceObj.curPage );
86 | mceObj.loadPage( mceObj.curPage );
87 | // mceObj.loadSplash();
88 | },
89 | loadSplash: function() {
90 | var splashBtn = document.querySelector('.splash-btn a');
91 | window.setTimeout(function() {
92 | splashBtn.dispatchEvent( new Event( 'click' ) );
93 | }, 1000);
94 | },
95 | buildToc: function( curPage ) {
96 | var mceObj = this,
97 | pages = mceObj.pages;
98 | var ul = document.querySelector( '.section-list' );
99 | var frag = document.createDocumentFragment();
100 | pages.forEach( function(pg) {
101 | var li = document.createElement( 'li' );
102 | li.className = 'section-item';
103 | var a = document.createElement( 'a' );
104 | a.className = 'item';
105 | if ( +curPage === pg.num ) {
106 | a.classList.add( 'selected' );
107 | }
108 | a.href = '?pg=' + pg.num;
109 | a.dataset.page = pg.num;
110 | a.onclick = function( evt ) {
111 | return mceObj.clickTocItem( evt );
112 | };
113 |
114 | var div = document.createElement( 'div' );
115 | div.className = 'title-container';
116 | div.innerText = pg.num + '. ' + pg.subtitle;
117 |
118 | a.appendChild( div );
119 | li.appendChild( a );
120 | frag.appendChild( li );
121 | } );
122 | ul.appendChild( frag );
123 | },
124 | getPage: function() {
125 | var mceObj = this,
126 | page =1;
127 | if ( mceObj.getParameterByName( 'pg' ) ) {
128 | page = mceObj.getParameterByName( 'pg' );
129 | if ( page < 1 ) { page = 1; }
130 | var pagesLen = mceObj.pages.length;
131 | if ( page > pagesLen ) { page = pagesLen; }
132 | // console.log( 'got something - page:', page );
133 | if ( !( page > 1 && page <= pagesLen ) ) { page = 1; }
134 | }
135 | return page;
136 | },
137 | getParameterByName: function(name, url) {
138 | if ( !url ) { url = window.location.href; }
139 | name = name.replace(/[[\]]/g, "\\$&");
140 | var regex = new RegExp('[?&]' + name + '(=([^]*)|&|#|$)'),
141 | results = regex.exec(url);
142 | if ( !results ) { return null; }
143 | if ( !results[ 2 ] ) { return ''; }
144 | return decodeURIComponent(results[2].replace(/\+/g, ' '));
145 | },
146 | loadPage: function( pg ) {
147 | var mceObj = this;
148 | // var pages = mceObj.pages;
149 | var pages = getPages();
150 | var idx = pg - 1;
151 | var num = pages[ idx ].num,
152 | title = pages[ idx ].title,
153 | subtitle = pages[ idx ].subtitle,
154 | part1 = pages[ idx ].instructions.part1,
155 | part2 = pages[ idx ].instructions.part2,
156 | preFill = pages[ idx ].code.preFill,
157 | follow = pages[ idx ].code.follow,
158 | solution = pages[ idx ].code.solution,
159 | guide = pages[ idx ].gridContainer.guide,
160 | guidelines = pages[ idx ].gridContainer.guidelines,
161 | grid = pages[ idx ].gridContainer.grid,
162 | style = pages[ idx ].style;
163 |
164 | mceObj.resetDefaults();
165 | document.title = num + '-' + title + ': ' + subtitle;
166 | document.querySelector( '.title' ).innerText = title;
167 | document.querySelector( '.subtitle' ).innerText = num + '. ' + subtitle;
168 | document.querySelector( '.part1' ).innerHTML = part1;
169 | document.querySelector( '.part2' ).innerHTML = part2;
170 | mceObj.setTaVal( preFill );
171 | mceObj.formatFollowCode( follow );
172 | mceObj.formatSolution( solution );
173 | mceObj.doNavButtons( pg, pages );
174 | document.querySelector( '.guide' ).innerHTML = guide;
175 | document.querySelector( '.guidelines' ).innerHTML = guidelines;
176 | document.querySelector( '.grid' ).innerHTML = grid;
177 | mceObj.hightlightTocItem( pg );
178 | mceObj.curPage = pg;
179 |
180 | mceObj.doStyle( style );
181 |
182 | if ( preFill.length <=0 && follow.length <= 0 && solution.length <= 0 ) {
183 | mceObj.hideEditors();
184 | }
185 | },
186 | doStyle: function( style ) {
187 | // var styleSheet = document.styleSheets.guideStyle;
188 | // var styleSheet = document.styleSheets[ 1 ];
189 | var styleSheet = '';
190 | var sheets = document.styleSheets;
191 | for ( var i = 0; i < sheets.length; i++ ) {
192 | // console.log( 'sheets[' + i + ']', 'ownerNode.id:', sheets[ i ].ownerNode.id );
193 | // console.log( 'sheets[' + i + ']', sheets[ i ] );
194 | if ( sheets[ i ].ownerNode.id === 'guideStyle' ) {
195 | styleSheet = sheets[ i ];
196 | break;
197 | }
198 | }
199 |
200 | if ( typeof styleSheet.rules === 'undefined' ) {
201 | // console.log( 'cssRules' );
202 | for ( let i = styleSheet.cssRules.length -1; i >= 0; i-- ) {
203 | styleSheet.deleteRule( i );
204 | }
205 | } else {
206 | // console.log( 'rules' );
207 | for ( let i = styleSheet.rules.length -1; i >= 0; i-- ) {
208 | styleSheet.deleteRule( i );
209 | }
210 | }
211 |
212 | if ( style.length > 0 ) {
213 | style.forEach( function( rule ) {
214 | styleSheet.insertRule( rule );
215 | } );
216 | }
217 | },
218 | resetDefaults: function() {
219 | var mceObj = this;
220 | mceObj.hideAnswer();
221 | document.querySelector( '.paste-btn' ).innerText = 'Paste';
222 | document.querySelector( '.solution' ).classList.remove( 'hide' );
223 | mceObj.divCodeDisplay.classList.remove( 'hide' );
224 | mceObj.taCodeEditor.classList.remove( 'hide' );
225 | document.querySelector( '.toggle-code-btn' ).classList.remove( 'hide' );
226 | document.querySelector( '.toggle-code-btn' ).classList.add( 'show' );
227 | document.querySelector( '.comment' ).classList.remove( 'hide' );
228 |
229 | },
230 | hideEditors: function() {
231 | this.divCodeDisplay.classList.add( 'hide' );
232 | this.taCodeEditor.classList.add( 'hide' );
233 | document.querySelector( '.toggle-code-btn' ).classList.add( 'hide' );
234 | document.querySelector( '.comment' ).classList.add( 'hide' );
235 | },
236 | doNavButtons( pg, pages ) {
237 | var mceObj = this;
238 | // console.log( pages.length );
239 | if ( pg > 1 ) {
240 | mceObj.backBtn.classList.remove( 'disabled' );
241 | mceObj.backBtn.dataset.prev = +pg - 1;
242 | mceObj.backBtn.href = '?pg=' + ( +pg - 1 );
243 | } else {
244 | mceObj.backBtn.classList.add( 'disabled' );
245 | }
246 | if ( pg < pages.length) {
247 | mceObj.nextBtn.classList.remove( 'disabled' );
248 | mceObj.nextBtn.dataset.next = +pg + 1;
249 | mceObj.nextBtn.href = '?pg=' + ( +pg + 1 );
250 | } else {
251 | mceObj.nextBtn.classList.add( 'disabled' );
252 | }
253 | },
254 | formatFollowCode: function( follow ) {
255 | if ( follow.length > 0 ) {
256 | this.divFollow.innerText = follow;
257 | document.querySelector( '.follow' ).classList.remove( 'hide' );
258 | } else {
259 | document.querySelector( '.follow' ).classList.add( 'hide' );
260 | }
261 | },
262 | formatSolution: function(solution) {
263 | if ( solution.length > 0 ) {
264 | this.divSolution.innerText = solution;
265 | this.answerCode = solution;
266 | } else {
267 | document.querySelector( '.solution' ).classList.add( 'hide' );
268 | }
269 | },
270 | setTaVal: function( preFill ) {
271 | var mceObj = this,
272 | curPos = 0;
273 | if ( preFill.length > 0 ) {
274 | mceObj.preFill = preFill;
275 | curPos = preFill.indexOf( '@' );
276 | mceObj.curPos = curPos;
277 | mceObj.taCodeEditor.value = preFill.replace( /@/, '' );
278 | mceObj.taCodeEditor.selectionStart = mceObj.taCodeEditor.selectionEnd = curPos;
279 | mceObj.taCodeEditor.focus();
280 | mceObj.triggerInputEvent();
281 | }
282 |
283 | },
284 | clickBack: function( evt ) {
285 | var mceObj = this;
286 | if ( mceObj.backBtn.classList.contains( 'disabled' ) === false ) {
287 | mceObj.transitionPage( evt.target.dataset.prev );
288 | }
289 | return false;
290 | },
291 | clickNext: function( evt ) {
292 | var mceObj = this;
293 | if ( mceObj.nextBtn.classList.contains( 'disabled' ) === false ) {
294 | mceObj.transitionPage( evt.target.dataset.next );
295 | }
296 | return false;
297 | },
298 | clickTocItem: function( evt ) {
299 | var mceObj = this,
300 | clickPage = evt.currentTarget.dataset.page;
301 | if ( mceObj.curPage !== clickPage ) {
302 | // document.querySelector( '.toc' ).classList.remove( 'show' );
303 | mceObj.transitionPage( clickPage );
304 | }
305 | return false;
306 | },
307 | transitionPage: function( pg ) {
308 | var mceObj = this;
309 | //document.body.style.opacity = 0; // transition
310 | document.querySelector('header').style.opacity = 0; // transition
311 | document.querySelector('main').style.opacity = 0; // transition
312 |
313 | window.setTimeout( function() {
314 | //document.body.style.opacity = 1;
315 | document.querySelector('header').style.opacity = 1;
316 | document.querySelector('main').style.opacity = 1;
317 | mceObj.updateBrowserUrlBar( 'pg=' + pg );
318 | mceObj.loadPage( pg );
319 | }, 300 );
320 | },
321 | hightlightTocItem: function( pg ) {
322 | document.querySelector( '.selected' ).classList.remove( 'selected' );
323 | document.querySelector( '.section-list' ).children[ pg-1 ].children[ 0 ].classList.add( 'selected' );
324 | },
325 | updateBrowserUrlBar: function(qs) {
326 | if (history.pushState) {
327 | var newurl = window.location.protocol + '//' + window.location.host +
328 | window.location.pathname + '?' + qs;
329 | window.history.pushState({path:newurl},'',newurl);
330 | }
331 | },
332 | triggerInputEvent: function() {
333 | var mceObj = this;
334 | if ( mceObj.browser.ie ) {
335 | var event = document.createEvent( 'Event' );
336 | event.initEvent( 'input', false, true );
337 | mceObj.taCodeEditor.dispatchEvent( event );
338 | } else {
339 | mceObj.taCodeEditor.dispatchEvent( new Event( 'input' ) );
340 | }
341 | },
342 | sniffBrowser: function() {
343 | this.browser.chrome = navigator.userAgent.indexOf('Chrome') > -1;
344 | this.browser.ie = navigator.userAgent.indexOf( 'MSIE' ) > -1 ||
345 | navigator.userAgent.indexOf( 'Trident/7.' ) > -1;
346 | this.browser.firefox = navigator.userAgent.indexOf('Firefox') > -1;
347 | this.browser.safari = navigator.userAgent.indexOf('Safari') > -1;
348 | this.browser.edge = navigator.userAgent.indexOf('Edge') > -1;
349 | this.browser.opera = navigator.userAgent.toLowerCase().indexOf('op') > -1;
350 | if ( ( this.browser.chrome ) && ( this.browser.safari ) ) { this.browser.safari = false; }
351 | if ( ( this.browser.chrome ) && ( this.browser.opera ) ) { this.browser.chrome = false; }
352 | },
353 | captureKeyUp: function( evt ) {
354 | const SLASH = '/';
355 | var mceObj = this,
356 | // ta = mceObj.taCodeEditor,
357 | ta = evt.target,
358 | val = ta.value,
359 | selStart = ta.selectionStart,
360 | selEnd = ta.selectionEnd,
361 | selDir = ta.selectionDirection,
362 | lineStartPos = val.lastIndexOf( '\n', selStart - 1 ) > -1 ? val.lastIndexOf( '\n', selStart - 1 ) + 1 : 0,
363 | lineEndPos = val.indexOf( '\n', selEnd ) > -1 ? val.indexOf( '\n', selEnd ) : val.length,
364 | firstLineEndPos = val.indexOf( '\n' ) > -1 ? val.indexOf( '\n' ) : val.length,
365 | lastLineStartPos = val.lastIndexOf( '\n' ) > -1 ? val.lastIndexOf( '\n' ) + 1 : 0,
366 | posInLine = selStart - lineStartPos;
367 | if ( mceObj.debug ) {
368 | console.log( '' );
369 | if ( ta.selectionDirection === 'forward' ) {
370 | console.log( 'after: ', 'selStart:', selStart, 'selEnd:', selEnd, 'selDirection:', selDir );
371 | } else {
372 | console.log( 'after: ', 'selStart:', selEnd, 'selEnd:', selStart, 'selDirection:', selDir );
373 | }
374 | console.log( ' lineStartPos:', lineStartPos, 'lineEndPos:', lineEndPos, 'posInLine', posInLine );
375 | console.log( ' firstLineStartPos:', 0, 'firstLineEndPos:', firstLineEndPos );
376 | console.log( ' lastLineStartPos:', lastLineStartPos, 'lastLineEndPos:', val.length );
377 | }
378 |
379 | switch ( evt.key ) {
380 | case SLASH:
381 | if ( evt.ctrlKey ) {
382 | mceObj.commentSelection( ta );
383 | } else {
384 | return true;
385 | }
386 | break;
387 | }
388 | mceObj.triggerInputEvent();
389 | return false;
390 | },
391 | commentSelection: function( el ) {
392 | var mceObj = this,
393 | val = el.value,
394 | selStart = el.selectionStart,
395 | selEnd = el.selectionEnd,
396 | selDir = el.selectionDirection,
397 | firstLineStartPos = val.lastIndexOf( '\n', selStart - 1 ) > -1 ? val.lastIndexOf( '\n', selStart - 1 ) + 1 : 0,
398 | lastLineEndPos = val.indexOf( '\n', selEnd ) > -1 ? val.indexOf( '\n', selEnd ) : val.length,
399 | lineStartPos = 0, lineEndPos = 0;
400 |
401 | if ( typeof selStart === 'number' && typeof selEnd === 'number' ) {
402 | if ( selStart === selEnd ) {
403 | var slicedLine, newSlicedLine,
404 | posModifier = 0, curPos = 0;
405 |
406 | lineStartPos = firstLineStartPos;
407 | lineEndPos = lastLineEndPos;
408 | slicedLine = val.slice( lineStartPos, lineEndPos );
409 |
410 | newSlicedLine = this.toggleComment( slicedLine, false );
411 | el.value = val.slice( 0, lineStartPos ) + newSlicedLine + val.slice( lineEndPos );
412 |
413 | // set caret selection for comment
414 | if ( this.rxFindComment.test( slicedLine ) ) {
415 | if ( selStart === lineStartPos ) {
416 | posModifier = 0;
417 | } else if ( selStart === lineEndPos ) {
418 | posModifier = -4;
419 | } else {
420 | posModifier = -2;
421 | }
422 | } else {
423 | if ( selStart === lineStartPos ) {
424 | posModifier = 0;
425 | } else if ( selStart === lineEndPos ) {
426 | posModifier = +4;
427 | } else {
428 | posModifier = +2;
429 | }
430 | }
431 | curPos = selDir === 'forward' ? selEnd : selStart;
432 | el.selectionStart = el.selectionEnd = curPos + posModifier;
433 | } else {
434 | var lines = val.split( '\n' ),
435 | selLineCount = 0, prevCommentLineCount = 0;
436 | console.log( '##########################' );
437 | console.log( 'before (lines):', lines );
438 |
439 | // lines.forEach( ( line ) => {
440 | lines.forEach( function( line ){
441 | lineEndPos = lineStartPos + line.length;
442 | if ( ( selStart <= lineEndPos ) && ( lineStartPos < selEnd ) ) {
443 | if ( mceObj.rxFindComment.test( line ) === true ) {
444 | prevCommentLineCount += 1;
445 | }
446 | selLineCount += 1;
447 | }
448 | lineStartPos = lineStartPos + line.length + 1;
449 | } );
450 | var doComment = true;
451 | if ( selLineCount === prevCommentLineCount ) {
452 | doComment = false;
453 | }
454 | console.log( ' ', 'selLineCount:', selLineCount, 'prevCommentLineCount:', prevCommentLineCount );
455 |
456 | lineStartPos = 0; lineEndPos = 0;
457 | var newCommentLineCount = 0;
458 | // lines.forEach( ( line, idx, arr ) => {
459 | lines.forEach( function( line, idx, arr ) {
460 | lineEndPos = lineStartPos + line.length;
461 | if ( ( selStart <= lineEndPos ) && ( lineStartPos < selEnd ) ) {
462 | var hasComment = mceObj.rxFindComment.test( line );
463 | if ( hasComment === false && doComment === true ) {
464 | // arr[ idx ] = mceObj.toggleComment( line );
465 | newCommentLineCount += 1;
466 | } else if ( hasComment && doComment === false ) {
467 | // arr[ idx ] = mceObj.toggleComment( line );
468 | newCommentLineCount -= 1;
469 | }
470 | arr[ idx ] = mceObj.toggleComment( line, true );
471 | selLineCount += 1;
472 | }
473 | lineStartPos = lineStartPos + line.length + 1;
474 | } );
475 |
476 | console.log( 'after (lines):', lines );
477 | console.log( ' ', 'newCommentLineCount:', newCommentLineCount );
478 | el.value = lines.join( '\n' );
479 |
480 | // set caret selection for comment
481 | if ( doComment ) {
482 | if ( selStart === firstLineStartPos ) {
483 | el.selectionStart = selStart;
484 | } else if ( selStart > firstLineStartPos ) {
485 | el.selectionStart = selStart + 2;
486 | }
487 | if ( selEnd < lastLineEndPos ) {
488 | el.selectionEnd = selEnd + ( (prevCommentLineCount + newCommentLineCount) * 4 ) - 2;
489 | } else if ( selEnd === lastLineEndPos ) {
490 | el.selectionEnd = selEnd + ( (prevCommentLineCount + newCommentLineCount) * 4 );
491 | }
492 | } else {
493 | if ( selStart === firstLineStartPos ) {
494 | el.selectionStart = selStart;
495 | } else if ( selStart > firstLineStartPos ) {
496 | el.selectionStart = selStart - 2;
497 | }
498 | if ( selEnd < lastLineEndPos ) {
499 | el.selectionEnd = selEnd + ( (newCommentLineCount) * 4 ) + 2;
500 | } else if ( selEnd === lastLineEndPos ) {
501 | el.selectionEnd = selEnd + ( (newCommentLineCount) * 4 );
502 | }
503 | }
504 | }
505 | }
506 | },
507 | toggleComment: function( slicedLine, isBlock ) {
508 | var newSlicedLine,
509 | hasComment = this.rxFindComment.test( slicedLine );
510 | if ( hasComment ) {
511 | newSlicedLine = slicedLine.replace( this.rxReplaceComment, '' );
512 | } else {
513 | if ( isBlock ) {
514 | newSlicedLine = slicedLine.replace( this.rxBlockToWrap, '/*' + '$&' + '*/' );
515 | } else {
516 | if ( slicedLine === String.fromCharCode( 9 ) ) {
517 | newSlicedLine = slicedLine.replace( this.rxBlockToWrap, '\t/**/' );
518 | } else {
519 | newSlicedLine = slicedLine.replace( this.rxTextToWrap, '/*' + '$&' + '*/' );
520 | }
521 | }
522 | }
523 | return newSlicedLine;
524 | },
525 | captureKeyDown: function( evt ) {
526 | const ENTER = 'Enter',
527 | TAB = 'Tab',
528 | BACKSPACE = 'Backspace',
529 | COLON = String.fromCharCode(58),
530 | SEMI = String.fromCharCode(59),
531 | QUOTE = String.fromCharCode(34),
532 | APOSTROPHE = String.fromCharCode( 39 ),
533 | LEFT_PAREN = String.fromCharCode(40),
534 | RIGHT_PAREN = String.fromCharCode( 41 ),
535 | LEFT_BRACKET = String.fromCharCode(91),
536 | RIGHT_BRACKET = String.fromCharCode(93),
537 | LEFT_CURLY_BRACE = String.fromCharCode(123),
538 | RIGHT_CURLY_BRACE = String.fromCharCode(125),
539 | charNewLine = String.fromCharCode( 10 ),
540 | charTab = String.fromCharCode( 9 ),
541 | FIREFOX = this.browser.firefox;
542 | // charBackspace = String.fromCharCode( 8 );
543 | var mceObj = this,
544 | ta = evt.target,
545 | val = ta.value,
546 | selStart = ta.selectionStart,
547 | lineStartPos = val.lastIndexOf( '\n', selStart-1 ) > -1 ? val.lastIndexOf( '\n', selStart-1 ) + 1 : 0,
548 | lineEndPos = val.indexOf( '\n', selStart ) > -1 ? val.indexOf( '\n', selStart ) : val.length;
549 | switch ( evt.key ) {
550 | case ENTER:
551 | var openBracketPos = val.lastIndexOf( '{', ta.selectionStart ),
552 | closeBracketPos = val.lastIndexOf( '}', ta.selectionStart );
553 | if ( ( openBracketPos - ta.selectionStart ) > ( closeBracketPos - ta.selectionStart ) ) {
554 | if ( FIREFOX ) {
555 | mceObj.insertText( ta, charNewLine + charTab, 2 );
556 | } else {
557 | // mceObj.insertText2( ta, charNewLine + charTab, 0 );
558 | document.execCommand( 'insertText', false, charNewLine + charTab );
559 | }
560 | } else {
561 | if ( FIREFOX ) {
562 | mceObj.insertText( ta, charNewLine, 1 );
563 | } else {
564 | // mceObj.insertText2( ta, charNewLine, 0 );
565 | document.execCommand( 'insertText', false, charNewLine );
566 | }
567 | }
568 | break;
569 | case TAB:
570 | if ( FIREFOX ) {
571 | mceObj.insertText( ta, charTab, 1 );
572 | } else {
573 | // mceObj.insertText2( ta, charTab, 0 );
574 | document.execCommand( 'insertText', false, charTab );
575 | }
576 | break;
577 | case COLON:
578 | var colonPos = val.indexOf( COLON, lineStartPos );
579 | var semiPos = val.indexOf( SEMI, lineStartPos );
580 | if ((colonPos > lineEndPos || colonPos === -1) && (semiPos > lineEndPos || semiPos === -1) ){
581 | if ( FIREFOX ) {
582 | mceObj.insertText( ta, COLON + SEMI, 1 );
583 | } else {
584 | // mceObj.insertText( ta, COLON + SEMI, 1 );
585 | mceObj.insertText2( ta, COLON + SEMI, -1 );
586 | }
587 | } else {
588 | return true;
589 | }
590 | break;
591 | case SEMI:
592 | if ( val.charAt( selStart ) === SEMI ) {
593 | if ( FIREFOX ) {
594 | // mceObj.insertText( ta, '', 1 );
595 | mceObj.moveCursor( ta, 1 );
596 | } else {
597 | // mceObj.insertText2( ta, '', 1 );
598 | document.execCommand( 'forwardDelete', false );
599 | document.execCommand( 'insertText', false, SEMI );
600 | }
601 | } else {
602 | return true;
603 | }
604 | break;
605 | case BACKSPACE:
606 | return true;
607 | // break;
608 | case QUOTE:
609 | if ( FIREFOX ) {
610 | mceObj.insertText( ta, QUOTE + QUOTE, 1 );
611 | } else {
612 | mceObj.insertText2( ta, QUOTE + QUOTE, -1 );
613 | }
614 | break;
615 | case APOSTROPHE:
616 | if ( FIREFOX ) {
617 | mceObj.insertText( ta, APOSTROPHE + APOSTROPHE, 1 );
618 | } else {
619 | mceObj.insertText2( ta, APOSTROPHE + APOSTROPHE, -1 );
620 | }
621 | break;
622 | case LEFT_PAREN:
623 | if ( FIREFOX ) {
624 | mceObj.insertText( ta, LEFT_PAREN + RIGHT_PAREN, 1 );
625 | } else {
626 | mceObj.insertText2( ta, LEFT_PAREN + RIGHT_PAREN, -1 );
627 | }
628 | break;
629 | case LEFT_BRACKET:
630 | if ( FIREFOX ) {
631 | mceObj.insertText( ta, LEFT_BRACKET + RIGHT_BRACKET, 1 );
632 | } else {
633 | mceObj.insertText2( ta, LEFT_BRACKET + RIGHT_BRACKET, -1 );
634 | }
635 | break;
636 | case LEFT_CURLY_BRACE:
637 | if ( val.lastIndexOf( '{', selStart ) === -1 || ( val.lastIndexOf( '}', selStart ) > val.lastIndexOf( '{', selStart ) ) ) {
638 | if ( FIREFOX ) {
639 | mceObj.insertText( ta, LEFT_CURLY_BRACE + charNewLine + charTab + charNewLine + RIGHT_CURLY_BRACE, 3 );
640 | } else {
641 | mceObj.insertText2( ta, LEFT_CURLY_BRACE + charNewLine + charTab + charNewLine + RIGHT_CURLY_BRACE, -2 );
642 | }
643 | } else {
644 | return true;
645 | }
646 | break;
647 | default:
648 | return true;
649 | }
650 | mceObj.triggerInputEvent();
651 | return false;
652 | },
653 | insertText: function( el, text, curPos) {
654 | var start, end, val = el.value;
655 | if ( typeof el.selectionStart === 'number' && typeof el.selectionEnd === 'number' ) {
656 | start = el.selectionStart;
657 | end = el.selectionEnd;
658 | el.value = val.slice( 0, start ) + text + val.slice( end );
659 | // move the caret
660 | el.selectionStart = el.selectionEnd = start + curPos;
661 | }
662 | },
663 | insertText2: function( el, text, m ) {
664 | // this allows the browser's undo & redo features to work
665 | var result = document.execCommand( 'insertText', false, text );
666 | console.log( 'insertText result:', result );
667 | el.selectionStart = el.selectionEnd = el.selectionStart + m;
668 | },
669 | moveCursor: function( el, m ) {
670 | el.selectionStart = el.selectionEnd = el.selectionStart + m;
671 | },
672 | highlightSyntax: function( css ) {
673 | var formatted = css;
674 |
675 | formatted = formatted.replace( this.rxSelectors, '$& ' );
676 | formatted = formatted.replace( this.rxHtmlElements, function(m, group1) {
677 | if (group1 !== undefined) {
678 | return '' + group1 + ' ';
679 | }
680 | return m;
681 | } );
682 | formatted = formatted.replace( this.rxConstants, function(m, group1) {
683 | if (group1 !== undefined) {
684 | return '' + group1 + ' ';
685 | }
686 | return m;
687 | } );
688 | formatted = formatted.replace( this.rxKeywords, function(m, group1, group2) {
689 | if (group1 !== undefined) {
690 | return group1 + '' + group2 + ' ';
691 | }
692 | return m;
693 | } );
694 | formatted = formatted.replace( this.rxProperties, '$& ' );
695 | formatted = formatted.replace( this.rxFunctions, function(m, group1) {
696 | if (group1 !== undefined) {
697 | return '' + group1 + ' ';
698 | }
699 | return m;
700 | } );
701 | formatted = formatted.replace( this.rxNumbers, function(m, group1) {
702 | if (group1 !== undefined) {
703 | return '' + group1 + ' ';
704 | }
705 | return m;
706 | } );
707 | formatted = formatted.replace( this.rxQuotes, function(m, group1) {
708 | if (group1 !== undefined) {
709 | return '' + group1 + ' ';
710 | }
711 | return m;
712 | } );
713 | formatted = formatted.replace( this.rxComments, '' );
714 |
715 | this.divCodeDisplay.innerHTML = formatted;
716 | },
717 | applyStyle: function( selector, declarations ) {
718 | if ( selector ) {
719 | // var elArr = document.getElementById('sandbox').querySelectorAll( selector );
720 | var el = document.getElementById( 'sandbox' );
721 | var elArr = el.querySelectorAll( selector );
722 | // elArr.forEach( el => {
723 | elArr.forEach( function(el) {
724 | el.style = declarations;
725 | } );
726 | }
727 | },
728 | clearStyle: function() {
729 | if ( this.browser.ie ) {
730 | NodeList.prototype.forEach = Array.prototype.forEach;
731 | }
732 | var elArr = document.getElementById('sandbox').querySelectorAll( '*' );
733 | // elArr.forEach( el => {
734 | elArr.forEach( function(el) {
735 | el.removeAttribute( 'style' );
736 | } );
737 | },
738 | processRules: function( evt ) {
739 | var mceObj = this;
740 | var css = evt.target.value;
741 | var m, selector, prev = '', rules = '', rulesArr = [], count=0;
742 |
743 | while ( ( m = this.rxRules.exec( css ) ) !== null ) {
744 | if ( m.index === this.rxRules.lastIndex ) {
745 | this.rxRules.lastIndex++;
746 | }
747 | // console.log( count, 'm[1]:', m[1] );
748 | // console.log( count, 'm[2]:', m[2] );
749 |
750 | if ( m[ 1 ] !== undefined ) {
751 | prev = prev ? prev : m[ 1 ];
752 | selector = m[ 1 ];
753 | if ( selector !== prev ) {
754 | rulesArr.push([prev.trim(), rules] );
755 | rules = [];
756 | prev = selector;
757 | }
758 | } else {
759 | if ( this.rxFindComment.test( m[ 2 ] ) === false ) {
760 | rules += m[ 2 ];
761 | }
762 | }
763 | count += 1;
764 | }
765 | rulesArr.push( [prev.trim(), rules] );
766 | // console.log( rulesArr );
767 |
768 | this.clearStyle();
769 | // rulesArr.forEach( rule => {
770 | rulesArr.forEach( function( rule ) {
771 | if ( rule[ 0 ].startsWith( '/*' ) === false ) {
772 | mceObj.applyStyle( rule[ 0 ], rule[ 1 ] );
773 | }
774 | } );
775 | this.displaySolved();
776 | },
777 | displaySolved: function() {
778 | var ta = this.taCodeEditor,
779 | answer = this.preFill.replace( /\t@/, this.answerCode.replace( /^[\xA0 ]+/gm, '\t' ) ),
780 | gridContainer = document.querySelector('.grid-container');
781 |
782 | if ( ta.value === answer ) {
783 | gridContainer.classList.add( 'solved' );
784 | } else {
785 | gridContainer.classList.remove( 'solved' );
786 | }
787 | },
788 | showAnswer: function( e ) {
789 | e.preventDefault();
790 | document.querySelector( '.answer' ).classList.add( 'show' );
791 | document.querySelector( '.answer-btn' ).classList.add( 'disabled' );
792 | return false;
793 | },
794 | hideAnswer: function() {
795 | document.querySelector( '.answer' ).classList.remove( 'show' );
796 | document.querySelector( '.answer-btn' ).classList.remove( 'disabled' );
797 | return false;
798 | },
799 | pasteAnswer: function(e) {
800 | var mceObj = this,
801 | ta = this.taCodeEditor,
802 | code = document.querySelector( '.answer code' ).innerText,
803 | pasteBtn = document.querySelector( '.paste-btn' );
804 |
805 | e.preventDefault();
806 | if ( pasteBtn.innerText === 'Paste' && code.trim().length > 0) {
807 | console.log( code.trim().length );
808 | pasteBtn.innerText = 'Clear';
809 | // replace non-breaking space (char code 160) with tab
810 | ta.value = mceObj.preFill.replace( /\t@/, code.replace( /^[\xA0 ]+/gm, '\t' ) );
811 | mceObj.taCodeEditor.selectionStart = mceObj.taCodeEditor.selectionEnd = mceObj.curPos;
812 | mceObj.taCodeEditor.focus();
813 | mceObj.triggerInputEvent();
814 | } else if(pasteBtn.innerText === 'Clear') {
815 | pasteBtn.innerText = 'Paste';
816 | this.setTaVal( mceObj.preFill );
817 | }
818 | return false;
819 | },
820 | toggleCode: function(e) {
821 | var mceObj = this,
822 | ta = this.taCodeEditor,
823 | val = ta.value,
824 | selStart = ta.selectionStart,
825 | selEnd = ta.selectionEnd,
826 | lineStartPos = val.lastIndexOf( '\n', selStart - 1 ) > -1 ? val.lastIndexOf( '\n', selStart - 1 ) + 1 : 0,
827 | lineEndPos = val.indexOf( '\n', selEnd ) > -1 ? val.indexOf( '\n', selEnd ) : val.length,
828 | doComment = false,
829 | posModifier = 0;
830 |
831 | var startPart = val.slice( 0, selStart ),
832 | startPrevLines = ( startPart.match( /\n/gm ) || [] ).length;
833 | var endPart = val.slice( 0, selEnd ),
834 | endPrevLines = ( endPart.match( /\n/gm ) || [] ).length;
835 |
836 | e.preventDefault();
837 | // select all to comment all lines
838 | ta.selectionStart = 0;
839 | ta.selectionEnd = ta.value.length;
840 | mceObj.commentSelection(ta);
841 |
842 | if ( document.querySelector( '.toggle-code-btn' ).classList.contains( 'show' ) ) {
843 | document.querySelector( '.toggle-code-btn' ).classList.remove( 'show' );
844 | doComment = true;
845 | } else {
846 | document.querySelector( '.toggle-code-btn' ).classList.add( 'show' );
847 | doComment = false;
848 | }
849 | mceObj.triggerInputEvent();
850 |
851 | // pos on the current line
852 | if ( doComment ) {
853 | if ( selStart === lineStartPos ) {
854 | posModifier = 0;
855 | } else if ( selStart === lineEndPos ) {
856 | posModifier = +4;
857 | } else {
858 | posModifier = +2;
859 | }
860 | ta.selectionStart = selStart + posModifier + ( startPrevLines * 4 );
861 | ta.selectionEnd = selEnd + posModifier + ( endPrevLines * 4 );
862 | } else {
863 | if ( selStart === lineStartPos ) {
864 | posModifier = 0;
865 | } else if ( selStart === lineEndPos ) {
866 | posModifier = -4;
867 | } else {
868 | posModifier = -2;
869 | }
870 | ta.selectionStart = selStart + posModifier - ( startPrevLines * 4 );
871 | ta.selectionEnd = selEnd + posModifier - ( endPrevLines * 4 );
872 | }
873 | ta.focus();
874 | return false;
875 | },
876 | toggleToc: function(evt) {
877 | evt.preventDefault();
878 | this.toc.classList.toggle( 'show' );
879 | this.taCodeEditor.focus();
880 | },
881 | toggleSplash: function( evt ) {
882 | evt.preventDefault();
883 | this.splashContainer.classList.toggle( 'show' );
884 | this.taCodeEditor.focus();
885 | }
886 | };
887 |
888 | window.onload = myTACodeEditor.init();
--------------------------------------------------------------------------------
/script/pages.js:
--------------------------------------------------------------------------------
1 | var getPages = function() {
2 | return pages;
3 | };
4 | var pages = [
5 | {
6 | num: 1,
7 | title: 'CSS Grid',
8 | subtitle: 'Grid',
9 | instructions: {
10 | part1: "Establish a grid.
",
11 | part2: "This fill the open space.
"
12 | },
13 | code: {
14 | preFill: '.grid {\n\t@\n}',
15 | follow: '\tdisplay: grid;',
16 | solution: '',
17 | },
18 | gridContainer: {
19 | guide: '
',
20 | guidelines: '',
21 | grid: '1
'
22 | },
23 | style: [
24 | '.guide {\n\tgrid-template-rows: 40%;\n}',
25 | '.grid {\n\tgrid-template-rows: 40%;\n}'
26 | ]
27 | },
28 | {
29 | num: 2,
30 | title: 'CSS Grid',
31 | subtitle: 'Grid with two elements',
32 | instructions: {
33 | part1: "Establish the grid once again.
",
34 | part2: " This uses the child elements to fill the space with equal height rows .
"
35 | },
36 | code: {
37 | preFill: '.grid {\n\t@\n}',
38 | follow: '\tdisplay: grid;',
39 | solution: '',
40 | },
41 | gridContainer: {
42 | guide: '
',
43 | guidelines: '
',
44 | grid: '1
\n2
'
45 | },
46 | style: [
47 | '.guide, guidelines {\n\tgrid-template-rows: 1fr 1fr;\n}'
48 | ]
49 | },
50 | {
51 | num: 3,
52 | title: 'CSS Grid',
53 | subtitle: 'Grid with margin',
54 | instructions: {
55 | part1: "Child elements with margin.
",
56 | part2: "Margin is taken into account when filling the space with equal height rows .
"
57 | },
58 | code: {
59 | preFill: '.grid {\n\t@\n}',
60 | follow: '\tdisplay: grid;',
61 | solution: '',
62 | },
63 | gridContainer: {
64 | guide: '',
65 | guidelines: '',
66 | grid: '<p>
\n<p>
'
67 | },
68 | style: []
69 | },
70 | {
71 | num: 4,
72 | title: 'CSS Grid',
73 | subtitle: 'Grid template rows',
74 | instructions: {
75 | part1: "Using grid-template-rows
.
",
76 | part2: "This lets you configure the size of every row in the Grid.
\nIt even lets you add new rows when you need.
"
77 | },
78 | code: {
79 | preFill: '.grid {\n\t@\n}',
80 | follow: '\tdisplay: grid;\n\tgrid-template-rows: 100px 200px;',
81 | solution: '',
82 | },
83 | gridContainer: {
84 | guide: '
\n
',
85 | guidelines: '
\n
',
86 | grid: '
\n
'
87 | },
88 | style: [
89 | '.guide {\n\tgrid-template-rows: 100px 200px;\n}',
90 | '.guidelines {\n\tgrid-template-rows: 100px 200px;\n}'
91 | ]
92 | },
93 | {
94 | num: 5,
95 | title: 'CSS Grid',
96 | subtitle: 'Grid template columns',
97 | instructions: {
98 | part1: "Use grid-template-columns
.
",
99 | part2: " This lets you configure the size of every column in the Grid.
\nEach column definition is separated by a space.
"
100 | },
101 | code: {
102 | preFill: '.grid {\n\t@\n}',
103 | follow: '\tdisplay: grid;\n\tgrid-template-columns: 100px 200px 100px;',
104 | solution: '',
105 | },
106 | gridContainer: {
107 | guide: '
\n
\n
',
108 | guidelines: '',
109 | grid: '
\n
\n
'
110 | },
111 | style: ['.guide {\n\tgrid-template-columns: 100px 200px 100px;\n}']
112 | },
113 | {
114 | num: 6,
115 | title: 'CSS Grid',
116 | subtitle: 'Fills (fr unit)',
117 | instructions: {
118 | part1: "Use fr
in place of px
.
",
119 | part2: "Here we use a brand new unit fr for specifying fractions .
\nThe center column will take up twice the space as the outer columns.
\nAll three columns will fill the entire space.
"
120 | },
121 | code: {
122 | preFill: '.grid {\n\t@\n}',
123 | follow: '\tdisplay: grid;\n\tgrid-template-columns: 1fr 2fr 1fr;',
124 | solution: '',
125 | },
126 | gridContainer: {
127 | guide: '
\n
\n
',
128 | guidelines: '',
129 | grid: '
\n
\n
'
130 | },
131 | style: ['.guide {\n\tgrid-template-columns: 1fr 2fr 1fr;\n}']
132 | },
133 | {
134 | num: 7,
135 | title: 'CSS Grid',
136 | subtitle: 'Mixing units',
137 | instructions: {
138 | part1: "Use px
and fr
together.
",
139 | part2: "Start by establishing the Grid.
\nThe Grid's default behavior is to layout the rows equally .
\nNext define the two columns using 100px
and 1fr
.
\nLastly define the rows with 1fr
and 100px
.
"
140 | },
141 | code: {
142 | preFill: '.grid {\n\t@\n}',
143 | follow: '\tdisplay: grid;\n\tgrid-template-columns: 100px 1fr;\n\tgrid-template-rows: 1fr 100px;',
144 | solution: '',
145 | },
146 | gridContainer: {
147 | guide: '
\n
\n
\n
',
148 | guidelines: '',
149 | grid: '
\n
\n
\n
'
150 | },
151 | style: ['.guide {\n\tgrid-template-columns: 100px 1fr;\n\tgrid-template-rows: 1fr 100px;\n']
152 | },
153 | {
154 | num: 8,
155 | title: 'CSS Grid',
156 | subtitle: 'Repeat',
157 | instructions: {
158 | part1: "Use the repeat
function as a shorthand.
",
159 | part2: "Use repeat()
with the number of columns or rows followed by size.
\nNormally this would be typed as \ngrid-template-columns: 100px 100px 100px 100px;
\nInstead this can be typed as \ngrid-template-columns: repeat(4, 100px);
.
"
160 | },
161 | code: {
162 | preFill: '.grid {\n\t@\n}',
163 | follow: '\tdisplay: grid;\n\tgrid-template-columns: repeat(4, 100px);\n\tgrid-template-rows: repeat(4, 100px);',
164 | solution: '',
165 | },
166 | gridContainer: {
167 | guide: '
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
',
168 | guidelines: '
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
',
169 | grid: '
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
'
170 | },
171 | style: [
172 | '.guide {\n\tgrid-template-columns: repeat(4, 100px);\n\tgrid-template-rows: repeat(4, 100px);\n}',
173 | '.guidelines {\n\tgrid-template-columns: repeat(4, 100px) 1fr;\n\tgrid-template-rows: repeat(4, 100px) 1fr;\n}'
174 | ]
175 | },
176 | {
177 | num: 9,
178 | title: 'CSS Grid',
179 | subtitle: 'Repeating fractions',
180 | instructions: {
181 | part1: "The repeat
function can also be used with fr
.
",
182 | part2: ""
183 | },
184 | code: {
185 | preFill: '.grid {\n\t@\n}',
186 | follow: '\tdisplay: grid;\n\tgrid-template-columns: repeat(7, 1fr);',
187 | solution: '',
188 | },
189 | gridContainer: {
190 | guide: '
\n
\n
\n
\n
\n
\n
',
191 | guidelines: '
\n
\n
\n
\n
\n
\n
',
192 | grid: '
\n
\n
\n
\n
\n
\n
'
193 | },
194 | style: ['.guide, .guidelines {\n\tgrid-template-columns: repeat(7, 1fr);\n}']
195 | },
196 | {
197 | num: 10,
198 | title: 'CSS Grid',
199 | subtitle: 'Repeating pattern',
200 | instructions: {
201 | part1: "Multiple sizes can be passed to the repeat
function.
",
202 | part2: "When multiple sizes are passed in it will repeat those columns or rows as a pattern.
"
203 | },
204 | code: {
205 | preFill: '.grid {\n\t@\n}',
206 | follow: '\tdisplay: grid;\n\tgrid-template-columns: repeat(3, 50px 100px);',
207 | solution: ''
208 | },
209 | gridContainer: {
210 | guide: '
\n
',
211 | guidelines: '
\n
\n
\n
\n
\n
',
212 | grid: '
\n
\n
\n
\n
\n
'
213 | },
214 | style: ['.guide, .guidelines {\n\tgrid-template-columns: repeat(3, 50px 100px);\n}']
215 | },
216 | {
217 | num: 11,
218 | title: 'CSS Grid',
219 | subtitle: 'Tracks',
220 | instructions: {
221 | part1: "By default, terrain items flow into the Grid as equal height rows . But we can control how the Grid terrains are sized and positioned by defining the number and size of the rows and columns.
\nFor columns we use grid-template-columns
, and for rows we use grid-template-rows
.
\nWe can use a more general term for both rows and columns. Let's call them tracks : a horizontal or vertical slice of the Grid which can be sized and filled with items .
\nThe new fr
unit lets us tell rows and columns - a.k.a tracks - how much of the available space to take up, after any fixed-unit tracks get their dibs.
\nLastly, thanks to this shorthand, repeat(*number of tracks*, *size of tracks*)
, the Grid can be defined even faster.
",
222 | part2: ''
223 | },
224 | code: {
225 | preFill: '',
226 | follow: '',
227 | solution: ''
228 | },
229 | gridContainer: {
230 | guide: '',
231 | guidelines: '',
232 | grid: ''
233 | },
234 | style: [],
235 | },
236 | {
237 | num: 12,
238 | title: 'CSS Grid',
239 | subtitle: 'Challenge 1.1',
240 | instructions: {
241 | part1: "Steps to solve the challenge:
\n\n\tPower up the Grid using display: grid;
\n\tDefine the correct number of tracks using fr
units to begin.grid-template-columns: 1fr 1fr 1fr ;
\n\tAdjust the sizes of the tracks until everything lines up.grid-template-columns: 1fr 1fr 2fr ;
\n ",
242 | part2: ''
243 | },
244 | code: {
245 | preFill: '.grid {\n\t@\n}',
246 | follow: '',
247 | solution: '\tdisplay: grid;\n\tgrid-template-columns: 1fr 1fr 2fr;'
248 | },
249 | gridContainer: {
250 | guide: '
\n
\n
',
251 | guidelines: '
\n
\n
',
252 | grid: '
\n
\n
'
253 | },
254 | style: ['.guide, .guidelines {\n\tgrid-template-columns: 1fr 1fr 2fr;\n}']
255 | },
256 | {
257 | num: 13,
258 | title: 'CSS Grid',
259 | subtitle: 'Challenge 1.2',
260 | instructions: {
261 | part1: "Steps to solve the challenge:
\n\n\tPower up the Grid. \n\tDefine the correct number of tracks using fr
units. \n\t Don't forget you can use repeat()
for equal sized columns. \n ",
262 | part2: ''
263 | },
264 | code: {
265 | preFill: '.grid {\n\t@\n}',
266 | follow: '',
267 | solution: '\tdisplay: grid;\n\tgrid-template-columns: repeat(4, 1fr);\n\tgrid-template-rows: 1fr 2fr 1fr;'
268 | },
269 | gridContainer: {
270 | guide: '
\n
\n
\n
',
271 | guidelines: '
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
',
272 | grid: '
\n
\n
\n
'
273 | },
274 | style: ['.guide, .guidelines {\n\tgrid-template-columns: repeat(4, 1fr);\n\tgrid-template-rows: 1fr 2fr 1fr;\n}']
275 | },
276 | {
277 | num: 14,
278 | title: 'CSS Grid',
279 | subtitle: 'Challenge 1.3',
280 | instructions: {
281 | part1: "The top and bottom rows are each using 20vh
units.
\nThose are just like percents(%
) but are relative to the viewport's height rather than to the Grid element's height.
",
282 | part2: "In this case, the vh
units will match the sizes and the fr
units will take up the rest of the space.
"
283 | },
284 | code: {
285 | preFill: '.grid {\n\t@\n}',
286 | follow: '',
287 | solution: '\tdisplay: grid;\n\tgrid-template-columns: 1fr 1fr;\n\tgrid-template-rows: 20vh 1fr 20vh;'
288 | },
289 | gridContainer: {
290 | guide: '
\n
\n
\n
\n
\n
',
291 | guidelines: '
\n
\n
\n
\n
\n
\n
',
292 | grid: '
\n
\n
\n
\n
\n
'
293 | },
294 | style: ['.guide, .guidelines {\n\tgrid-template-columns: 1fr 1fr;\n\tgrid-template-rows: 20vh 1fr 20vh;\n}']
295 | },
296 | {
297 | num: 15,
298 | title: 'CSS Grid',
299 | subtitle: 'Challenge 1.4',
300 | instructions: {
301 | part1: "Looks like that second column uses 20vw
",
302 | part2: "So vw
units, are the same as percents but relative to the viewport's width instead of the Grid element's width.
"
303 | },
304 | code: {
305 | preFill: '.grid {\n\t@\n}',
306 | follow: '',
307 | solution: '\tdisplay: grid;\n\tgrid-template-columns: 1fr 20vw;\n\tgrid-template-rows: 1fr repeat(4, 1fr);',
308 | },
309 | gridContainer: {
310 | guide: '
',
311 | guidelines: '
',
312 | grid: '
'
313 | },
314 | style: [
315 | '.guide {\n\tgrid-template-columns: 1fr 20vw;\n\tgrid-template-rows: 1fr 4fr; }',
316 | '.guidelines { \n\tgrid-template-columns: 1fr 20vw;\n\tgrid-template-rows: repeat(5, 1fr);}'
317 | ]
318 | },
319 | {
320 | num: 16,
321 | title: 'CSS Grid',
322 | subtitle: 'Challenge 1.5',
323 | instructions: {
324 | part1: "This next challenge uses only percentages.
Percentages are relative to the height of the Grid.
The top row is 25%
of the Grid's height.
",
325 | part2: "The bottom line could also have been written asgrid-template-rows: 1fr 1fr 2fr;
"
326 | },
327 | code: {
328 | preFill: '.grid {\n\t@\n}',
329 | follow: '',
330 | solution: '\tdisplay: grid;\n\tgrid-template-columns: 50% 50%;\n\tgrid-template-rows: 25% 25% 50%;',
331 | },
332 | gridContainer: {
333 | guide: '
\n
',
334 | guidelines: '
',
335 | grid: '
'
336 | },
337 | style: [
338 | '.guide { grid-template-columns: 50% 50%; grid-template-rows: 25% 25% 50%;',
339 | '.guidelines { grid-template-columns: 50% 50%; grid-template-rows: 25% 25% 50%;'
340 | ]
341 | },
342 | {
343 | num: 17,
344 | title: 'CSS Grid',
345 | subtitle: 'Challenge 1.6',
346 | instructions: {
347 | part1: "A single fixed pixel sized cell in the center.
",
348 | part2: ''
349 | },
350 | code: {
351 | preFill: '.grid {\n\t@\n}',
352 | follow: '',
353 | solution: '\tdisplay: grid;\n\tgrid-template-columns: 1fr 100px 1fr;\n\tgrid-template-rows: 1fr 100px 1fr;',
354 | },
355 | gridContainer: {
356 | guide: '
',
357 | guidelines: '
',
358 | grid: '
'
359 | },
360 | style: [
361 | '.guide, .guidelines { grid-template-columns: 1fr 100px 1fr; grid-template-rows: 1fr 100px 1fr; }'
362 | ]
363 | },
364 | {
365 | num: 18,
366 | title: 'CSS Grid',
367 | subtitle: 'Challenge 1.7',
368 | instructions: {
369 | part1: "The middle column is 10%
, and the top row is 200px
.",
370 | part2: "
Only the top row needs to be set since the Grid will fill in the rest of the available space.
"
371 | },
372 | code: {
373 | preFill: '.grid {\n\t@\n}',
374 | follow: '',
375 | solution: '\tdisplay: grid;\n\tgrid-template-columns: 2fr 10% 1fr;\n\tgrid-template-rows: 200px;',
376 | },
377 | gridContainer: {
378 | guide: '
',
379 | guidelines: '
',
380 | grid: '
'
381 | },
382 | style: [
383 | '.guide, .guidelines { grid-template-columns: 2fr 10% 1fr; grid-template-rows: 200px; }'
384 | ]
385 | },
386 | {
387 | num: 19,
388 | title: 'CSS Grid',
389 | subtitle: 'Challenge 1.8',
390 | instructions: {
391 | part1: "These columns are using %
units, and the bottom row is using px
units.
",
392 | part2: ""
393 | },
394 | code: {
395 | preFill: '.grid {\n\t@\n}',
396 | follow: '',
397 | solution: '\tdisplay: grid;\n\tgrid-template-columns: 25% 25%;\n\tgrid-template-rows: 1fr 1fr 50px;',
398 | },
399 | gridContainer: {
400 | guide: '
',
401 | guidelines: '
',
402 | grid: '
'
403 | },
404 | style: [
405 | '.guide { grid-template-columns: 25% 25%; grid-template-rows: 1fr 1fr 50px; }',
406 | '.guidelines { grid-template-columns: 25% 25% 1fr; grid-template-rows: 1fr 1fr 50px; }'
407 | ]
408 | },
409 | {
410 | num: 20,
411 | title: 'CSS Grid',
412 | subtitle: 'Challenge 1.9',
413 | instructions: {
414 | part1: "The red rows are 50px
.
Try using the repeat()
shorthand to specify the pattern .
",
415 | part2: ""
416 | },
417 | code: {
418 | preFill: '.grid {\n\t@\n}',
419 | follow: '',
420 | solution: '\tdisplay: grid;\n\tgrid-template-rows: repeat(2, 50px 1fr);',
421 | },
422 | gridContainer: {
423 | guide: '
',
424 | guidelines: '
',
425 | grid: '
'
426 | },
427 | style: [
428 | '.guide, .guidelines { grid-template-rows: repeat(2, 50px 1fr);'
429 | ]
430 | },
431 | {
432 | num: 21,
433 | title: 'CSS Grid',
434 | subtitle: 'Challenge 1.10',
435 | instructions: {
436 | part1: "",
437 | part2: ""
438 | },
439 | code: {
440 | preFill: '.grid {\n\t@\n}',
441 | follow: '',
442 | solution: '\tdisplay: grid;\n\tgrid-template-columns: repeat(3, 1fr);',
443 | },
444 | gridContainer: {
445 | guide: '
',
446 | guidelines: '
',
447 | grid: '
'
448 | },
449 | style: [
450 | '.guide, .guidelines { grid-template-columns: repeat(3, 1fr); }'
451 | ]
452 | },
453 | // {
454 | // num: 0,
455 | // title: 'CSS Grid',
456 | // subtitle: '',
457 | // instructions: {
458 | // part1: "",
459 | // part2: ""
460 | // },
461 | // code: {
462 | // preFill: '.grid {\n\t@\n}',
463 | // follow: '',
464 | // solution: '',
465 | // },
466 | // gridContainer: {
467 | // guide: '',
468 | // guidelines: '',
469 | // grid: ''
470 | // },
471 | // style: []
472 | // },
473 | ];
--------------------------------------------------------------------------------
/static_pages/a-grid1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1-Grid
5 |
6 |
7 |
8 |
16 |
17 |
18 |
21 |
22 |
23 |
24 |
1. Grid
25 |
Establish a grid.
26 |
27 |
28 | display: grid;
29 |
30 |
31 |
This fill the open space.
32 |
33 |
34 |
35 |
36 |
53 |
54 |
55 |
56 |
60 |
61 |
--------------------------------------------------------------------------------
/static_pages/a-grid10.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 10-Grid
5 |
6 |
7 |
8 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
10. Repeating pattern
22 |
Multiple sizes can be passed to the repeat
function.
23 |
24 |
25 | display: grid;
26 | grid-template-columns: repeat(3, 50px 100px);
27 |
28 |
When multiple sizes are passed in it will repeat those columns or rows as a pattern.
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
66 |
67 |
68 |
69 |
70 |
71 |
75 |
76 |
--------------------------------------------------------------------------------
/static_pages/a-grid11.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 11-Grid
5 |
6 |
7 |
8 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
11. Tracks
22 |
By default, terrain items flow into the Grid as equal height rows . But we can control how the Grid terrains are sized and positioned by defining the number and size of the rows and columns.
23 |
For columns we use grid-template-columns
, and for rows we use grid-template-rows
.
24 |
We can use a more general term for both rows and columns. Let's call them tracks : a horizontal or vertical slice of the Grid which can be sized and filled with items .
25 |
The new fr
unit lets us tell rows and columns - a.k.a tracks - how much of the available space to take up, after any fixed-unit tracks get their dibs.
26 |
Lastly, thanks to this shorthand, repeat(*number of tracks*, *size of tracks*)
, the Grid can be defined even faster.
27 |
28 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
38 |
43 |
44 |
45 |
46 |
47 |
48 |
53 |
54 |
--------------------------------------------------------------------------------
/static_pages/a-grid12.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 12-Grid
5 |
6 |
7 |
8 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
12. Challenge 1.1
22 |
Steps to solve the challenge:
23 |
24 | Power up the Grid using display: grid;
25 | Define the correct number of tracks using fr
units to begin.
26 | Adjust the sizes of the tracks until everything lines up.
27 |
28 |
Answer
29 |
34 |
35 | Show
36 | Paste
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
50 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
66 |
67 |
68 |
69 |
70 |
71 |
76 |
77 |
--------------------------------------------------------------------------------
/static_pages/a-grid13.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 13-Grid
5 |
6 |
7 |
8 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
13. Challenge 1.2
23 |
Steps to solve the challenge:
24 |
25 | Power up the Grid using display: grid;
26 | Define the correct number of tracks using fr
units to begin.
27 | Adjust the sizes of the tracks until everything lines up.
28 |
29 |
Answer
30 |
35 |
36 | Show
37 | Paste
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
78 |
79 |
80 |
81 |
82 |
83 |
89 |
90 |
--------------------------------------------------------------------------------
/static_pages/a-grid14.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 14-Grid
5 |
6 |
7 |
8 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
14. Challenge 1.3
23 |
The top and bottom rows are each using 20vh
units.
24 |
Those are just like percents(%
) but are relative to the viewport's height rather than to the Grid element's height.
25 |
Answer
26 |
31 |
32 | Show
33 | Paste
34 |
35 |
In this case, the vh
units will match the sizes and the fr
units will take up the rest of the space.
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
75 |
76 |
77 |
78 |
79 |
80 |
85 |
86 |
--------------------------------------------------------------------------------
/static_pages/a-grid2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 2-Grid
5 |
6 |
7 |
8 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
2. Grid
22 |
Establish the grid once again.
23 |
24 |
25 | display: grid;
26 |
27 |
28 |
This uses the child elements to fill the space with equal height rows .
29 |
30 |
31 |
32 |
33 |
48 |
49 |
50 |
51 |
55 |
56 |
--------------------------------------------------------------------------------
/static_pages/a-grid3.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 3-Grid
5 |
6 |
7 |
8 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
3. Grid
22 |
Child elements with margin.
23 |
24 |
25 | display: grid;
26 |
27 |
28 |
Margin is taken into account when filling the space with equal height rows .
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
38 | <p>
39 | <p>
40 |
41 |
42 |
43 |
48 |
49 |
50 |
51 |
52 |
53 |
57 |
58 |
--------------------------------------------------------------------------------
/static_pages/a-grid4.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 4-Grid
5 |
6 |
7 |
8 |
16 |
17 |
18 |
21 |
22 |
23 |
24 |
4. Grid template rows
25 |
Using grid-template-rows
.
26 |
27 |
28 | display: grid;
29 | grid-template-rows: 100px 200px;
30 |
31 |
32 |
This lets you configure the size of every row in the Grid.
33 |
It even lets you add new rows when you need.
34 |
35 |
36 |
37 |
38 |
61 |
62 |
63 |
64 |
68 |
69 |
--------------------------------------------------------------------------------
/static_pages/a-grid5.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 5-Grid
5 |
6 |
7 |
8 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
5. Grid template columns
22 |
Use grid-template-columns
.
23 |
24 |
25 | display: grid;
26 | grid-template-columns: 100px 200px 100px;
27 |
28 |
29 |
This lets you configure the size of every column in the Grid.
30 |
Each column definition is separated by a space.
31 |
32 |
33 |
34 |
35 |
56 |
57 |
58 |
59 |
63 |
64 |
--------------------------------------------------------------------------------
/static_pages/a-grid6.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 6-Grid
5 |
6 |
7 |
8 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
6. Fills (fr unit)
22 |
Use fr
in place of px
.
23 |
24 |
25 | display: grid;
26 | grid-template-columns: 1fr 2fr 1fr;
27 |
28 |
29 |
Here we use a brand new unit fr for specifying fractions .
30 |
The center column will take up twice the space as the outer columns.
31 |
All three columns will fill the entire space.
32 |
33 |
34 |
35 |
36 |
57 |
58 |
59 |
60 |
64 |
65 |
--------------------------------------------------------------------------------
/static_pages/a-grid7.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 7-Grid
5 |
6 |
7 |
8 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
7. Mixing units
23 |
Use px
and fr
together.
24 |
25 |
26 | display: grid;
27 | grid-template-columns: 100px 1fr;
28 | grid-template-rows: 1fr 100px;
29 |
30 |
31 |
Start by establishing the Grid.
32 |
The Grid's default behavior is to layout the rows equally .
33 |
Next define the two columns using 100px
and 1fr
.
34 |
Lastly define the rows with 1fr
and 100px
.
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
60 |
61 |
62 |
63 |
64 |
65 |
69 |
70 |
--------------------------------------------------------------------------------
/static_pages/a-grid8.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 8-Grid
5 |
6 |
7 |
8 |
18 |
19 |
20 |
23 |
24 |
25 |
26 |
8. Repeat
27 |
Use the repeat
function as a shorthand.
28 |
29 |
30 | display: grid;
31 | grid-template-columns: repeat(4, 100px);
32 | grid-template-rows: repeat(4, 100px);
33 |
34 |
35 |
Use repeat()
with the number of columns or rows followed by size.
36 |
Normally this would be typed as
37 | grid-template-columns: 100px 100px 100px 100px;
38 |
Instead this can be typed as
39 | grid-template-columns: repeat(4, 100px);
.
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
116 |
117 |
118 |
119 |
120 |
121 |
125 |
126 |
--------------------------------------------------------------------------------
/static_pages/a-grid9.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 9-Grid
5 |
6 |
7 |
8 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
9. Repeating fractions
22 |
The repeat
function can also be used with fr
.
23 |
24 |
25 | display: grid;
26 | grid-template-columns: repeat(7, 1fr);
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
69 |
70 |
71 |
72 |
73 |
74 |
78 |
79 |
--------------------------------------------------------------------------------
/static_pages/css/style.css:
--------------------------------------------------------------------------------
1 | html { font-size: 16px; height: 90%; }
2 | body * { box-sizing: border-box; }
3 | body {
4 | font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
5 | font-size: 1em;
6 | background-color: #666;
7 | color: #cccccc;
8 | margin: 1em;
9 | /* display: flex;
10 | flex-direction: column; */
11 | height: 100%;
12 | }
13 | header h1 {
14 | margin: 10px 0;
15 | }
16 | main {
17 | /* margin:auto; */
18 | /* height: 75vh; */
19 | /* width: 90vw; */
20 | /* max-width: 700px; */
21 | height: 96%;
22 | display: flex;
23 | justify-content: space-between;
24 | }
25 | .sandbox-wrapper {
26 | flex-basis: 58%;
27 | width: 100%;
28 | height: 100%;
29 | display: flex;
30 | flex-direction: column;
31 | justify-content: space-between;
32 | padding-bottom: 1px;
33 | }
34 | #sandbox {
35 | box-sizing: border-box;
36 | flex-basis: 95%;
37 | margin-bottom: 10px;
38 | }
39 | .page-nav {
40 | /* border: 1px solid gray; */
41 | text-align: right;
42 | height: 28px;
43 | }
44 | .page-nav ul {
45 | list-style-type: none;
46 | margin: 0;
47 | padding: 0;
48 | overflow: hidden;
49 | font-size: .875em; /* 16px/14px */
50 | }
51 | .page-nav .right {
52 | float: right;
53 | margin-left: 6px;
54 | }
55 | .page-nav .left {
56 | float: left;
57 | margin-right: 6px;
58 | }
59 | .page-nav ul li {
60 | display: inline-block;
61 | /* background-color: #333; */
62 | padding: 6px 0px;
63 | }
64 | /* .page-nav ul li a { */
65 | .page-nav a.nav-btn {
66 | padding: 8px 14px;
67 | color: darkgrey;
68 | text-align: center;
69 | text-decoration: none;
70 | font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
71 | background-color: #333;
72 | /* border-radius: 10px; */
73 | }
74 | /* .page-nav ul li a:hover { */
75 | .page-nav a.nav-btn:hover {
76 | color: #eee;
77 | background-color: #111;
78 | }
79 | .page-nav a.nav-btn.disabled {
80 | color: #555;
81 | }
82 | .page-nav a.nav-btn.disabled:hover {
83 | color: #555;
84 | background-color: #333;
85 | cursor: default;
86 | }
87 | .page-nav a.toggle-code-btn {
88 | color: #eee;
89 | background-color: #888;
90 | border: 1px solid #777;
91 | }
92 | .page-nav a.toggle-code-btn:hover {
93 | color: #ccc;
94 | background-color: #888;
95 | }
96 | .page-nav a.toggle-code-btn.show {
97 | color: darkgrey;
98 | background-color: #333;
99 | }
100 | .page-nav a.toggle-code-btn.show:hover {
101 | color: #ccc;
102 | background-color: #333;
103 | }
104 |
105 | section.grid-container {
106 | position: relative;
107 | box-sizing: border-box;
108 | border: 8px solid #555;
109 | height: 100%;
110 | width: 100%;
111 | transition: height 1.4s ease-out, width 1.4s ease-out, border .1s .4s;
112 | }
113 | .grid-container.solved {
114 | border: 12px solid #A6E22E;
115 | }
116 | section.flex-container.expand {
117 | min-height: 80vh;
118 | min-width: 80vw;
119 | }
120 | section.flex-container.shrink {
121 | height: 250px;
122 | width: 400px;
123 | }
124 | .guide, .guidelines {
125 | position: absolute;
126 | z-index: 1;
127 | box-sizing: border-box;
128 | /* min-height: 100%;
129 | min-width: 100%; */
130 | height: 100%;
131 | width: 100%;
132 | display: grid;
133 | }
134 | .guide>* {
135 | border: 8px dashed #bbb;
136 | }
137 | .guide>.dunes {
138 | border: 8px dashed #F99;
139 | }
140 | .guide>.rocky {
141 | border: 8px dashed aquamarine;
142 | }
143 | .guide>.cloudy {
144 | border: 8px dashed rgb(146, 150, 204);
145 | }
146 | .guidelines>div {
147 | border: 1px solid #eee;
148 | }
149 | .target {
150 | /* width: 100px; */
151 | /* height: 100px; */
152 | color: #333;
153 | font-size: 1.25em; /* 20px/16px */
154 | font-weight: bold;
155 | /* padding: 1px; */
156 | border-bottom: 2px dotted gray;
157 |
158 | display: flex;
159 | justify-content: center;
160 | align-items: center;
161 | }
162 | .target:nth-of-type(1) {
163 | background-color: #7f7f7f;
164 | }
165 | .target:nth-of-type(2) {
166 | background-color: #999;
167 | }
168 | .target.dunes {
169 | background-color: lightcoral;
170 | }
171 | .target.rocky {
172 | background-color: rgb(100, 201, 165);
173 | }
174 | .target.cloudy {
175 | background-color: rgb(100, 107, 201);
176 | }
177 |
178 | /* .target.zombie {
179 | height: 200px;
180 | background-image: url('../images/zombie-male.png');
181 | color: #eee;
182 | }
183 | .target.goo {
184 | background: url('../images/goo.png') no-repeat;
185 | background-size: 100% 100%;
186 | color: #eee;
187 | }
188 | .target.dave {
189 | height: 200px;
190 | width: 150px;
191 | color: #eee;
192 | background-color: #c33
193 | } */
194 | .control-container {
195 | flex-basis: 40%;
196 | min-width: 380px;
197 | display: flex;
198 | flex-direction: column;
199 | justify-content: space-between;
200 | height: 100%;
201 | margin-right: 16px;
202 | }
203 | .ta-code-editor, .div-code-display, .instructions {
204 | /* flex-basis: 32%; */
205 | border: 1px solid darkgray;
206 | margin: 0;
207 | padding: 0.8em 1em;
208 | background-color: darkslategrey
209 | }
210 | .ta-code-editor, .div-code-display {
211 | flex-basis: 24%;
212 | font-family: Consolas, monospace;
213 | font-size: .8125em; /* 13px/16px */
214 | font-weight: 400;
215 | background-color: #333;
216 | tab-size: 2;
217 | -moz-tab-size: 2;
218 | }
219 | .instructions {
220 | flex-basis: 50%;
221 | font-size: .875em; /* 14px/16px */
222 | }
223 | .instructions h2 {
224 | margin: 0 0 14px;
225 | font-size: 1.428571em; /* 20px/14px */
226 | /* text-decoration: underline; */
227 | border-bottom: 1px solid;
228 | }
229 | .instructions em {
230 | color: lightcoral;
231 | color: #ae81ff;
232 | color: #A6E22E;
233 | font-style: normal;
234 | }
235 | .instructions code {
236 | color: #66D9EF;
237 | font-size: .928571em; /* 13px/14px */
238 | }
239 | .instructions .code {
240 | color: lightcoral;
241 | font-size: .923077em; /* 12px/13px */
242 | /* margin: 20px; */
243 | background-color: #333;
244 | padding: .5em .5em .8em;
245 | border: 1px solid grey;
246 | }
247 | .instructions .code code {
248 | color: darkgrey;
249 | }
250 | .instructions .code code em {
251 | color: lightcoral;
252 | }
253 | .instructions .code .answer code {
254 | color: #66D9EF;
255 | color: #dadb6e;
256 | color: #A6E22E;;
257 | }
258 | .inst-btn {
259 | color: #fff;
260 | background-color: #999;
261 | border: 1px solid #777;
262 | padding: 3px 10px;
263 | text-decoration: none;
264 | font-size: .857143em; /* 12px/14px */
265 |
266 | }
267 | .inst-btn:hover { color: #ccc; background-color: #666;}
268 | .inst-btn.disabled {color: #ccc; background-color: #666; cursor: default; }
269 | .instructions .right { text-align: right; /*margin-right: 20px;*/ }
270 | .left { text-align: left; }
271 | .answer {
272 | transition: visibility .3s ease-out, opacity .3s ease-out;
273 | }
274 | .answer.hide { visibility: hidden; opacity: 0; }
275 | .answer.show { visibility: visible; opacity: 1; }
276 | .answer code {
277 | white-space: pre-wrap;
278 | tab-size: 2;
279 | -moz-tab-size: 2;
280 | }
281 | .ta-code-editor {
282 | color: #66D9EF;
283 | resize: none;
284 | color: lightgrey;
285 | background-color: rgb(20, 20, 20);
286 | }
287 | .div-code-display {
288 | color: #FFF;
289 | word-wrap: normal;
290 | white-space: pre-wrap;
291 | cursor: not-allowed;
292 | }
293 | .mce-line { color: #fff; }
294 | .mce-selector { color: #A6E22E; }
295 | .mce-element { color: #F92672; }
296 | .mce-property { color: #66D9EF; font-style: italic; }
297 | .mce-constant { color: #66D9EF; }
298 | .mce-keyword { color: #F92672; }
299 | .mce-number { color: #ae81ff; }
300 | .mce-quotes { color: #dadb6e; }
301 | .mce-function { color: #FD971F;}
302 | /* .mce-comment { color: rgb(255, 118, 248); } */
303 | .mce-comment { color: gray !important; }
--------------------------------------------------------------------------------
/static_pages/lib/css-constants.js:
--------------------------------------------------------------------------------
1 | // This format only allows for one module export.
2 | var getConstants = function() {
3 | return 'absolute|alias|aliceblue|all|all-scroll|allow-end|alternate|alternate-reverse|always|antiquewhite|aqua|aqua|aquamarine|armenian|auto|avoid|azure|backwards|balance|baseline|beige|bidi-override|bisque|black|blanchedalmond|block|blue|blueviolet|border-box|both|bottom|break-all|break-word|brown|burlywood|cadetblue|capitalize|caption|cell|center|chartreuse|chocolate|circle|cjk-ideographic|clip|clone|close-quote|col-resize|collapse|color-dodge|column|column dense|column-reverse|condensed|contain|content-box|context-menu|copy|coral|cornflowerblue|cornsilk|counter|cover|crimson|crosshair|cursive|cyan|darkblue|darkcyan|darken|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|dashed|decimal|decimal-leading-zero|deeppink|deepskyblue|default|dimgray|dimgrey|disc|distribute|dodgerblue|dotted|double|e-resize|ease|ease-in|ease-in-out|ease-out|ellipsis|embed|end|ew-resize|expanded|extra-condensed|extra-expanded|fantasy|fill|firebrick|first|fixed|flat|flex|flex-end|flex-start|floralwhite|force-end|forestgreen|forwards|fuchsia|fuchsia|gainsboro|georgian|ghostwhite|gold|goldenrod|grab|grabbing|gray|green|greenyellow|grey|grid|groove|hebrew|help|hidden|hide|hiragana|hiragana-iroha|honeydew|horizontal|hotpink|icon|indianred|indigo|infinite|inherit|initial|inline|inline-block|inline-flex|inline-table|inset|inside|inter-cluster|inter-ideograph|inter-word|invert|italic|ivory|justify|kashida|katakana|katakana-iroha|keep-all |khaki|large|larger|last|lavender|lavenderblush|lawngreen|left|lemonchiffon|lightblue|lightcoral|lightcyan|lighten|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|line-through|linear|linen|list-item|local|lower-alpha|lower-greek|lower-latin|lower-roman|lowercase|ltr|luminosity|magenta|maroon|max-content|medium|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|menu|message-box|middle|midnightblue|min-content|mintcream|mistyrose|moccasin|monospace|move|multiply|n-resize|navajowhite|navy|ne-resize|nesw-resize|no-close-quote|no-drop|no-open-quote|no-repeat|none|normal|not-allowed|nowrap|ns-resize|nw-resize|nwse-resize|oblique|oldlace|olive|olivedrab|open-quote|orange|orangered|orchid|outset|outside|overlay|overline|padding-box|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|paused|peachpuff|peru|pink|plum|pointer|powderblue|pre|pre-line|pre-wrap|preserve-3d|progress|purple|red|relative|repeat|repeat-x|repeat-y|reverse|ridge|right|rosybrown|round|row|row dense|row-line|row-resize|row-reverse|royalblue|rtl|run-in|running|s-resize|saddlebrown|salmon|sandybrown|sans-serif|saturation|scale-down|screen|scroll|se-resize|seagreen|seashell|semi-condensed|semi-expanded|separate|serif|show|sienna|silver|skyblue|slateblue|slategray|slategrey|slice|small|small-caps|small-caption|smaller|snow|solid|space|space-around|space-between|span|springgreen|square|start|static|status-bar|steelblue|step-end|step-start|sticky|stretch|sub|super|sw-resize|system-ui|table|table-caption|table-cell|table-column|table-column-group|table-footer-group|table-header-group|table-row|table-row-group|tan|teal|text|text-bottom|text-top|thick|thin|thistle|tomato|top|transparent|trim|turquoise|ultra-condensed|ultra-expanded|underline|unset|upper-alpha|upper-greek|upper-latin|upper-roman|uppercase|vertical|vertical-text|violet|visible|w-resize|wait|wavy|wheat|white|whitesmoke|wrap|wrap-reverse|x-large|x-small|xx-large|xx-small|yellow|yellowgreen|zoom-in|zoom-out';
4 | // return 'solid|dashed|dotted|grid';
5 | };
6 |
7 | var getFunctions = function() {
8 | return 'url|translateZ|translateY|translateX|translate3d|translate|steps|skewY|skewX|skew|sepia|scaleZ|scaleY|scaleX|scale3d|scale|saturate|rotateZ|rotateY|rotateX|rotate3d|rotate|repeating-radial-gradient|repeating-linear-gradient|repeat|radial-gradient|perspective|opacity|matrix3d|matrix|linear-gradient|invert|hue-rotate|grayscale|drop-shadow|cubic-bezier|contrast|calc|brightness|blur|attr';
9 | };
10 |
11 | var getHtmlElements = function() {
12 | return 'a|abbr|address|area|article|aside|audio|b|base|bdi|bdo|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|datalist|dd|del|details|dfn|dialog|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|head|header|hr|html|i|iframe|img|input|ins|kbd|label|legend|li|link|main|map|mark|menu|menuitem|meta|meter|nav|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|section|select|small|source|span|strong|style|sub|summary|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|track|u|ul|var|video|wbr';
13 | };
14 |
15 | if ( typeof module !== 'undefined' ) {
16 | module.exports = getConstants;
17 | }
18 |
19 | // export function message( msg ) { alert( msg ); };
--------------------------------------------------------------------------------
/static_pages/script/mce_ta.js:
--------------------------------------------------------------------------------
1 | var myTACodeEditor = {
2 | gridContainer: document.querySelector( '.grid-container' ),
3 | divAnswer: document.querySelector( '.answer code' ),
4 | taCodeEditor: document.querySelector( '.ta-code-editor' ),
5 | divCodeDisplay: document.querySelector( '.div-code-display' ),
6 |
7 | rxSelectors: /^[\w .\-#[\]'"=:()>^~*+,|$]+(?={)/gm,
8 | rxHtmlElements: new RegExp('\\/\\*.*|<.*?>|\\b(' + getHtmlElements() + ')\\b(?=.*{)','gm'),
9 | rxConstants: new RegExp('^[\\s\\w-]+|.*?{|\\w+\\(.*\\)|\\/\\*.*|<.*>|(\\b(' + getConstants() + ')(?![\\w-\\()]))', 'gm'),
10 | rxKeywords: new RegExp('^[\\s\\w-]+:|\\/\\*.*|\\(.*\\)|([\\d.]+)(ch|cm|deg|em|ex|fr|gd|grad|Hz|in|kHz|mm|ms|pc|pt|px|rad|rem|s|turn|vh|vm|vmin|vmax|vw|%)(?=\\W)', 'gm'),
11 | rxNumbers: /^[\s\w-]+:|.*?{|[a-z]\d+|\/\*.*|\(.*\)|([^:>("'/_-]\d*\.?\d+|#[0-9A-Fa-f]{3,6})/gm,
12 | // rxProperties: /^[ \t\w-]+(?=:)/gm,
13 | rxProperties: /^[^{][ \t\w-]+(?=:)/gm,
14 | rxFunctions: new RegExp( '\\/\\*.*|((' + getFunctions() + ')\\([\\w\\d `~!@#$%^&*()\\-_=+[\\]{}\\\\|:;' + String.fromCharCode(39) + '",.\\/?]*\\))', 'gm' ),
15 | rxQuotes: /^[/*].*\*\/|("[\w\s-]*"(?!>)|'[\w\s-]*'(?!>))/gm,
16 | rxRules: /(.+)\s*(?={)|^[\t\s]*(.+;)/gm,
17 | rxTextToWrap: /[\w\d\-[\] {}.:;#,>+'"=()/~^$*]+$/gm,
18 | rxBlockToWrap: /[\w\d\-[\] {}.:;#,>+'"=()/~^$*\t]+$/gm,
19 | rxFindComment: /\/\*|\*\//,
20 | rxReplaceComment: /\/\*|\*\//gm,
21 | rxComments: /\/\*.*\*\//gm,
22 |
23 | preFill: '',
24 | curPos: 0,
25 | answerCode: '',
26 | browser: { chrome: 0, ie: 0, firefox: 0, safari: 0, opera: 0, edge: 0 },
27 | init: function(preFill, curPos, answer) {
28 | var mceObj = this;
29 |
30 | mceObj.sniffBrowser();
31 |
32 | mceObj.taCodeEditor.onkeydown = function( evt ) {
33 | return mceObj.captureKeyDown( evt );
34 | };
35 |
36 | mceObj.taCodeEditor.oninput = function( evt ) {
37 | mceObj.highlightSyntax( mceObj.taCodeEditor.value );
38 | mceObj.processRules( evt );
39 | };
40 |
41 | mceObj.taCodeEditor.onkeyup = function( evt ) {
42 | return mceObj.captureKeyUp( evt );
43 | };
44 |
45 | mceObj.setTaVal( preFill, curPos );
46 | mceObj.preFill = preFill;
47 | mceObj.curPos = curPos;
48 | mceObj.formatAnswer(answer);
49 | },
50 | formatAnswer: function(answer) {
51 | // var code = document.querySelector( '.answer code' ).innerText;
52 | // this.answerCode = code.replace( /^[\xA0 ]+/gm, '\t' );
53 | if ( answer.length > 0 ) {
54 | this.divAnswer.innerText = answer;
55 | this.answerCode = answer;
56 | }
57 | },
58 | setTaVal: function( preFill, curPos ) {
59 | var mceObj = this;
60 | mceObj.taCodeEditor.value = preFill;
61 | mceObj.taCodeEditor.selectionStart = mceObj.taCodeEditor.selectionEnd = curPos;
62 | mceObj.triggerInputEvent();
63 | },
64 | triggerInputEvent: function() {
65 | var mceObj = this;
66 | if ( mceObj.browser.ie ) {
67 | var event = document.createEvent( 'Event' );
68 | event.initEvent( 'input', false, true );
69 | mceObj.taCodeEditor.dispatchEvent( event );
70 | } else {
71 | mceObj.taCodeEditor.dispatchEvent( new Event( 'input' ) );
72 | }
73 | },
74 | sniffBrowser: function() {
75 | this.browser.chrome = navigator.userAgent.indexOf('Chrome') > -1;
76 | this.browser.ie = navigator.userAgent.indexOf( 'MSIE' ) > -1 ||
77 | navigator.userAgent.indexOf( 'Trident/7.' ) > -1;
78 | this.browser.firefox = navigator.userAgent.indexOf('Firefox') > -1;
79 | this.browser.safari = navigator.userAgent.indexOf('Safari') > -1;
80 | this.browser.edge = navigator.userAgent.indexOf('Edge') > -1;
81 | this.browser.opera = navigator.userAgent.toLowerCase().indexOf('op') > -1;
82 | if ( ( this.browser.chrome ) && ( this.browser.safari ) ) { this.browser.safari = false; }
83 | if ( ( this.browser.chrome ) && ( this.browser.opera ) ) { this.browser.chrome = false; }
84 | },
85 | captureKeyUp: function( evt ) {
86 | const SLASH = '/';
87 | var mceObj = this,
88 | // ta = mceObj.taCodeEditor,
89 | ta = evt.target,
90 | val = ta.value,
91 | selStart = ta.selectionStart,
92 | selEnd = ta.selectionEnd,
93 | selDir = ta.selectionDirection,
94 | lineStartPos = val.lastIndexOf( '\n', selStart - 1 ) > -1 ? val.lastIndexOf( '\n', selStart - 1 ) + 1 : 0,
95 | lineEndPos = val.indexOf( '\n', selEnd ) > -1 ? val.indexOf( '\n', selEnd ) : val.length,
96 | firstLineEndPos = val.indexOf( '\n' ) > -1 ? val.indexOf( '\n' ) : val.length,
97 | lastLineStartPos = val.lastIndexOf( '\n' ) > -1 ? val.lastIndexOf( '\n' ) + 1 : 0,
98 | posInLine = selStart - lineStartPos;
99 | console.log( '' );
100 | if ( ta.selectionDirection === 'forward' ) {
101 | console.log( 'after: ', 'selStart:', selStart, 'selEnd:', selEnd, 'selDirection:', selDir );
102 | } else {
103 | console.log( 'after: ', 'selStart:', selEnd, 'selEnd:', selStart, 'selDirection:', selDir );
104 | }
105 | console.log( ' lineStartPos:', lineStartPos, 'lineEndPos:', lineEndPos, 'posInLine', posInLine );
106 | console.log( ' firstLineStartPos:', 0, 'firstLineEndPos:', firstLineEndPos );
107 | console.log( ' lastLineStartPos:', lastLineStartPos, 'lastLineEndPos:', val.length );
108 | switch ( evt.key ) {
109 | case SLASH:
110 | if ( evt.ctrlKey ) {
111 | mceObj.commentSelection( ta );
112 | } else {
113 | return true;
114 | }
115 | break;
116 | }
117 | mceObj.triggerInputEvent();
118 | return false;
119 | },
120 | commentSelection: function( el ) {
121 | var mceObj = this,
122 | val = el.value,
123 | selStart = el.selectionStart,
124 | selEnd = el.selectionEnd,
125 | selDir = el.selectionDirection,
126 | firstLineStartPos = val.lastIndexOf( '\n', selStart - 1 ) > -1 ? val.lastIndexOf( '\n', selStart - 1 ) + 1 : 0,
127 | lastLineEndPos = val.indexOf( '\n', selEnd ) > -1 ? val.indexOf( '\n', selEnd ) : val.length,
128 | lineStartPos = 0, lineEndPos = 0;
129 |
130 | if ( typeof selStart === 'number' && typeof selEnd === 'number' ) {
131 | if ( selStart === selEnd ) {
132 | var slicedLine, newSlicedLine,
133 | posModifier = 0, curPos = 0;
134 |
135 | lineStartPos = firstLineStartPos;
136 | lineEndPos = lastLineEndPos;
137 | slicedLine = val.slice( lineStartPos, lineEndPos );
138 |
139 | newSlicedLine = this.toggleComment( slicedLine, false );
140 | el.value = val.slice( 0, lineStartPos ) + newSlicedLine + val.slice( lineEndPos );
141 |
142 | // set caret selection for comment
143 | if ( this.rxFindComment.test( slicedLine ) ) {
144 | if ( selStart === lineStartPos ) {
145 | posModifier = 0;
146 | } else if ( selStart === lineEndPos ) {
147 | posModifier = -4;
148 | } else {
149 | posModifier = -2;
150 | }
151 | } else {
152 | if ( selStart === lineStartPos ) {
153 | posModifier = 0;
154 | } else if ( selStart === lineEndPos ) {
155 | posModifier = +4;
156 | } else {
157 | posModifier = +2;
158 | }
159 | }
160 | curPos = selDir === 'forward' ? selEnd : selStart;
161 | el.selectionStart = el.selectionEnd = curPos + posModifier;
162 | } else {
163 | var lines = val.split( '\n' ),
164 | selLineCount = 0, prevCommentLineCount = 0;
165 | console.log( '##########################' );
166 | console.log( 'before (lines):', lines );
167 |
168 | // lines.forEach( ( line ) => {
169 | lines.forEach( function( line ){
170 | lineEndPos = lineStartPos + line.length;
171 | if ( ( selStart <= lineEndPos ) && ( lineStartPos < selEnd ) ) {
172 | if ( mceObj.rxFindComment.test( line ) === true ) {
173 | prevCommentLineCount += 1;
174 | }
175 | selLineCount += 1;
176 | }
177 | lineStartPos = lineStartPos + line.length + 1;
178 | } );
179 | var doComment = true;
180 | if ( selLineCount === prevCommentLineCount ) {
181 | doComment = false;
182 | }
183 | console.log( ' ', 'selLineCount:', selLineCount, 'prevCommentLineCount:', prevCommentLineCount );
184 |
185 | lineStartPos = 0; lineEndPos = 0;
186 | var newCommentLineCount = 0;
187 | // lines.forEach( ( line, idx, arr ) => {
188 | lines.forEach( function( line, idx, arr ) {
189 | lineEndPos = lineStartPos + line.length;
190 | if ( ( selStart <= lineEndPos ) && ( lineStartPos < selEnd ) ) {
191 | var hasComment = mceObj.rxFindComment.test( line );
192 | if ( hasComment === false && doComment === true ) {
193 | // arr[ idx ] = mceObj.toggleComment( line );
194 | newCommentLineCount += 1;
195 | } else if ( hasComment && doComment === false ) {
196 | // arr[ idx ] = mceObj.toggleComment( line );
197 | newCommentLineCount -= 1;
198 | }
199 | arr[ idx ] = mceObj.toggleComment( line, true );
200 | selLineCount += 1;
201 | }
202 | lineStartPos = lineStartPos + line.length + 1;
203 | } );
204 |
205 | console.log( 'after (lines):', lines );
206 | console.log( ' ', 'newCommentLineCount:', newCommentLineCount );
207 | el.value = lines.join( '\n' );
208 |
209 | // set caret selection for comment
210 | if ( doComment ) {
211 | if ( selStart === firstLineStartPos ) {
212 | el.selectionStart = selStart;
213 | } else if ( selStart > firstLineStartPos ) {
214 | el.selectionStart = selStart + 2;
215 | }
216 | if ( selEnd < lastLineEndPos ) {
217 | el.selectionEnd = selEnd + ( (prevCommentLineCount + newCommentLineCount) * 4 ) - 2;
218 | } else if ( selEnd === lastLineEndPos ) {
219 | el.selectionEnd = selEnd + ( (prevCommentLineCount + newCommentLineCount) * 4 );
220 | }
221 | } else {
222 | if ( selStart === firstLineStartPos ) {
223 | el.selectionStart = selStart;
224 | } else if ( selStart > firstLineStartPos ) {
225 | el.selectionStart = selStart - 2;
226 | }
227 | if ( selEnd < lastLineEndPos ) {
228 | el.selectionEnd = selEnd + ( (newCommentLineCount) * 4 ) + 2;
229 | } else if ( selEnd === lastLineEndPos ) {
230 | el.selectionEnd = selEnd + ( (newCommentLineCount) * 4 );
231 | }
232 | }
233 | }
234 | }
235 | },
236 | toggleComment: function( slicedLine, isBlock ) {
237 | var newSlicedLine,
238 | hasComment = this.rxFindComment.test( slicedLine );
239 | if ( hasComment ) {
240 | newSlicedLine = slicedLine.replace( this.rxReplaceComment, '' );
241 | } else {
242 | if ( isBlock ) {
243 | newSlicedLine = slicedLine.replace( this.rxBlockToWrap, '/*' + '$&' + '*/' );
244 | } else {
245 | newSlicedLine = slicedLine.replace( this.rxTextToWrap, '/*' + '$&' + '*/' );
246 | }
247 |
248 | }
249 | return newSlicedLine;
250 | },
251 | captureKeyDown: function( evt ) {
252 | const ENTER = 'Enter',
253 | TAB = 'Tab',
254 | BACKSPACE = 'Backspace',
255 | COLON = String.fromCharCode(58),
256 | SEMI = String.fromCharCode(59),
257 | QUOTE = String.fromCharCode(34),
258 | APOSTROPHE = String.fromCharCode( 39 ),
259 | LEFT_PAREN = String.fromCharCode(40),
260 | RIGHT_PAREN = String.fromCharCode( 41 ),
261 | LEFT_BRACKET = String.fromCharCode(91),
262 | RIGHT_BRACKET = String.fromCharCode(93),
263 | LEFT_CURLY_BRACE = String.fromCharCode(123),
264 | RIGHT_CURLY_BRACE = String.fromCharCode(125),
265 | charNewLine = String.fromCharCode( 10 ),
266 | charTab = String.fromCharCode( 9 ),
267 | FIREFOX = this.browser.firefox;
268 | // charBackspace = String.fromCharCode( 8 );
269 | var mceObj = this,
270 | ta = evt.target,
271 | val = ta.value,
272 | selStart = ta.selectionStart,
273 | lineStartPos = val.lastIndexOf( '\n', selStart-1 ) > -1 ? val.lastIndexOf( '\n', selStart-1 ) + 1 : 0,
274 | lineEndPos = val.indexOf( '\n', selStart ) > -1 ? val.indexOf( '\n', selStart ) : val.length;
275 | switch ( evt.key ) {
276 | case ENTER:
277 | var openBracketPos = val.lastIndexOf( '{', ta.selectionStart ),
278 | closeBracketPos = val.lastIndexOf( '}', ta.selectionStart );
279 | if ( ( openBracketPos - ta.selectionStart ) > ( closeBracketPos - ta.selectionStart ) ) {
280 | if ( FIREFOX ) {
281 | mceObj.insertText( ta, charNewLine + charTab, 2 );
282 | } else {
283 | // mceObj.insertText2( ta, charNewLine + charTab, 0 );
284 | document.execCommand( 'insertText', false, charNewLine + charTab );
285 | }
286 | } else {
287 | if ( FIREFOX ) {
288 | mceObj.insertText( ta, charNewLine, 1 );
289 | } else {
290 | // mceObj.insertText2( ta, charNewLine, 0 );
291 | document.execCommand( 'insertText', false, charNewLine );
292 | }
293 | }
294 | break;
295 | case TAB:
296 | if ( FIREFOX ) {
297 | mceObj.insertText( ta, charTab, 1 );
298 | } else {
299 | // mceObj.insertText2( ta, charTab, 0 );
300 | document.execCommand( 'insertText', false, charTab );
301 | }
302 | break;
303 | case COLON:
304 | var colonPos = val.indexOf( COLON, lineStartPos );
305 | var semiPos = val.indexOf( SEMI, lineStartPos );
306 | if ((colonPos > lineEndPos || colonPos === -1) && (semiPos > lineEndPos || semiPos === -1) ){
307 | if ( FIREFOX ) {
308 | mceObj.insertText( ta, COLON + SEMI, 1 );
309 | } else {
310 | // mceObj.insertText( ta, COLON + SEMI, 1 );
311 | mceObj.insertText2( ta, COLON + SEMI, -1 );
312 | }
313 | } else {
314 | return true;
315 | }
316 | break;
317 | case SEMI:
318 | if ( val.charAt( selStart ) === SEMI ) {
319 | if ( FIREFOX ) {
320 | // mceObj.insertText( ta, '', 1 );
321 | mceObj.moveCursor( ta, 1 );
322 | } else {
323 | // mceObj.insertText2( ta, '', 1 );
324 | document.execCommand( 'forwardDelete', false );
325 | document.execCommand( 'insertText', false, SEMI );
326 | }
327 | } else {
328 | return true;
329 | }
330 | break;
331 | case BACKSPACE:
332 | return true;
333 | // break;
334 | case QUOTE:
335 | if ( FIREFOX ) {
336 | mceObj.insertText( ta, QUOTE + QUOTE, 1 );
337 | } else {
338 | mceObj.insertText2( ta, QUOTE + QUOTE, -1 );
339 | }
340 | break;
341 | case APOSTROPHE:
342 | if ( FIREFOX ) {
343 | mceObj.insertText( ta, APOSTROPHE + APOSTROPHE, 1 );
344 | } else {
345 | mceObj.insertText2( ta, APOSTROPHE + APOSTROPHE, -1 );
346 | }
347 | break;
348 | case LEFT_PAREN:
349 | if ( FIREFOX ) {
350 | mceObj.insertText( ta, LEFT_PAREN + RIGHT_PAREN, 1 );
351 | } else {
352 | mceObj.insertText2( ta, LEFT_PAREN + RIGHT_PAREN, -1 );
353 | }
354 | break;
355 | case LEFT_BRACKET:
356 | if ( FIREFOX ) {
357 | mceObj.insertText( ta, LEFT_BRACKET + RIGHT_BRACKET, 1 );
358 | } else {
359 | mceObj.insertText2( ta, LEFT_BRACKET + RIGHT_BRACKET, -1 );
360 | }
361 | break;
362 | case LEFT_CURLY_BRACE:
363 | if ( val.lastIndexOf( '{', selStart ) === -1 || ( val.lastIndexOf( '}', selStart ) > val.lastIndexOf( '{', selStart ) ) ) {
364 | if ( FIREFOX ) {
365 | mceObj.insertText( ta, LEFT_CURLY_BRACE + charNewLine + charTab + charNewLine + RIGHT_CURLY_BRACE, 3 );
366 | } else {
367 | mceObj.insertText2( ta, LEFT_CURLY_BRACE + charNewLine + charTab + charNewLine + RIGHT_CURLY_BRACE, -2 );
368 | }
369 | } else {
370 | return true;
371 | }
372 | break;
373 | default:
374 | return true;
375 | }
376 | mceObj.triggerInputEvent();
377 | return false;
378 | },
379 | insertText: function( el, text, curPos) {
380 | var start, end, val = el.value;
381 | if ( typeof el.selectionStart === 'number' && typeof el.selectionEnd === 'number' ) {
382 | start = el.selectionStart;
383 | end = el.selectionEnd;
384 | el.value = val.slice( 0, start ) + text + val.slice( end );
385 | // move the caret
386 | el.selectionStart = el.selectionEnd = start + curPos;
387 | }
388 | },
389 | insertText2: function( el, text, m ) {
390 | // this allows the browser's undo & redo features to work
391 | var result = document.execCommand( 'insertText', false, text );
392 | console.log( 'insertText result:', result );
393 | el.selectionStart = el.selectionEnd = el.selectionStart + m;
394 | },
395 | moveCursor: function( el, m ) {
396 | el.selectionStart = el.selectionEnd = el.selectionStart + m;
397 | },
398 | highlightSyntax: function( css ) {
399 | var formatted = css;
400 |
401 | formatted = formatted.replace( this.rxSelectors, '$& ' );
402 | formatted = formatted.replace( this.rxHtmlElements, function(m, group1) {
403 | if (group1 !== undefined) {
404 | return '' + group1 + ' ';
405 | }
406 | return m;
407 | } );
408 | formatted = formatted.replace( this.rxConstants, function(m, group1) {
409 | if (group1 !== undefined) {
410 | return '' + group1 + ' ';
411 | }
412 | return m;
413 | } );
414 | formatted = formatted.replace( this.rxKeywords, function(m, group1, group2) {
415 | if (group1 !== undefined) {
416 | return group1 + '' + group2 + ' ';
417 | }
418 | return m;
419 | } );
420 | formatted = formatted.replace( this.rxProperties, '$& ' );
421 | formatted = formatted.replace( this.rxFunctions, function(m, group1) {
422 | if (group1 !== undefined) {
423 | return '' + group1 + ' ';
424 | }
425 | return m;
426 | } );
427 | formatted = formatted.replace( this.rxNumbers, function(m, group1) {
428 | if (group1 !== undefined) {
429 | return '' + group1 + ' ';
430 | }
431 | return m;
432 | } );
433 | formatted = formatted.replace( this.rxQuotes, function(m, group1) {
434 | if (group1 !== undefined) {
435 | return '' + group1 + ' ';
436 | }
437 | return m;
438 | } );
439 | formatted = formatted.replace( this.rxComments, '' );
440 |
441 | this.divCodeDisplay.innerHTML = formatted;
442 | },
443 | applyStyle: function( selector, declarations ) {
444 | if ( selector ) {
445 | // var elArr = document.getElementById('sandbox').querySelectorAll( selector );
446 | var el = document.getElementById( 'sandbox' );
447 | var elArr = el.querySelectorAll( selector );
448 | // elArr.forEach( el => {
449 | elArr.forEach( function(el) {
450 | el.style = declarations;
451 | } );
452 | }
453 | },
454 | clearStyle: function() {
455 | if ( this.browser.ie ) {
456 | NodeList.prototype.forEach = Array.prototype.forEach;
457 | }
458 | var elArr = document.getElementById('sandbox').querySelectorAll( '*' );
459 | // elArr.forEach( el => {
460 | elArr.forEach( function(el) {
461 | el.removeAttribute( 'style' );
462 | } );
463 | },
464 | processRules: function( evt ) {
465 | var mceObj = this;
466 | var css = evt.target.value;
467 | var m, selector, prev = '', rules = '', rulesArr = [], count=0;
468 |
469 | while ( ( m = this.rxRules.exec( css ) ) !== null ) {
470 | if ( m.index === this.rxRules.lastIndex ) {
471 | this.rxRules.lastIndex++;
472 | }
473 | // console.log( count, 'm[1]:', m[1] );
474 | // console.log( count, 'm[2]:', m[2] );
475 |
476 | if ( m[ 1 ] !== undefined ) {
477 | prev = prev ? prev : m[ 1 ];
478 | selector = m[ 1 ];
479 | if ( selector !== prev ) {
480 | rulesArr.push([prev.trim(), rules] );
481 | rules = [];
482 | prev = selector;
483 | }
484 | } else {
485 | if ( this.rxFindComment.test( m[ 2 ] ) === false ) {
486 | rules += m[ 2 ];
487 | }
488 | }
489 | count += 1;
490 | }
491 | rulesArr.push( [prev.trim(), rules] );
492 | // console.log( rulesArr );
493 |
494 | this.clearStyle();
495 | // rulesArr.forEach( rule => {
496 | rulesArr.forEach( function( rule ) {
497 | if ( rule[ 0 ].startsWith( '/*' ) === false ) {
498 | mceObj.applyStyle( rule[ 0 ], rule[ 1 ] );
499 | }
500 | } );
501 | this.displaySolved();
502 | },
503 | displaySolved: function() {
504 | var ta = this.taCodeEditor,
505 | answer = '.grid-container {\n' + this.answerCode + '\n}',
506 | gridContainer = document.querySelector('.grid-container');
507 |
508 | // console.log( 'ta.value:', ta.value );
509 | // console.log( 'answer:', answer );
510 |
511 | if ( ta.value === answer ) {
512 | // gridContainer.style.border = '8px solid LimeGreen';
513 | // gridContainer.style.border = '8px solid #A6E22E';
514 | gridContainer.classList.add( 'solved' );
515 | } else {
516 | // gridContainer.style.border = '8px solid #555';
517 | gridContainer.classList.remove( 'solved' );
518 | }
519 | },
520 | showAnswer: function() {
521 | document.querySelector( '.answer' ).classList.add( 'show' );
522 | document.querySelector( '.answer-btn' ).classList.add( 'disabled' );
523 | return false;
524 | },
525 | pasteAnswer: function() {
526 | var mceObj = this,
527 | ta = this.taCodeEditor,
528 | // code = document.querySelector( '.answer code' ).innerText,
529 | code = document.querySelector( '.answer code' ).innerText,
530 | pasteBtn = document.querySelector( '.paste-btn' );
531 |
532 | if ( pasteBtn.innerText === 'Paste' && code.trim().length > 0) {
533 | console.log( code.trim().length );
534 | pasteBtn.innerText = 'Clear';
535 | // replace non-breaking space (char code 160) with tab
536 | ta.value = '.grid-container {\n' + code.replace(/^[\xA0 ]+/gm, '\t') + '\n}';
537 | mceObj.triggerInputEvent();
538 | } else if(pasteBtn.innerText === 'Clear') {
539 | pasteBtn.innerText = 'Paste';
540 | this.setTaVal( mceObj.preFill, mceObj.curPos );
541 | }
542 | },
543 | toggleCode: function() {
544 | var mceObj = this,
545 | ta = this.taCodeEditor,
546 | val = ta.value,
547 | selStart = ta.selectionStart,
548 | selEnd = ta.selectionEnd,
549 | doComment = false,
550 | startPartial = '', endPartial = '',
551 | startCount = 0, endCount = 0;
552 |
553 | ta.selectionStart = 0;
554 | ta.selectionEnd = ta.value.length;
555 | mceObj.commentSelection(ta);
556 |
557 | if ( document.querySelector( '.toggle-code-btn' ).classList.contains( 'show' ) ) {
558 | document.querySelector( '.toggle-code-btn' ).classList.remove( 'show' );
559 | doComment = true;
560 | } else {
561 | document.querySelector( '.toggle-code-btn' ).classList.add( 'show' );
562 | doComment = false;
563 | }
564 | mceObj.triggerInputEvent();
565 |
566 | if ( doComment ) {
567 | startPartial = ta.value.slice( 0, selStart );
568 | endPartial = ta.value.slice( 0, selEnd );
569 | } else {
570 | startPartial = val.slice( 0, selStart );
571 | endPartial = val.slice( 0, selEnd );
572 | }
573 |
574 | startCount = ( startPartial.match( /\/\*|\*\//g ) || []).length;
575 | endCount = ( endPartial.match( /\/\*|\*\//g ) || []).length;
576 |
577 | ta.focus();
578 | if ( doComment ) {
579 | ta.selectionStart = selStart + (startCount * 2);
580 | ta.selectionEnd = selEnd + (endCount * 2);
581 | } else {
582 | ta.selectionStart = selStart - (startCount * 2);
583 | ta.selectionEnd = selEnd - (endCount * 2);
584 | }
585 |
586 | return false;
587 | }
588 | };
589 |
--------------------------------------------------------------------------------