├── .gitignore ├── LICENSE ├── README.md ├── app ├── css │ ├── base16-ocean-dark.css │ ├── codemirror.css │ ├── dialog.css │ ├── fonts.css │ ├── simple-hint.css │ ├── simplescrollbars.css │ └── style.css ├── editor.js ├── fileMenu.js ├── helpMenu.js ├── index.html ├── js │ ├── closetag.js │ ├── codemirror.js │ ├── css.js │ ├── dialog.js │ ├── foldcode.js │ ├── formatting.js │ ├── htmlmixed.js │ ├── javascript-hint.js │ ├── javascript.js │ ├── jquery-3.1.1.min.js │ ├── loadmode.js │ ├── match-highlighter.js │ ├── meta.js │ ├── overlay.js │ ├── runmode.js │ ├── search.js │ ├── searchcursor.js │ ├── simple-hint.js │ ├── simplescrollbars.js │ └── xml.js ├── lib │ ├── animate.css │ ├── anime.min.js │ ├── bootstrap.min.css │ ├── bootstrap.min.js │ ├── font-awesome.min.css │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── fontawesome-webfont.woff2 │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ ├── glyphicons-halflings-regular.woff2 │ ├── jquery-3.1.1.min.js │ ├── materialize.js │ ├── materialize.min.css │ ├── p5.min.js │ └── three.min.js ├── main.js ├── package.json ├── scripts.js ├── shortcuts.js ├── styles.js ├── viewMenu.js └── windowControl.js ├── build ├── icon.icns ├── icon.ico └── icons │ ├── 128x128.png │ ├── 512x512.png │ └── CB.svg └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | Thumbs.db 4 | *.log 5 | *.autogenerated 6 | /dist 7 | .vscode 8 | /lib -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Abhishek Warokar, Jay Shah 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |
4 | CodePad
5 | An Offline Front-End Development Playground

6 | 7 | 8 | 9 | 10 | 11 | 12 |

13 |

14 | 15 | > **NEW: Save your snippets!** 16 | 17 | __CodePad__ instantly previews your code so you don't have to run back and forth between an editor and a browser. You can also add your favorite icon fonts, libraries and frameworks such as Font Awesome, anime.js and Bootstrap with a click. And the best of it all, everything works offline. 18 | 19 | ## Screenshot 🖼️ 20 | 21 | [CodePad](https://thecodepad.github.io) 22 | 23 | 24 | ## Installation 💻 25 | 26 | Download CodePad for Windows [here](https://github.com/Jay9596/CodePad/releases/download/v1.1-beta/CodePad.rar). Extract and run CodePad.exe. Coming Soon for Ubuntu and macOS. 27 | 28 | ## Run From Source 🎩 29 | 30 | You can run the app via command line by following these steps: 31 | 32 | ``` 33 | $ git clone https://github.com/thecodepad/codepad.git 34 | $ cd codepad 35 | $ npm install 36 | $ npm start 37 | ``` 38 | 39 | > Please note that 'Save' will not work here. 40 | 41 | ## Dev To-Do List 🌈 42 | 43 | - [x] Save the code snippet 44 | - [ ] Resizeable editor 45 | - [ ] Editor themes/appearance settings 46 | - [ ] Preprocessor support 47 | - [ ] Import custom libs 48 | - [ ] View output in fullscreen/browser 49 | 50 | ## Credits 👨 51 | 52 | Designed and developed by: [@apollonian11](https://www.github.com/apollonian11) and [@jay9596](https://www.github.com/jay9596) 53 | -------------------------------------------------------------------------------- /app/css/base16-ocean-dark.css: -------------------------------------------------------------------------------- 1 | .cm-s-base16-ocean-dark.CodeMirror {background: #2b303b; color: #dfe1e8;} 2 | .cm-s-base16-ocean-dark div.CodeMirror-selected {background: #343d46 !important;} 3 | .cm-s-base16-ocean-dark .CodeMirror-gutters {background: #2b303b; border-right: 0px;} 4 | .cm-s-base16-ocean-dark .CodeMirror-linenumber {color: #65737e;} 5 | .cm-s-base16-ocean-dark .CodeMirror-cursor {border-left: 2px solid #a7adba !important;} 6 | 7 | .cm-s-base16-ocean-dark span.cm-comment {color: #ab7967;} 8 | .cm-s-base16-ocean-dark span.cm-atom {color: #b48ead;} 9 | .cm-s-base16-ocean-dark span.cm-number {color: #b48ead;} 10 | 11 | .cm-s-base16-ocean-dark span.cm-property, .cm-s-base16-ocean-dark span.cm-attribute {color: #a3be8c;} 12 | .cm-s-base16-ocean-dark span.cm-keyword {color: #bf616a;} 13 | .cm-s-base16-ocean-dark span.cm-string {color: #ebcb8b;} 14 | 15 | .cm-s-base16-ocean-dark span.cm-variable {color: #a3be8c;} 16 | .cm-s-base16-ocean-dark span.cm-variable-2 {color: #8fa1b3;} 17 | .cm-s-base16-ocean-dark span.cm-def {color: #d08770;} 18 | .cm-s-base16-ocean-dark span.cm-error {background: rgba(191, 97, 106, 0.3); color: #a7adba;} 19 | .cm-s-base16-ocean-dark span.cm-bracket {color: #dfe1e8;} 20 | .cm-s-base16-ocean-dark span.cm-tag {color: #bf616a;} 21 | .cm-s-base16-ocean-dark span.cm-link {color: #b48ead;} 22 | 23 | .cm-s-base16-ocean-dark .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} -------------------------------------------------------------------------------- /app/css/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | height: 320px; 6 | color: black; 7 | font-size: 14px; 8 | line-height: 24px; 9 | font-family: 'Source Code Pro', 'Roboto Mono', 'Courier New', 'Courier', monospace; 10 | font-weight: 400; 11 | } 12 | 13 | 14 | /* PADDING */ 15 | 16 | .CodeMirror-lines { 17 | padding: 4px 0; 18 | /* Vertical padding around content */ 19 | } 20 | 21 | .CodeMirror pre { 22 | padding: 0 4px; 23 | /* Horizontal padding of content */ 24 | } 25 | 26 | .CodeMirror-scrollbar-filler, 27 | .CodeMirror-gutter-filler { 28 | background-color: white; 29 | /* The little square between H and V scrollbars */ 30 | } 31 | 32 | 33 | /* GUTTER */ 34 | 35 | .CodeMirror-gutters { 36 | border-right: 1px solid #ddd; 37 | background-color: #f7f7f7; 38 | white-space: nowrap; 39 | } 40 | 41 | .CodeMirror-linenumbers {} 42 | 43 | .CodeMirror-linenumber { 44 | padding: 0 10px 0 10px; 45 | min-width: 20px; 46 | text-align: right; 47 | color: #999; 48 | white-space: nowrap; 49 | } 50 | 51 | .CodeMirror-guttermarker { 52 | color: black; 53 | } 54 | 55 | .CodeMirror-guttermarker-subtle { 56 | color: #999; 57 | } 58 | 59 | 60 | /* CURSOR */ 61 | 62 | .CodeMirror-cursor { 63 | border-left: 1px solid black; 64 | border-right: none; 65 | width: 0; 66 | } 67 | 68 | 69 | /* Shown when moving in bi-directional text */ 70 | 71 | .CodeMirror div.CodeMirror-secondarycursor { 72 | border-left: 1px solid silver; 73 | } 74 | 75 | .cm-fat-cursor .CodeMirror-cursor { 76 | width: auto; 77 | border: 0 !important; 78 | background: #7e7; 79 | } 80 | 81 | .cm-fat-cursor div.CodeMirror-cursors { 82 | z-index: 1; 83 | } 84 | 85 | .cm-animate-fat-cursor { 86 | width: auto; 87 | border: 0; 88 | -webkit-animation: blink 1.06s steps(1) infinite; 89 | -moz-animation: blink 1.06s steps(1) infinite; 90 | animation: blink 1.06s steps(1) infinite; 91 | background-color: #7e7; 92 | } 93 | 94 | @-moz-keyframes blink { 95 | 0% {} 96 | 50% { 97 | background-color: transparent; 98 | } 99 | 100% {} 100 | } 101 | 102 | @-webkit-keyframes blink { 103 | 0% {} 104 | 50% { 105 | background-color: transparent; 106 | } 107 | 100% {} 108 | } 109 | 110 | @keyframes blink { 111 | 0% {} 112 | 50% { 113 | background-color: transparent; 114 | } 115 | 100% {} 116 | } 117 | 118 | 119 | /* Can style cursor different in overwrite (non-insert) mode */ 120 | 121 | .CodeMirror-overwrite .CodeMirror-cursor {} 122 | 123 | .cm-tab { 124 | display: inline-block; 125 | text-decoration: inherit; 126 | } 127 | 128 | .CodeMirror-rulers { 129 | position: absolute; 130 | left: 0; 131 | right: 0; 132 | top: -50px; 133 | bottom: -20px; 134 | overflow: hidden; 135 | } 136 | 137 | .CodeMirror-ruler { 138 | border-left: 1px solid #ccc; 139 | top: 0; 140 | bottom: 0; 141 | position: absolute; 142 | } 143 | 144 | 145 | /* DEFAULT THEME */ 146 | 147 | .cm-s-default .cm-header { 148 | color: blue; 149 | } 150 | 151 | .cm-s-default .cm-quote { 152 | color: #090; 153 | } 154 | 155 | .cm-negative { 156 | color: #d44; 157 | } 158 | 159 | .cm-positive { 160 | color: #292; 161 | } 162 | 163 | .cm-header, 164 | .cm-strong { 165 | font-weight: bold; 166 | } 167 | 168 | .cm-em { 169 | font-style: italic; 170 | } 171 | 172 | .cm-link { 173 | text-decoration: underline; 174 | } 175 | 176 | .cm-strikethrough { 177 | text-decoration: line-through; 178 | } 179 | 180 | .cm-s-default .cm-keyword { 181 | color: #708; 182 | } 183 | 184 | .cm-s-default .cm-atom { 185 | color: #219; 186 | } 187 | 188 | .cm-s-default .cm-number { 189 | color: #164; 190 | } 191 | 192 | .cm-s-default .cm-def { 193 | color: #00f; 194 | } 195 | 196 | .cm-s-default .cm-variable, 197 | .cm-s-default .cm-punctuation, 198 | .cm-s-default .cm-property, 199 | .cm-s-default .cm-operator {} 200 | 201 | .cm-s-default .cm-variable-2 { 202 | color: #05a; 203 | } 204 | 205 | .cm-s-default .cm-variable-3 { 206 | color: #085; 207 | } 208 | 209 | .cm-s-default .cm-comment { 210 | color: #a50; 211 | } 212 | 213 | .cm-s-default .cm-string { 214 | color: #a11; 215 | } 216 | 217 | .cm-s-default .cm-string-2 { 218 | color: #f50; 219 | } 220 | 221 | .cm-s-default .cm-meta { 222 | color: #555; 223 | } 224 | 225 | .cm-s-default .cm-qualifier { 226 | color: #555; 227 | } 228 | 229 | .cm-s-default .cm-builtin { 230 | color: #30a; 231 | } 232 | 233 | .cm-s-default .cm-bracket { 234 | color: #997; 235 | } 236 | 237 | .cm-s-default .cm-tag { 238 | color: #170; 239 | } 240 | 241 | .cm-s-default .cm-attribute { 242 | color: #00c; 243 | } 244 | 245 | .cm-s-default .cm-hr { 246 | color: #999; 247 | } 248 | 249 | .cm-s-default .cm-link { 250 | color: #00c; 251 | } 252 | 253 | .cm-s-default .cm-error { 254 | color: #f00; 255 | } 256 | 257 | .cm-invalidchar { 258 | color: #f00; 259 | } 260 | 261 | .CodeMirror-composing { 262 | border-bottom: 2px solid; 263 | } 264 | 265 | 266 | /* Default styles for common addons */ 267 | 268 | div.CodeMirror span.CodeMirror-matchingbracket { 269 | color: #0f0; 270 | } 271 | 272 | div.CodeMirror span.CodeMirror-nonmatchingbracket { 273 | color: #f22; 274 | } 275 | 276 | .CodeMirror-matchingtag { 277 | background: rgba(255, 150, 0, .3); 278 | } 279 | 280 | .CodeMirror-activeline-background { 281 | background: #e8f2ff; 282 | } 283 | 284 | 285 | /* STOP */ 286 | 287 | 288 | /* The rest of this file contains styles related to the mechanics of 289 | the editor. You probably shouldn't touch them. */ 290 | 291 | .CodeMirror { 292 | position: relative; 293 | overflow: hidden; 294 | background: white; 295 | } 296 | 297 | .CodeMirror-scroll { 298 | overflow: scroll !important; 299 | /* Things will break if this is overridden */ 300 | /* 30px is the magic margin used to hide the element's real scrollbars */ 301 | /* See overflow: hidden in .CodeMirror */ 302 | margin-bottom: -30px; 303 | margin-right: -30px; 304 | padding-bottom: 30px; 305 | height: 100%; 306 | outline: none; 307 | /* Prevent dragging from highlighting the element */ 308 | position: relative; 309 | } 310 | 311 | 312 | /*.CodeMirror-scroll { 313 | height: auto; 314 | overflow-y: hidden; 315 | overflow-x: auto; 316 | }*/ 317 | 318 | .CodeMirror-sizer { 319 | position: relative; 320 | border-right: 30px solid transparent; 321 | } 322 | 323 | 324 | /* The fake, visible scrollbars. Used to force redraw during scrolling 325 | before actual scrolling happens, thus preventing shaking and 326 | flickering artifacts. */ 327 | 328 | .CodeMirror-vscrollbar, 329 | .CodeMirror-hscrollbar, 330 | .CodeMirror-scrollbar-filler, 331 | .CodeMirror-gutter-filler { 332 | position: absolute; 333 | z-index: 6; 334 | display: none; 335 | } 336 | 337 | .CodeMirror-vscrollbar { 338 | right: 0; 339 | top: 0; 340 | overflow-x: hidden; 341 | overflow-y: scroll; 342 | } 343 | 344 | .CodeMirror-hscrollbar { 345 | bottom: 0; 346 | left: 0; 347 | overflow-y: hidden; 348 | overflow-x: scroll; 349 | } 350 | 351 | .CodeMirror-scrollbar-filler { 352 | right: 0; 353 | bottom: 0; 354 | } 355 | 356 | .CodeMirror-gutter-filler { 357 | left: 0; 358 | bottom: 0; 359 | } 360 | 361 | .CodeMirror-gutters { 362 | position: absolute; 363 | left: 0; 364 | top: 0; 365 | min-height: 100%; 366 | z-index: 3; 367 | } 368 | 369 | .CodeMirror-gutter { 370 | white-space: normal; 371 | height: 100%; 372 | display: inline-block; 373 | vertical-align: top; 374 | margin-bottom: -30px; 375 | } 376 | 377 | .CodeMirror-gutter-wrapper { 378 | position: absolute; 379 | z-index: 4; 380 | background: none !important; 381 | border: none !important; 382 | } 383 | 384 | .CodeMirror-gutter-background { 385 | position: absolute; 386 | top: 0; 387 | bottom: 0; 388 | z-index: 4; 389 | } 390 | 391 | .CodeMirror-gutter-elt { 392 | position: absolute; 393 | cursor: default; 394 | z-index: 4; 395 | } 396 | 397 | .CodeMirror-gutter-wrapper { 398 | -webkit-user-select: none; 399 | -moz-user-select: none; 400 | user-select: none; 401 | } 402 | 403 | .CodeMirror-lines { 404 | cursor: text; 405 | min-height: 1px; 406 | /* prevents collapsing before first draw */ 407 | } 408 | 409 | .CodeMirror pre { 410 | /* Reset some styles that the rest of the page might have set */ 411 | -moz-border-radius: 0; 412 | -webkit-border-radius: 0; 413 | border-radius: 0; 414 | border-width: 0; 415 | background: transparent; 416 | font-family: inherit; 417 | font-size: inherit; 418 | margin: 0; 419 | white-space: pre; 420 | word-wrap: normal; 421 | line-height: inherit; 422 | color: inherit; 423 | z-index: 2; 424 | position: relative; 425 | overflow: visible; 426 | -webkit-tap-highlight-color: transparent; 427 | -webkit-font-variant-ligatures: contextual; 428 | font-variant-ligatures: contextual; 429 | } 430 | 431 | .CodeMirror-wrap pre { 432 | word-wrap: break-word; 433 | white-space: pre-wrap; 434 | word-break: normal; 435 | } 436 | 437 | .CodeMirror-linebackground { 438 | position: absolute; 439 | left: 0; 440 | right: 0; 441 | top: 0; 442 | bottom: 0; 443 | z-index: 0; 444 | } 445 | 446 | .CodeMirror-linewidget { 447 | position: relative; 448 | z-index: 2; 449 | overflow: auto; 450 | } 451 | 452 | .CodeMirror-widget {} 453 | 454 | .CodeMirror-code { 455 | outline: none; 456 | } 457 | 458 | 459 | /* Force content-box sizing for the elements where we expect it */ 460 | 461 | .CodeMirror-scroll, 462 | .CodeMirror-sizer, 463 | .CodeMirror-gutter, 464 | .CodeMirror-gutters, 465 | .CodeMirror-linenumber { 466 | -moz-box-sizing: content-box; 467 | box-sizing: content-box; 468 | } 469 | 470 | .CodeMirror-measure { 471 | position: absolute; 472 | width: 100%; 473 | height: 0; 474 | overflow: hidden; 475 | visibility: hidden; 476 | } 477 | 478 | .CodeMirror-cursor { 479 | position: absolute; 480 | pointer-events: none; 481 | } 482 | 483 | .CodeMirror-measure pre { 484 | position: static; 485 | } 486 | 487 | div.CodeMirror-cursors { 488 | visibility: hidden; 489 | position: relative; 490 | z-index: 3; 491 | } 492 | 493 | div.CodeMirror-dragcursors { 494 | visibility: visible; 495 | } 496 | 497 | .CodeMirror-focused div.CodeMirror-cursors { 498 | visibility: visible; 499 | } 500 | 501 | .CodeMirror-selected { 502 | background: #d9d9d9; 503 | } 504 | 505 | .CodeMirror-focused .CodeMirror-selected { 506 | background: #d7d4f0; 507 | } 508 | 509 | .CodeMirror-crosshair { 510 | cursor: crosshair; 511 | } 512 | 513 | .CodeMirror-line::selection, 514 | .CodeMirror-line>span::selection, 515 | .CodeMirror-line>span>span::selection { 516 | background: #d7d4f0; 517 | } 518 | 519 | .CodeMirror-line::-moz-selection, 520 | .CodeMirror-line>span::-moz-selection, 521 | .CodeMirror-line>span>span::-moz-selection { 522 | background: #d7d4f0; 523 | } 524 | 525 | .cm-searching { 526 | background: #ffa; 527 | background: rgba(255, 255, 0, .4); 528 | } 529 | 530 | 531 | /* Used to force a border model for a node */ 532 | 533 | .cm-force-border { 534 | padding-right: .1px; 535 | } 536 | 537 | @media print { 538 | /* Hide the cursor when printing */ 539 | .CodeMirror div.CodeMirror-cursors { 540 | visibility: hidden; 541 | } 542 | } 543 | 544 | 545 | /* See issue #2901 */ 546 | 547 | .cm-tab-wrap-hack:after { 548 | content: ''; 549 | } 550 | 551 | 552 | /* Help users use markselection to safely style text background */ 553 | 554 | span.CodeMirror-selectedtext { 555 | background: none; 556 | } -------------------------------------------------------------------------------- /app/css/dialog.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-dialog { 2 | position: relative; 3 | } 4 | 5 | .CodeMirror-dialog > div { 6 | position: absolute; 7 | top: 0; left: 0; right: 0; 8 | background: white; 9 | border-bottom: 1px solid #eee; 10 | z-index: 15; 11 | padding: .1em .8em; 12 | overflow: hidden; 13 | color: #333; 14 | } 15 | 16 | .CodeMirror-dialog input { 17 | border: none; 18 | outline: none; 19 | background: transparent; 20 | width: 20em; 21 | color: inherit; 22 | font-family: monospace; 23 | } 24 | -------------------------------------------------------------------------------- /app/css/fonts.css: -------------------------------------------------------------------------------- 1 | /* latin-ext */ 2 | 3 | @font-face { 4 | font-family: 'Source Code Pro'; 5 | font-style: normal; 6 | font-weight: 400; 7 | src: local('Source Code Pro'), local('SourceCodePro-Regular'), url(https://fonts.gstatic.com/s/sourcecodepro/v6/mrl8jkM18OlOQN8JLgasDy2Q8seG17bfDXYR_jUsrzg.woff2) format('woff2'); 8 | unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; 9 | } 10 | 11 | 12 | /* latin */ 13 | 14 | @font-face { 15 | font-family: 'Source Code Pro'; 16 | font-style: normal; 17 | font-weight: 400; 18 | src: local('Source Code Pro'), local('SourceCodePro-Regular'), url(https://fonts.gstatic.com/s/sourcecodepro/v6/mrl8jkM18OlOQN8JLgasD9V_2ngZ8dMf8fLgjYEouxg.woff2) format('woff2'); 19 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; 20 | } 21 | 22 | 23 | /* latin-ext */ 24 | 25 | @font-face { 26 | font-family: 'Source Code Pro'; 27 | font-style: normal; 28 | font-weight: 500; 29 | src: local('Source Code Pro Medium'), local('SourceCodePro-Medium'), url(https://fonts.gstatic.com/s/sourcecodepro/v6/leqv3v-yTsJNC7nFznSMqWPwYXaUNREbAUyQ5RuiVI0.woff2) format('woff2'); 30 | unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; 31 | } 32 | 33 | 34 | /* latin */ 35 | 36 | @font-face { 37 | font-family: 'Source Code Pro'; 38 | font-style: normal; 39 | font-weight: 500; 40 | src: local('Source Code Pro Medium'), local('SourceCodePro-Medium'), url(https://fonts.gstatic.com/s/sourcecodepro/v6/leqv3v-yTsJNC7nFznSMqUHj7CJK1I4bLnYZkMY-kd4.woff2) format('woff2'); 41 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; 42 | } 43 | 44 | 45 | /* latin-ext */ 46 | 47 | @font-face { 48 | font-family: 'Source Code Pro'; 49 | font-style: normal; 50 | font-weight: 600; 51 | src: local('Source Code Pro Semibold'), local('SourceCodePro-Semibold'), url(https://fonts.gstatic.com/s/sourcecodepro/v6/leqv3v-yTsJNC7nFznSMqVKFh1TDTPrUZWzVp6FtpG8.woff2) format('woff2'); 52 | unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; 53 | } 54 | 55 | 56 | /* latin */ 57 | 58 | @font-face { 59 | font-family: 'Source Code Pro'; 60 | font-style: normal; 61 | font-weight: 600; 62 | src: local('Source Code Pro Semibold'), local('SourceCodePro-Semibold'), url(https://fonts.gstatic.com/s/sourcecodepro/v6/leqv3v-yTsJNC7nFznSMqSOFnW3Jk0f09zW_Yln67Ac.woff2) format('woff2'); 63 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; 64 | } -------------------------------------------------------------------------------- /app/css/simple-hint.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-completions { 2 | position: absolute; 3 | z-index: 10; 4 | overflow: hidden; 5 | -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 6 | -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); 7 | box-shadow: 2px 3px 5px rgba(0,0,0,.2); 8 | } 9 | .CodeMirror-completions select { 10 | background: #fafafa; 11 | outline: none; 12 | border: none; 13 | padding: 0; 14 | margin: 0; 15 | font-family: monospace; 16 | } 17 | -------------------------------------------------------------------------------- /app/css/simplescrollbars.css: -------------------------------------------------------------------------------- 1 | .CodeMirror-simplescroll-horizontal div, .CodeMirror-simplescroll-vertical div { 2 | position: absolute; 3 | background: #ccc; 4 | -moz-box-sizing: border-box; 5 | box-sizing: border-box; 6 | border: 1px solid #bbb; 7 | border-radius: 2px; 8 | } 9 | 10 | .CodeMirror-simplescroll-horizontal, .CodeMirror-simplescroll-vertical { 11 | position: absolute; 12 | z-index: 6; 13 | background: #eee; 14 | } 15 | 16 | .CodeMirror-simplescroll-horizontal { 17 | bottom: 0; left: 0; 18 | height: 8px; 19 | } 20 | .CodeMirror-simplescroll-horizontal div { 21 | bottom: 0; 22 | height: 100%; 23 | } 24 | 25 | .CodeMirror-simplescroll-vertical { 26 | right: 0; top: 0; 27 | width: 8px; 28 | } 29 | .CodeMirror-simplescroll-vertical div { 30 | right: 0; 31 | width: 100%; 32 | } 33 | 34 | 35 | .CodeMirror-overlayscroll .CodeMirror-scrollbar-filler, .CodeMirror-overlayscroll .CodeMirror-gutter-filler { 36 | display: none; 37 | } 38 | 39 | .CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div { 40 | position: absolute; 41 | background: #bcd; 42 | border-radius: 3px; 43 | } 44 | 45 | .CodeMirror-overlayscroll-horizontal, .CodeMirror-overlayscroll-vertical { 46 | position: absolute; 47 | z-index: 6; 48 | } 49 | 50 | .CodeMirror-overlayscroll-horizontal { 51 | bottom: 0; left: 0; 52 | height: 6px; 53 | } 54 | .CodeMirror-overlayscroll-horizontal div { 55 | bottom: 0; 56 | height: 100%; 57 | } 58 | 59 | .CodeMirror-overlayscroll-vertical { 60 | right: 0; top: 0; 61 | width: 6px; 62 | } 63 | .CodeMirror-overlayscroll-vertical div { 64 | right: 0; 65 | width: 100%; 66 | } 67 | -------------------------------------------------------------------------------- /app/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | border: 0; 3 | font-size: 14px; 4 | height: 100vh; 5 | margin: 0; 6 | overflow: hidden; 7 | padding: 0; 8 | } 9 | 10 | 11 | /*Menubar styles begin here*/ 12 | 13 | #menu-bar { 14 | -webkit-app-region: drag; 15 | background-color: #101216; 16 | height: 32px; 17 | } 18 | 19 | #menu-bar a::selection { 20 | background: transparent; 21 | } 22 | 23 | #menu-items ul { 24 | -webkit-app-region: no-drag; 25 | float: left; 26 | list-style: none; 27 | margin: 0; 28 | padding: 0; 29 | z-index: 9999; 30 | } 31 | 32 | a.menu-item { 33 | color: #C0C5CE; 34 | } 35 | 36 | a.menu-item:hover { 37 | cursor: default; 38 | } 39 | 40 | #menu-items ul a { 41 | display: block; 42 | font-family: 'Segoe UI Semibold', Tahoma, Geneva, Verdana, sans-serif; 43 | font-size: 12px; 44 | line-height: 32px; 45 | outline: none; 46 | padding: 0 12px; 47 | text-decoration: none; 48 | } 49 | 50 | #menu-items ul li:hover { 51 | background: #EEEEEE; 52 | } 53 | 54 | ul { 55 | list-style-type: none; 56 | margin: 0; 57 | padding: 0; 58 | overflow: hidden; 59 | } 60 | 61 | ul:hover { 62 | cursor: default; 63 | } 64 | 65 | li a, 66 | .menu-item { 67 | display: inline-block; 68 | color: white; 69 | text-align: center; 70 | padding: 14px 16px; 71 | text-decoration: none; 72 | } 73 | 74 | li a:hover, 75 | .dropdown:hover .menu-item { 76 | background-color: #303238; 77 | } 78 | 79 | li.dropdown { 80 | display: inline-block; 81 | z-index: 9999; 82 | } 83 | 84 | .dropdown-content { 85 | z-index: 9999; 86 | display: none; 87 | position: absolute; 88 | background-color: #FDFDFD; 89 | border-radius: 0 0 2px 2px; 90 | min-width: 200px; 91 | box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); 92 | } 93 | 94 | .dropdown-content a { 95 | color: black; 96 | padding: 12px 16px; 97 | text-decoration: none; 98 | display: block; 99 | text-align: left; 100 | } 101 | 102 | .dropdown-content a:hover { 103 | background-color: #F1F1F1; 104 | } 105 | 106 | .dropdown:hover .dropdown-content { 107 | display: block; 108 | } 109 | 110 | .disabled { 111 | opacity: 0.4; 112 | } 113 | 114 | .shortcut { 115 | float: right; 116 | opacity: 0.5; 117 | } 118 | 119 | .status { 120 | border-radius: 200px; 121 | background: linear-gradient(#00D89F, #09E17B); 122 | opacity: 0; 123 | float: right; 124 | height: 10px; 125 | margin: 11px 2px; 126 | width: 10px; 127 | } 128 | 129 | .status-active { 130 | opacity: 1 131 | } 132 | 133 | #codepad-center { 134 | position: absolute; 135 | color: #EAEAEA; 136 | font-size: 12px; 137 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 138 | line-height: 32px; 139 | left: 50%; 140 | margin-right: -50%; 141 | transform: translateX(-50%); 142 | } 143 | 144 | #codepad-center::selection { 145 | background: transparent; 146 | } 147 | 148 | #window-control { 149 | float: right; 150 | height: 32px; 151 | margin-right: 10px; 152 | } 153 | 154 | #window-control::selection { 155 | background: transparent; 156 | } 157 | 158 | .buttons { 159 | -webkit-app-region: no-drag; 160 | border-radius: 200px; 161 | display: inline-block; 162 | height: 12px; 163 | margin: 10px 2px; 164 | transition: 0.08s ease-out; 165 | width: 12px; 166 | } 167 | 168 | .buttons:hover { 169 | border-radius: 200px; 170 | cursor: pointer; 171 | height: 16px; 172 | margin: 8px 0px; 173 | width: 16px; 174 | } 175 | 176 | #max-button { 177 | background: linear-gradient(#00D89F, #09E17B); 178 | } 179 | 180 | #min-button { 181 | background: linear-gradient(#FFE066, #F0C808); 182 | } 183 | 184 | #close-button { 185 | background: linear-gradient(#F88734, #FD6A51); 186 | } 187 | 188 | 189 | /* Menubar style ends */ 190 | 191 | 192 | /* editor: The container holding HTML/CSS/JS editors */ 193 | 194 | #editor { 195 | display: flex; 196 | left: 0; 197 | top: 0; 198 | width: 100%; 199 | border-bottom: 2px solid #121418; 200 | } 201 | 202 | .editor-item { 203 | border-right: 2px solid #121418; 204 | height: 50%; 205 | min-width: 33.28%; 206 | width: auto; 207 | } 208 | 209 | 210 | /* No border-right for JS editor*/ 211 | 212 | .editor-item:last-child { 213 | border-right: 0; 214 | } 215 | 216 | .editor-label { 217 | background-color: #232831; 218 | color: #EAEAEA; 219 | font-family: 'Segoe UI Semibold', Tahoma, Geneva, Verdana, sans-serif; 220 | height: 32px; 221 | line-height: 32px; 222 | text-align: center; 223 | /*padding-right: 5px;*/ 224 | } 225 | 226 | .editor-focus { 227 | box-shadow: inset 0 2px 0 0 #8A99BB; 228 | } 229 | 230 | .editor-label:hover { 231 | cursor: default; 232 | } 233 | 234 | .editor-label::selection { 235 | background: transparent; 236 | } 237 | 238 | .editor-item:hover { 239 | cursor: text; 240 | } 241 | 242 | 243 | /* result-viewport: The container holding iframe element */ 244 | 245 | #result-viewport { 246 | height: calc(100% - 384px); 247 | border: 0; 248 | margin: 0; 249 | background: white; 250 | width: 100%; 251 | top: 0; 252 | left: 0; 253 | z-index: 1; 254 | } 255 | 256 | 257 | /* output: iframe element*/ 258 | 259 | #output { 260 | border: 0; 261 | height: 100%; 262 | margin: 0; 263 | padding: 0; 264 | width: 100%; 265 | } -------------------------------------------------------------------------------- /app/editor.js: -------------------------------------------------------------------------------- 1 | // 1. Declarations and Node Modules Import 2 | const electron = require('electron') 3 | const path = require('path') 4 | const shell = electron.shell 5 | const remote = electron.remote 6 | const fs = require('fs') 7 | const { 8 | dialog, 9 | Menu, 10 | MenuItem 11 | } = remote 12 | const VIEW = require('./viewMenu') 13 | const HELP = require('./helpMenu') 14 | const SHORTCUT = require('./shortcuts') 15 | const WINDOW = require('./windowControl') 16 | const JSMenu = require('./scripts') 17 | const CSSMenu = require('./styles') 18 | const FILE = require('./fileMenu') 19 | 20 | var scripts = '' 21 | var styles = '' 22 | var currentEditor 23 | var editor = [] 24 | var output, html, css, js, editorLabels 25 | var saveFlag = false 26 | var styFlags = [0, 0, 0] 27 | var scrFlags = [0, 0, 0, 0, 0] 28 | 29 | var cssLib = [ 30 | ['animate.css', ""], 31 | ['bootstrap.min.css', ""], 32 | ['font-awesome.min.css', ""] 33 | ] 34 | 35 | var jsLib = [ 36 | ['jquery-3.1.1.min.js', ""], 37 | ['anime.min.js', ""], 38 | ['bootstrap.min.js', ""], 39 | ['p5.min.js', ""], 40 | ['three.min.js', ""] 41 | ] 42 | 43 | // 3. Main Functions for Electron 44 | onload = function () { 45 | WINDOW.initContextMenu() 46 | WINDOW.windowClicks() 47 | 48 | editor[0] = CodeMirror( 49 | document.getElementById('html-editor'), { 50 | mode: { 51 | name: 'htmlmixed' 52 | }, 53 | lineNumbers: true, 54 | lineWrapping: true, 55 | autofocus: true, 56 | indentWithTabs: false, 57 | indentUnit: 2, 58 | smartIndent: true, 59 | scrollbarStyle: 'overlay', 60 | theme: 'base16-ocean-dark' 61 | }) 62 | 63 | editor[1] = CodeMirror( 64 | document.getElementById('css-editor'), { 65 | mode: { 66 | name: 'css' 67 | }, 68 | lineNumbers: true, 69 | lineWrapping: true, 70 | indentWithTabs: false, 71 | indentUnit: 2, 72 | smartIndent: true, 73 | scrollbarStyle: 'overlay', 74 | theme: 'base16-ocean-dark' 75 | }) 76 | 77 | editor[2] = CodeMirror( 78 | document.getElementById('js-editor'), { 79 | mode: { 80 | name: 'javascript', 81 | json: true 82 | }, 83 | lineNumbers: true, 84 | lineWrapping: true, 85 | indentWithTabs: false, 86 | indentUnit: 2, 87 | smartIndent: true, 88 | scrollbarStyle: 'overlay', 89 | theme: 'base16-ocean-dark' 90 | }) 91 | 92 | html = editor[0] 93 | css = editor[1] 94 | js = editor[2] 95 | 96 | // Output refresh and editor focus 97 | output = document.getElementById('output') 98 | editorLabels = document.getElementsByClassName('editor-label') 99 | 100 | for (var k = 0; k < 3; k++) { 101 | editor[k].on('change', function (change) { 102 | paint() 103 | }) 104 | } 105 | 106 | html.on('focus', changeEditor) 107 | css.on('focus', changeEditor) 108 | js.on('focus', changeEditor) 109 | 110 | FILE.fileMenu() 111 | VIEW.viewMenu() 112 | HELP.helpMenu() 113 | JSMenu.addScript() 114 | CSSMenu.addStyle() 115 | SHORTCUT.shortcuts() 116 | FILE.newFile() 117 | onresize() 118 | } 119 | 120 | // 5. Editor Functions 121 | const getScr = () => scripts 122 | const getSty = () => styles 123 | const getCurrenEditor = () => { 124 | return currentEditor 125 | } 126 | 127 | function toggleEditors(editorI) { 128 | if (editorI === html) { 129 | css.focus() 130 | } 131 | if (editorI === css) { 132 | js.focus() 133 | } 134 | if (editorI === js) { 135 | html.focus() 136 | } 137 | } 138 | 139 | function removeFocus(editor) { 140 | for (var i = 0; i < editor.length; i++) { 141 | editor[i].classList.remove('editor-focus') 142 | } 143 | } 144 | // 6. Save the snippet functions 145 | function toggleStatus(i, span) { 146 | if (span[i].classList.contains('status-active')) { 147 | span[i].classList.remove('status-active') 148 | } else { 149 | span[i].classList.add('status-active') 150 | } 151 | } 152 | 153 | // Refresh on resize 154 | onresize = function () { 155 | for (var i = 0; i < 3; i++) { 156 | editor[i].refresh() 157 | } 158 | } 159 | 160 | function paint() { 161 | output.srcdoc = '' + '' + getSty() + '' + '' + '' + html.getValue() + getScr() + '' + '' + '' 162 | } 163 | 164 | function changeEditor(editor){ 165 | removeFocus(editorLabels) 166 | if(editor == html) 167 | { 168 | editorLabels[0].classList.add('editor-focus') 169 | currentEditor = html 170 | } 171 | if(editor == css) 172 | { 173 | editorLabels[1].classList.add('editor-focus') 174 | currentEditor = css 175 | } 176 | if(editor == js) 177 | { 178 | editorLabels[2].classList.add('editor-focus') 179 | currentEditor = js 180 | } 181 | } -------------------------------------------------------------------------------- /app/fileMenu.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | fileMenu, 3 | newFile, 4 | handleNew, 5 | handleSave, 6 | handleSaveAs 7 | } 8 | let saveFlag 9 | const getSavePath = () => savePath 10 | const setSavePath = (path) => { 11 | savePath = path 12 | } 13 | 14 | function fileMenu () { 15 | document.getElementById('save').addEventListener('click', handleSave) 16 | document.getElementById('save-as').addEventListener('click', handleSaveAs) 17 | document.getElementById('new').addEventListener('click', handleNew) 18 | document.getElementById('close-window').addEventListener('click', function (e) { 19 | window.close() 20 | }) 21 | } 22 | 23 | function handleNew (i) { 24 | if (false) { 25 | newFile() 26 | editor[i].setValue('') 27 | } else { 28 | window.open(path.join('file://', __dirname, '/index.html')) 29 | } 30 | } 31 | 32 | function handleSaveAs () { 33 | var path = dialog.showOpenDialog({ 34 | properties: ['openDirectory'] 35 | }) 36 | if (path !== undefined) { 37 | saveFlag = true 38 | setSavePath(path) 39 | handleSave() 40 | } 41 | } 42 | 43 | function handleSave () { 44 | if (saveFlag === true) { 45 | var path = getSavePath() 46 | // console.log('save path: ' + path) 47 | var htmlString = '\n' + '\n' + 'CodePad Save\n' + CSSMenu.getCssLibs() + '\n\n' + '\n' + '\n' + html.getValue() + JSMenu.getJsLibs() + '\n\n' + '\n' + '' 48 | // Write HTML 49 | fs.writeFile(path + '\\index.html', htmlString, (err) => { 50 | if (err) { 51 | console.error(err) 52 | } 53 | // console.log('success HTML') 54 | }) 55 | // Write CSS 56 | fs.writeFile(path + '\\style.css', css.getValue(), (err) => { 57 | if (err) { 58 | console.error(err) 59 | } 60 | // console.log('success CSS') 61 | }) 62 | // Write JS 63 | fs.writeFile(path + '\\script.js', js.getValue(), (err) => { 64 | if (err) { 65 | console.error(err) 66 | } 67 | // console.log('success JS') 68 | }) 69 | for (var j = 0; j < styFlags.length; j++) { 70 | if (styFlags[j] === 1) { 71 | fs.createReadStream('resources/app.asar/app/lib/' + cssLib[j][0]).pipe(fs.createWriteStream(path + '/' + cssLib[j][0])) 72 | if (j === 1) { 73 | fs.createReadStream('resources/app.asar/app/lib/glyphicons-halflings-regular.eot').pipe(fs.createWriteStream(path + '/glyphicons-halflings-regular.eot')) 74 | fs.createReadStream('resources/app.asar/app/lib/glyphicons-halflings-regular.ttf').pipe(fs.createWriteStream(path + '/glyphicons-halflings-regular.tff')) 75 | fs.createReadStream('resources/app.asar/app/lib/glyphicons-halflings-regular.woff').pipe(fs.createWriteStream(path + '/glyphicons-halflings-regular.woff')) 76 | fs.createReadStream('resources/app.asar/app/lib/glyphicons-halflings-regular.woff2').pipe(fs.createWriteStream(path + '/glyphicons-halflings-regular.woff2')) 77 | } 78 | if (j === 2) { 79 | fs.createReadStream('resources/app.asar/app/lib/fontawesome-webfont.ttf').pipe(fs.createWriteStream(path + '/fontawesome-webfont.ttf')) 80 | fs.createReadStream('resources/app.asar/app/lib/fontawesome-webfont.woff').pipe(fs.createWriteStream(path + '/fontawesome-webfont.woff')) 81 | fs.createReadStream('resources/app.asar/app/lib/fontawesome-webfont.woff2').pipe(fs.createWriteStream(path + '/fontawesome-webfont.woff2')) 82 | } 83 | } 84 | } 85 | for (var i = 0; i < scrFlags.length; i++) { 86 | if (scrFlags[i] === 1) { 87 | fs.createReadStream('resources/app.asar/app/lib/' + jsLib[i][0]).pipe(fs.createWriteStream(path + '/' + jsLib[i][0])) 88 | } 89 | } 90 | dialog.showMessageBox({ 91 | message: 'Saved to ' + path + '\\', 92 | buttons: ['OK'] 93 | }) 94 | } else { 95 | handleSaveAs() 96 | } 97 | } 98 | 99 | function newFile () { 100 | fileEntry = null 101 | hasWriteAccess = false 102 | } 103 | -------------------------------------------------------------------------------- /app/helpMenu.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | helpMenu 3 | } 4 | 5 | function helpMenu () { 6 | // Open links in the 'Help' menu in the default browser 7 | var helpa = document.getElementById('help-menu').getElementsByTagName('a') 8 | for (var i = 0; i < helpa.length; i++) { 9 | helpa[i].addEventListener('click', function (e) { 10 | e.preventDefault() 11 | shell.openExternal(this.href) 12 | }) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CodePad 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 89 |
90 |
91 |
HTML
92 |
93 |
94 |
CSS
95 |
96 |
97 |
JS
98 |
99 |
100 |
101 | 102 |
103 | 104 | 105 | -------------------------------------------------------------------------------- /app/js/closetag.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tag-closer extension for CodeMirror. 3 | * 4 | * This extension adds a "closeTag" utility function that can be used with key bindings to 5 | * insert a matching end tag after the ">" character of a start tag has been typed. It can 6 | * also complete " 17 | * Contributed under the same license terms as CodeMirror. 18 | */ 19 | (function() { 20 | /** Option that allows tag closing behavior to be toggled. Default is true. */ 21 | CodeMirror.defaults['closeTagEnabled'] = true; 22 | 23 | /** Array of tag names to add indentation after the start tag for. Default is the list of block-level html tags. */ 24 | CodeMirror.defaults['closeTagIndent'] = ['applet', 'blockquote', 'body', 'button', 'div', 'dl', 'fieldset', 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html', 'iframe', 'layer', 'legend', 'object', 'ol', 'p', 'select', 'table', 'ul']; 25 | 26 | /** 27 | * Call during key processing to close tags. Handles the key event if the tag is closed, otherwise throws CodeMirror.Pass. 28 | * - cm: The editor instance. 29 | * - ch: The character being processed. 30 | * - indent: Optional. Omit or pass true to use the default indentation tag list defined in the 'closeTagIndent' option. 31 | * Pass false to disable indentation. Pass an array to override the default list of tag names. 32 | */ 33 | CodeMirror.defineExtension("closeTag", function(cm, ch, indent) { 34 | if (!cm.getOption('closeTagEnabled')) { 35 | throw CodeMirror.Pass; 36 | } 37 | 38 | var mode = cm.getOption('mode'); 39 | 40 | if (mode == 'text/html') { 41 | 42 | /* 43 | * Relevant structure of token: 44 | * 45 | * htmlmixed 46 | * className 47 | * state 48 | * htmlState 49 | * type 50 | * context 51 | * tagName 52 | * mode 53 | * 54 | * xml 55 | * className 56 | * state 57 | * tagName 58 | * type 59 | */ 60 | 61 | var pos = cm.getCursor(); 62 | var tok = cm.getTokenAt(pos); 63 | var state = tok.state; 64 | 65 | if (state.mode && state.mode != 'html') { 66 | throw CodeMirror.Pass; // With htmlmixed, we only care about the html sub-mode. 67 | } 68 | 69 | if (ch == '>') { 70 | var type = state.htmlState ? state.htmlState.type : state.type; // htmlmixed : xml 71 | 72 | if (tok.className == 'tag' && type == 'closeTag') { 73 | throw CodeMirror.Pass; // Don't process the '>' at the end of an end-tag. 74 | } 75 | 76 | cm.replaceSelection('>'); // Mode state won't update until we finish the tag. 77 | pos = {line: pos.line, ch: pos.ch + 1}; 78 | cm.setCursor(pos); 79 | 80 | tok = cm.getTokenAt(cm.getCursor()); 81 | state = tok.state; 82 | type = state.htmlState ? state.htmlState.type : state.type; // htmlmixed : xml 83 | 84 | if (tok.className == 'tag' && type != 'selfcloseTag') { 85 | var tagName = state.htmlState ? state.htmlState.context.tagName : state.tagName; // htmlmixed : xml 86 | if (tagName.length > 0) { 87 | insertEndTag(cm, indent, pos, tagName); 88 | } 89 | return; 90 | } 91 | 92 | // Undo the '>' insert and allow cm to handle the key instead. 93 | cm.setSelection({line: pos.line, ch: pos.ch - 1}, pos); 94 | cm.replaceSelection(""); 95 | 96 | } else if (ch == '/') { 97 | if (tok.className == 'tag' && tok.string == '<') { 98 | var tagName = state.htmlState ? (state.htmlState.context ? state.htmlState.context.tagName : '') : state.context.tagName; // htmlmixed : xml # extra htmlmized check is for ' 0) { 100 | completeEndTag(cm, pos, tagName); 101 | return; 102 | } 103 | } 104 | } 105 | 106 | } 107 | 108 | throw CodeMirror.Pass; // Bubble if not handled 109 | }); 110 | 111 | function insertEndTag(cm, indent, pos, tagName) { 112 | if (shouldIndent(cm, indent, tagName)) { 113 | cm.replaceSelection('\n\n', 'end'); 114 | cm.indentLine(pos.line + 1); 115 | cm.indentLine(pos.line + 2); 116 | cm.setCursor({line: pos.line + 1, ch: cm.getLine(pos.line + 1).length}); 117 | } else { 118 | cm.replaceSelection(''); 119 | cm.setCursor(pos); 120 | } 121 | } 122 | 123 | function shouldIndent(cm, indent, tagName) { 124 | if (typeof indent == 'undefined' || indent == null || indent == true) { 125 | indent = cm.getOption('closeTagIndent'); 126 | } 127 | if (!indent) { 128 | indent = []; 129 | } 130 | return indexOf(indent, tagName.toLowerCase()) != -1; 131 | } 132 | 133 | // C&P from codemirror.js...would be nice if this were visible to utilities. 134 | function indexOf(collection, elt) { 135 | if (collection.indexOf) return collection.indexOf(elt); 136 | for (var i = 0, e = collection.length; i < e; ++i) 137 | if (collection[i] == elt) return i; 138 | return -1; 139 | } 140 | 141 | function completeEndTag(cm, pos, tagName) { 142 | cm.replaceSelection('/' + tagName + '>'); 143 | cm.setCursor({line: pos.line, ch: pos.ch + tagName.length + 2 }); 144 | } 145 | 146 | })(); 147 | -------------------------------------------------------------------------------- /app/js/dialog.js: -------------------------------------------------------------------------------- 1 | // Open simple dialogs on top of an editor. Relies on dialog.css. 2 | 3 | (function() { 4 | function dialogDiv(cm, template) { 5 | var wrap = cm.getWrapperElement(); 6 | var dialog = wrap.insertBefore(document.createElement("div"), wrap.firstChild); 7 | dialog.className = "CodeMirror-dialog"; 8 | dialog.innerHTML = '
' + template + '
'; 9 | return dialog; 10 | } 11 | 12 | CodeMirror.defineExtension("openDialog", function(template, callback) { 13 | var dialog = dialogDiv(this, template); 14 | var closed = false, me = this; 15 | function close() { 16 | if (closed) return; 17 | closed = true; 18 | dialog.parentNode.removeChild(dialog); 19 | } 20 | var inp = dialog.getElementsByTagName("input")[0]; 21 | if (inp) { 22 | CodeMirror.connect(inp, "keydown", function(e) { 23 | if (e.keyCode == 13 || e.keyCode == 27) { 24 | CodeMirror.e_stop(e); 25 | close(); 26 | me.focus(); 27 | if (e.keyCode == 13) callback(inp.value); 28 | } 29 | }); 30 | inp.focus(); 31 | CodeMirror.connect(inp, "blur", close); 32 | } 33 | return close; 34 | }); 35 | 36 | CodeMirror.defineExtension("openConfirm", function(template, callbacks) { 37 | var dialog = dialogDiv(this, template); 38 | var buttons = dialog.getElementsByTagName("button"); 39 | var closed = false, me = this, blurring = 1; 40 | function close() { 41 | if (closed) return; 42 | closed = true; 43 | dialog.parentNode.removeChild(dialog); 44 | me.focus(); 45 | } 46 | buttons[0].focus(); 47 | for (var i = 0; i < buttons.length; ++i) { 48 | var b = buttons[i]; 49 | (function(callback) { 50 | CodeMirror.connect(b, "click", function(e) { 51 | CodeMirror.e_preventDefault(e); 52 | close(); 53 | if (callback) callback(me); 54 | }); 55 | })(callbacks[i]); 56 | CodeMirror.connect(b, "blur", function() { 57 | --blurring; 58 | setTimeout(function() { if (blurring <= 0) close(); }, 200); 59 | }); 60 | CodeMirror.connect(b, "focus", function() { ++blurring; }); 61 | } 62 | }); 63 | })(); 64 | -------------------------------------------------------------------------------- /app/js/foldcode.js: -------------------------------------------------------------------------------- 1 | // the tagRangeFinder function is 2 | // Copyright (C) 2011 by Daniel Glazman 3 | // released under the MIT license (../../LICENSE) like the rest of CodeMirror 4 | CodeMirror.tagRangeFinder = function(cm, line, hideEnd) { 5 | var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD"; 6 | var nameChar = nameStartChar + "\-\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; 7 | var xmlNAMERegExp = new RegExp("^[" + nameStartChar + "][" + nameChar + "]*"); 8 | 9 | var lineText = cm.getLine(line); 10 | var found = false; 11 | var tag = null; 12 | var pos = 0; 13 | while (!found) { 14 | pos = lineText.indexOf("<", pos); 15 | if (-1 == pos) // no tag on line 16 | return; 17 | if (pos + 1 < lineText.length && lineText[pos + 1] == "/") { // closing tag 18 | pos++; 19 | continue; 20 | } 21 | // ok we weem to have a start tag 22 | if (!lineText.substr(pos + 1).match(xmlNAMERegExp)) { // not a tag name... 23 | pos++; 24 | continue; 25 | } 26 | var gtPos = lineText.indexOf(">", pos + 1); 27 | if (-1 == gtPos) { // end of start tag not in line 28 | var l = line + 1; 29 | var foundGt = false; 30 | var lastLine = cm.lineCount(); 31 | while (l < lastLine && !foundGt) { 32 | var lt = cm.getLine(l); 33 | var gt = lt.indexOf(">"); 34 | if (-1 != gt) { // found a > 35 | foundGt = true; 36 | var slash = lt.lastIndexOf("/", gt); 37 | if (-1 != slash && slash < gt) { 38 | var str = lineText.substr(slash, gt - slash + 1); 39 | if (!str.match( /\/\s*\>/ )) { // yep, that's the end of empty tag 40 | if (hideEnd === true) l++; 41 | return l; 42 | } 43 | } 44 | } 45 | l++; 46 | } 47 | found = true; 48 | } 49 | else { 50 | var slashPos = lineText.lastIndexOf("/", gtPos); 51 | if (-1 == slashPos) { // cannot be empty tag 52 | found = true; 53 | // don't continue 54 | } 55 | else { // empty tag? 56 | // check if really empty tag 57 | var str = lineText.substr(slashPos, gtPos - slashPos + 1); 58 | if (!str.match( /\/\s*\>/ )) { // finally not empty 59 | found = true; 60 | // don't continue 61 | } 62 | } 63 | } 64 | if (found) { 65 | var subLine = lineText.substr(pos + 1); 66 | tag = subLine.match(xmlNAMERegExp); 67 | if (tag) { 68 | // we have an element name, wooohooo ! 69 | tag = tag[0]; 70 | // do we have the close tag on same line ??? 71 | if (-1 != lineText.indexOf("", pos)) // yep 72 | { 73 | found = false; 74 | } 75 | // we don't, so we have a candidate... 76 | } 77 | else 78 | found = false; 79 | } 80 | if (!found) 81 | pos++; 82 | } 83 | 84 | if (found) { 85 | var startTag = "(\\<\\/" + tag + "\\>)|(\\<" + tag + "\\>)|(\\<" + tag + "\\s)|(\\<" + tag + "$)"; 86 | var startTagRegExp = new RegExp(startTag, "g"); 87 | var endTag = ""; 88 | var depth = 1; 89 | var l = line + 1; 90 | var lastLine = cm.lineCount(); 91 | while (l < lastLine) { 92 | lineText = cm.getLine(l); 93 | var match = lineText.match(startTagRegExp); 94 | if (match) { 95 | for (var i = 0; i < match.length; i++) { 96 | if (match[i] == endTag) 97 | depth--; 98 | else 99 | depth++; 100 | if (!depth) { 101 | if (hideEnd === true) l++; 102 | return l; 103 | } 104 | } 105 | } 106 | l++; 107 | } 108 | return; 109 | } 110 | }; 111 | 112 | CodeMirror.braceRangeFinder = function(cm, line, hideEnd) { 113 | var lineText = cm.getLine(line); 114 | var startChar = lineText.lastIndexOf("{"); 115 | if (startChar < 0 || lineText.lastIndexOf("}") > startChar) return; 116 | var tokenType = cm.getTokenAt({line: line, ch: startChar}).className; 117 | var count = 1, lastLine = cm.lineCount(), end; 118 | outer: for (var i = line + 1; i < lastLine; ++i) { 119 | var text = cm.getLine(i), pos = 0; 120 | for (;;) { 121 | var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos); 122 | if (nextOpen < 0) nextOpen = text.length; 123 | if (nextClose < 0) nextClose = text.length; 124 | pos = Math.min(nextOpen, nextClose); 125 | if (pos == text.length) break; 126 | if (cm.getTokenAt({line: i, ch: pos + 1}).className == tokenType) { 127 | if (pos == nextOpen) ++count; 128 | else if (!--count) { end = i; break outer; } 129 | } 130 | ++pos; 131 | } 132 | } 133 | if (end == null || end == line + 1) return; 134 | if (hideEnd === true) end++; 135 | return end; 136 | }; 137 | 138 | CodeMirror.indentRangeFinder = function(cm, line) { 139 | var tabSize = cm.getOption("tabSize"); 140 | var myIndent = cm.getLineHandle(line).indentation(tabSize), last; 141 | for (var i = line + 1, end = cm.lineCount(); i < end; ++i) { 142 | var handle = cm.getLineHandle(i); 143 | if (!/^\s*$/.test(handle.text)) { 144 | if (handle.indentation(tabSize) <= myIndent) break; 145 | last = i; 146 | } 147 | } 148 | if (!last) return null; 149 | return last + 1; 150 | }; 151 | 152 | CodeMirror.newFoldFunction = function(rangeFinder, markText, hideEnd) { 153 | var folded = []; 154 | if (markText == null) markText = '
%N%'; 155 | 156 | function isFolded(cm, n) { 157 | for (var i = 0; i < folded.length; ++i) { 158 | var start = cm.lineInfo(folded[i].start); 159 | if (!start) folded.splice(i--, 1); 160 | else if (start.line == n) return {pos: i, region: folded[i]}; 161 | } 162 | } 163 | 164 | function expand(cm, region) { 165 | cm.clearMarker(region.start); 166 | for (var i = 0; i < region.hidden.length; ++i) 167 | cm.showLine(region.hidden[i]); 168 | } 169 | 170 | return function(cm, line) { 171 | cm.operation(function() { 172 | var known = isFolded(cm, line); 173 | if (known) { 174 | folded.splice(known.pos, 1); 175 | expand(cm, known.region); 176 | } else { 177 | var end = rangeFinder(cm, line, hideEnd); 178 | if (end == null) return; 179 | var hidden = []; 180 | for (var i = line + 1; i < end; ++i) { 181 | var handle = cm.hideLine(i); 182 | if (handle) hidden.push(handle); 183 | } 184 | var first = cm.setMarker(line, markText); 185 | var region = {start: first, hidden: hidden}; 186 | cm.onDeleteLine(first, function() { expand(cm, region); }); 187 | folded.push(region); 188 | } 189 | }); 190 | }; 191 | }; 192 | -------------------------------------------------------------------------------- /app/js/formatting.js: -------------------------------------------------------------------------------- 1 | // ============== Formatting extensions ============================ 2 | // A common storage for all mode-specific formatting features 3 | if (!CodeMirror.modeExtensions) CodeMirror.modeExtensions = {}; 4 | 5 | // Returns the extension of the editor's current mode 6 | CodeMirror.defineExtension("getModeExt", function () { 7 | var mname = CodeMirror.resolveMode(this.getOption("mode")).name; 8 | var ext = CodeMirror.modeExtensions[mname]; 9 | if (!ext) throw new Error("No extensions found for mode " + mname); 10 | return ext; 11 | }); 12 | 13 | // If the current mode is 'htmlmixed', returns the extension of a mode located at 14 | // the specified position (can be htmlmixed, css or javascript). Otherwise, simply 15 | // returns the extension of the editor's current mode. 16 | CodeMirror.defineExtension("getModeExtAtPos", function (pos) { 17 | var token = this.getTokenAt(pos); 18 | if (token && token.state && token.state.mode) 19 | return CodeMirror.modeExtensions[token.state.mode == "html" ? "htmlmixed" : token.state.mode]; 20 | else 21 | return this.getModeExt(); 22 | }); 23 | 24 | // Comment/uncomment the specified range 25 | CodeMirror.defineExtension("commentRange", function (isComment, from, to) { 26 | var curMode = this.getModeExtAtPos(this.getCursor()); 27 | if (isComment) { // Comment range 28 | var commentedText = this.getRange(from, to); 29 | this.replaceRange(curMode.commentStart + this.getRange(from, to) + curMode.commentEnd 30 | , from, to); 31 | if (from.line == to.line && from.ch == to.ch) { // An empty comment inserted - put cursor inside 32 | this.setCursor(from.line, from.ch + curMode.commentStart.length); 33 | } 34 | } 35 | else { // Uncomment range 36 | var selText = this.getRange(from, to); 37 | var startIndex = selText.indexOf(curMode.commentStart); 38 | var endIndex = selText.lastIndexOf(curMode.commentEnd); 39 | if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) { 40 | // Take string till comment start 41 | selText = selText.substr(0, startIndex) 42 | // From comment start till comment end 43 | + selText.substring(startIndex + curMode.commentStart.length, endIndex) 44 | // From comment end till string end 45 | + selText.substr(endIndex + curMode.commentEnd.length); 46 | } 47 | this.replaceRange(selText, from, to); 48 | } 49 | }); 50 | 51 | // Applies automatic mode-aware indentation to the specified range 52 | CodeMirror.defineExtension("autoIndentRange", function (from, to) { 53 | var cmInstance = this; 54 | this.operation(function () { 55 | for (var i = from.line; i <= to.line; i++) { 56 | cmInstance.indentLine(i, "smart"); 57 | } 58 | }); 59 | }); 60 | 61 | // Applies automatic formatting to the specified range 62 | CodeMirror.defineExtension("autoFormatRange", function (from, to) { 63 | var absStart = this.indexFromPos(from); 64 | var absEnd = this.indexFromPos(to); 65 | // Insert additional line breaks where necessary according to the 66 | // mode's syntax 67 | var res = this.getModeExt().autoFormatLineBreaks(this.getValue(), absStart, absEnd); 68 | var cmInstance = this; 69 | 70 | // Replace and auto-indent the range 71 | this.operation(function () { 72 | cmInstance.replaceRange(res, from, to); 73 | var startLine = cmInstance.posFromIndex(absStart).line; 74 | var endLine = cmInstance.posFromIndex(absStart + res.length).line; 75 | for (var i = startLine; i <= endLine; i++) { 76 | cmInstance.indentLine(i, "smart"); 77 | } 78 | }); 79 | }); 80 | 81 | // Define extensions for a few modes 82 | 83 | CodeMirror.modeExtensions["css"] = { 84 | commentStart: "/*", 85 | commentEnd: "*/", 86 | wordWrapChars: [";", "\\{", "\\}"], 87 | autoFormatLineBreaks: function (text) { 88 | return text.replace(new RegExp("(;|\\{|\\})([^\r\n])", "g"), "$1\n$2"); 89 | } 90 | }; 91 | 92 | CodeMirror.modeExtensions["javascript"] = { 93 | commentStart: "/*", 94 | commentEnd: "*/", 95 | wordWrapChars: [";", "\\{", "\\}"], 96 | 97 | getNonBreakableBlocks: function (text) { 98 | var nonBreakableRegexes = [ 99 | new RegExp("for\\s*?\\(([\\s\\S]*?)\\)"), 100 | new RegExp("'([\\s\\S]*?)('|$)"), 101 | new RegExp("\"([\\s\\S]*?)(\"|$)"), 102 | new RegExp("//.*([\r\n]|$)") 103 | ]; 104 | var nonBreakableBlocks = new Array(); 105 | for (var i = 0; i < nonBreakableRegexes.length; i++) { 106 | var curPos = 0; 107 | while (curPos < text.length) { 108 | var m = text.substr(curPos).match(nonBreakableRegexes[i]); 109 | if (m != null) { 110 | nonBreakableBlocks.push({ 111 | start: curPos + m.index, 112 | end: curPos + m.index + m[0].length 113 | }); 114 | curPos += m.index + Math.max(1, m[0].length); 115 | } 116 | else { // No more matches 117 | break; 118 | } 119 | } 120 | } 121 | nonBreakableBlocks.sort(function (a, b) { 122 | return a.start - b.start; 123 | }); 124 | 125 | return nonBreakableBlocks; 126 | }, 127 | 128 | autoFormatLineBreaks: function (text) { 129 | var curPos = 0; 130 | var reLinesSplitter = new RegExp("(;|\\{|\\})([^\r\n])", "g"); 131 | var nonBreakableBlocks = this.getNonBreakableBlocks(text); 132 | if (nonBreakableBlocks != null) { 133 | var res = ""; 134 | for (var i = 0; i < nonBreakableBlocks.length; i++) { 135 | if (nonBreakableBlocks[i].start > curPos) { // Break lines till the block 136 | res += text.substring(curPos, nonBreakableBlocks[i].start).replace(reLinesSplitter, "$1\n$2"); 137 | curPos = nonBreakableBlocks[i].start; 138 | } 139 | if (nonBreakableBlocks[i].start <= curPos 140 | && nonBreakableBlocks[i].end >= curPos) { // Skip non-breakable block 141 | res += text.substring(curPos, nonBreakableBlocks[i].end); 142 | curPos = nonBreakableBlocks[i].end; 143 | } 144 | } 145 | if (curPos < text.length - 1) { 146 | res += text.substr(curPos).replace(reLinesSplitter, "$1\n$2"); 147 | } 148 | return res; 149 | } 150 | else { 151 | return text.replace(reLinesSplitter, "$1\n$2"); 152 | } 153 | } 154 | }; 155 | 156 | CodeMirror.modeExtensions["xml"] = { 157 | commentStart: "", 159 | wordWrapChars: [">"], 160 | 161 | autoFormatLineBreaks: function (text) { 162 | var lines = text.split("\n"); 163 | var reProcessedPortion = new RegExp("(^\\s*?<|^[^<]*?)(.+)(>\\s*?$|[^>]*?$)"); 164 | var reOpenBrackets = new RegExp("<", "g"); 165 | var reCloseBrackets = new RegExp("(>)([^\r\n])", "g"); 166 | for (var i = 0; i < lines.length; i++) { 167 | var mToProcess = lines[i].match(reProcessedPortion); 168 | if (mToProcess != null && mToProcess.length > 3) { // The line starts with whitespaces and ends with whitespaces 169 | lines[i] = mToProcess[1] 170 | + mToProcess[2].replace(reOpenBrackets, "\n$&").replace(reCloseBrackets, "$1\n$2") 171 | + mToProcess[3]; 172 | continue; 173 | } 174 | } 175 | 176 | return lines.join("\n"); 177 | } 178 | }; 179 | 180 | CodeMirror.modeExtensions["htmlmixed"] = { 181 | commentStart: "", 183 | wordWrapChars: [">", ";", "\\{", "\\}"], 184 | 185 | getModeInfos: function (text, absPos) { 186 | var modeInfos = new Array(); 187 | modeInfos[0] = 188 | { 189 | pos: 0, 190 | modeExt: CodeMirror.modeExtensions["xml"], 191 | modeName: "xml" 192 | }; 193 | 194 | var modeMatchers = new Array(); 195 | modeMatchers[0] = 196 | { 197 | regex: new RegExp("]*>([\\s\\S]*?)(]*>|$)", "i"), 198 | modeExt: CodeMirror.modeExtensions["css"], 199 | modeName: "css" 200 | }; 201 | modeMatchers[1] = 202 | { 203 | regex: new RegExp("]*>([\\s\\S]*?)(]*>|$)", "i"), 204 | modeExt: CodeMirror.modeExtensions["javascript"], 205 | modeName: "javascript" 206 | }; 207 | 208 | var lastCharPos = (typeof (absPos) !== "undefined" ? absPos : text.length - 1); 209 | // Detect modes for the entire text 210 | for (var i = 0; i < modeMatchers.length; i++) { 211 | var curPos = 0; 212 | while (curPos <= lastCharPos) { 213 | var m = text.substr(curPos).match(modeMatchers[i].regex); 214 | if (m != null) { 215 | if (m.length > 1 && m[1].length > 0) { 216 | // Push block begin pos 217 | var blockBegin = curPos + m.index + m[0].indexOf(m[1]); 218 | modeInfos.push( 219 | { 220 | pos: blockBegin, 221 | modeExt: modeMatchers[i].modeExt, 222 | modeName: modeMatchers[i].modeName 223 | }); 224 | // Push block end pos 225 | modeInfos.push( 226 | { 227 | pos: blockBegin + m[1].length, 228 | modeExt: modeInfos[0].modeExt, 229 | modeName: modeInfos[0].modeName 230 | }); 231 | curPos += m.index + m[0].length; 232 | continue; 233 | } 234 | else { 235 | curPos += m.index + Math.max(m[0].length, 1); 236 | } 237 | } 238 | else { // No more matches 239 | break; 240 | } 241 | } 242 | } 243 | // Sort mode infos 244 | modeInfos.sort(function sortModeInfo(a, b) { 245 | return a.pos - b.pos; 246 | }); 247 | 248 | return modeInfos; 249 | }, 250 | 251 | autoFormatLineBreaks: function (text, startPos, endPos) { 252 | var modeInfos = this.getModeInfos(text); 253 | var reBlockStartsWithNewline = new RegExp("^\\s*?\n"); 254 | var reBlockEndsWithNewline = new RegExp("\n\\s*?$"); 255 | var res = ""; 256 | // Use modes info to break lines correspondingly 257 | if (modeInfos.length > 1) { // Deal with multi-mode text 258 | for (var i = 1; i <= modeInfos.length; i++) { 259 | var selStart = modeInfos[i - 1].pos; 260 | var selEnd = (i < modeInfos.length ? modeInfos[i].pos : endPos); 261 | 262 | if (selStart >= endPos) { // The block starts later than the needed fragment 263 | break; 264 | } 265 | if (selStart < startPos) { 266 | if (selEnd <= startPos) { // The block starts earlier than the needed fragment 267 | continue; 268 | } 269 | selStart = startPos; 270 | } 271 | if (selEnd > endPos) { 272 | selEnd = endPos; 273 | } 274 | var textPortion = text.substring(selStart, selEnd); 275 | if (modeInfos[i - 1].modeName != "xml") { // Starting a CSS or JavaScript block 276 | if (!reBlockStartsWithNewline.test(textPortion) 277 | && selStart > 0) { // The block does not start with a line break 278 | textPortion = "\n" + textPortion; 279 | } 280 | if (!reBlockEndsWithNewline.test(textPortion) 281 | && selEnd < text.length - 1) { // The block does not end with a line break 282 | textPortion += "\n"; 283 | } 284 | } 285 | res += modeInfos[i - 1].modeExt.autoFormatLineBreaks(textPortion); 286 | } 287 | } 288 | else { // Single-mode text 289 | res = modeInfos[0].modeExt.autoFormatLineBreaks(text.substring(startPos, endPos)); 290 | } 291 | 292 | return res; 293 | } 294 | }; 295 | -------------------------------------------------------------------------------- /app/js/htmlmixed.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var defaultTags = { 15 | script: [ 16 | ["lang", /(javascript|babel)/i, "javascript"], 17 | ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i, "javascript"], 18 | ["type", /./, "text/plain"], 19 | [null, null, "javascript"] 20 | ], 21 | style: [ 22 | ["lang", /^css$/i, "css"], 23 | ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"], 24 | ["type", /./, "text/plain"], 25 | [null, null, "css"] 26 | ] 27 | }; 28 | 29 | function maybeBackup(stream, pat, style) { 30 | var cur = stream.current(), close = cur.search(pat); 31 | if (close > -1) { 32 | stream.backUp(cur.length - close); 33 | } else if (cur.match(/<\/?$/)) { 34 | stream.backUp(cur.length); 35 | if (!stream.match(pat, false)) stream.match(cur); 36 | } 37 | return style; 38 | } 39 | 40 | var attrRegexpCache = {}; 41 | function getAttrRegexp(attr) { 42 | var regexp = attrRegexpCache[attr]; 43 | if (regexp) return regexp; 44 | return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*"); 45 | } 46 | 47 | function getAttrValue(text, attr) { 48 | var match = text.match(getAttrRegexp(attr)) 49 | return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : "" 50 | } 51 | 52 | function getTagRegexp(tagName, anchored) { 53 | return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i"); 54 | } 55 | 56 | function addTags(from, to) { 57 | for (var tag in from) { 58 | var dest = to[tag] || (to[tag] = []); 59 | var source = from[tag]; 60 | for (var i = source.length - 1; i >= 0; i--) 61 | dest.unshift(source[i]) 62 | } 63 | } 64 | 65 | function findMatchingMode(tagInfo, tagText) { 66 | for (var i = 0; i < tagInfo.length; i++) { 67 | var spec = tagInfo[i]; 68 | if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2]; 69 | } 70 | } 71 | 72 | CodeMirror.defineMode("htmlmixed", function (config, parserConfig) { 73 | var htmlMode = CodeMirror.getMode(config, { 74 | name: "xml", 75 | htmlMode: true, 76 | multilineTagIndentFactor: parserConfig.multilineTagIndentFactor, 77 | multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag 78 | }); 79 | 80 | var tags = {}; 81 | var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes; 82 | addTags(defaultTags, tags); 83 | if (configTags) addTags(configTags, tags); 84 | if (configScript) for (var i = configScript.length - 1; i >= 0; i--) 85 | tags.script.unshift(["type", configScript[i].matches, configScript[i].mode]) 86 | 87 | function html(stream, state) { 88 | var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName 89 | if (tag && !/[<>\s\/]/.test(stream.current()) && 90 | (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) && 91 | tags.hasOwnProperty(tagName)) { 92 | state.inTag = tagName + " " 93 | } else if (state.inTag && tag && />$/.test(stream.current())) { 94 | var inTag = /^([\S]+) (.*)/.exec(state.inTag) 95 | state.inTag = null 96 | var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2]) 97 | var mode = CodeMirror.getMode(config, modeSpec) 98 | var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false); 99 | state.token = function (stream, state) { 100 | if (stream.match(endTagA, false)) { 101 | state.token = html; 102 | state.localState = state.localMode = null; 103 | return null; 104 | } 105 | return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState)); 106 | }; 107 | state.localMode = mode; 108 | state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "")); 109 | } else if (state.inTag) { 110 | state.inTag += stream.current() 111 | if (stream.eol()) state.inTag += " " 112 | } 113 | return style; 114 | }; 115 | 116 | return { 117 | startState: function () { 118 | var state = CodeMirror.startState(htmlMode); 119 | return {token: html, inTag: null, localMode: null, localState: null, htmlState: state}; 120 | }, 121 | 122 | copyState: function (state) { 123 | var local; 124 | if (state.localState) { 125 | local = CodeMirror.copyState(state.localMode, state.localState); 126 | } 127 | return {token: state.token, inTag: state.inTag, 128 | localMode: state.localMode, localState: local, 129 | htmlState: CodeMirror.copyState(htmlMode, state.htmlState)}; 130 | }, 131 | 132 | token: function (stream, state) { 133 | return state.token(stream, state); 134 | }, 135 | 136 | indent: function (state, textAfter) { 137 | if (!state.localMode || /^\s*<\//.test(textAfter)) 138 | return htmlMode.indent(state.htmlState, textAfter); 139 | else if (state.localMode.indent) 140 | return state.localMode.indent(state.localState, textAfter); 141 | else 142 | return CodeMirror.Pass; 143 | }, 144 | 145 | innerMode: function (state) { 146 | return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode}; 147 | } 148 | }; 149 | }, "xml", "javascript", "css"); 150 | 151 | CodeMirror.defineMIME("text/html", "htmlmixed"); 152 | }); 153 | -------------------------------------------------------------------------------- /app/js/javascript-hint.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | function forEach(arr, f) { 3 | for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); 4 | } 5 | 6 | function arrayContains(arr, item) { 7 | if (!Array.prototype.indexOf) { 8 | var i = arr.length; 9 | while (i--) { 10 | if (arr[i] === item) { 11 | return true; 12 | } 13 | } 14 | return false; 15 | } 16 | return arr.indexOf(item) != -1; 17 | } 18 | 19 | function scriptHint(editor, keywords, getToken) { 20 | // Find the token at the cursor 21 | var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token; 22 | // If it's not a 'word-style' token, ignore the token. 23 | if (!/^[\w$_]*$/.test(token.string)) { 24 | token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state, 25 | className: token.string == "." ? "property" : null}; 26 | } 27 | // If it is a property, find out what it is a property of. 28 | while (tprop.className == "property") { 29 | tprop = getToken(editor, {line: cur.line, ch: tprop.start}); 30 | if (tprop.string != ".") return; 31 | tprop = getToken(editor, {line: cur.line, ch: tprop.start}); 32 | if (tprop.string == ')') { 33 | var level = 1; 34 | do { 35 | tprop = getToken(editor, {line: cur.line, ch: tprop.start}); 36 | switch (tprop.string) { 37 | case ')': level++; break; 38 | case '(': level--; break; 39 | default: break; 40 | } 41 | } while (level > 0) 42 | tprop = getToken(editor, {line: cur.line, ch: tprop.start}); 43 | if (tprop.className == 'variable') 44 | tprop.className = 'function'; 45 | else return; // no clue 46 | } 47 | if (!context) var context = []; 48 | context.push(tprop); 49 | } 50 | return {list: getCompletions(token, context, keywords), 51 | from: {line: cur.line, ch: token.start}, 52 | to: {line: cur.line, ch: token.end}}; 53 | } 54 | 55 | CodeMirror.javascriptHint = function(editor) { 56 | return scriptHint(editor, javascriptKeywords, 57 | function (e, cur) {return e.getTokenAt(cur);}); 58 | } 59 | 60 | function getCoffeeScriptToken(editor, cur) { 61 | // This getToken, it is for coffeescript, imitates the behavior of 62 | // getTokenAt method in javascript.js, that is, returning "property" 63 | // type and treat "." as indepenent token. 64 | var token = editor.getTokenAt(cur); 65 | if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') { 66 | token.end = token.start; 67 | token.string = '.'; 68 | token.className = "property"; 69 | } 70 | else if (/^\.[\w$_]*$/.test(token.string)) { 71 | token.className = "property"; 72 | token.start++; 73 | token.string = token.string.replace(/\./, ''); 74 | } 75 | return token; 76 | } 77 | 78 | CodeMirror.coffeescriptHint = function(editor) { 79 | return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken); 80 | } 81 | 82 | var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " + 83 | "toUpperCase toLowerCase split concat match replace search").split(" "); 84 | var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " + 85 | "lastIndexOf every some filter forEach map reduce reduceRight ").split(" "); 86 | var funcProps = "prototype apply call bind".split(" "); 87 | var javascriptKeywords = ("break case catch continue debugger default delete do else false finally for function " + 88 | "if in instanceof new null return switch throw true try typeof var void while with").split(" "); 89 | var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " + 90 | "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" "); 91 | 92 | function getCompletions(token, context, keywords) { 93 | var found = [], start = token.string; 94 | function maybeAdd(str) { 95 | if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); 96 | } 97 | function gatherCompletions(obj) { 98 | if (typeof obj == "string") forEach(stringProps, maybeAdd); 99 | else if (obj instanceof Array) forEach(arrayProps, maybeAdd); 100 | else if (obj instanceof Function) forEach(funcProps, maybeAdd); 101 | for (var name in obj) maybeAdd(name); 102 | } 103 | 104 | if (context) { 105 | // If this is a property, see if it belongs to some object we can 106 | // find in the current environment. 107 | var obj = context.pop(), base; 108 | if (obj.className == "variable") 109 | base = window[obj.string]; 110 | else if (obj.className == "string") 111 | base = ""; 112 | else if (obj.className == "atom") 113 | base = 1; 114 | else if (obj.className == "function") { 115 | if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') && 116 | (typeof window.jQuery == 'function')) 117 | base = window.jQuery(); 118 | else if (window._ != null && (obj.string == '_') && (typeof window._ == 'function')) 119 | base = window._(); 120 | } 121 | while (base != null && context.length) 122 | base = base[context.pop().string]; 123 | if (base != null) gatherCompletions(base); 124 | } 125 | else { 126 | // If not, just look in the window object and any local scope 127 | // (reading into JS mode internals to get at the local variables) 128 | for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name); 129 | gatherCompletions(window); 130 | forEach(keywords, maybeAdd); 131 | } 132 | return found; 133 | } 134 | })(); 135 | -------------------------------------------------------------------------------- /app/js/javascript.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | function expressionAllowed(stream, state, backUp) { 15 | return /^(?:operator|sof|keyword c|case|new|export|default|[\[{}\(,;:]|=>)$/.test(state.lastType) || 16 | (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0)))) 17 | } 18 | 19 | CodeMirror.defineMode("javascript", function(config, parserConfig) { 20 | var indentUnit = config.indentUnit; 21 | var statementIndent = parserConfig.statementIndent; 22 | var jsonldMode = parserConfig.jsonld; 23 | var jsonMode = parserConfig.json || jsonldMode; 24 | var isTS = parserConfig.typescript; 25 | var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/; 26 | 27 | // Tokenizer 28 | 29 | var keywords = function(){ 30 | function kw(type) {return {type: type, style: "keyword"};} 31 | var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); 32 | var operator = kw("operator"), atom = {type: "atom", style: "atom"}; 33 | 34 | var jsKeywords = { 35 | "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, 36 | "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C, 37 | "var": kw("var"), "const": kw("var"), "let": kw("var"), 38 | "function": kw("function"), "catch": kw("catch"), 39 | "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), 40 | "in": operator, "typeof": operator, "instanceof": operator, 41 | "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, 42 | "this": kw("this"), "class": kw("class"), "super": kw("atom"), 43 | "yield": C, "export": kw("export"), "import": kw("import"), "extends": C, 44 | "await": C, "async": kw("async") 45 | }; 46 | 47 | // Extend the 'normal' keywords with the TypeScript language extensions 48 | if (isTS) { 49 | var type = {type: "variable", style: "variable-3"}; 50 | var tsKeywords = { 51 | // object-like things 52 | "interface": kw("class"), 53 | "implements": C, 54 | "namespace": C, 55 | "module": kw("module"), 56 | "enum": kw("module"), 57 | "type": kw("type"), 58 | 59 | // scope modifiers 60 | "public": kw("modifier"), 61 | "private": kw("modifier"), 62 | "protected": kw("modifier"), 63 | "abstract": kw("modifier"), 64 | 65 | // operators 66 | "as": operator, 67 | 68 | // types 69 | "string": type, "number": type, "boolean": type, "any": type 70 | }; 71 | 72 | for (var attr in tsKeywords) { 73 | jsKeywords[attr] = tsKeywords[attr]; 74 | } 75 | } 76 | 77 | return jsKeywords; 78 | }(); 79 | 80 | var isOperatorChar = /[+\-*&%=<>!?|~^]/; 81 | var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/; 82 | 83 | function readRegexp(stream) { 84 | var escaped = false, next, inSet = false; 85 | while ((next = stream.next()) != null) { 86 | if (!escaped) { 87 | if (next == "/" && !inSet) return; 88 | if (next == "[") inSet = true; 89 | else if (inSet && next == "]") inSet = false; 90 | } 91 | escaped = !escaped && next == "\\"; 92 | } 93 | } 94 | 95 | // Used as scratch variables to communicate multiple values without 96 | // consing up tons of objects. 97 | var type, content; 98 | function ret(tp, style, cont) { 99 | type = tp; content = cont; 100 | return style; 101 | } 102 | function tokenBase(stream, state) { 103 | var ch = stream.next(); 104 | if (ch == '"' || ch == "'") { 105 | state.tokenize = tokenString(ch); 106 | return state.tokenize(stream, state); 107 | } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) { 108 | return ret("number", "number"); 109 | } else if (ch == "." && stream.match("..")) { 110 | return ret("spread", "meta"); 111 | } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) { 112 | return ret(ch); 113 | } else if (ch == "=" && stream.eat(">")) { 114 | return ret("=>", "operator"); 115 | } else if (ch == "0" && stream.eat(/x/i)) { 116 | stream.eatWhile(/[\da-f]/i); 117 | return ret("number", "number"); 118 | } else if (ch == "0" && stream.eat(/o/i)) { 119 | stream.eatWhile(/[0-7]/i); 120 | return ret("number", "number"); 121 | } else if (ch == "0" && stream.eat(/b/i)) { 122 | stream.eatWhile(/[01]/i); 123 | return ret("number", "number"); 124 | } else if (/\d/.test(ch)) { 125 | stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/); 126 | return ret("number", "number"); 127 | } else if (ch == "/") { 128 | if (stream.eat("*")) { 129 | state.tokenize = tokenComment; 130 | return tokenComment(stream, state); 131 | } else if (stream.eat("/")) { 132 | stream.skipToEnd(); 133 | return ret("comment", "comment"); 134 | } else if (expressionAllowed(stream, state, 1)) { 135 | readRegexp(stream); 136 | stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/); 137 | return ret("regexp", "string-2"); 138 | } else { 139 | stream.eatWhile(isOperatorChar); 140 | return ret("operator", "operator", stream.current()); 141 | } 142 | } else if (ch == "`") { 143 | state.tokenize = tokenQuasi; 144 | return tokenQuasi(stream, state); 145 | } else if (ch == "#") { 146 | stream.skipToEnd(); 147 | return ret("error", "error"); 148 | } else if (isOperatorChar.test(ch)) { 149 | if (ch != ">" || !state.lexical || state.lexical.type != ">") 150 | stream.eatWhile(isOperatorChar); 151 | return ret("operator", "operator", stream.current()); 152 | } else if (wordRE.test(ch)) { 153 | stream.eatWhile(wordRE); 154 | var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word]; 155 | return (known && state.lastType != ".") ? ret(known.type, known.style, word) : 156 | ret("variable", "variable", word); 157 | } 158 | } 159 | 160 | function tokenString(quote) { 161 | return function(stream, state) { 162 | var escaped = false, next; 163 | if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){ 164 | state.tokenize = tokenBase; 165 | return ret("jsonld-keyword", "meta"); 166 | } 167 | while ((next = stream.next()) != null) { 168 | if (next == quote && !escaped) break; 169 | escaped = !escaped && next == "\\"; 170 | } 171 | if (!escaped) state.tokenize = tokenBase; 172 | return ret("string", "string"); 173 | }; 174 | } 175 | 176 | function tokenComment(stream, state) { 177 | var maybeEnd = false, ch; 178 | while (ch = stream.next()) { 179 | if (ch == "/" && maybeEnd) { 180 | state.tokenize = tokenBase; 181 | break; 182 | } 183 | maybeEnd = (ch == "*"); 184 | } 185 | return ret("comment", "comment"); 186 | } 187 | 188 | function tokenQuasi(stream, state) { 189 | var escaped = false, next; 190 | while ((next = stream.next()) != null) { 191 | if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) { 192 | state.tokenize = tokenBase; 193 | break; 194 | } 195 | escaped = !escaped && next == "\\"; 196 | } 197 | return ret("quasi", "string-2", stream.current()); 198 | } 199 | 200 | var brackets = "([{}])"; 201 | // This is a crude lookahead trick to try and notice that we're 202 | // parsing the argument patterns for a fat-arrow function before we 203 | // actually hit the arrow token. It only works if the arrow is on 204 | // the same line as the arguments and there's no strange noise 205 | // (comments) in between. Fallback is to only notice when we hit the 206 | // arrow, and not declare the arguments as locals for the arrow 207 | // body. 208 | function findFatArrow(stream, state) { 209 | if (state.fatArrowAt) state.fatArrowAt = null; 210 | var arrow = stream.string.indexOf("=>", stream.start); 211 | if (arrow < 0) return; 212 | 213 | if (isTS) { // Try to skip TypeScript return type declarations after the arguments 214 | var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow)) 215 | if (m) arrow = m.index 216 | } 217 | 218 | var depth = 0, sawSomething = false; 219 | for (var pos = arrow - 1; pos >= 0; --pos) { 220 | var ch = stream.string.charAt(pos); 221 | var bracket = brackets.indexOf(ch); 222 | if (bracket >= 0 && bracket < 3) { 223 | if (!depth) { ++pos; break; } 224 | if (--depth == 0) { if (ch == "(") sawSomething = true; break; } 225 | } else if (bracket >= 3 && bracket < 6) { 226 | ++depth; 227 | } else if (wordRE.test(ch)) { 228 | sawSomething = true; 229 | } else if (/["'\/]/.test(ch)) { 230 | return; 231 | } else if (sawSomething && !depth) { 232 | ++pos; 233 | break; 234 | } 235 | } 236 | if (sawSomething && !depth) state.fatArrowAt = pos; 237 | } 238 | 239 | // Parser 240 | 241 | var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true}; 242 | 243 | function JSLexical(indented, column, type, align, prev, info) { 244 | this.indented = indented; 245 | this.column = column; 246 | this.type = type; 247 | this.prev = prev; 248 | this.info = info; 249 | if (align != null) this.align = align; 250 | } 251 | 252 | function inScope(state, varname) { 253 | for (var v = state.localVars; v; v = v.next) 254 | if (v.name == varname) return true; 255 | for (var cx = state.context; cx; cx = cx.prev) { 256 | for (var v = cx.vars; v; v = v.next) 257 | if (v.name == varname) return true; 258 | } 259 | } 260 | 261 | function parseJS(state, style, type, content, stream) { 262 | var cc = state.cc; 263 | // Communicate our context to the combinators. 264 | // (Less wasteful than consing up a hundred closures on every call.) 265 | cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style; 266 | 267 | if (!state.lexical.hasOwnProperty("align")) 268 | state.lexical.align = true; 269 | 270 | while(true) { 271 | var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; 272 | if (combinator(type, content)) { 273 | while(cc.length && cc[cc.length - 1].lex) 274 | cc.pop()(); 275 | if (cx.marked) return cx.marked; 276 | if (type == "variable" && inScope(state, content)) return "variable-2"; 277 | return style; 278 | } 279 | } 280 | } 281 | 282 | // Combinator utils 283 | 284 | var cx = {state: null, column: null, marked: null, cc: null}; 285 | function pass() { 286 | for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); 287 | } 288 | function cont() { 289 | pass.apply(null, arguments); 290 | return true; 291 | } 292 | function register(varname) { 293 | function inList(list) { 294 | for (var v = list; v; v = v.next) 295 | if (v.name == varname) return true; 296 | return false; 297 | } 298 | var state = cx.state; 299 | cx.marked = "def"; 300 | if (state.context) { 301 | if (inList(state.localVars)) return; 302 | state.localVars = {name: varname, next: state.localVars}; 303 | } else { 304 | if (inList(state.globalVars)) return; 305 | if (parserConfig.globalVars) 306 | state.globalVars = {name: varname, next: state.globalVars}; 307 | } 308 | } 309 | 310 | // Combinators 311 | 312 | var defaultVars = {name: "this", next: {name: "arguments"}}; 313 | function pushcontext() { 314 | cx.state.context = {prev: cx.state.context, vars: cx.state.localVars}; 315 | cx.state.localVars = defaultVars; 316 | } 317 | function popcontext() { 318 | cx.state.localVars = cx.state.context.vars; 319 | cx.state.context = cx.state.context.prev; 320 | } 321 | function pushlex(type, info) { 322 | var result = function() { 323 | var state = cx.state, indent = state.indented; 324 | if (state.lexical.type == "stat") indent = state.lexical.indented; 325 | else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev) 326 | indent = outer.indented; 327 | state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info); 328 | }; 329 | result.lex = true; 330 | return result; 331 | } 332 | function poplex() { 333 | var state = cx.state; 334 | if (state.lexical.prev) { 335 | if (state.lexical.type == ")") 336 | state.indented = state.lexical.indented; 337 | state.lexical = state.lexical.prev; 338 | } 339 | } 340 | poplex.lex = true; 341 | 342 | function expect(wanted) { 343 | function exp(type) { 344 | if (type == wanted) return cont(); 345 | else if (wanted == ";") return pass(); 346 | else return cont(exp); 347 | }; 348 | return exp; 349 | } 350 | 351 | function statement(type, value) { 352 | if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex); 353 | if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex); 354 | if (type == "keyword b") return cont(pushlex("form"), statement, poplex); 355 | if (type == "{") return cont(pushlex("}"), block, poplex); 356 | if (type == ";") return cont(); 357 | if (type == "if") { 358 | if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex) 359 | cx.state.cc.pop()(); 360 | return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse); 361 | } 362 | if (type == "function") return cont(functiondef); 363 | if (type == "for") return cont(pushlex("form"), forspec, statement, poplex); 364 | if (type == "variable") return cont(pushlex("stat"), maybelabel); 365 | if (type == "switch") return cont(pushlex("form"), parenExpr, pushlex("}", "switch"), expect("{"), 366 | block, poplex, poplex); 367 | if (type == "case") return cont(expression, expect(":")); 368 | if (type == "default") return cont(expect(":")); 369 | if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), 370 | statement, poplex, popcontext); 371 | if (type == "class") return cont(pushlex("form"), className, poplex); 372 | if (type == "export") return cont(pushlex("stat"), afterExport, poplex); 373 | if (type == "import") return cont(pushlex("stat"), afterImport, poplex); 374 | if (type == "module") return cont(pushlex("form"), pattern, pushlex("}"), expect("{"), block, poplex, poplex) 375 | if (type == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";")); 376 | if (type == "async") return cont(statement) 377 | return pass(pushlex("stat"), expression, expect(";"), poplex); 378 | } 379 | function expression(type) { 380 | return expressionInner(type, false); 381 | } 382 | function expressionNoComma(type) { 383 | return expressionInner(type, true); 384 | } 385 | function parenExpr(type) { 386 | if (type != "(") return pass() 387 | return cont(pushlex(")"), expression, expect(")"), poplex) 388 | } 389 | function expressionInner(type, noComma) { 390 | if (cx.state.fatArrowAt == cx.stream.start) { 391 | var body = noComma ? arrowBodyNoComma : arrowBody; 392 | if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext); 393 | else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); 394 | } 395 | 396 | var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; 397 | if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); 398 | if (type == "function") return cont(functiondef, maybeop); 399 | if (type == "class") return cont(pushlex("form"), classExpression, poplex); 400 | if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression); 401 | if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); 402 | if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); 403 | if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); 404 | if (type == "{") return contCommasep(objprop, "}", null, maybeop); 405 | if (type == "quasi") return pass(quasi, maybeop); 406 | if (type == "new") return cont(maybeTarget(noComma)); 407 | return cont(); 408 | } 409 | function maybeexpression(type) { 410 | if (type.match(/[;\}\)\],]/)) return pass(); 411 | return pass(expression); 412 | } 413 | function maybeexpressionNoComma(type) { 414 | if (type.match(/[;\}\)\],]/)) return pass(); 415 | return pass(expressionNoComma); 416 | } 417 | 418 | function maybeoperatorComma(type, value) { 419 | if (type == ",") return cont(expression); 420 | return maybeoperatorNoComma(type, value, false); 421 | } 422 | function maybeoperatorNoComma(type, value, noComma) { 423 | var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; 424 | var expr = noComma == false ? expression : expressionNoComma; 425 | if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); 426 | if (type == "operator") { 427 | if (/\+\+|--/.test(value)) return cont(me); 428 | if (value == "?") return cont(expression, expect(":"), expr); 429 | return cont(expr); 430 | } 431 | if (type == "quasi") { return pass(quasi, me); } 432 | if (type == ";") return; 433 | if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); 434 | if (type == ".") return cont(property, me); 435 | if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); 436 | } 437 | function quasi(type, value) { 438 | if (type != "quasi") return pass(); 439 | if (value.slice(value.length - 2) != "${") return cont(quasi); 440 | return cont(expression, continueQuasi); 441 | } 442 | function continueQuasi(type) { 443 | if (type == "}") { 444 | cx.marked = "string-2"; 445 | cx.state.tokenize = tokenQuasi; 446 | return cont(quasi); 447 | } 448 | } 449 | function arrowBody(type) { 450 | findFatArrow(cx.stream, cx.state); 451 | return pass(type == "{" ? statement : expression); 452 | } 453 | function arrowBodyNoComma(type) { 454 | findFatArrow(cx.stream, cx.state); 455 | return pass(type == "{" ? statement : expressionNoComma); 456 | } 457 | function maybeTarget(noComma) { 458 | return function(type) { 459 | if (type == ".") return cont(noComma ? targetNoComma : target); 460 | else return pass(noComma ? expressionNoComma : expression); 461 | }; 462 | } 463 | function target(_, value) { 464 | if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); } 465 | } 466 | function targetNoComma(_, value) { 467 | if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); } 468 | } 469 | function maybelabel(type) { 470 | if (type == ":") return cont(poplex, statement); 471 | return pass(maybeoperatorComma, expect(";"), poplex); 472 | } 473 | function property(type) { 474 | if (type == "variable") {cx.marked = "property"; return cont();} 475 | } 476 | function objprop(type, value) { 477 | if (type == "async") { 478 | cx.marked = "property"; 479 | return cont(objprop); 480 | } else if (type == "variable" || cx.style == "keyword") { 481 | cx.marked = "property"; 482 | if (value == "get" || value == "set") return cont(getterSetter); 483 | return cont(afterprop); 484 | } else if (type == "number" || type == "string") { 485 | cx.marked = jsonldMode ? "property" : (cx.style + " property"); 486 | return cont(afterprop); 487 | } else if (type == "jsonld-keyword") { 488 | return cont(afterprop); 489 | } else if (type == "modifier") { 490 | return cont(objprop) 491 | } else if (type == "[") { 492 | return cont(expression, expect("]"), afterprop); 493 | } else if (type == "spread") { 494 | return cont(expression); 495 | } else if (type == ":") { 496 | return pass(afterprop) 497 | } 498 | } 499 | function getterSetter(type) { 500 | if (type != "variable") return pass(afterprop); 501 | cx.marked = "property"; 502 | return cont(functiondef); 503 | } 504 | function afterprop(type) { 505 | if (type == ":") return cont(expressionNoComma); 506 | if (type == "(") return pass(functiondef); 507 | } 508 | function commasep(what, end, sep) { 509 | function proceed(type, value) { 510 | if (sep ? sep.indexOf(type) > -1 : type == ",") { 511 | var lex = cx.state.lexical; 512 | if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; 513 | return cont(function(type, value) { 514 | if (type == end || value == end) return pass() 515 | return pass(what) 516 | }, proceed); 517 | } 518 | if (type == end || value == end) return cont(); 519 | return cont(expect(end)); 520 | } 521 | return function(type, value) { 522 | if (type == end || value == end) return cont(); 523 | return pass(what, proceed); 524 | }; 525 | } 526 | function contCommasep(what, end, info) { 527 | for (var i = 3; i < arguments.length; i++) 528 | cx.cc.push(arguments[i]); 529 | return cont(pushlex(end, info), commasep(what, end), poplex); 530 | } 531 | function block(type) { 532 | if (type == "}") return cont(); 533 | return pass(statement, block); 534 | } 535 | function maybetype(type, value) { 536 | if (isTS) { 537 | if (type == ":") return cont(typeexpr); 538 | if (value == "?") return cont(maybetype); 539 | } 540 | } 541 | function typeexpr(type) { 542 | if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);} 543 | if (type == "string" || type == "number" || type == "atom") return cont(afterType); 544 | if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex) 545 | if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType) 546 | } 547 | function maybeReturnType(type) { 548 | if (type == "=>") return cont(typeexpr) 549 | } 550 | function typeprop(type, value) { 551 | if (type == "variable" || cx.style == "keyword") { 552 | cx.marked = "property" 553 | return cont(typeprop) 554 | } else if (value == "?") { 555 | return cont(typeprop) 556 | } else if (type == ":") { 557 | return cont(typeexpr) 558 | } 559 | } 560 | function typearg(type) { 561 | if (type == "variable") return cont(typearg) 562 | else if (type == ":") return cont(typeexpr) 563 | } 564 | function afterType(type, value) { 565 | if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) 566 | if (value == "|" || type == ".") return cont(typeexpr) 567 | if (type == "[") return cont(expect("]"), afterType) 568 | } 569 | function vardef() { 570 | return pass(pattern, maybetype, maybeAssign, vardefCont); 571 | } 572 | function pattern(type, value) { 573 | if (type == "modifier") return cont(pattern) 574 | if (type == "variable") { register(value); return cont(); } 575 | if (type == "spread") return cont(pattern); 576 | if (type == "[") return contCommasep(pattern, "]"); 577 | if (type == "{") return contCommasep(proppattern, "}"); 578 | } 579 | function proppattern(type, value) { 580 | if (type == "variable" && !cx.stream.match(/^\s*:/, false)) { 581 | register(value); 582 | return cont(maybeAssign); 583 | } 584 | if (type == "variable") cx.marked = "property"; 585 | if (type == "spread") return cont(pattern); 586 | if (type == "}") return pass(); 587 | return cont(expect(":"), pattern, maybeAssign); 588 | } 589 | function maybeAssign(_type, value) { 590 | if (value == "=") return cont(expressionNoComma); 591 | } 592 | function vardefCont(type) { 593 | if (type == ",") return cont(vardef); 594 | } 595 | function maybeelse(type, value) { 596 | if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); 597 | } 598 | function forspec(type) { 599 | if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex); 600 | } 601 | function forspec1(type) { 602 | if (type == "var") return cont(vardef, expect(";"), forspec2); 603 | if (type == ";") return cont(forspec2); 604 | if (type == "variable") return cont(formaybeinof); 605 | return pass(expression, expect(";"), forspec2); 606 | } 607 | function formaybeinof(_type, value) { 608 | if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } 609 | return cont(maybeoperatorComma, forspec2); 610 | } 611 | function forspec2(type, value) { 612 | if (type == ";") return cont(forspec3); 613 | if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } 614 | return pass(expression, expect(";"), forspec3); 615 | } 616 | function forspec3(type) { 617 | if (type != ")") cont(expression); 618 | } 619 | function functiondef(type, value) { 620 | if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} 621 | if (type == "variable") {register(value); return cont(functiondef);} 622 | if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext); 623 | } 624 | function funarg(type) { 625 | if (type == "spread") return cont(funarg); 626 | return pass(pattern, maybetype, maybeAssign); 627 | } 628 | function classExpression(type, value) { 629 | // Class expressions may have an optional name. 630 | if (type == "variable") return className(type, value); 631 | return classNameAfter(type, value); 632 | } 633 | function className(type, value) { 634 | if (type == "variable") {register(value); return cont(classNameAfter);} 635 | } 636 | function classNameAfter(type, value) { 637 | if (value == "extends" || value == "implements") return cont(isTS ? typeexpr : expression, classNameAfter); 638 | if (type == "{") return cont(pushlex("}"), classBody, poplex); 639 | } 640 | function classBody(type, value) { 641 | if (type == "variable" || cx.style == "keyword") { 642 | if ((value == "static" || value == "get" || value == "set" || 643 | (isTS && (value == "public" || value == "private" || value == "protected" || value == "readonly" || value == "abstract"))) && 644 | cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false)) { 645 | cx.marked = "keyword"; 646 | return cont(classBody); 647 | } 648 | cx.marked = "property"; 649 | return cont(isTS ? classfield : functiondef, classBody); 650 | } 651 | if (value == "*") { 652 | cx.marked = "keyword"; 653 | return cont(classBody); 654 | } 655 | if (type == ";") return cont(classBody); 656 | if (type == "}") return cont(); 657 | } 658 | function classfield(type, value) { 659 | if (value == "?") return cont(classfield) 660 | if (type == ":") return cont(typeexpr, maybeAssign) 661 | return pass(functiondef) 662 | } 663 | function afterExport(type, value) { 664 | if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } 665 | if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } 666 | if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";")); 667 | return pass(statement); 668 | } 669 | function exportField(type, value) { 670 | if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); } 671 | if (type == "variable") return pass(expressionNoComma, exportField); 672 | } 673 | function afterImport(type) { 674 | if (type == "string") return cont(); 675 | return pass(importSpec, maybeMoreImports, maybeFrom); 676 | } 677 | function importSpec(type, value) { 678 | if (type == "{") return contCommasep(importSpec, "}"); 679 | if (type == "variable") register(value); 680 | if (value == "*") cx.marked = "keyword"; 681 | return cont(maybeAs); 682 | } 683 | function maybeMoreImports(type) { 684 | if (type == ",") return cont(importSpec, maybeMoreImports) 685 | } 686 | function maybeAs(_type, value) { 687 | if (value == "as") { cx.marked = "keyword"; return cont(importSpec); } 688 | } 689 | function maybeFrom(_type, value) { 690 | if (value == "from") { cx.marked = "keyword"; return cont(expression); } 691 | } 692 | function arrayLiteral(type) { 693 | if (type == "]") return cont(); 694 | return pass(commasep(expressionNoComma, "]")); 695 | } 696 | 697 | function isContinuedStatement(state, textAfter) { 698 | return state.lastType == "operator" || state.lastType == "," || 699 | isOperatorChar.test(textAfter.charAt(0)) || 700 | /[,.]/.test(textAfter.charAt(0)); 701 | } 702 | 703 | // Interface 704 | 705 | return { 706 | startState: function(basecolumn) { 707 | var state = { 708 | tokenize: tokenBase, 709 | lastType: "sof", 710 | cc: [], 711 | lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), 712 | localVars: parserConfig.localVars, 713 | context: parserConfig.localVars && {vars: parserConfig.localVars}, 714 | indented: basecolumn || 0 715 | }; 716 | if (parserConfig.globalVars && typeof parserConfig.globalVars == "object") 717 | state.globalVars = parserConfig.globalVars; 718 | return state; 719 | }, 720 | 721 | token: function(stream, state) { 722 | if (stream.sol()) { 723 | if (!state.lexical.hasOwnProperty("align")) 724 | state.lexical.align = false; 725 | state.indented = stream.indentation(); 726 | findFatArrow(stream, state); 727 | } 728 | if (state.tokenize != tokenComment && stream.eatSpace()) return null; 729 | var style = state.tokenize(stream, state); 730 | if (type == "comment") return style; 731 | state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; 732 | return parseJS(state, style, type, content, stream); 733 | }, 734 | 735 | indent: function(state, textAfter) { 736 | if (state.tokenize == tokenComment) return CodeMirror.Pass; 737 | if (state.tokenize != tokenBase) return 0; 738 | var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top 739 | // Kludge to prevent 'maybelse' from blocking lexical scope pops 740 | if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) { 741 | var c = state.cc[i]; 742 | if (c == poplex) lexical = lexical.prev; 743 | else if (c != maybeelse) break; 744 | } 745 | while ((lexical.type == "stat" || lexical.type == "form") && 746 | (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) && 747 | (top == maybeoperatorComma || top == maybeoperatorNoComma) && 748 | !/^[,\.=+\-*:?[\(]/.test(textAfter)))) 749 | lexical = lexical.prev; 750 | if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") 751 | lexical = lexical.prev; 752 | var type = lexical.type, closing = firstChar == type; 753 | 754 | if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0); 755 | else if (type == "form" && firstChar == "{") return lexical.indented; 756 | else if (type == "form") return lexical.indented + indentUnit; 757 | else if (type == "stat") 758 | return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0); 759 | else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false) 760 | return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); 761 | else if (lexical.align) return lexical.column + (closing ? 0 : 1); 762 | else return lexical.indented + (closing ? 0 : indentUnit); 763 | }, 764 | 765 | electricInput: /^\s*(?:case .*?:|default:|\{|\})$/, 766 | blockCommentStart: jsonMode ? null : "/*", 767 | blockCommentEnd: jsonMode ? null : "*/", 768 | lineComment: jsonMode ? null : "//", 769 | fold: "brace", 770 | closeBrackets: "()[]{}''\"\"``", 771 | 772 | helperType: jsonMode ? "json" : "javascript", 773 | jsonldMode: jsonldMode, 774 | jsonMode: jsonMode, 775 | 776 | expressionAllowed: expressionAllowed, 777 | skipExpression: function(state) { 778 | var top = state.cc[state.cc.length - 1] 779 | if (top == expression || top == expressionNoComma) state.cc.pop() 780 | } 781 | }; 782 | }); 783 | 784 | CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/); 785 | 786 | CodeMirror.defineMIME("text/javascript", "javascript"); 787 | CodeMirror.defineMIME("text/ecmascript", "javascript"); 788 | CodeMirror.defineMIME("application/javascript", "javascript"); 789 | CodeMirror.defineMIME("application/x-javascript", "javascript"); 790 | CodeMirror.defineMIME("application/ecmascript", "javascript"); 791 | CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); 792 | CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true}); 793 | CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true}); 794 | CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); 795 | CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); 796 | 797 | }); 798 | -------------------------------------------------------------------------------- /app/js/loadmode.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js"; 3 | 4 | var loading = {}; 5 | function splitCallback(cont, n) { 6 | var countDown = n; 7 | return function() { if (--countDown == 0) cont(); } 8 | } 9 | function ensureDeps(mode, cont) { 10 | var deps = CodeMirror.modes[mode].dependencies; 11 | if (!deps) return cont(); 12 | var missing = []; 13 | for (var i = 0; i < deps.length; ++i) { 14 | if (!CodeMirror.modes.hasOwnProperty(deps[i])) 15 | missing.push(deps[i]); 16 | } 17 | if (!missing.length) return cont(); 18 | var split = splitCallback(cont, missing.length); 19 | for (var i = 0; i < missing.length; ++i) 20 | CodeMirror.requireMode(missing[i], split); 21 | } 22 | 23 | CodeMirror.requireMode = function(mode, cont) { 24 | if (typeof mode != "string") mode = mode.name; 25 | if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont); 26 | if (loading.hasOwnProperty(mode)) return loading[mode].push(cont); 27 | 28 | var script = document.createElement("script"); 29 | script.src = CodeMirror.modeURL.replace(/%N/g, mode); 30 | var others = document.getElementsByTagName("script")[0]; 31 | others.parentNode.insertBefore(script, others); 32 | var list = loading[mode] = [cont]; 33 | var count = 0, poll = setInterval(function() { 34 | if (++count > 100) return clearInterval(poll); 35 | if (CodeMirror.modes.hasOwnProperty(mode)) { 36 | clearInterval(poll); 37 | loading[mode] = null; 38 | ensureDeps(mode, function() { 39 | for (var i = 0; i < list.length; ++i) list[i](); 40 | }); 41 | } 42 | }, 200); 43 | }; 44 | 45 | CodeMirror.autoLoadMode = function(instance, mode) { 46 | if (!CodeMirror.modes.hasOwnProperty(mode)) 47 | CodeMirror.requireMode(mode, function() { 48 | instance.setOption("mode", instance.getOption("mode")); 49 | }); 50 | }; 51 | }()); 52 | -------------------------------------------------------------------------------- /app/js/match-highlighter.js: -------------------------------------------------------------------------------- 1 | // Define match-highlighter commands. Depends on searchcursor.js 2 | // Use by attaching the following function call to the onCursorActivity event: 3 | //myCodeMirror.matchHighlight(minChars); 4 | // And including a special span.CodeMirror-matchhighlight css class (also optionally a separate one for .CodeMirror-focused -- see demo matchhighlighter.html) 5 | 6 | (function() { 7 | var DEFAULT_MIN_CHARS = 2; 8 | 9 | function MatchHighlightState() { 10 | this.marked = []; 11 | } 12 | function getMatchHighlightState(cm) { 13 | return cm._matchHighlightState || (cm._matchHighlightState = new MatchHighlightState()); 14 | } 15 | 16 | function clearMarks(cm) { 17 | var state = getMatchHighlightState(cm); 18 | for (var i = 0; i < state.marked.length; ++i) 19 | state.marked[i].clear(); 20 | state.marked = []; 21 | } 22 | 23 | function markDocument(cm, className, minChars) { 24 | clearMarks(cm); 25 | minChars = (typeof minChars !== 'undefined' ? minChars : DEFAULT_MIN_CHARS); 26 | if (cm.somethingSelected() && cm.getSelection().replace(/^\s+|\s+$/g, "").length >= minChars) { 27 | var state = getMatchHighlightState(cm); 28 | var query = cm.getSelection(); 29 | cm.operation(function() { 30 | if (cm.lineCount() < 2000) { // This is too expensive on big documents. 31 | for (var cursor = cm.getSearchCursor(query); cursor.findNext();) { 32 | //Only apply matchhighlight to the matches other than the one actually selected 33 | if (!(cursor.from().line === cm.getCursor(true).line && cursor.from().ch === cm.getCursor(true).ch)) 34 | state.marked.push(cm.markText(cursor.from(), cursor.to(), className)); 35 | } 36 | } 37 | }); 38 | } 39 | } 40 | 41 | CodeMirror.defineExtension("matchHighlight", function(className, minChars) { 42 | markDocument(this, className, minChars); 43 | }); 44 | })(); 45 | -------------------------------------------------------------------------------- /app/js/meta.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | CodeMirror.modeInfo = [ 15 | {name: "APL", mime: "text/apl", mode: "apl", ext: ["dyalog", "apl"]}, 16 | {name: "PGP", mimes: ["application/pgp", "application/pgp-keys", "application/pgp-signature"], mode: "asciiarmor", ext: ["pgp"]}, 17 | {name: "ASN.1", mime: "text/x-ttcn-asn", mode: "asn.1", ext: ["asn", "asn1"]}, 18 | {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk", file: /^extensions\.conf$/i}, 19 | {name: "Brainfuck", mime: "text/x-brainfuck", mode: "brainfuck", ext: ["b", "bf"]}, 20 | {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h"]}, 21 | {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx"], alias: ["cpp"]}, 22 | {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]}, 23 | {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]}, 24 | {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj", "cljc", "cljx"]}, 25 | {name: "ClojureScript", mime: "text/x-clojurescript", mode: "clojure", ext: ["cljs"]}, 26 | {name: "Closure Stylesheets (GSS)", mime: "text/x-gss", mode: "css", ext: ["gss"]}, 27 | {name: "CMake", mime: "text/x-cmake", mode: "cmake", ext: ["cmake", "cmake.in"], file: /^CMakeLists.txt$/}, 28 | {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]}, 29 | {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]}, 30 | {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher", ext: ["cyp", "cypher"]}, 31 | {name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]}, 32 | {name: "Crystal", mime: "text/x-crystal", mode: "crystal", ext: ["cr"]}, 33 | {name: "CSS", mime: "text/css", mode: "css", ext: ["css"]}, 34 | {name: "CQL", mime: "text/x-cassandra", mode: "sql", ext: ["cql"]}, 35 | {name: "D", mime: "text/x-d", mode: "d", ext: ["d"]}, 36 | {name: "Dart", mimes: ["application/dart", "text/x-dart"], mode: "dart", ext: ["dart"]}, 37 | {name: "diff", mime: "text/x-diff", mode: "diff", ext: ["diff", "patch"]}, 38 | {name: "Django", mime: "text/x-django", mode: "django"}, 39 | {name: "Dockerfile", mime: "text/x-dockerfile", mode: "dockerfile", file: /^Dockerfile$/}, 40 | {name: "DTD", mime: "application/xml-dtd", mode: "dtd", ext: ["dtd"]}, 41 | {name: "Dylan", mime: "text/x-dylan", mode: "dylan", ext: ["dylan", "dyl", "intr"]}, 42 | {name: "EBNF", mime: "text/x-ebnf", mode: "ebnf"}, 43 | {name: "ECL", mime: "text/x-ecl", mode: "ecl", ext: ["ecl"]}, 44 | {name: "edn", mime: "application/edn", mode: "clojure", ext: ["edn"]}, 45 | {name: "Eiffel", mime: "text/x-eiffel", mode: "eiffel", ext: ["e"]}, 46 | {name: "Elm", mime: "text/x-elm", mode: "elm", ext: ["elm"]}, 47 | {name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]}, 48 | {name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]}, 49 | {name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]}, 50 | {name: "Factor", mime: "text/x-factor", mode: "factor", ext: ["factor"]}, 51 | {name: "FCL", mime: "text/x-fcl", mode: "fcl"}, 52 | {name: "Forth", mime: "text/x-forth", mode: "forth", ext: ["forth", "fth", "4th"]}, 53 | {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]}, 54 | {name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]}, 55 | {name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]}, 56 | {name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]}, 57 | {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm", file: /^(readme|contributing|history).md$/i}, 58 | {name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]}, 59 | {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy", "gradle"], file: /^Jenkinsfile$/}, 60 | {name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]}, 61 | {name: "Haskell", mime: "text/x-haskell", mode: "haskell", ext: ["hs"]}, 62 | {name: "Haskell (Literate)", mime: "text/x-literate-haskell", mode: "haskell-literate", ext: ["lhs"]}, 63 | {name: "Haxe", mime: "text/x-haxe", mode: "haxe", ext: ["hx"]}, 64 | {name: "HXML", mime: "text/x-hxml", mode: "haxe", ext: ["hxml"]}, 65 | {name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded", ext: ["aspx"], alias: ["asp", "aspx"]}, 66 | {name: "HTML", mime: "text/html", mode: "htmlmixed", ext: ["html", "htm"], alias: ["xhtml"]}, 67 | {name: "HTTP", mime: "message/http", mode: "http"}, 68 | {name: "IDL", mime: "text/x-idl", mode: "idl", ext: ["pro"]}, 69 | {name: "Pug", mime: "text/x-pug", mode: "pug", ext: ["jade", "pug"], alias: ["jade"]}, 70 | {name: "Java", mime: "text/x-java", mode: "clike", ext: ["java"]}, 71 | {name: "Java Server Pages", mime: "application/x-jsp", mode: "htmlembedded", ext: ["jsp"], alias: ["jsp"]}, 72 | {name: "JavaScript", mimes: ["text/javascript", "text/ecmascript", "application/javascript", "application/x-javascript", "application/ecmascript"], 73 | mode: "javascript", ext: ["js"], alias: ["ecmascript", "js", "node"]}, 74 | {name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]}, 75 | {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]}, 76 | {name: "JSX", mime: "text/jsx", mode: "jsx", ext: ["jsx"]}, 77 | {name: "Jinja2", mime: "null", mode: "jinja2"}, 78 | {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]}, 79 | {name: "Kotlin", mime: "text/x-kotlin", mode: "clike", ext: ["kt"]}, 80 | {name: "LESS", mime: "text/x-less", mode: "css", ext: ["less"]}, 81 | {name: "LiveScript", mime: "text/x-livescript", mode: "livescript", ext: ["ls"], alias: ["ls"]}, 82 | {name: "Lua", mime: "text/x-lua", mode: "lua", ext: ["lua"]}, 83 | {name: "Markdown", mime: "text/x-markdown", mode: "markdown", ext: ["markdown", "md", "mkd"]}, 84 | {name: "mIRC", mime: "text/mirc", mode: "mirc"}, 85 | {name: "MariaDB SQL", mime: "text/x-mariadb", mode: "sql"}, 86 | {name: "Mathematica", mime: "text/x-mathematica", mode: "mathematica", ext: ["m", "nb"]}, 87 | {name: "Modelica", mime: "text/x-modelica", mode: "modelica", ext: ["mo"]}, 88 | {name: "MUMPS", mime: "text/x-mumps", mode: "mumps", ext: ["mps"]}, 89 | {name: "MS SQL", mime: "text/x-mssql", mode: "sql"}, 90 | {name: "mbox", mime: "application/mbox", mode: "mbox", ext: ["mbox"]}, 91 | {name: "MySQL", mime: "text/x-mysql", mode: "sql"}, 92 | {name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx", file: /nginx.*\.conf$/i}, 93 | {name: "NSIS", mime: "text/x-nsis", mode: "nsis", ext: ["nsh", "nsi"]}, 94 | {name: "NTriples", mime: "text/n-triples", mode: "ntriples", ext: ["nt"]}, 95 | {name: "Objective C", mime: "text/x-objectivec", mode: "clike", ext: ["m", "mm"], alias: ["objective-c", "objc"]}, 96 | {name: "OCaml", mime: "text/x-ocaml", mode: "mllike", ext: ["ml", "mli", "mll", "mly"]}, 97 | {name: "Octave", mime: "text/x-octave", mode: "octave", ext: ["m"]}, 98 | {name: "Oz", mime: "text/x-oz", mode: "oz", ext: ["oz"]}, 99 | {name: "Pascal", mime: "text/x-pascal", mode: "pascal", ext: ["p", "pas"]}, 100 | {name: "PEG.js", mime: "null", mode: "pegjs", ext: ["jsonld"]}, 101 | {name: "Perl", mime: "text/x-perl", mode: "perl", ext: ["pl", "pm"]}, 102 | {name: "PHP", mime: "application/x-httpd-php", mode: "php", ext: ["php", "php3", "php4", "php5", "phtml"]}, 103 | {name: "Pig", mime: "text/x-pig", mode: "pig", ext: ["pig"]}, 104 | {name: "Plain Text", mime: "text/plain", mode: "null", ext: ["txt", "text", "conf", "def", "list", "log"]}, 105 | {name: "PLSQL", mime: "text/x-plsql", mode: "sql", ext: ["pls"]}, 106 | {name: "PowerShell", mime: "application/x-powershell", mode: "powershell", ext: ["ps1", "psd1", "psm1"]}, 107 | {name: "Properties files", mime: "text/x-properties", mode: "properties", ext: ["properties", "ini", "in"], alias: ["ini", "properties"]}, 108 | {name: "ProtoBuf", mime: "text/x-protobuf", mode: "protobuf", ext: ["proto"]}, 109 | {name: "Python", mime: "text/x-python", mode: "python", ext: ["BUILD", "bzl", "py", "pyw"], file: /^(BUCK|BUILD)$/}, 110 | {name: "Puppet", mime: "text/x-puppet", mode: "puppet", ext: ["pp"]}, 111 | {name: "Q", mime: "text/x-q", mode: "q", ext: ["q"]}, 112 | {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r", "R"], alias: ["rscript"]}, 113 | {name: "reStructuredText", mime: "text/x-rst", mode: "rst", ext: ["rst"], alias: ["rst"]}, 114 | {name: "RPM Changes", mime: "text/x-rpm-changes", mode: "rpm"}, 115 | {name: "RPM Spec", mime: "text/x-rpm-spec", mode: "rpm", ext: ["spec"]}, 116 | {name: "Ruby", mime: "text/x-ruby", mode: "ruby", ext: ["rb"], alias: ["jruby", "macruby", "rake", "rb", "rbx"]}, 117 | {name: "Rust", mime: "text/x-rustsrc", mode: "rust", ext: ["rs"]}, 118 | {name: "SAS", mime: "text/x-sas", mode: "sas", ext: ["sas"]}, 119 | {name: "Sass", mime: "text/x-sass", mode: "sass", ext: ["sass"]}, 120 | {name: "Scala", mime: "text/x-scala", mode: "clike", ext: ["scala"]}, 121 | {name: "Scheme", mime: "text/x-scheme", mode: "scheme", ext: ["scm", "ss"]}, 122 | {name: "SCSS", mime: "text/x-scss", mode: "css", ext: ["scss"]}, 123 | {name: "Shell", mime: "text/x-sh", mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"], file: /^PKGBUILD$/}, 124 | {name: "Sieve", mime: "application/sieve", mode: "sieve", ext: ["siv", "sieve"]}, 125 | {name: "Slim", mimes: ["text/x-slim", "application/x-slim"], mode: "slim", ext: ["slim"]}, 126 | {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk", ext: ["st"]}, 127 | {name: "Smarty", mime: "text/x-smarty", mode: "smarty", ext: ["tpl"]}, 128 | {name: "Solr", mime: "text/x-solr", mode: "solr"}, 129 | {name: "Soy", mime: "text/x-soy", mode: "soy", ext: ["soy"], alias: ["closure template"]}, 130 | {name: "SPARQL", mime: "application/sparql-query", mode: "sparql", ext: ["rq", "sparql"], alias: ["sparul"]}, 131 | {name: "Spreadsheet", mime: "text/x-spreadsheet", mode: "spreadsheet", alias: ["excel", "formula"]}, 132 | {name: "SQL", mime: "text/x-sql", mode: "sql", ext: ["sql"]}, 133 | {name: "Squirrel", mime: "text/x-squirrel", mode: "clike", ext: ["nut"]}, 134 | {name: "Stylus", mime: "text/x-styl", mode: "stylus", ext: ["styl"]}, 135 | {name: "Swift", mime: "text/x-swift", mode: "swift", ext: ["swift"]}, 136 | {name: "sTeX", mime: "text/x-stex", mode: "stex"}, 137 | {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx"], alias: ["tex"]}, 138 | {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v"]}, 139 | {name: "Tcl", mime: "text/x-tcl", mode: "tcl", ext: ["tcl"]}, 140 | {name: "Textile", mime: "text/x-textile", mode: "textile", ext: ["textile"]}, 141 | {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"}, 142 | {name: "Tiki wiki", mime: "text/tiki", mode: "tiki"}, 143 | {name: "TOML", mime: "text/x-toml", mode: "toml", ext: ["toml"]}, 144 | {name: "Tornado", mime: "text/x-tornado", mode: "tornado"}, 145 | {name: "troff", mime: "text/troff", mode: "troff", ext: ["1", "2", "3", "4", "5", "6", "7", "8", "9"]}, 146 | {name: "TTCN", mime: "text/x-ttcn", mode: "ttcn", ext: ["ttcn", "ttcn3", "ttcnpp"]}, 147 | {name: "TTCN_CFG", mime: "text/x-ttcn-cfg", mode: "ttcn-cfg", ext: ["cfg"]}, 148 | {name: "Turtle", mime: "text/turtle", mode: "turtle", ext: ["ttl"]}, 149 | {name: "TypeScript", mime: "application/typescript", mode: "javascript", ext: ["ts"], alias: ["ts"]}, 150 | {name: "Twig", mime: "text/x-twig", mode: "twig"}, 151 | {name: "Web IDL", mime: "text/x-webidl", mode: "webidl", ext: ["webidl"]}, 152 | {name: "VB.NET", mime: "text/x-vb", mode: "vb", ext: ["vb"]}, 153 | {name: "VBScript", mime: "text/vbscript", mode: "vbscript", ext: ["vbs"]}, 154 | {name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]}, 155 | {name: "Verilog", mime: "text/x-verilog", mode: "verilog", ext: ["v"]}, 156 | {name: "VHDL", mime: "text/x-vhdl", mode: "vhdl", ext: ["vhd", "vhdl"]}, 157 | {name: "Vue.js Component", mimes: ["script/x-vue", "text/x-vue"], mode: "vue", ext: ["vue"]}, 158 | {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd", "svg"], alias: ["rss", "wsdl", "xsd"]}, 159 | {name: "XQuery", mime: "application/xquery", mode: "xquery", ext: ["xy", "xquery"]}, 160 | {name: "Yacas", mime: "text/x-yacas", mode: "yacas", ext: ["ys"]}, 161 | {name: "YAML", mimes: ["text/x-yaml", "text/yaml"], mode: "yaml", ext: ["yaml", "yml"], alias: ["yml"]}, 162 | {name: "Z80", mime: "text/x-z80", mode: "z80", ext: ["z80"]}, 163 | {name: "mscgen", mime: "text/x-mscgen", mode: "mscgen", ext: ["mscgen", "mscin", "msc"]}, 164 | {name: "xu", mime: "text/x-xu", mode: "mscgen", ext: ["xu"]}, 165 | {name: "msgenny", mime: "text/x-msgenny", mode: "mscgen", ext: ["msgenny"]} 166 | ]; 167 | // Ensure all modes have a mime property for backwards compatibility 168 | for (var i = 0; i < CodeMirror.modeInfo.length; i++) { 169 | var info = CodeMirror.modeInfo[i]; 170 | if (info.mimes) info.mime = info.mimes[0]; 171 | } 172 | 173 | CodeMirror.findModeByMIME = function(mime) { 174 | mime = mime.toLowerCase(); 175 | for (var i = 0; i < CodeMirror.modeInfo.length; i++) { 176 | var info = CodeMirror.modeInfo[i]; 177 | if (info.mime == mime) return info; 178 | if (info.mimes) for (var j = 0; j < info.mimes.length; j++) 179 | if (info.mimes[j] == mime) return info; 180 | } 181 | if (/\+xml$/.test(mime)) return CodeMirror.findModeByMIME("application/xml") 182 | if (/\+json$/.test(mime)) return CodeMirror.findModeByMIME("application/json") 183 | }; 184 | 185 | CodeMirror.findModeByExtension = function(ext) { 186 | for (var i = 0; i < CodeMirror.modeInfo.length; i++) { 187 | var info = CodeMirror.modeInfo[i]; 188 | if (info.ext) for (var j = 0; j < info.ext.length; j++) 189 | if (info.ext[j] == ext) return info; 190 | } 191 | }; 192 | 193 | CodeMirror.findModeByFileName = function(filename) { 194 | for (var i = 0; i < CodeMirror.modeInfo.length; i++) { 195 | var info = CodeMirror.modeInfo[i]; 196 | if (info.file && info.file.test(filename)) return info; 197 | } 198 | var dot = filename.lastIndexOf("."); 199 | var ext = dot > -1 && filename.substring(dot + 1, filename.length); 200 | if (ext) return CodeMirror.findModeByExtension(ext); 201 | }; 202 | 203 | CodeMirror.findModeByName = function(name) { 204 | name = name.toLowerCase(); 205 | for (var i = 0; i < CodeMirror.modeInfo.length; i++) { 206 | var info = CodeMirror.modeInfo[i]; 207 | if (info.name.toLowerCase() == name) return info; 208 | if (info.alias) for (var j = 0; j < info.alias.length; j++) 209 | if (info.alias[j].toLowerCase() == name) return info; 210 | } 211 | }; 212 | }); 213 | -------------------------------------------------------------------------------- /app/js/overlay.js: -------------------------------------------------------------------------------- 1 | // Utility function that allows modes to be combined. The mode given 2 | // as the base argument takes care of most of the normal mode 3 | // functionality, but a second (typically simple) mode is used, which 4 | // can override the style of text. Both modes get to parse all of the 5 | // text, but when both assign a non-null style to a piece of code, the 6 | // overlay wins, unless the combine argument was true, in which case 7 | // the styles are combined. 8 | 9 | CodeMirror.overlayParser = function(base, overlay, combine) { 10 | return { 11 | startState: function() { 12 | return { 13 | base: CodeMirror.startState(base), 14 | overlay: CodeMirror.startState(overlay), 15 | basePos: 0, baseCur: null, 16 | overlayPos: 0, overlayCur: null 17 | }; 18 | }, 19 | copyState: function(state) { 20 | return { 21 | base: CodeMirror.copyState(base, state.base), 22 | overlay: CodeMirror.copyState(overlay, state.overlay), 23 | basePos: state.basePos, baseCur: null, 24 | overlayPos: state.overlayPos, overlayCur: null 25 | }; 26 | }, 27 | 28 | token: function(stream, state) { 29 | if (stream.start == state.basePos) { 30 | state.baseCur = base.token(stream, state.base); 31 | state.basePos = stream.pos; 32 | } 33 | if (stream.start == state.overlayPos) { 34 | stream.pos = stream.start; 35 | state.overlayCur = overlay.token(stream, state.overlay); 36 | state.overlayPos = stream.pos; 37 | } 38 | stream.pos = Math.min(state.basePos, state.overlayPos); 39 | if (stream.eol()) state.basePos = state.overlayPos = 0; 40 | 41 | if (state.overlayCur == null) return state.baseCur; 42 | if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur; 43 | else return state.overlayCur; 44 | }, 45 | 46 | indent: base.indent && function(state, textAfter) { 47 | return base.indent(state.base, textAfter); 48 | }, 49 | electricChars: base.electricChars 50 | }; 51 | }; 52 | -------------------------------------------------------------------------------- /app/js/runmode.js: -------------------------------------------------------------------------------- 1 | CodeMirror.runMode = function(string, modespec, callback, options) { 2 | var mode = CodeMirror.getMode(CodeMirror.defaults, modespec); 3 | var isNode = callback.nodeType == 1; 4 | var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize; 5 | if (isNode) { 6 | var node = callback, accum = [], col = 0; 7 | callback = function(text, style) { 8 | if (text == "\n") { 9 | accum.push("
"); 10 | col = 0; 11 | return; 12 | } 13 | var escaped = ""; 14 | // HTML-escape and replace tabs 15 | for (var pos = 0;;) { 16 | var idx = text.indexOf("\t", pos); 17 | if (idx == -1) { 18 | escaped += CodeMirror.htmlEscape(text.slice(pos)); 19 | col += text.length - pos; 20 | break; 21 | } else { 22 | col += idx - pos; 23 | escaped += CodeMirror.htmlEscape(text.slice(pos, idx)); 24 | var size = tabSize - col % tabSize; 25 | col += size; 26 | for (var i = 0; i < size; ++i) escaped += " "; 27 | pos = idx + 1; 28 | } 29 | } 30 | 31 | if (style) 32 | accum.push("" + escaped + ""); 33 | else 34 | accum.push(escaped); 35 | } 36 | } 37 | var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode); 38 | for (var i = 0, e = lines.length; i < e; ++i) { 39 | if (i) callback("\n"); 40 | var stream = new CodeMirror.StringStream(lines[i]); 41 | while (!stream.eol()) { 42 | var style = mode.token(stream, state); 43 | callback(stream.current(), style, i, stream.start); 44 | stream.start = stream.pos; 45 | } 46 | } 47 | if (isNode) 48 | node.innerHTML = accum.join(""); 49 | }; 50 | -------------------------------------------------------------------------------- /app/js/search.js: -------------------------------------------------------------------------------- 1 | // Define search commands. Depends on dialog.js or another 2 | // implementation of the openDialog method. 3 | 4 | // Replace works a little oddly -- it will do the replace on the next 5 | // Ctrl-G (or whatever is bound to findNext) press. You prevent a 6 | // replace by making sure the match is no longer selected when hitting 7 | // Ctrl-G. 8 | 9 | (function() { 10 | function SearchState() { 11 | this.posFrom = this.posTo = this.query = null; 12 | this.marked = []; 13 | } 14 | function getSearchState(cm) { 15 | return cm._searchState || (cm._searchState = new SearchState()); 16 | } 17 | function dialog(cm, text, shortText, f) { 18 | if (cm.openDialog) cm.openDialog(text, f); 19 | else f(prompt(shortText, "")); 20 | } 21 | function confirmDialog(cm, text, shortText, fs) { 22 | if (cm.openConfirm) cm.openConfirm(text, fs); 23 | else if (confirm(shortText)) fs[0](); 24 | } 25 | function parseQuery(query) { 26 | var isRE = query.match(/^\/(.*)\/$/); 27 | return isRE ? new RegExp(isRE[1]) : query; 28 | } 29 | var queryDialog = 30 | 'Search: (Use /re/ syntax for regexp search)'; 31 | function doSearch(cm, rev) { 32 | var state = getSearchState(cm); 33 | if (state.query) return findNext(cm, rev); 34 | dialog(cm, queryDialog, "Search for:", function(query) { 35 | cm.operation(function() { 36 | if (!query || state.query) return; 37 | state.query = parseQuery(query); 38 | if (cm.lineCount() < 2000) { // This is too expensive on big documents. 39 | for (var cursor = cm.getSearchCursor(query); cursor.findNext();) 40 | state.marked.push(cm.markText(cursor.from(), cursor.to(), "CodeMirror-searching")); 41 | } 42 | state.posFrom = state.posTo = cm.getCursor(); 43 | findNext(cm, rev); 44 | }); 45 | }); 46 | } 47 | function findNext(cm, rev) {cm.operation(function() { 48 | var state = getSearchState(cm); 49 | var cursor = cm.getSearchCursor(state.query, rev ? state.posFrom : state.posTo); 50 | if (!cursor.find(rev)) { 51 | cursor = cm.getSearchCursor(state.query, rev ? {line: cm.lineCount() - 1} : {line: 0, ch: 0}); 52 | if (!cursor.find(rev)) return; 53 | } 54 | cm.setSelection(cursor.from(), cursor.to()); 55 | state.posFrom = cursor.from(); state.posTo = cursor.to(); 56 | })} 57 | function clearSearch(cm) {cm.operation(function() { 58 | var state = getSearchState(cm); 59 | if (!state.query) return; 60 | state.query = null; 61 | for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear(); 62 | state.marked.length = 0; 63 | })} 64 | 65 | var replaceQueryDialog = 66 | 'Replace: (Use /re/ syntax for regexp search)'; 67 | var replacementQueryDialog = 'With: '; 68 | var doReplaceConfirm = "Replace? "; 69 | function replace(cm, all) { 70 | dialog(cm, replaceQueryDialog, "Replace:", function(query) { 71 | if (!query) return; 72 | query = parseQuery(query); 73 | dialog(cm, replacementQueryDialog, "Replace with:", function(text) { 74 | if (all) { 75 | cm.compoundChange(function() { cm.operation(function() { 76 | for (var cursor = cm.getSearchCursor(query); cursor.findNext();) { 77 | if (typeof query != "string") { 78 | var match = cm.getRange(cursor.from(), cursor.to()).match(query); 79 | cursor.replace(text.replace(/\$(\d)/, function(w, i) {return match[i];})); 80 | } else cursor.replace(text); 81 | } 82 | })}); 83 | } else { 84 | clearSearch(cm); 85 | var cursor = cm.getSearchCursor(query, cm.getCursor()); 86 | function advance() { 87 | var start = cursor.from(), match; 88 | if (!(match = cursor.findNext())) { 89 | cursor = cm.getSearchCursor(query); 90 | if (!(match = cursor.findNext()) || 91 | (cursor.from().line == start.line && cursor.from().ch == start.ch)) return; 92 | } 93 | cm.setSelection(cursor.from(), cursor.to()); 94 | confirmDialog(cm, doReplaceConfirm, "Replace?", 95 | [function() {doReplace(match);}, advance]); 96 | } 97 | function doReplace(match) { 98 | cursor.replace(typeof query == "string" ? text : 99 | text.replace(/\$(\d)/, function(w, i) {return match[i];})); 100 | advance(); 101 | } 102 | advance(); 103 | } 104 | }); 105 | }); 106 | } 107 | 108 | CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);}; 109 | CodeMirror.commands.findNext = doSearch; 110 | CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);}; 111 | CodeMirror.commands.clearSearch = clearSearch; 112 | CodeMirror.commands.replace = replace; 113 | CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);}; 114 | })(); 115 | -------------------------------------------------------------------------------- /app/js/searchcursor.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | function SearchCursor(cm, query, pos, caseFold) { 3 | this.atOccurrence = false; this.cm = cm; 4 | if (caseFold == null && typeof query == "string") caseFold = false; 5 | 6 | pos = pos ? cm.clipPos(pos) : {line: 0, ch: 0}; 7 | this.pos = {from: pos, to: pos}; 8 | 9 | // The matches method is filled in based on the type of query. 10 | // It takes a position and a direction, and returns an object 11 | // describing the next occurrence of the query, or null if no 12 | // more matches were found. 13 | if (typeof query != "string") // Regexp match 14 | this.matches = function(reverse, pos) { 15 | if (reverse) { 16 | var line = cm.getLine(pos.line).slice(0, pos.ch), match = line.match(query), start = 0; 17 | while (match) { 18 | var ind = line.indexOf(match[0]); 19 | start += ind; 20 | line = line.slice(ind + 1); 21 | var newmatch = line.match(query); 22 | if (newmatch) match = newmatch; 23 | else break; 24 | start++; 25 | } 26 | } 27 | else { 28 | var line = cm.getLine(pos.line).slice(pos.ch), match = line.match(query), 29 | start = match && pos.ch + line.indexOf(match[0]); 30 | } 31 | if (match) 32 | return {from: {line: pos.line, ch: start}, 33 | to: {line: pos.line, ch: start + match[0].length}, 34 | match: match}; 35 | }; 36 | else { // String query 37 | if (caseFold) query = query.toLowerCase(); 38 | var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;}; 39 | var target = query.split("\n"); 40 | // Different methods for single-line and multi-line queries 41 | if (target.length == 1) 42 | this.matches = function(reverse, pos) { 43 | var line = fold(cm.getLine(pos.line)), len = query.length, match; 44 | if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1) 45 | : (match = line.indexOf(query, pos.ch)) != -1) 46 | return {from: {line: pos.line, ch: match}, 47 | to: {line: pos.line, ch: match + len}}; 48 | }; 49 | else 50 | this.matches = function(reverse, pos) { 51 | var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln)); 52 | var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match)); 53 | if (reverse ? offsetA >= pos.ch || offsetA != match.length 54 | : offsetA <= pos.ch || offsetA != line.length - match.length) 55 | return; 56 | for (;;) { 57 | if (reverse ? !ln : ln == cm.lineCount() - 1) return; 58 | line = fold(cm.getLine(ln += reverse ? -1 : 1)); 59 | match = target[reverse ? --idx : ++idx]; 60 | if (idx > 0 && idx < target.length - 1) { 61 | if (line != match) return; 62 | else continue; 63 | } 64 | var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length); 65 | if (reverse ? offsetB != line.length - match.length : offsetB != match.length) 66 | return; 67 | var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB}; 68 | return {from: reverse ? end : start, to: reverse ? start : end}; 69 | } 70 | }; 71 | } 72 | } 73 | 74 | SearchCursor.prototype = { 75 | findNext: function() {return this.find(false);}, 76 | findPrevious: function() {return this.find(true);}, 77 | 78 | find: function(reverse) { 79 | var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to); 80 | function savePosAndFail(line) { 81 | var pos = {line: line, ch: 0}; 82 | self.pos = {from: pos, to: pos}; 83 | self.atOccurrence = false; 84 | return false; 85 | } 86 | 87 | for (;;) { 88 | if (this.pos = this.matches(reverse, pos)) { 89 | this.atOccurrence = true; 90 | return this.pos.match || true; 91 | } 92 | if (reverse) { 93 | if (!pos.line) return savePosAndFail(0); 94 | pos = {line: pos.line-1, ch: this.cm.getLine(pos.line-1).length}; 95 | } 96 | else { 97 | var maxLine = this.cm.lineCount(); 98 | if (pos.line == maxLine - 1) return savePosAndFail(maxLine); 99 | pos = {line: pos.line+1, ch: 0}; 100 | } 101 | } 102 | }, 103 | 104 | from: function() {if (this.atOccurrence) return this.pos.from;}, 105 | to: function() {if (this.atOccurrence) return this.pos.to;}, 106 | 107 | replace: function(newText) { 108 | var self = this; 109 | if (this.atOccurrence) 110 | self.pos.to = this.cm.replaceRange(newText, self.pos.from, self.pos.to); 111 | } 112 | }; 113 | 114 | CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) { 115 | return new SearchCursor(this, query, pos, caseFold); 116 | }); 117 | })(); 118 | -------------------------------------------------------------------------------- /app/js/simple-hint.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | CodeMirror.simpleHint = function(editor, getHints) { 3 | // We want a single cursor position. 4 | if (editor.somethingSelected()) return; 5 | var result = getHints(editor); 6 | if (!result || !result.list.length) return; 7 | var completions = result.list; 8 | function insert(str) { 9 | editor.replaceRange(str, result.from, result.to); 10 | } 11 | // When there is only one completion, use it directly. 12 | if (completions.length == 1) {insert(completions[0]); return true;} 13 | 14 | // Build the select widget 15 | var complete = document.createElement("div"); 16 | complete.className = "CodeMirror-completions"; 17 | var sel = complete.appendChild(document.createElement("select")); 18 | // Opera doesn't move the selection when pressing up/down in a 19 | // multi-select, but it does properly support the size property on 20 | // single-selects, so no multi-select is necessary. 21 | if (!window.opera) sel.multiple = true; 22 | for (var i = 0; i < completions.length; ++i) { 23 | var opt = sel.appendChild(document.createElement("option")); 24 | opt.appendChild(document.createTextNode(completions[i])); 25 | } 26 | sel.firstChild.selected = true; 27 | sel.size = Math.min(10, completions.length); 28 | var pos = editor.cursorCoords(); 29 | complete.style.left = pos.x + "px"; 30 | complete.style.top = pos.yBot + "px"; 31 | document.body.appendChild(complete); 32 | // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. 33 | var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); 34 | if(winW - pos.x < sel.clientWidth) 35 | complete.style.left = (pos.x - sel.clientWidth) + "px"; 36 | // Hack to hide the scrollbar. 37 | if (completions.length <= 10) 38 | complete.style.width = (sel.clientWidth - 1) + "px"; 39 | 40 | var done = false; 41 | function close() { 42 | if (done) return; 43 | done = true; 44 | complete.parentNode.removeChild(complete); 45 | } 46 | function pick() { 47 | insert(completions[sel.selectedIndex]); 48 | close(); 49 | setTimeout(function(){editor.focus();}, 50); 50 | } 51 | CodeMirror.connect(sel, "blur", close); 52 | CodeMirror.connect(sel, "keydown", function(event) { 53 | var code = event.keyCode; 54 | // Enter 55 | if (code == 13) {CodeMirror.e_stop(event); pick();} 56 | // Escape 57 | else if (code == 27) {CodeMirror.e_stop(event); close(); editor.focus();} 58 | else if (code != 38 && code != 40) { 59 | close(); editor.focus(); 60 | // Pass the event to the CodeMirror instance so that it can handle things like backspace properly. 61 | editor.triggerOnKeyDown(event); 62 | setTimeout(function(){CodeMirror.simpleHint(editor, getHints);}, 50); 63 | } 64 | }); 65 | CodeMirror.connect(sel, "dblclick", pick); 66 | 67 | sel.focus(); 68 | // Opera sometimes ignores focusing a freshly created node 69 | if (window.opera) setTimeout(function(){if (!done) sel.focus();}, 100); 70 | return true; 71 | }; 72 | })(); 73 | -------------------------------------------------------------------------------- /app/js/simplescrollbars.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | function Bar(cls, orientation, scroll) { 15 | this.orientation = orientation; 16 | this.scroll = scroll; 17 | this.screen = this.total = this.size = 1; 18 | this.pos = 0; 19 | 20 | this.node = document.createElement("div"); 21 | this.node.className = cls + "-" + orientation; 22 | this.inner = this.node.appendChild(document.createElement("div")); 23 | 24 | var self = this; 25 | CodeMirror.on(this.inner, "mousedown", function(e) { 26 | if (e.which != 1) return; 27 | CodeMirror.e_preventDefault(e); 28 | var axis = self.orientation == "horizontal" ? "pageX" : "pageY"; 29 | var start = e[axis], startpos = self.pos; 30 | function done() { 31 | CodeMirror.off(document, "mousemove", move); 32 | CodeMirror.off(document, "mouseup", done); 33 | } 34 | function move(e) { 35 | if (e.which != 1) return done(); 36 | self.moveTo(startpos + (e[axis] - start) * (self.total / self.size)); 37 | } 38 | CodeMirror.on(document, "mousemove", move); 39 | CodeMirror.on(document, "mouseup", done); 40 | }); 41 | 42 | CodeMirror.on(this.node, "click", function(e) { 43 | CodeMirror.e_preventDefault(e); 44 | var innerBox = self.inner.getBoundingClientRect(), where; 45 | if (self.orientation == "horizontal") 46 | where = e.clientX < innerBox.left ? -1 : e.clientX > innerBox.right ? 1 : 0; 47 | else 48 | where = e.clientY < innerBox.top ? -1 : e.clientY > innerBox.bottom ? 1 : 0; 49 | self.moveTo(self.pos + where * self.screen); 50 | }); 51 | 52 | function onWheel(e) { 53 | var moved = CodeMirror.wheelEventPixels(e)[self.orientation == "horizontal" ? "x" : "y"]; 54 | var oldPos = self.pos; 55 | self.moveTo(self.pos + moved); 56 | if (self.pos != oldPos) CodeMirror.e_preventDefault(e); 57 | } 58 | CodeMirror.on(this.node, "mousewheel", onWheel); 59 | CodeMirror.on(this.node, "DOMMouseScroll", onWheel); 60 | } 61 | 62 | Bar.prototype.setPos = function(pos, force) { 63 | if (pos < 0) pos = 0; 64 | if (pos > this.total - this.screen) pos = this.total - this.screen; 65 | if (!force && pos == this.pos) return false; 66 | this.pos = pos; 67 | this.inner.style[this.orientation == "horizontal" ? "left" : "top"] = 68 | (pos * (this.size / this.total)) + "px"; 69 | return true 70 | }; 71 | 72 | Bar.prototype.moveTo = function(pos) { 73 | if (this.setPos(pos)) this.scroll(pos, this.orientation); 74 | } 75 | 76 | var minButtonSize = 10; 77 | 78 | Bar.prototype.update = function(scrollSize, clientSize, barSize) { 79 | var sizeChanged = this.screen != clientSize || this.total != scrollSize || this.size != barSize 80 | if (sizeChanged) { 81 | this.screen = clientSize; 82 | this.total = scrollSize; 83 | this.size = barSize; 84 | } 85 | 86 | var buttonSize = this.screen * (this.size / this.total); 87 | if (buttonSize < minButtonSize) { 88 | this.size -= minButtonSize - buttonSize; 89 | buttonSize = minButtonSize; 90 | } 91 | this.inner.style[this.orientation == "horizontal" ? "width" : "height"] = 92 | buttonSize + "px"; 93 | this.setPos(this.pos, sizeChanged); 94 | }; 95 | 96 | function SimpleScrollbars(cls, place, scroll) { 97 | this.addClass = cls; 98 | this.horiz = new Bar(cls, "horizontal", scroll); 99 | place(this.horiz.node); 100 | this.vert = new Bar(cls, "vertical", scroll); 101 | place(this.vert.node); 102 | this.width = null; 103 | } 104 | 105 | SimpleScrollbars.prototype.update = function(measure) { 106 | if (this.width == null) { 107 | var style = window.getComputedStyle ? window.getComputedStyle(this.horiz.node) : this.horiz.node.currentStyle; 108 | if (style) this.width = parseInt(style.height); 109 | } 110 | var width = this.width || 0; 111 | 112 | var needsH = measure.scrollWidth > measure.clientWidth + 1; 113 | var needsV = measure.scrollHeight > measure.clientHeight + 1; 114 | this.vert.node.style.display = needsV ? "block" : "none"; 115 | this.horiz.node.style.display = needsH ? "block" : "none"; 116 | 117 | if (needsV) { 118 | this.vert.update(measure.scrollHeight, measure.clientHeight, 119 | measure.viewHeight - (needsH ? width : 0)); 120 | this.vert.node.style.bottom = needsH ? width + "px" : "0"; 121 | } 122 | if (needsH) { 123 | this.horiz.update(measure.scrollWidth, measure.clientWidth, 124 | measure.viewWidth - (needsV ? width : 0) - measure.barLeft); 125 | this.horiz.node.style.right = needsV ? width + "px" : "0"; 126 | this.horiz.node.style.left = measure.barLeft + "px"; 127 | } 128 | 129 | return {right: needsV ? width : 0, bottom: needsH ? width : 0}; 130 | }; 131 | 132 | SimpleScrollbars.prototype.setScrollTop = function(pos) { 133 | this.vert.setPos(pos); 134 | }; 135 | 136 | SimpleScrollbars.prototype.setScrollLeft = function(pos) { 137 | this.horiz.setPos(pos); 138 | }; 139 | 140 | SimpleScrollbars.prototype.clear = function() { 141 | var parent = this.horiz.node.parentNode; 142 | parent.removeChild(this.horiz.node); 143 | parent.removeChild(this.vert.node); 144 | }; 145 | 146 | CodeMirror.scrollbarModel.simple = function(place, scroll) { 147 | return new SimpleScrollbars("CodeMirror-simplescroll", place, scroll); 148 | }; 149 | CodeMirror.scrollbarModel.overlay = function(place, scroll) { 150 | return new SimpleScrollbars("CodeMirror-overlayscroll", place, scroll); 151 | }; 152 | }); 153 | -------------------------------------------------------------------------------- /app/js/xml.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | "use strict"; 13 | 14 | var htmlConfig = { 15 | autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true, 16 | 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true, 17 | 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true, 18 | 'track': true, 'wbr': true, 'menuitem': true}, 19 | implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true, 20 | 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true, 21 | 'th': true, 'tr': true}, 22 | contextGrabbers: { 23 | 'dd': {'dd': true, 'dt': true}, 24 | 'dt': {'dd': true, 'dt': true}, 25 | 'li': {'li': true}, 26 | 'option': {'option': true, 'optgroup': true}, 27 | 'optgroup': {'optgroup': true}, 28 | 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true, 29 | 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true, 30 | 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true, 31 | 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true, 32 | 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true}, 33 | 'rp': {'rp': true, 'rt': true}, 34 | 'rt': {'rp': true, 'rt': true}, 35 | 'tbody': {'tbody': true, 'tfoot': true}, 36 | 'td': {'td': true, 'th': true}, 37 | 'tfoot': {'tbody': true}, 38 | 'th': {'td': true, 'th': true}, 39 | 'thead': {'tbody': true, 'tfoot': true}, 40 | 'tr': {'tr': true} 41 | }, 42 | doNotIndent: {"pre": true}, 43 | allowUnquoted: true, 44 | allowMissing: true, 45 | caseFold: true 46 | } 47 | 48 | var xmlConfig = { 49 | autoSelfClosers: {}, 50 | implicitlyClosed: {}, 51 | contextGrabbers: {}, 52 | doNotIndent: {}, 53 | allowUnquoted: false, 54 | allowMissing: false, 55 | caseFold: false 56 | } 57 | 58 | CodeMirror.defineMode("xml", function(editorConf, config_) { 59 | var indentUnit = editorConf.indentUnit 60 | var config = {} 61 | var defaults = config_.htmlMode ? htmlConfig : xmlConfig 62 | for (var prop in defaults) config[prop] = defaults[prop] 63 | for (var prop in config_) config[prop] = config_[prop] 64 | 65 | // Return variables for tokenizers 66 | var type, setStyle; 67 | 68 | function inText(stream, state) { 69 | function chain(parser) { 70 | state.tokenize = parser; 71 | return parser(stream, state); 72 | } 73 | 74 | var ch = stream.next(); 75 | if (ch == "<") { 76 | if (stream.eat("!")) { 77 | if (stream.eat("[")) { 78 | if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>")); 79 | else return null; 80 | } else if (stream.match("--")) { 81 | return chain(inBlock("comment", "-->")); 82 | } else if (stream.match("DOCTYPE", true, true)) { 83 | stream.eatWhile(/[\w\._\-]/); 84 | return chain(doctype(1)); 85 | } else { 86 | return null; 87 | } 88 | } else if (stream.eat("?")) { 89 | stream.eatWhile(/[\w\._\-]/); 90 | state.tokenize = inBlock("meta", "?>"); 91 | return "meta"; 92 | } else { 93 | type = stream.eat("/") ? "closeTag" : "openTag"; 94 | state.tokenize = inTag; 95 | return "tag bracket"; 96 | } 97 | } else if (ch == "&") { 98 | var ok; 99 | if (stream.eat("#")) { 100 | if (stream.eat("x")) { 101 | ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); 102 | } else { 103 | ok = stream.eatWhile(/[\d]/) && stream.eat(";"); 104 | } 105 | } else { 106 | ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";"); 107 | } 108 | return ok ? "atom" : "error"; 109 | } else { 110 | stream.eatWhile(/[^&<]/); 111 | return null; 112 | } 113 | } 114 | inText.isInText = true; 115 | 116 | function inTag(stream, state) { 117 | var ch = stream.next(); 118 | if (ch == ">" || (ch == "/" && stream.eat(">"))) { 119 | state.tokenize = inText; 120 | type = ch == ">" ? "endTag" : "selfcloseTag"; 121 | return "tag bracket"; 122 | } else if (ch == "=") { 123 | type = "equals"; 124 | return null; 125 | } else if (ch == "<") { 126 | state.tokenize = inText; 127 | state.state = baseState; 128 | state.tagName = state.tagStart = null; 129 | var next = state.tokenize(stream, state); 130 | return next ? next + " tag error" : "tag error"; 131 | } else if (/[\'\"]/.test(ch)) { 132 | state.tokenize = inAttribute(ch); 133 | state.stringStartCol = stream.column(); 134 | return state.tokenize(stream, state); 135 | } else { 136 | stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/); 137 | return "word"; 138 | } 139 | } 140 | 141 | function inAttribute(quote) { 142 | var closure = function(stream, state) { 143 | while (!stream.eol()) { 144 | if (stream.next() == quote) { 145 | state.tokenize = inTag; 146 | break; 147 | } 148 | } 149 | return "string"; 150 | }; 151 | closure.isInAttribute = true; 152 | return closure; 153 | } 154 | 155 | function inBlock(style, terminator) { 156 | return function(stream, state) { 157 | while (!stream.eol()) { 158 | if (stream.match(terminator)) { 159 | state.tokenize = inText; 160 | break; 161 | } 162 | stream.next(); 163 | } 164 | return style; 165 | }; 166 | } 167 | function doctype(depth) { 168 | return function(stream, state) { 169 | var ch; 170 | while ((ch = stream.next()) != null) { 171 | if (ch == "<") { 172 | state.tokenize = doctype(depth + 1); 173 | return state.tokenize(stream, state); 174 | } else if (ch == ">") { 175 | if (depth == 1) { 176 | state.tokenize = inText; 177 | break; 178 | } else { 179 | state.tokenize = doctype(depth - 1); 180 | return state.tokenize(stream, state); 181 | } 182 | } 183 | } 184 | return "meta"; 185 | }; 186 | } 187 | 188 | function Context(state, tagName, startOfLine) { 189 | this.prev = state.context; 190 | this.tagName = tagName; 191 | this.indent = state.indented; 192 | this.startOfLine = startOfLine; 193 | if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent)) 194 | this.noIndent = true; 195 | } 196 | function popContext(state) { 197 | if (state.context) state.context = state.context.prev; 198 | } 199 | function maybePopContext(state, nextTagName) { 200 | var parentTagName; 201 | while (true) { 202 | if (!state.context) { 203 | return; 204 | } 205 | parentTagName = state.context.tagName; 206 | if (!config.contextGrabbers.hasOwnProperty(parentTagName) || 207 | !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { 208 | return; 209 | } 210 | popContext(state); 211 | } 212 | } 213 | 214 | function baseState(type, stream, state) { 215 | if (type == "openTag") { 216 | state.tagStart = stream.column(); 217 | return tagNameState; 218 | } else if (type == "closeTag") { 219 | return closeTagNameState; 220 | } else { 221 | return baseState; 222 | } 223 | } 224 | function tagNameState(type, stream, state) { 225 | if (type == "word") { 226 | state.tagName = stream.current(); 227 | setStyle = "tag"; 228 | return attrState; 229 | } else { 230 | setStyle = "error"; 231 | return tagNameState; 232 | } 233 | } 234 | function closeTagNameState(type, stream, state) { 235 | if (type == "word") { 236 | var tagName = stream.current(); 237 | if (state.context && state.context.tagName != tagName && 238 | config.implicitlyClosed.hasOwnProperty(state.context.tagName)) 239 | popContext(state); 240 | if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) { 241 | setStyle = "tag"; 242 | return closeState; 243 | } else { 244 | setStyle = "tag error"; 245 | return closeStateErr; 246 | } 247 | } else { 248 | setStyle = "error"; 249 | return closeStateErr; 250 | } 251 | } 252 | 253 | function closeState(type, _stream, state) { 254 | if (type != "endTag") { 255 | setStyle = "error"; 256 | return closeState; 257 | } 258 | popContext(state); 259 | return baseState; 260 | } 261 | function closeStateErr(type, stream, state) { 262 | setStyle = "error"; 263 | return closeState(type, stream, state); 264 | } 265 | 266 | function attrState(type, _stream, state) { 267 | if (type == "word") { 268 | setStyle = "attribute"; 269 | return attrEqState; 270 | } else if (type == "endTag" || type == "selfcloseTag") { 271 | var tagName = state.tagName, tagStart = state.tagStart; 272 | state.tagName = state.tagStart = null; 273 | if (type == "selfcloseTag" || 274 | config.autoSelfClosers.hasOwnProperty(tagName)) { 275 | maybePopContext(state, tagName); 276 | } else { 277 | maybePopContext(state, tagName); 278 | state.context = new Context(state, tagName, tagStart == state.indented); 279 | } 280 | return baseState; 281 | } 282 | setStyle = "error"; 283 | return attrState; 284 | } 285 | function attrEqState(type, stream, state) { 286 | if (type == "equals") return attrValueState; 287 | if (!config.allowMissing) setStyle = "error"; 288 | return attrState(type, stream, state); 289 | } 290 | function attrValueState(type, stream, state) { 291 | if (type == "string") return attrContinuedState; 292 | if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;} 293 | setStyle = "error"; 294 | return attrState(type, stream, state); 295 | } 296 | function attrContinuedState(type, stream, state) { 297 | if (type == "string") return attrContinuedState; 298 | return attrState(type, stream, state); 299 | } 300 | 301 | return { 302 | startState: function(baseIndent) { 303 | var state = {tokenize: inText, 304 | state: baseState, 305 | indented: baseIndent || 0, 306 | tagName: null, tagStart: null, 307 | context: null} 308 | if (baseIndent != null) state.baseIndent = baseIndent 309 | return state 310 | }, 311 | 312 | token: function(stream, state) { 313 | if (!state.tagName && stream.sol()) 314 | state.indented = stream.indentation(); 315 | 316 | if (stream.eatSpace()) return null; 317 | type = null; 318 | var style = state.tokenize(stream, state); 319 | if ((style || type) && style != "comment") { 320 | setStyle = null; 321 | state.state = state.state(type || style, stream, state); 322 | if (setStyle) 323 | style = setStyle == "error" ? style + " error" : setStyle; 324 | } 325 | return style; 326 | }, 327 | 328 | indent: function(state, textAfter, fullLine) { 329 | var context = state.context; 330 | // Indent multi-line strings (e.g. css). 331 | if (state.tokenize.isInAttribute) { 332 | if (state.tagStart == state.indented) 333 | return state.stringStartCol + 1; 334 | else 335 | return state.indented + indentUnit; 336 | } 337 | if (context && context.noIndent) return CodeMirror.Pass; 338 | if (state.tokenize != inTag && state.tokenize != inText) 339 | return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; 340 | // Indent the starts of attribute names. 341 | if (state.tagName) { 342 | if (config.multilineTagIndentPastTag !== false) 343 | return state.tagStart + state.tagName.length + 2; 344 | else 345 | return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1); 346 | } 347 | if (config.alignCDATA && /$/, 376 | blockCommentStart: "", 378 | 379 | configuration: config.htmlMode ? "html" : "xml", 380 | helperType: config.htmlMode ? "html" : "xml", 381 | 382 | skipAttribute: function(state) { 383 | if (state.state == attrValueState) 384 | state.state = attrState 385 | } 386 | }; 387 | }); 388 | 389 | CodeMirror.defineMIME("text/xml", "xml"); 390 | CodeMirror.defineMIME("application/xml", "xml"); 391 | if (!CodeMirror.mimeModes.hasOwnProperty("text/html")) 392 | CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true}); 393 | 394 | }); 395 | -------------------------------------------------------------------------------- /app/lib/anime.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | 2017 Julian Garnier 3 | Released under the MIT license 4 | */ 5 | var $jscomp$this=this; 6 | (function(u,r){"function"===typeof define&&define.amd?define([],r):"object"===typeof module&&module.exports?module.exports=r():u.anime=r()})(this,function(){function u(a){if(!g.col(a))try{return document.querySelectorAll(a)}catch(b){}}function r(a){return a.reduce(function(a,c){return a.concat(g.arr(c)?r(c):c)},[])}function v(a){if(g.arr(a))return a;g.str(a)&&(a=u(a)||a);return a instanceof NodeList||a instanceof HTMLCollection?[].slice.call(a):[a]}function E(a,b){return a.some(function(a){return a===b})} 7 | function z(a){var b={},c;for(c in a)b[c]=a[c];return b}function F(a,b){var c=z(a),d;for(d in a)c[d]=b.hasOwnProperty(d)?b[d]:a[d];return c}function A(a,b){var c=z(a),d;for(d in b)c[d]=g.und(a[d])?b[d]:a[d];return c}function R(a){a=a.replace(/^#?([a-f\d])([a-f\d])([a-f\d])$/i,function(a,b,c,h){return b+b+c+c+h+h});var b=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(a);a=parseInt(b[1],16);var c=parseInt(b[2],16),b=parseInt(b[3],16);return"rgb("+a+","+c+","+b+")"}function S(a){function b(a,b,c){0> 8 | c&&(c+=1);1c?b:c<2/3?a+(b-a)*(2/3-c)*6:a}var c=/hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(a);a=parseInt(c[1])/360;var d=parseInt(c[2])/100,c=parseInt(c[3])/100;if(0==d)d=c=a=c;else{var e=.5>c?c*(1+d):c+d-c*d,k=2*c-e,d=b(k,e,a+1/3),c=b(k,e,a);a=b(k,e,a-1/3)}return"rgb("+255*d+","+255*c+","+255*a+")"}function w(a){if(a=/([\+\-]?[0-9#\.]+)(%|px|pt|em|rem|in|cm|mm|ex|pc|vw|vh|deg|rad|turn)?/.exec(a))return a[2]}function T(a){if(-1k&&q=m&&(f.began=!0,e("begin")),e("run")):(q<=k&&0!==O&&(d(0),p&&g()),q>=h&&O!==h&&(d(h),p||g()));a>=h&&(f.remaining?(t=n,"alternate"===f.direction&&(f.reversed=!f.reversed)):(f.pause(),P(),Q=b(),f.completed||(f.completed=!0,e("complete"))),l=0);if(f.children)for(a=f.children,h=0;h=b&& 22 | 0<=d&&1>=d){var g=new Float32Array(11);if(b!==c||d!==e)for(var h=0;11>h;++h)g[h]=a(.1*h,b,d);return function(h){if(b===c&&d===e)return h;if(0===h)return 0;if(1===h)return 1;for(var k=0,l=1;10!==l&&g[l]<=h;++l)k+=.1;--l;var l=k+(h-g[l])/(g[l+1]-g[l])*.1,n=3*(1-3*d+3*b)*l*l+2*(3*d-6*b)*l+3*b;if(.001<=n){for(k=0;4>k;++k){n=3*(1-3*d+3*b)*l*l+2*(3*d-6*b)*l+3*b;if(0===n)break;var m=a(l,b,d)-h,l=l-m/n}h=l}else if(0===n)h=l;else{var l=k,k=k+.1,f=0;do m=l+(k-l)/2,n=a(m,b,d)-h,0++f);h=m}return a(h,c,e)}}}}(),M=function(){function a(a,b){return 0===a||1===a?a:-Math.pow(2,10*(a-1))*Math.sin(2*(a-1-b/(2*Math.PI)*Math.asin(1))*Math.PI/b)}var b="Quad Cubic Quart Quint Sine Expo Circ Back Elastic".split(" "),c={In:[[.55,.085,.68,.53],[.55,.055,.675,.19],[.895,.03,.685,.22],[.755,.05,.855,.06],[.47,0,.745,.715],[.95,.05,.795,.035],[.6,.04,.98,.335],[.6,-.28,.735,.045],a],Out:[[.25,.46,.45,.94],[.215,.61,.355,1],[.165,.84,.44,1],[.23,1,.32,1],[.39,.575,.565,1],[.19,1,.22,1], 24 | [.075,.82,.165,1],[.175,.885,.32,1.275],function(b,c){return 1-a(1-b,c)}],InOut:[[.455,.03,.515,.955],[.645,.045,.355,1],[.77,0,.175,1],[.86,0,.07,1],[.445,.05,.55,.95],[1,0,0,1],[.785,.135,.15,.86],[.68,-.55,.265,1.55],function(b,c){return.5>b?a(2*b,c)/2:1-a(-2*b+2,c)/2}]},d={linear:x(.25,.25,.75,.75)},e={},k;for(k in c)e.type=k,c[e.type].forEach(function(a){return function(c,e){d["ease"+a.type+b[e]]=g.fnc(c)?c:x.apply($jscomp$this,c)}}(e)),e={type:e.type};return d}(),ha={css:function(a,b,c){return a.style[b]= 25 | c},attribute:function(a,b,c){return a.setAttribute(b,c)},object:function(a,b,c){return a[b]=c},transform:function(a,b,c,d,e){d[e]||(d[e]=[]);d[e].push(b+"("+c+")")}},p=[],y=0,ia=function(){function a(){y=requestAnimationFrame(b)}function b(b){var c=p.length;if(c){for(var e=0;ed&&(b.duration=a.duration);b.children.push(a)});return b};return b};m.random=function(a,b){return Math.floor(Math.random()*(b-a+1))+a};return m}); -------------------------------------------------------------------------------- /app/lib/font-awesome.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */@font-face{font-family:'FontAwesome';src:url('fontawesome-webfont.eot?v=4.7.0');src:url('fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('fontawesome-webfont.woff?v=4.7.0') format('woff'),url('fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} 5 | -------------------------------------------------------------------------------- /app/lib/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecodepad/codepad/7bb457c6bd741da50f11b156e44d7893ba2291b1/app/lib/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /app/lib/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecodepad/codepad/7bb457c6bd741da50f11b156e44d7893ba2291b1/app/lib/fontawesome-webfont.woff -------------------------------------------------------------------------------- /app/lib/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecodepad/codepad/7bb457c6bd741da50f11b156e44d7893ba2291b1/app/lib/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /app/lib/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecodepad/codepad/7bb457c6bd741da50f11b156e44d7893ba2291b1/app/lib/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /app/lib/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecodepad/codepad/7bb457c6bd741da50f11b156e44d7893ba2291b1/app/lib/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /app/lib/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecodepad/codepad/7bb457c6bd741da50f11b156e44d7893ba2291b1/app/lib/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /app/lib/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecodepad/codepad/7bb457c6bd741da50f11b156e44d7893ba2291b1/app/lib/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /app/main.js: -------------------------------------------------------------------------------- 1 | const electron = require('electron') 2 | // Module to control application life. 3 | const app = electron.app 4 | // Module to create native browser window. 5 | const BrowserWindow = electron.BrowserWindow 6 | const path = require('path') 7 | 8 | let mainWindow 9 | 10 | // Quit when all windows are closed. 11 | app.on('window-all-closed', () => { 12 | if (process.platform != 'darwin') { 13 | app.quit() 14 | } 15 | }) 16 | 17 | app.setAppUserModelId('CodePad') 18 | 19 | // This method will be called when Electron has done everything 20 | // initialization and ready for creating browser windows. 21 | app.on('ready', () => { 22 | // Create the browser window. 23 | mainWindow = new BrowserWindow({ 24 | title: 'CodePad', 25 | width: 1024, 26 | height: 720, 27 | frame: false, 28 | minHeight: 600, 29 | minWidth: 600, 30 | icon: path.join(__dirname, '../build/CB.ico') 31 | }) 32 | 33 | // and load the index.html of the app. 34 | mainWindow.loadURL(path.join('file://', __dirname, '/index.html')) 35 | 36 | // Emitted when the window is closed. 37 | mainWindow.on('closed', () => { 38 | // Dereference the window object, usually you would store windows 39 | // in an array if your app supports multi windows, this is the time 40 | // when you should delete the corresponding element. 41 | mainWindow = null 42 | }) 43 | }) 44 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codepad", 3 | "version": "0.1.1", 4 | "description": "An offline front-end development playground", 5 | "productName": "CodePad", 6 | "main": "main.js", 7 | "scripts": { 8 | "start": "electron ." 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/thecodepad/codepad" 13 | }, 14 | "keywords": [ 15 | "electron", 16 | "front-end", 17 | "editor" 18 | ], 19 | "license": "MIT", 20 | "devDependencies": { 21 | "electron": "^1.6.1", 22 | "standard": "^9.0.0" 23 | } 24 | } -------------------------------------------------------------------------------- /app/scripts.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | addScript, 3 | getJsLibs 4 | } 5 | 6 | function getJsLibs () { 7 | var jsHtml = '' 8 | for (var i = 0; i < jsLib.length; i++) { 9 | if (scrFlags[i] === 1) { 10 | jsHtml += jsLib[i][1].replace('lib/', '') 11 | } 12 | } 13 | return jsHtml 14 | } 15 | 16 | function addScript () { 17 | var jsMenu = document.getElementById('js-menu') 18 | var jsButtons = jsMenu.getElementsByTagName('a') 19 | let jsSpan = jsMenu.querySelectorAll('span') 20 | jsButtons[0].addEventListener('click', function (e) { 21 | var jqScr = jsLib[0][1] 22 | if (scrFlags[0] === 0) { 23 | scripts += jqScr 24 | scrFlags[0] = 1 25 | toggleStatus(0, jsSpan) 26 | } else if (scrFlags[2] === 0) { 27 | scripts = scripts.replace(jqScr, '') 28 | scrFlags[0] = 0 29 | toggleStatus(0, jsSpan) 30 | } 31 | }) 32 | jsButtons[1].addEventListener('click', function (e) { 33 | var aniScr = jsLib[1][1] 34 | if (scrFlags[1] === 0) { 35 | scripts += aniScr 36 | scrFlags[1] = 1 37 | toggleStatus(1, jsSpan) 38 | } else { 39 | scripts = scripts.replace(aniScr, '') 40 | scrFlags[1] = 0 41 | toggleStatus(1, jsSpan) 42 | } 43 | }) 44 | jsButtons[2].addEventListener('click', function (e) { 45 | var jqScr = jsLib[0][1] 46 | var boScr = jsLib[2][1] 47 | if (scrFlags[0] === 0) { 48 | toggleStatus(0, jsSpan) 49 | scripts += jqScr 50 | scrFlags[0] = 1 51 | } 52 | if (scrFlags[2] === 0) { 53 | scripts += boScr 54 | scrFlags[2] = 1 55 | toggleStatus(2, jsSpan) 56 | } else { 57 | scripts = scripts.replace(boScr, '') 58 | scrFlags[2] = 0 59 | toggleStatus(2, jsSpan) 60 | } 61 | }) 62 | jsButtons[3].addEventListener('click', function (e) { 63 | var p5Scr = jsLib[3][1] 64 | if (scrFlags[3] === 0) { 65 | scripts += p5Scr 66 | scrFlags[3] = 1 67 | toggleStatus(3, jsSpan) 68 | } else { 69 | scripts = scripts.replace(p5Scr, '') 70 | scrFlags[3] = 0 71 | toggleStatus(3, jsSpan) 72 | } 73 | }) 74 | jsButtons[4].addEventListener('click', function (e) { 75 | var thScr = jsLib[4][1] 76 | if (scrFlags[4] === 0) { 77 | scripts += thScr 78 | scrFlags[4] = 1 79 | toggleStatus(4, jsSpan) 80 | } else { 81 | scripts = scripts.replace(thScr, '') 82 | scrFlags[4] = 0 83 | toggleStatus(4, jsSpan) 84 | } 85 | }) 86 | } 87 | -------------------------------------------------------------------------------- /app/shortcuts.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | shortcuts 3 | } 4 | 5 | function shortcuts () { 6 | window.addEventListener('keydown', (e) => { 7 | if (e.ctrlKey && e.key === 'Tab') { 8 | toggleEditors(getCurrenEditor()) 9 | } 10 | if (e.key === 'F11') { 11 | if (!remote.getCurrentWindow().isMaximized()) { 12 | remote.getCurrentWindow().maximize() 13 | } else { 14 | remote.getCurrentWindow().unmaximize() 15 | } 16 | } 17 | if (e.key === 'F12') { 18 | remote.getCurrentWindow().toggleDevTools() 19 | } 20 | if (e.ctrlKey && (e.key === 's' || e.key === 'S')) { 21 | FILE.handleSave() 22 | } 23 | if (e.ctrlKey && (e.key === 'n' || e.key === 'N')) { 24 | FILE.handleNew() 25 | } 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /app/styles.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | addStyle, 3 | getCssLibs 4 | } 5 | 6 | function getCssLibs () { 7 | var cssHtml = '' 8 | for (var i = 0; i < cssLib.length; i++) { 9 | if (styFlags[i] === 1) { 10 | cssHtml += cssLib[i][1].replace('lib/', '') 11 | } 12 | } 13 | return cssHtml 14 | } 15 | 16 | function addStyle () { 17 | var cssMenu = document.getElementById('css-menu') 18 | var cssButtons = cssMenu.getElementsByTagName('a') 19 | let cssSpan = cssMenu.querySelectorAll('span') 20 | cssButtons[0].addEventListener('click', function (e) { 21 | var anSty = cssLib[0][1] 22 | if (styFlags[0] === 0) { 23 | styles += anSty 24 | styFlags[0] = 1 25 | toggleStatus(0, cssSpan) 26 | } else { 27 | styles = styles.replace(anSty, '') 28 | styFlags[0] = 0 29 | toggleStatus(0, cssSpan) 30 | } 31 | }) 32 | cssButtons[1].addEventListener('click', function (e) { 33 | var boSty = cssLib[1][1] 34 | if (styFlags[1] === 0) { 35 | styles += boSty 36 | styFlags[1] = 1 37 | toggleStatus(1, cssSpan) 38 | } else { 39 | styles = styles.replace(boSty, '') 40 | styFlags[1] = 0 41 | toggleStatus(1, cssSpan) 42 | } 43 | }) 44 | cssButtons[2].addEventListener('click', function (e) { 45 | var faSty = cssLib[2][1] 46 | if (styFlags[2] === 0) { 47 | styles += faSty 48 | styFlags[2] = 1 49 | toggleStatus(2, cssSpan) 50 | } else { 51 | styles = styles.replace(faSty, '') 52 | styFlags[2] = 0 53 | toggleStatus(2, cssSpan) 54 | } 55 | }) 56 | } 57 | -------------------------------------------------------------------------------- /app/viewMenu.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | viewMenu 3 | } 4 | 5 | // 'View' Menu Buttons 6 | function viewMenu () { 7 | document.getElementById('dev').addEventListener('click', () => { 8 | remote.getCurrentWindow().toggleDevTools() 9 | }) 10 | document.getElementById('full').addEventListener('click', () => { 11 | if (!remote.getCurrentWindow().isMaximized()) { 12 | remote.getCurrentWindow().maximize() 13 | } else { 14 | remote.getCurrentWindow().unmaximize() 15 | } 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /app/windowControl.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | windowClicks, 3 | initContextMenu 4 | } 5 | // Window Control [_][+][x] Buttons 6 | function windowClicks () { 7 | document.getElementById('min-button').addEventListener('click', function (e) { 8 | remote.getCurrentWindow().minimize() 9 | }) 10 | 11 | document.getElementById('max-button').addEventListener('click', function (e) { 12 | if (!remote.getCurrentWindow().isMaximized()) { 13 | remote.getCurrentWindow().maximize() 14 | } else { 15 | remote.getCurrentWindow().unmaximize() 16 | } 17 | }) 18 | 19 | document.getElementById('close-button').addEventListener('click', function (e) { 20 | remote.getCurrentWindow().close() 21 | }) 22 | } 23 | 24 | // Context menu init() 25 | function initContextMenu () { 26 | menu = new Menu() 27 | menu.append(new MenuItem({ 28 | label: 'Copy', 29 | role: 'copy' 30 | // click: copy 31 | })) 32 | menu.append(new MenuItem({ 33 | label: 'Cut', 34 | role: 'cut' 35 | // click: cut 36 | })) 37 | menu.append(new MenuItem({ 38 | label: 'Paste', 39 | role: 'paste' 40 | // click: paste 41 | })) 42 | 43 | window.addEventListener('contextmenu', function (ev) { 44 | ev.preventDefault() 45 | menu.popup(remote.getCurrentWindow(), ev.x, ev.y) 46 | }, false) 47 | } 48 | -------------------------------------------------------------------------------- /build/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecodepad/codepad/7bb457c6bd741da50f11b156e44d7893ba2291b1/build/icon.icns -------------------------------------------------------------------------------- /build/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecodepad/codepad/7bb457c6bd741da50f11b156e44d7893ba2291b1/build/icon.ico -------------------------------------------------------------------------------- /build/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecodepad/codepad/7bb457c6bd741da50f11b156e44d7893ba2291b1/build/icons/128x128.png -------------------------------------------------------------------------------- /build/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecodepad/codepad/7bb457c6bd741da50f11b156e44d7893ba2291b1/build/icons/512x512.png -------------------------------------------------------------------------------- /build/icons/CB.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Codepad Blue 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codepad", 3 | "version": "0.1.1", 4 | "description": "An offline front-end development playground", 5 | "productName": "CodePad", 6 | "main": "./app/", 7 | "scripts": { 8 | "start": "electron ./app/", 9 | "build:win32": "electron-packager . CodePad --app-bundle-id=codepad --overwrite --asar=true --platform=win32 --arch=ia32 --icon=build/icon.ico --prune=true --out=dist --version-string.ProductName=CodePad --version-string.FileDescription=CodePad", 10 | "build:win64": "electron-packager . CodePad --app-bundle-id=codepad --overwrite --asar=true --platform=win32 --arch=x64 --icon=build/icon.ico --prune=true --out=dist --version-string.ProductName=CodePad --version-string.FileDescription=CodePad" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/thecodepad/codepad" 15 | }, 16 | "keywords": [ 17 | "electron", 18 | "front-end", 19 | "editor" 20 | ], 21 | "license": "MIT", 22 | "devDependencies": { 23 | "asar": "^0.13.0", 24 | "electron": "^1.6.1", 25 | "electron-packager": "^8.5.2", 26 | "standard": "^9.0.0" 27 | } 28 | } --------------------------------------------------------------------------------