├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── CONTRIBUTIONS.md ├── LICENCE.md ├── README.md ├── _src ├── build-assets │ ├── linux-icon.png │ ├── mac-icon.icns │ └── win-icon.ico └── scss │ ├── _editor.scss │ ├── _elements.scss │ ├── _helpers.scss │ ├── _layout.scss │ ├── _menu-editor.scss │ ├── _menu-mixer.scss │ ├── _preferences.scss │ └── main.scss ├── gulpfile.js ├── main.js ├── package.json ├── private ├── ambiance │ ├── fieldrecordings │ │ ├── 01_rain_mixdown_01.mp3 │ │ ├── 02_.mp3 │ │ ├── 03_office_mixdown.mp3 │ │ └── 04_lake_rocks_01.mp3 │ ├── images │ │ ├── .DS_Store │ │ ├── bg_03.jpeg │ │ ├── cork-wallet.png │ │ ├── stardust.png │ │ └── water.jpg │ ├── keySounds │ │ ├── key_01.wav │ │ └── key_02.wav │ └── stems │ │ ├── 01 │ │ ├── Viv - loop1_01.mp3 │ │ ├── Viv - loop2_01.mp3 │ │ └── Viv - loop3_01.mp3 │ │ ├── 02 │ │ ├── valuse_01.mp3 │ │ └── valuse_02.mp3 │ │ └── 03 │ │ ├── e-piano_01.mp3 │ │ ├── kalimba_ish_01.mp3 │ │ └── sines_01.mp3 └── settings.json ├── public ├── assets │ ├── css │ │ ├── github-markdown.css │ │ ├── ionicons.css │ │ └── main.css │ ├── fonts │ │ ├── ionicons.eot │ │ ├── ionicons.svg │ │ ├── ionicons.ttf │ │ └── ionicons.woff │ └── js │ │ ├── core │ │ ├── content-update.js │ │ ├── file-io.js │ │ ├── interaction │ │ │ ├── audio.js │ │ │ ├── back-drop.js │ │ │ ├── field-recording.js │ │ │ └── font.js │ │ ├── menu-ambient.js │ │ ├── menu-app.js │ │ ├── preview-render.js │ │ └── settings.js │ │ ├── helpers │ │ ├── dom-elements.js │ │ ├── monochrome-thief.js │ │ ├── tree.js │ │ └── utilities.js │ │ └── setup.js ├── index.html └── preview.html └── renderer.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = spaces 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "standard", 3 | "plugins": [ 4 | "standard", 5 | "promise" 6 | ] 7 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | 35 | testfile.txt 36 | .atomignore 37 | 38 | 39 | jsconfig.json 40 | media 41 | .vscode 42 | documentation 43 | 44 | .DS_STORE 45 | 46 | dist -------------------------------------------------------------------------------- /CONTRIBUTIONS.md: -------------------------------------------------------------------------------- 1 | Contributions to MoonView are welcome. MoonView is a personal project with no deadlines (yet) or really no great ambitions. I just want to make stuff. 2 | 3 | Please checkout the issues for a roadmap of possible features that need work. 4 | 5 | If you do have an interest in getting involved, the project follows `standardjs` code formatting, with eslint. I'm trying out the semi-free lifestyle. 6 | 7 | Also note, this project is currently written in plain javascript. While I'm open to the idea of integrating a cool, neat-o framework, the start of the project didn't warrant it. Also, I didn't feel like doing config for days. 8 | 9 | This doc (like everything else) is a work in progress. 10 | 11 | -------------------------------------------------------------------------------- /LICENCE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Tyler 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 | # moonview 2 | 3 | ## What is moonview? 4 | 5 | moonview allows you to write text files inside an ambient bubble. Pick from a collection of medias and create your own writing environment: moonview comes packaged with a handful of fonts, backdrops, field recordings, and audio stems. 6 | 7 |  8 | 9 | ## Download / Install 10 | 11 | You can head over to the `releases` tab to get the latest version of moonview. 12 | Currently Mac builds are being supported, and soon Linux and Windows will follow. If you just can't wait, check out the build instructions below. Currently, `electron-packager` is used for bundling the app, and a linux / windows build script has not been written (hint pull requests hint). 13 | 14 | ## Can I customize moonview Further? 15 | 16 | If you are technically inclined you can customize moonview however you like. First note that the App is entirely open source, so you can fork the repo and make changes as you like. 17 | 18 | If you would like to replace the media assets that come with moonview, you can do that with a bit of folder digging. 19 | 20 | **How Audio Files work** 21 | 22 | moonview gets a bit creative with working with playing audio files. For some, you may be disappointed to find that you cannot simply drop an audio file into a folder and have it available in moonview. 23 | 24 | **A brief Preface to customizing ambient media assets** 25 | 26 | moonview works with _folders_ of audio files. Think of each folder as a "song", and the contents of that folder as the different sounds you hear in the song. For example, `Song 1` Might have a `Bass.mp3`, `Synth.mp3` and `Chamber choir.mp3` stem file inside it. moonview uses folders in this way to randomly play back the audio stems in a way so that each time you listen to one of the "songs" it will never be the same as before. 27 | 28 | Note: If you do want to use your own audio stems, seamless audio loops will make for a more pleasant audio experience (unless you don't mind obvious audio looping). 29 | 30 | ## Can I contribute? 31 | 32 | Yes. moonview uses Standard Js for linting standards. 33 | 34 | moonview is intentionally written with Just JavaScript (and Electron); so any PR's to add Javascript front end frameworks will likely be turned down. 35 | 36 | ## Build Instructions 37 | 38 | Install dependencies: 39 | 40 | `npm install` 41 | 42 | Start the electron application: 43 | 44 | ` npm start ` 45 | 46 | Run gulp to compile `.css` files: 47 | 48 | `gulp` 49 | -------------------------------------------------------------------------------- /_src/build-assets/linux-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/_src/build-assets/linux-icon.png -------------------------------------------------------------------------------- /_src/build-assets/mac-icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/_src/build-assets/mac-icon.icns -------------------------------------------------------------------------------- /_src/build-assets/win-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/_src/build-assets/win-icon.ico -------------------------------------------------------------------------------- /_src/scss/_editor.scss: -------------------------------------------------------------------------------- 1 | #editor { 2 | align-items: center; 3 | background: rgba(255, 255, 255, 0.0); 4 | display: flex; 5 | letter-spacing: 1px; 6 | padding: 6% 5% 2%; 7 | } 8 | textarea { 9 | border: 0; 10 | display: flex; 11 | margin: 0 auto; 12 | margin: 0; 13 | outline: none; 14 | padding: 0; 15 | resize: none; 16 | width: 540px; 17 | margin: 6% auto; 18 | } 19 | -------------------------------------------------------------------------------- /_src/scss/_elements.scss: -------------------------------------------------------------------------------- 1 | button { 2 | background: none; 3 | border: 0; 4 | font: inherit; 5 | line-height: normal; 6 | outline: none; 7 | overflow: visible; 8 | } 9 | .btn { 10 | &.gourd { 11 | background: rgb(217, 217, 217); 12 | border-bottom: 2px solid transparent; 13 | border-radius: 50%; 14 | font-size: 10px; 15 | height: 25px; 16 | margin: 0 20px; 17 | padding-top: 5px; 18 | width: 25px; 19 | &:hover { 20 | background: rgb(200, 200, 200); 21 | } 22 | &:active { 23 | background: rgb(180, 180, 180); 24 | } 25 | &.on { 26 | background: rgb(219, 219, 219); 27 | box-shadow: 0 0 5px rgba(235, 235, 235, 1); 28 | } 29 | } 30 | } 31 | #mixer { 32 | input[type=range] { 33 | -webkit-appearance: none; 34 | } 35 | input[type=range] { 36 | background: #ddd; 37 | border-radius: 10px; 38 | border: none; 39 | height: 5px; 40 | width: 150px; 41 | 42 | @media(max-width: 500px) { 43 | width: 75px; 44 | } 45 | } 46 | input[type=range]::-webkit-slider-thumb { 47 | -webkit-appearance: none; 48 | background: goldenrod; 49 | border-radius: 50%; 50 | border: none; 51 | box-shadow: 0 2px 3px rgba(0,0,0,0.22); 52 | height: 16px; 53 | width: 16px; 54 | 55 | &:active { 56 | box-shadow: 0 0px 0px rgba(0,0,0,0.0); 57 | } 58 | } 59 | 60 | input[type=range]:focus { 61 | outline: none; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /_src/scss/_helpers.scss: -------------------------------------------------------------------------------- 1 | /* ====================== Border Helpers ====================== */ 2 | ._border { 3 | &.top { 4 | border-top: solid; 5 | } 6 | &.right { 7 | border-right: solid; 8 | } 9 | &.bottom { 10 | border-bottom: solid; 11 | } 12 | &.left { 13 | border-left: solid; 14 | } 15 | &.thin { 16 | border-width: 1px; 17 | } 18 | &.thick { 19 | border-width: 3px; 20 | } 21 | } 22 | /* ====================== Padding Helpers ====================== */ 23 | .p-none { 24 | padding: 0; 25 | } 26 | // all around padding 27 | .p-two { 28 | padding: $padding-base * 2; 29 | } 30 | .p-one { 31 | padding: $padding-base; 32 | } 33 | .p-half { 34 | padding: $padding-base / 2; 35 | } 36 | .p-quarter { 37 | padding: $padding-base / 4; 38 | } 39 | // padding top and bottom 40 | .ptb-two { 41 | padding-top: $padding-base * 2; 42 | padding-bottom: $padding-base * 2; 43 | } 44 | .ptb-one { 45 | padding-top: $padding-base; 46 | padding-bottom: $padding-base; 47 | } 48 | .ptb-half { 49 | padding-top: $padding-base / 2; 50 | padding-bottom: $padding-base / 2; 51 | } 52 | .ptb-quarter { 53 | padding-top: $padding-base / 4; 54 | padding-bottom: $padding-base / 4; 55 | } 56 | // padding left and ride 57 | .plr-two { 58 | padding-left: $padding-base * 2; 59 | padding-right: $padding-base * 2; 60 | } 61 | .plr-one { 62 | padding-left: $padding-base; 63 | padding-right: $padding-base; 64 | } 65 | .plr-half { 66 | padding-left: $padding-base / 2; 67 | padding-right: $padding-base / 2; 68 | } 69 | .plr-quarter { 70 | padding-left: $padding-base / 4; 71 | padding-right: $padding-base / 4; 72 | } 73 | // padding top 74 | .pt-two { 75 | padding-top: $padding-base * 2; 76 | } 77 | .pt-one { 78 | padding-top: $padding-base; 79 | } 80 | .pt-half { 81 | padding-top: $padding-base / 2; 82 | } 83 | .pt-quarter { 84 | padding-top: $padding-base / 4; 85 | } 86 | // padding right 87 | .pr-two { 88 | padding-right: $padding-base * 2; 89 | } 90 | .pr-one { 91 | padding-right: $padding-base; 92 | } 93 | .pr-half { 94 | padding-right: $padding-base / 2; 95 | } 96 | .pr-quarter { 97 | padding-right: $padding-base / 4; 98 | } 99 | // padding bottom 100 | .pb-two { 101 | padding-bottom: $padding-base * 2; 102 | } 103 | .pb-one { 104 | padding-bottom: $padding-base; 105 | } 106 | .pb-half { 107 | padding-bottom: $padding-base / 2; 108 | } 109 | .pb-quarter { 110 | padding-bottom: $padding-base / 4; 111 | } 112 | // padding left 113 | .pl-two { 114 | padding-left: $padding-base * 2; 115 | } 116 | .pl-one { 117 | padding-left: $padding-base; 118 | } 119 | .pl-half { 120 | padding-left: $padding-base / 2; 121 | } 122 | .pl-quarter { 123 | padding-left: $padding-base / 4; 124 | } 125 | /* ====================== Margin Helpers ====================== */ 126 | .m-none { 127 | margin: 0; 128 | } 129 | // all around margin 130 | .m-two { 131 | margin: $margin-base * 2; 132 | } 133 | .m-one { 134 | margin: $margin-base; 135 | } 136 | .m-half { 137 | margin: $margin-base / 2; 138 | } 139 | .m-quarter { 140 | margin: $margin-base / 4; 141 | } 142 | // margin top and bottom 143 | .mtb-two { 144 | margin-top: $margin-base * 2; 145 | margin-bottom: $margin-base * 2; 146 | } 147 | .mtb-one { 148 | margin-top: $margin-base; 149 | margin-bottom: $margin-base; 150 | } 151 | .mtb-half { 152 | margin-top: $margin-base / 2; 153 | margin-bottom: $margin-base / 2; 154 | } 155 | .mtb-quarter { 156 | margin-top: $margin-base / 4; 157 | margin-bottom: $margin-base / 4; 158 | } 159 | // margin left and right 160 | .mlr-two { 161 | margin-left: $margin-base * 2; 162 | margin-right: $margin-base * 2; 163 | } 164 | .mlr-one { 165 | margin-left: $margin-base; 166 | margin-right: $margin-base; 167 | } 168 | .mlr-half { 169 | margin-left: $margin-base / 2; 170 | margin-right: $margin-base / 2; 171 | } 172 | .mlr-quarter { 173 | margin-left: $margin-base / 4; 174 | margin-right: $margin-base / 4; 175 | } 176 | // margin top 177 | .mt-two { 178 | margin-top: $margin-base * 2; 179 | } 180 | .mt-one { 181 | margin-top: $margin-base; 182 | } 183 | .mt-half { 184 | margin-top: $margin-base / 2; 185 | } 186 | .mt-quarter { 187 | margin-top: $margin-base / 4; 188 | } 189 | // margin right 190 | .mr-two { 191 | margin-right: $margin-base * 2; 192 | } 193 | .mr-one { 194 | margin-right: $margin-base; 195 | } 196 | .mr-half { 197 | margin-right: $margin-base / 2; 198 | } 199 | .mr-quarter { 200 | margin-right: $margin-base / 4; 201 | } 202 | // margin bottom 203 | .mb-two { 204 | margin-bottom: $margin-base * 2; 205 | } 206 | .mb-one { 207 | margin-bottom: $margin-base; 208 | } 209 | .mb-half { 210 | margin-bottom: $margin-base / 2; 211 | } 212 | .mb-quarter { 213 | margin-bottom: $margin-base / 4; 214 | } 215 | // margin left 216 | .ml-two { 217 | margin-left: $margin-base * 2; 218 | } 219 | .ml-one { 220 | margin-left: $margin-base; 221 | } 222 | .ml-half { 223 | margin-left: $margin-base / 2; 224 | } 225 | .ml-quarter { 226 | margin-left: $margin-base / 4; 227 | } 228 | /* ========= align it ====== */ 229 | .center { 230 | text-align: center; 231 | margin: 0 auto; 232 | } 233 | /*============= DOM HELPERS =============== */ 234 | .hide { 235 | display: none; 236 | } 237 | .no-bullet { 238 | list-style-type: none; 239 | } 240 | /*============= Typography =============== */ 241 | ._text { 242 | &.bold { 243 | font-weight: 800; 244 | } 245 | &.small { 246 | font-size: 14px; 247 | } 248 | &.medium { 249 | font-size: 16px; 250 | } 251 | &.large { 252 | font-size: 18px; 253 | } 254 | &.large-2x { 255 | font-size: 22px; 256 | } 257 | &.caps { 258 | text-transform: uppercase; 259 | } 260 | &.centered { 261 | text-align: center; 262 | } 263 | &.italic { 264 | font-style: italic; 265 | } 266 | &.underline { 267 | text-decoration: underline; 268 | } 269 | } 270 | ._icon { 271 | &.x-small { 272 | font-size: 14px; 273 | } 274 | &.small { 275 | font-size: 24px; 276 | } 277 | &.medium { 278 | font-size: 36px; 279 | } 280 | &.large { 281 | font-size: 48px; 282 | } 283 | &.white { 284 | color: rgb(236, 236, 236); 285 | } 286 | &.gray { 287 | color: rgb(181, 181, 181); 288 | } 289 | } 290 | 291 | .display-none { 292 | display: none !important; 293 | } 294 | 295 | .invisible { 296 | visibility: hidden; 297 | } 298 | 299 | .modal { 300 | position: fixed; 301 | z-index: 1; 302 | left: 0; 303 | top: 0; 304 | width: 100%; 305 | height: 100%; 306 | background-color: rgba(0,0,0,0.4); /* Black w/ opacity */ 307 | } 308 | 309 | .modal-close { 310 | position: absolute; 311 | top: 0; 312 | right: 0; 313 | padding: 5px; 314 | } -------------------------------------------------------------------------------- /_src/scss/_layout.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f0f0f0; 3 | background-size: cover; 4 | background: $mv-bg; 5 | display: flex; 6 | flex-direction: column; 7 | font-family: arial, sans-serif; 8 | height: 100vh; 9 | margin: 0; 10 | overflow-y: hidden; 11 | padding: 0; 12 | } 13 | 14 | #page { 15 | display: flex; 16 | flex-grow: 1; 17 | } 18 | -------------------------------------------------------------------------------- /_src/scss/_menu-editor.scss: -------------------------------------------------------------------------------- 1 | #menu-open { 2 | position: fixed; 3 | left: 0; 4 | bottom: 0; 5 | margin: 5px; 6 | } 7 | #menu-close { 8 | position: fixed; 9 | left: 0; 10 | bottom: 0; 11 | margin: 5px; 12 | z-index: 999; 13 | } 14 | 15 | .button-drawer { 16 | background: rgba(0, 0, 0, 0.6); 17 | bottom: -50px; 18 | display: flex; 19 | flex-direction: row; 20 | height: 45px; 21 | justify-content: space-around; 22 | left: 0; 23 | margin: 0 auto; 24 | position: fixed; 25 | width: 100%; 26 | transition-property: all; 27 | transition-duration: 0.75s; 28 | transition-timing-function: cubic-bezier(0, 1, 0.5, 1); 29 | &.open { 30 | bottom: 0; 31 | } 32 | } 33 | .button-drawer-style { 34 | color: rgb(231, 231, 231); 35 | font-size: 12px; 36 | font-weight: 300; 37 | letter-spacing: 2px; 38 | text-transform: uppercase; 39 | transition-duration: 0.75s; 40 | transition-property: all; 41 | border-bottom: 2px solid transparent; 42 | align-self: center; 43 | 44 | &.on { 45 | color: #fff; 46 | text-shadow: -1px 1px 3px rgba(208, 208, 208, 0.2), 1px -1px 3px rgba(252, 252, 252, 0.2); 47 | border-bottom: 2px solid white; 48 | } 49 | 50 | &.dim { 51 | color: rgba(255, 255, 255, 0.50) 52 | } 53 | } 54 | .asset-drawer { 55 | background: rgba(0, 0, 0, 0.6); 56 | display: flex; 57 | flex-direction: row; 58 | justify-content: center; 59 | align-items: center; 60 | width: 100%; 61 | position: fixed; 62 | bottom: 45px; 63 | 64 | height: 40px; 65 | padding: 5px; 66 | transition-property: all; 67 | transition-duration: 0.75s; 68 | transition-timing-function: cubic-bezier(0, 1, 0.5, 1); 69 | } 70 | .asset-row { 71 | display: flex; 72 | flex-direction: row; 73 | } 74 | -------------------------------------------------------------------------------- /_src/scss/_menu-mixer.scss: -------------------------------------------------------------------------------- 1 | #mixer-open { 2 | bottom: 0; 3 | margin: 5px; 4 | position: fixed; 5 | right: 0; 6 | z-index: 999; 7 | } 8 | #mixer-close { 9 | bottom: 0; 10 | margin: 5px; 11 | position: fixed; 12 | right: 0; 13 | z-index: 999; 14 | } 15 | 16 | .slider-container { 17 | align-items: center; 18 | display: flex; 19 | flex-direction: row; 20 | justify-content: center; 21 | } 22 | 23 | .slider-text { 24 | color: rgb(231, 231, 231); 25 | font-size: 12px; 26 | font-weight: 300; 27 | letter-spacing: 2px; 28 | margin-left: -15px; 29 | padding-right: 15px; 30 | text-transform: uppercase; 31 | } 32 | -------------------------------------------------------------------------------- /_src/scss/_preferences.scss: -------------------------------------------------------------------------------- 1 | #preferences { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | } 6 | 7 | .preferences { 8 | background: white; 9 | box-shadow: 2px 3px 3px rgba(0, 0, 0, 0.3); 10 | display: flex; 11 | flex-direction: column; 12 | height: 200px; 13 | width: 300px; 14 | .header { 15 | align-items: center; 16 | background: rgb(70, 70, 70); 17 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12); 18 | color: rgb(223, 232, 244); 19 | display: flex; 20 | font-weight: 100; 21 | justify-content: space-between; 22 | min-height: 60px; 23 | text-transform: uppercase; 24 | width: 100%; 25 | h1 { 26 | font-size: 14px; 27 | letter-spacing: 2px; 28 | padding-left: 20px; 29 | } 30 | span { 31 | padding-right: 20px; 32 | cursor: pointer; 33 | } 34 | } 35 | .settings { 36 | display: flex; 37 | flex-direction: column; 38 | flex-grow: 1; 39 | font-size: 13px; 40 | font-weight: thin; 41 | justify-content: space-between; 42 | margin: 0 auto; 43 | width: 100%; 44 | .row { 45 | align-items: center; 46 | border-bottom: 1px solid rgb(222, 222, 222); 47 | display: flex; 48 | flex-grow: 1; 49 | justify-content: space-between; 50 | padding: 0px 20px; 51 | &:hover { 52 | background: rgb(223, 232, 244); 53 | } 54 | } 55 | .label { 56 | color: rgb(80, 80, 80); 57 | } 58 | .value { 59 | width: 80px; 60 | input { 61 | width: inherit; 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /_src/scss/main.scss: -------------------------------------------------------------------------------- 1 | // variables 2 | $mv-bg: rgb(232, 232, 232); 3 | $padding-base: 10px; 4 | $margin-base: 10px; 5 | 6 | // Imports 7 | @import '_editor'; 8 | @import '_elements'; 9 | @import '_helpers'; 10 | @import '_layout'; 11 | @import '_menu-editor'; 12 | @import '_menu-mixer'; 13 | @import '_preferences'; 14 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp') 2 | const sass = require('gulp-sass') 3 | 4 | gulp.task('styles', () => { 5 | gulp.src('_src/scss/*.scss') 6 | .pipe(sass().on('error', sass.logError)) 7 | .pipe(gulp.dest('public/assets/css/')) 8 | }) 9 | 10 | gulp.task('build', ['styles']) 11 | 12 | gulp.task('default', () => { 13 | gulp.watch('sass/**/*.scss', ['build', 'styles']) 14 | }) 15 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | const electron = require('electron') 2 | const { ipcMain } = require('electron') 3 | const nativeImage = require('electron').nativeImage 4 | const app = electron.app 5 | const BrowserWindow = electron.BrowserWindow 6 | 7 | let moonview = { content: '' } 8 | 9 | let mainWindow 10 | let prefWindow 11 | let previewWindow 12 | 13 | /* ========== Main MoonView Logic ========== */ 14 | 15 | function createWindow () { 16 | // Create the browser window. 17 | mainWindow = new BrowserWindow({ 18 | autoHideMenuBar: true, 19 | width: 800, 20 | height: 600, 21 | minWidth: 600, 22 | icon: nativeImage.createFromPath(__dirname + '/_src/build-assets/linux-icon.png') 23 | }) 24 | 25 | mainWindow.loadURL(`file://${__dirname}/public/index.html`) 26 | mainWindow.on('closed', function () { mainWindow = null }) 27 | // mainWindow.webContents.openDevTools() // enable dev tools 28 | } 29 | 30 | /* ========== Markdown Window Logic ========== */ 31 | function createPreviewWindow () { 32 | const mainBounds = mainWindow.getBounds() 33 | previewWindow = new BrowserWindow({ 34 | autoHideMenuBar: true, 35 | title: 'Markdown Preview', 36 | x: mainBounds.x + 200, 37 | y: mainBounds.y + 200 38 | }) 39 | previewWindow.loadURL(`file://${__dirname}/public/preview.html`) 40 | previewWindow.on('closed', (e) => { previewWindow = null}) 41 | } 42 | 43 | function updatePreview () { 44 | previewWindow.webContents.send('update-preview', moonview.content) 45 | } 46 | 47 | /* == IPC / Event Listeners for MarkDown Window Logic == */ 48 | 49 | // set content on update-preview event 50 | ipcMain.on('update-content', function (e, content) { 51 | moonview.content = content 52 | if (previewWindow) { 53 | updatePreview() 54 | } 55 | }) 56 | 57 | // create previewWindow 58 | ipcMain.on('show-preview', function (e) { 59 | if (previewWindow) { 60 | previewWindow.focus() 61 | } else { 62 | createPreviewWindow() 63 | } 64 | }) 65 | 66 | // get content and update preview 67 | ipcMain.on('get-content', function (e) { 68 | updatePreview() 69 | }) 70 | 71 | /* ========== App Event Listeners ========== */ 72 | app.on('ready', createWindow) 73 | app.on('window-all-closed', () => { app.quit() }) 74 | app.on('activate', () => { if (mainWindow === null) createWindow()}) 75 | 76 | ipcMain.on('quitter', (e) => { 77 | mainWindow.destroy(); // necessary to bypass the repeat-quit-check in the render process. 78 | app.quit() 79 | }) 80 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "moonview", 3 | "version": "0.0.1", 4 | "description": "A minimal writing application", 5 | "main": "main.js", 6 | "appname": "MoonView", 7 | "app-version": "0.0.1", 8 | "scripts": { 9 | "start": "electron main.js", 10 | "build-mac": "electron-packager . Moonview --platform=darwin --arch=x64 --icon='./_src/build-assets/mac-icon.icns' --out=dist --ignore='^/dist$' --overwrite", 11 | "build-win": "electron-packager . Moonview --platform=win32 --arch=x64 --icon='./_src/build-assets/win-icon.ico' --out=dist --ignore='^/dist$' --overwrite" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/teesloane/moonview.git" 16 | }, 17 | "author": "Tyler Sloane", 18 | "license": "CC0-1.0", 19 | "bugs": { 20 | "url": "https://github.com/teesloane/moonview/issues" 21 | }, 22 | "homepage": "https://github.com/teesloane/moonview/blob/master/README.md", 23 | "devDependencies": { 24 | "babel-eslint": "^6.0.4", 25 | "electron-prebuilt": "^1.2.0", 26 | "eslint": "^2.11.0", 27 | "eslint-config-standard": "^5.3.1", 28 | "eslint-plugin-promise": "^2.0.0", 29 | "eslint-plugin-standard": "^1.3.2", 30 | "gulp": "^3.9.1", 31 | "gulp-sass": "^2.3.2" 32 | }, 33 | "dependencies": { 34 | "file-url": "^1.1.0", 35 | "image-size": "^0.5.0", 36 | "marked": "^0.3.5" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /private/ambiance/fieldrecordings/01_rain_mixdown_01.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/fieldrecordings/01_rain_mixdown_01.mp3 -------------------------------------------------------------------------------- /private/ambiance/fieldrecordings/02_.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/fieldrecordings/02_.mp3 -------------------------------------------------------------------------------- /private/ambiance/fieldrecordings/03_office_mixdown.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/fieldrecordings/03_office_mixdown.mp3 -------------------------------------------------------------------------------- /private/ambiance/fieldrecordings/04_lake_rocks_01.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/fieldrecordings/04_lake_rocks_01.mp3 -------------------------------------------------------------------------------- /private/ambiance/images/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/images/.DS_Store -------------------------------------------------------------------------------- /private/ambiance/images/bg_03.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/images/bg_03.jpeg -------------------------------------------------------------------------------- /private/ambiance/images/cork-wallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/images/cork-wallet.png -------------------------------------------------------------------------------- /private/ambiance/images/stardust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/images/stardust.png -------------------------------------------------------------------------------- /private/ambiance/images/water.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/images/water.jpg -------------------------------------------------------------------------------- /private/ambiance/keySounds/key_01.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/keySounds/key_01.wav -------------------------------------------------------------------------------- /private/ambiance/keySounds/key_02.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/keySounds/key_02.wav -------------------------------------------------------------------------------- /private/ambiance/stems/01/Viv - loop1_01.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/stems/01/Viv - loop1_01.mp3 -------------------------------------------------------------------------------- /private/ambiance/stems/01/Viv - loop2_01.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/stems/01/Viv - loop2_01.mp3 -------------------------------------------------------------------------------- /private/ambiance/stems/01/Viv - loop3_01.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/stems/01/Viv - loop3_01.mp3 -------------------------------------------------------------------------------- /private/ambiance/stems/02/valuse_01.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/stems/02/valuse_01.mp3 -------------------------------------------------------------------------------- /private/ambiance/stems/02/valuse_02.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/stems/02/valuse_02.mp3 -------------------------------------------------------------------------------- /private/ambiance/stems/03/e-piano_01.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/stems/03/e-piano_01.mp3 -------------------------------------------------------------------------------- /private/ambiance/stems/03/kalimba_ish_01.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/stems/03/kalimba_ish_01.mp3 -------------------------------------------------------------------------------- /private/ambiance/stems/03/sines_01.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/private/ambiance/stems/03/sines_01.mp3 -------------------------------------------------------------------------------- /private/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "general": { 3 | "font-size": 12, 4 | "last-file": "" 5 | }, 6 | 7 | "control-panel": { 8 | "audio-sound": false, 9 | "key-sounds": false, 10 | "audio-tracks" : [], 11 | "key-tracks": [], 12 | "backgrounds": [] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /public/assets/css/github-markdown.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: octicons-link; 3 | src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAZwABAAAAAACFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEU0lHAAAGaAAAAAgAAAAIAAAAAUdTVUIAAAZcAAAACgAAAAoAAQAAT1MvMgAAAyQAAABJAAAAYFYEU3RjbWFwAAADcAAAAEUAAACAAJThvmN2dCAAAATkAAAABAAAAAQAAAAAZnBnbQAAA7gAAACyAAABCUM+8IhnYXNwAAAGTAAAABAAAAAQABoAI2dseWYAAAFsAAABPAAAAZwcEq9taGVhZAAAAsgAAAA0AAAANgh4a91oaGVhAAADCAAAABoAAAAkCA8DRGhtdHgAAAL8AAAADAAAAAwGAACfbG9jYQAAAsAAAAAIAAAACABiATBtYXhwAAACqAAAABgAAAAgAA8ASm5hbWUAAAToAAABQgAAAlXu73sOcG9zdAAABiwAAAAeAAAAME3QpOBwcmVwAAAEbAAAAHYAAAB/aFGpk3jaTY6xa8JAGMW/O62BDi0tJLYQincXEypYIiGJjSgHniQ6umTsUEyLm5BV6NDBP8Tpts6F0v+k/0an2i+itHDw3v2+9+DBKTzsJNnWJNTgHEy4BgG3EMI9DCEDOGEXzDADU5hBKMIgNPZqoD3SilVaXZCER3/I7AtxEJLtzzuZfI+VVkprxTlXShWKb3TBecG11rwoNlmmn1P2WYcJczl32etSpKnziC7lQyWe1smVPy/Lt7Kc+0vWY/gAgIIEqAN9we0pwKXreiMasxvabDQMM4riO+qxM2ogwDGOZTXxwxDiycQIcoYFBLj5K3EIaSctAq2kTYiw+ymhce7vwM9jSqO8JyVd5RH9gyTt2+J/yUmYlIR0s04n6+7Vm1ozezUeLEaUjhaDSuXHwVRgvLJn1tQ7xiuVv/ocTRF42mNgZGBgYGbwZOBiAAFGJBIMAAizAFoAAABiAGIAznjaY2BkYGAA4in8zwXi+W2+MjCzMIDApSwvXzC97Z4Ig8N/BxYGZgcgl52BCSQKAA3jCV8CAABfAAAAAAQAAEB42mNgZGBg4f3vACQZQABIMjKgAmYAKEgBXgAAeNpjYGY6wTiBgZWBg2kmUxoDA4MPhGZMYzBi1AHygVLYQUCaawqDA4PChxhmh/8ODDEsvAwHgMKMIDnGL0x7gJQCAwMAJd4MFwAAAHjaY2BgYGaA4DAGRgYQkAHyGMF8NgYrIM3JIAGVYYDT+AEjAwuDFpBmA9KMDEwMCh9i/v8H8sH0/4dQc1iAmAkALaUKLgAAAHjaTY9LDsIgEIbtgqHUPpDi3gPoBVyRTmTddOmqTXThEXqrob2gQ1FjwpDvfwCBdmdXC5AVKFu3e5MfNFJ29KTQT48Ob9/lqYwOGZxeUelN2U2R6+cArgtCJpauW7UQBqnFkUsjAY/kOU1cP+DAgvxwn1chZDwUbd6CFimGXwzwF6tPbFIcjEl+vvmM/byA48e6tWrKArm4ZJlCbdsrxksL1AwWn/yBSJKpYbq8AXaaTb8AAHja28jAwOC00ZrBeQNDQOWO//sdBBgYGRiYWYAEELEwMTE4uzo5Zzo5b2BxdnFOcALxNjA6b2ByTswC8jYwg0VlNuoCTWAMqNzMzsoK1rEhNqByEyerg5PMJlYuVueETKcd/89uBpnpvIEVomeHLoMsAAe1Id4AAAAAAAB42oWQT07CQBTGv0JBhagk7HQzKxca2sJCE1hDt4QF+9JOS0nbaaYDCQfwCJ7Au3AHj+LO13FMmm6cl7785vven0kBjHCBhfpYuNa5Ph1c0e2Xu3jEvWG7UdPDLZ4N92nOm+EBXuAbHmIMSRMs+4aUEd4Nd3CHD8NdvOLTsA2GL8M9PODbcL+hD7C1xoaHeLJSEao0FEW14ckxC+TU8TxvsY6X0eLPmRhry2WVioLpkrbp84LLQPGI7c6sOiUzpWIWS5GzlSgUzzLBSikOPFTOXqly7rqx0Z1Q5BAIoZBSFihQYQOOBEdkCOgXTOHA07HAGjGWiIjaPZNW13/+lm6S9FT7rLHFJ6fQbkATOG1j2OFMucKJJsxIVfQORl+9Jyda6Sl1dUYhSCm1dyClfoeDve4qMYdLEbfqHf3O/AdDumsjAAB42mNgYoAAZQYjBmyAGYQZmdhL8zLdDEydARfoAqIAAAABAAMABwAKABMAB///AA8AAQAAAAAAAAAAAAAAAAABAAAAAA==) format('woff'); 4 | } 5 | 6 | .markdown-body { 7 | -ms-text-size-adjust: 100%; 8 | -webkit-text-size-adjust: 100%; 9 | color: #333; 10 | font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 11 | font-size: 16px; 12 | line-height: 1.6; 13 | word-wrap: break-word; 14 | } 15 | 16 | .markdown-body a { 17 | background-color: transparent; 18 | -webkit-text-decoration-skip: objects; 19 | } 20 | 21 | .markdown-body a:active, 22 | .markdown-body a:hover { 23 | outline-width: 0; 24 | } 25 | 26 | .markdown-body strong { 27 | font-weight: inherit; 28 | } 29 | 30 | .markdown-body strong { 31 | font-weight: bolder; 32 | } 33 | 34 | .markdown-body h1 { 35 | font-size: 2em; 36 | margin: 0.67em 0; 37 | } 38 | 39 | .markdown-body img { 40 | border-style: none; 41 | } 42 | 43 | .markdown-body svg:not(:root) { 44 | overflow: hidden; 45 | } 46 | 47 | .markdown-body code, 48 | .markdown-body kbd, 49 | .markdown-body pre { 50 | font-family: monospace, monospace; 51 | font-size: 1em; 52 | } 53 | 54 | .markdown-body hr { 55 | box-sizing: content-box; 56 | height: 0; 57 | overflow: visible; 58 | } 59 | 60 | .markdown-body input { 61 | font: inherit; 62 | margin: 0; 63 | } 64 | 65 | .markdown-body input { 66 | overflow: visible; 67 | } 68 | 69 | .markdown-body button:-moz-focusring, 70 | .markdown-body [type="button"]:-moz-focusring, 71 | .markdown-body [type="reset"]:-moz-focusring, 72 | .markdown-body [type="submit"]:-moz-focusring { 73 | outline: 1px dotted ButtonText; 74 | } 75 | 76 | .markdown-body [type="checkbox"] { 77 | box-sizing: border-box; 78 | padding: 0; 79 | } 80 | 81 | .markdown-body table { 82 | border-spacing: 0; 83 | border-collapse: collapse; 84 | } 85 | 86 | .markdown-body td, 87 | .markdown-body th { 88 | padding: 0; 89 | } 90 | 91 | .markdown-body * { 92 | box-sizing: border-box; 93 | } 94 | 95 | .markdown-body input { 96 | font: 13px/1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 97 | } 98 | 99 | .markdown-body a { 100 | color: #4078c0; 101 | text-decoration: none; 102 | } 103 | 104 | .markdown-body a:hover, 105 | .markdown-body a:active { 106 | text-decoration: underline; 107 | } 108 | 109 | .markdown-body hr { 110 | height: 0; 111 | margin: 15px 0; 112 | overflow: hidden; 113 | background: transparent; 114 | border: 0; 115 | border-bottom: 1px solid #ddd; 116 | } 117 | 118 | .markdown-body hr::before { 119 | display: table; 120 | content: ""; 121 | } 122 | 123 | .markdown-body hr::after { 124 | display: table; 125 | clear: both; 126 | content: ""; 127 | } 128 | 129 | .markdown-body h1, 130 | .markdown-body h2, 131 | .markdown-body h3, 132 | .markdown-body h4, 133 | .markdown-body h5, 134 | .markdown-body h6 { 135 | margin-top: 0; 136 | margin-bottom: 0; 137 | line-height: 1.5; 138 | } 139 | 140 | .markdown-body h1 { 141 | font-size: 30px; 142 | } 143 | 144 | .markdown-body h2 { 145 | font-size: 21px; 146 | } 147 | 148 | .markdown-body h3 { 149 | font-size: 16px; 150 | } 151 | 152 | .markdown-body h4 { 153 | font-size: 14px; 154 | } 155 | 156 | .markdown-body h5 { 157 | font-size: 12px; 158 | } 159 | 160 | .markdown-body h6 { 161 | font-size: 11px; 162 | } 163 | 164 | .markdown-body p { 165 | margin-top: 0; 166 | margin-bottom: 10px; 167 | } 168 | 169 | .markdown-body blockquote { 170 | margin: 0; 171 | } 172 | 173 | .markdown-body ul, 174 | .markdown-body ol { 175 | padding-left: 0; 176 | margin-top: 0; 177 | margin-bottom: 0; 178 | } 179 | 180 | .markdown-body ol ol, 181 | .markdown-body ul ol { 182 | list-style-type: lower-roman; 183 | } 184 | 185 | .markdown-body ul ul ol, 186 | .markdown-body ul ol ol, 187 | .markdown-body ol ul ol, 188 | .markdown-body ol ol ol { 189 | list-style-type: lower-alpha; 190 | } 191 | 192 | .markdown-body dd { 193 | margin-left: 0; 194 | } 195 | 196 | .markdown-body code { 197 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; 198 | font-size: 12px; 199 | } 200 | 201 | .markdown-body pre { 202 | margin-top: 0; 203 | margin-bottom: 0; 204 | font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace; 205 | } 206 | 207 | .markdown-body .pl-0 { 208 | padding-left: 0 !important; 209 | } 210 | 211 | .markdown-body .pl-1 { 212 | padding-left: 3px !important; 213 | } 214 | 215 | .markdown-body .pl-2 { 216 | padding-left: 6px !important; 217 | } 218 | 219 | .markdown-body .pl-3 { 220 | padding-left: 12px !important; 221 | } 222 | 223 | .markdown-body .pl-4 { 224 | padding-left: 24px !important; 225 | } 226 | 227 | .markdown-body .pl-5 { 228 | padding-left: 36px !important; 229 | } 230 | 231 | .markdown-body .pl-6 { 232 | padding-left: 48px !important; 233 | } 234 | 235 | .markdown-body .form-select::-ms-expand { 236 | opacity: 0; 237 | } 238 | 239 | .markdown-body:before { 240 | display: table; 241 | content: ""; 242 | } 243 | 244 | .markdown-body:after { 245 | display: table; 246 | clear: both; 247 | content: ""; 248 | } 249 | 250 | .markdown-body>*:first-child { 251 | margin-top: 0 !important; 252 | } 253 | 254 | .markdown-body>*:last-child { 255 | margin-bottom: 0 !important; 256 | } 257 | 258 | .markdown-body a:not([href]) { 259 | color: inherit; 260 | text-decoration: none; 261 | } 262 | 263 | .markdown-body .anchor { 264 | display: inline-block; 265 | padding-right: 2px; 266 | margin-left: -18px; 267 | } 268 | 269 | .markdown-body .anchor:focus { 270 | outline: none; 271 | } 272 | 273 | .markdown-body h1, 274 | .markdown-body h2, 275 | .markdown-body h3, 276 | .markdown-body h4, 277 | .markdown-body h5, 278 | .markdown-body h6 { 279 | margin-top: 1em; 280 | margin-bottom: 16px; 281 | font-weight: bold; 282 | line-height: 1.4; 283 | } 284 | 285 | .markdown-body h1 .octicon-link, 286 | .markdown-body h2 .octicon-link, 287 | .markdown-body h3 .octicon-link, 288 | .markdown-body h4 .octicon-link, 289 | .markdown-body h5 .octicon-link, 290 | .markdown-body h6 .octicon-link { 291 | color: #000; 292 | vertical-align: middle; 293 | visibility: hidden; 294 | } 295 | 296 | .markdown-body h1:hover .anchor, 297 | .markdown-body h2:hover .anchor, 298 | .markdown-body h3:hover .anchor, 299 | .markdown-body h4:hover .anchor, 300 | .markdown-body h5:hover .anchor, 301 | .markdown-body h6:hover .anchor { 302 | text-decoration: none; 303 | } 304 | 305 | .markdown-body h1:hover .anchor .octicon-link, 306 | .markdown-body h2:hover .anchor .octicon-link, 307 | .markdown-body h3:hover .anchor .octicon-link, 308 | .markdown-body h4:hover .anchor .octicon-link, 309 | .markdown-body h5:hover .anchor .octicon-link, 310 | .markdown-body h6:hover .anchor .octicon-link { 311 | visibility: visible; 312 | } 313 | 314 | .markdown-body h1 { 315 | padding-bottom: 0.3em; 316 | font-size: 2.25em; 317 | line-height: 1.2; 318 | border-bottom: 1px solid #eee; 319 | } 320 | 321 | .markdown-body h1 .anchor { 322 | line-height: 1; 323 | } 324 | 325 | .markdown-body h2 { 326 | padding-bottom: 0.3em; 327 | font-size: 1.75em; 328 | line-height: 1.225; 329 | border-bottom: 1px solid #eee; 330 | } 331 | 332 | .markdown-body h2 .anchor { 333 | line-height: 1; 334 | } 335 | 336 | .markdown-body h3 { 337 | font-size: 1.5em; 338 | line-height: 1.43; 339 | } 340 | 341 | .markdown-body h3 .anchor { 342 | line-height: 1.2; 343 | } 344 | 345 | .markdown-body h4 { 346 | font-size: 1.25em; 347 | } 348 | 349 | .markdown-body h4 .anchor { 350 | line-height: 1.2; 351 | } 352 | 353 | .markdown-body h5 { 354 | font-size: 1em; 355 | } 356 | 357 | .markdown-body h5 .anchor { 358 | line-height: 1.1; 359 | } 360 | 361 | .markdown-body h6 { 362 | font-size: 1em; 363 | color: #777; 364 | } 365 | 366 | .markdown-body h6 .anchor { 367 | line-height: 1.1; 368 | } 369 | 370 | .markdown-body p, 371 | .markdown-body blockquote, 372 | .markdown-body ul, 373 | .markdown-body ol, 374 | .markdown-body dl, 375 | .markdown-body table, 376 | .markdown-body pre { 377 | margin-top: 0; 378 | margin-bottom: 16px; 379 | } 380 | 381 | .markdown-body hr { 382 | height: 4px; 383 | padding: 0; 384 | margin: 16px 0; 385 | background-color: #e7e7e7; 386 | border: 0 none; 387 | } 388 | 389 | .markdown-body ul, 390 | .markdown-body ol { 391 | padding-left: 2em; 392 | } 393 | 394 | .markdown-body ul ul, 395 | .markdown-body ul ol, 396 | .markdown-body ol ol, 397 | .markdown-body ol ul { 398 | margin-top: 0; 399 | margin-bottom: 0; 400 | } 401 | 402 | .markdown-body li>p { 403 | margin-top: 16px; 404 | } 405 | 406 | .markdown-body dl { 407 | padding: 0; 408 | } 409 | 410 | .markdown-body dl dt { 411 | padding: 0; 412 | margin-top: 16px; 413 | font-size: 1em; 414 | font-style: italic; 415 | font-weight: bold; 416 | } 417 | 418 | .markdown-body dl dd { 419 | padding: 0 16px; 420 | margin-bottom: 16px; 421 | } 422 | 423 | .markdown-body blockquote { 424 | padding: 0 15px; 425 | color: #777; 426 | border-left: 4px solid #ddd; 427 | } 428 | 429 | .markdown-body blockquote>:first-child { 430 | margin-top: 0; 431 | } 432 | 433 | .markdown-body blockquote>:last-child { 434 | margin-bottom: 0; 435 | } 436 | 437 | .markdown-body table { 438 | display: block; 439 | width: 100%; 440 | overflow: auto; 441 | word-break: normal; 442 | word-break: keep-all; 443 | } 444 | 445 | .markdown-body table th { 446 | font-weight: bold; 447 | } 448 | 449 | .markdown-body table th, 450 | .markdown-body table td { 451 | padding: 6px 13px; 452 | border: 1px solid #ddd; 453 | } 454 | 455 | .markdown-body table tr { 456 | background-color: #fff; 457 | border-top: 1px solid #ccc; 458 | } 459 | 460 | .markdown-body table tr:nth-child(2n) { 461 | background-color: #f8f8f8; 462 | } 463 | 464 | .markdown-body img { 465 | max-width: 100%; 466 | box-sizing: content-box; 467 | background-color: #fff; 468 | } 469 | 470 | .markdown-body code { 471 | padding: 0; 472 | padding-top: 0.2em; 473 | padding-bottom: 0.2em; 474 | margin: 0; 475 | font-size: 85%; 476 | background-color: rgba(0,0,0,0.04); 477 | border-radius: 3px; 478 | } 479 | 480 | .markdown-body code:before, 481 | .markdown-body code:after { 482 | letter-spacing: -0.2em; 483 | content: "\00a0"; 484 | } 485 | 486 | .markdown-body pre>code { 487 | padding: 0; 488 | margin: 0; 489 | font-size: 100%; 490 | word-break: normal; 491 | white-space: pre; 492 | background: transparent; 493 | border: 0; 494 | } 495 | 496 | .markdown-body .highlight { 497 | margin-bottom: 16px; 498 | } 499 | 500 | .markdown-body .highlight pre, 501 | .markdown-body pre { 502 | padding: 16px; 503 | overflow: auto; 504 | font-size: 85%; 505 | line-height: 1.45; 506 | background-color: #f7f7f7; 507 | border-radius: 3px; 508 | } 509 | 510 | .markdown-body .highlight pre { 511 | margin-bottom: 0; 512 | word-break: normal; 513 | } 514 | 515 | .markdown-body pre { 516 | word-wrap: normal; 517 | } 518 | 519 | .markdown-body pre code { 520 | display: inline; 521 | max-width: initial; 522 | padding: 0; 523 | margin: 0; 524 | overflow: initial; 525 | line-height: inherit; 526 | word-wrap: normal; 527 | background-color: transparent; 528 | border: 0; 529 | } 530 | 531 | .markdown-body pre code:before, 532 | .markdown-body pre code:after { 533 | content: normal; 534 | } 535 | 536 | .markdown-body kbd { 537 | display: inline-block; 538 | padding: 3px 5px; 539 | font-size: 11px; 540 | line-height: 10px; 541 | color: #555; 542 | vertical-align: middle; 543 | background-color: #fcfcfc; 544 | border: solid 1px #ccc; 545 | border-bottom-color: #bbb; 546 | border-radius: 3px; 547 | box-shadow: inset 0 -1px 0 #bbb; 548 | } 549 | 550 | .markdown-body .pl-c { 551 | color: #969896; 552 | } 553 | 554 | .markdown-body .pl-c1, 555 | .markdown-body .pl-s .pl-v { 556 | color: #0086b3; 557 | } 558 | 559 | .markdown-body .pl-e, 560 | .markdown-body .pl-en { 561 | color: #795da3; 562 | } 563 | 564 | .markdown-body .pl-s .pl-s1, 565 | .markdown-body .pl-smi { 566 | color: #333; 567 | } 568 | 569 | .markdown-body .pl-ent { 570 | color: #63a35c; 571 | } 572 | 573 | .markdown-body .pl-k { 574 | color: #a71d5d; 575 | } 576 | 577 | .markdown-body .pl-pds, 578 | .markdown-body .pl-s, 579 | .markdown-body .pl-s .pl-pse .pl-s1, 580 | .markdown-body .pl-sr, 581 | .markdown-body .pl-sr .pl-cce, 582 | .markdown-body .pl-sr .pl-sra, 583 | .markdown-body .pl-sr .pl-sre { 584 | color: #183691; 585 | } 586 | 587 | .markdown-body .pl-v { 588 | color: #ed6a43; 589 | } 590 | 591 | .markdown-body .pl-id { 592 | color: #b52a1d; 593 | } 594 | 595 | .markdown-body .pl-ii { 596 | background-color: #b52a1d; 597 | color: #f8f8f8; 598 | } 599 | 600 | .markdown-body .pl-sr .pl-cce { 601 | color: #63a35c; 602 | font-weight: bold; 603 | } 604 | 605 | .markdown-body .pl-ml { 606 | color: #693a17; 607 | } 608 | 609 | .markdown-body .pl-mh, 610 | .markdown-body .pl-mh .pl-en, 611 | .markdown-body .pl-ms { 612 | color: #1d3e81; 613 | font-weight: bold; 614 | } 615 | 616 | .markdown-body .pl-mq { 617 | color: #008080; 618 | } 619 | 620 | .markdown-body .pl-mi { 621 | color: #333; 622 | font-style: italic; 623 | } 624 | 625 | .markdown-body .pl-mb { 626 | color: #333; 627 | font-weight: bold; 628 | } 629 | 630 | .markdown-body .pl-md { 631 | background-color: #ffecec; 632 | color: #bd2c00; 633 | } 634 | 635 | .markdown-body .pl-mi1 { 636 | background-color: #eaffea; 637 | color: #55a532; 638 | } 639 | 640 | .markdown-body .pl-mdr { 641 | color: #795da3; 642 | font-weight: bold; 643 | } 644 | 645 | .markdown-body .pl-mo { 646 | color: #1d3e81; 647 | } 648 | 649 | .markdown-body kbd { 650 | display: inline-block; 651 | padding: 3px 5px; 652 | font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace; 653 | line-height: 10px; 654 | color: #555; 655 | vertical-align: middle; 656 | background-color: #fcfcfc; 657 | border: solid 1px #ccc; 658 | border-bottom-color: #bbb; 659 | border-radius: 3px; 660 | box-shadow: inset 0 -1px 0 #bbb; 661 | } 662 | 663 | .markdown-body .full-commit .btn-outline:not(:disabled):hover { 664 | color: #4078c0; 665 | border: 1px solid #4078c0; 666 | } 667 | 668 | .markdown-body :checked+.radio-label { 669 | position: relative; 670 | z-index: 1; 671 | border-color: #4078c0; 672 | } 673 | 674 | .markdown-body .octicon { 675 | display: inline-block; 676 | vertical-align: text-top; 677 | fill: currentColor; 678 | } 679 | 680 | .markdown-body .task-list-item { 681 | list-style-type: none; 682 | } 683 | 684 | .markdown-body .task-list-item+.task-list-item { 685 | margin-top: 3px; 686 | } 687 | 688 | .markdown-body .task-list-item input { 689 | margin: 0 0.2em 0.25em -1.6em; 690 | vertical-align: middle; 691 | } 692 | 693 | .markdown-body hr { 694 | border-bottom-color: #eee; 695 | } 696 | -------------------------------------------------------------------------------- /public/assets/css/ionicons.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | /*! 3 | Ionicons, v2.0.0 4 | Created by Ben Sperry for the Ionic Framework, http://ionicons.com/ 5 | https://twitter.com/benjsperry https://twitter.com/ionicframework 6 | MIT License: https://github.com/driftyco/ionicons 7 | 8 | Android-style icons originally built by Google’s 9 | Material Design Icons: https://github.com/google/material-design-icons 10 | used under CC BY http://creativecommons.org/licenses/by/4.0/ 11 | Modified icons to fit ionicon’s grid from original. 12 | */ 13 | @font-face { font-family: "Ionicons"; src: url("../fonts/ionicons.eot?v=2.0.0"); src: url("../fonts/ionicons.eot?v=2.0.0#iefix") format("embedded-opentype"), url("../fonts/ionicons.ttf?v=2.0.0") format("truetype"), url("../fonts/ionicons.woff?v=2.0.0") format("woff"), url("../fonts/ionicons.svg?v=2.0.0#Ionicons") format("svg"); font-weight: normal; font-style: normal; } 14 | .ion, .ionicons, .ion-alert:before, .ion-alert-circled:before, .ion-android-add:before, .ion-android-add-circle:before, .ion-android-alarm-clock:before, .ion-android-alert:before, .ion-android-apps:before, .ion-android-archive:before, .ion-android-arrow-back:before, .ion-android-arrow-down:before, .ion-android-arrow-dropdown:before, .ion-android-arrow-dropdown-circle:before, .ion-android-arrow-dropleft:before, .ion-android-arrow-dropleft-circle:before, .ion-android-arrow-dropright:before, .ion-android-arrow-dropright-circle:before, .ion-android-arrow-dropup:before, .ion-android-arrow-dropup-circle:before, .ion-android-arrow-forward:before, .ion-android-arrow-up:before, .ion-android-attach:before, .ion-android-bar:before, .ion-android-bicycle:before, .ion-android-boat:before, .ion-android-bookmark:before, .ion-android-bulb:before, .ion-android-bus:before, .ion-android-calendar:before, .ion-android-call:before, .ion-android-camera:before, .ion-android-cancel:before, .ion-android-car:before, .ion-android-cart:before, .ion-android-chat:before, .ion-android-checkbox:before, .ion-android-checkbox-blank:before, .ion-android-checkbox-outline:before, .ion-android-checkbox-outline-blank:before, .ion-android-checkmark-circle:before, .ion-android-clipboard:before, .ion-android-close:before, .ion-android-cloud:before, .ion-android-cloud-circle:before, .ion-android-cloud-done:before, .ion-android-cloud-outline:before, .ion-android-color-palette:before, .ion-android-compass:before, .ion-android-contact:before, .ion-android-contacts:before, .ion-android-contract:before, .ion-android-create:before, .ion-android-delete:before, .ion-android-desktop:before, .ion-android-document:before, .ion-android-done:before, .ion-android-done-all:before, .ion-android-download:before, .ion-android-drafts:before, .ion-android-exit:before, .ion-android-expand:before, .ion-android-favorite:before, .ion-android-favorite-outline:before, .ion-android-film:before, .ion-android-folder:before, .ion-android-folder-open:before, .ion-android-funnel:before, .ion-android-globe:before, .ion-android-hand:before, .ion-android-hangout:before, .ion-android-happy:before, .ion-android-home:before, .ion-android-image:before, .ion-android-laptop:before, .ion-android-list:before, .ion-android-locate:before, .ion-android-lock:before, .ion-android-mail:before, .ion-android-map:before, .ion-android-menu:before, .ion-android-microphone:before, .ion-android-microphone-off:before, .ion-android-more-horizontal:before, .ion-android-more-vertical:before, .ion-android-navigate:before, .ion-android-notifications:before, .ion-android-notifications-none:before, .ion-android-notifications-off:before, .ion-android-open:before, .ion-android-options:before, .ion-android-people:before, .ion-android-person:before, .ion-android-person-add:before, .ion-android-phone-landscape:before, .ion-android-phone-portrait:before, .ion-android-pin:before, .ion-android-plane:before, .ion-android-playstore:before, .ion-android-print:before, .ion-android-radio-button-off:before, .ion-android-radio-button-on:before, .ion-android-refresh:before, .ion-android-remove:before, .ion-android-remove-circle:before, .ion-android-restaurant:before, .ion-android-sad:before, .ion-android-search:before, .ion-android-send:before, .ion-android-settings:before, .ion-android-share:before, .ion-android-share-alt:before, .ion-android-star:before, .ion-android-star-half:before, .ion-android-star-outline:before, .ion-android-stopwatch:before, .ion-android-subway:before, .ion-android-sunny:before, .ion-android-sync:before, .ion-android-textsms:before, .ion-android-time:before, .ion-android-train:before, .ion-android-unlock:before, .ion-android-upload:before, .ion-android-volume-down:before, .ion-android-volume-mute:before, .ion-android-volume-off:before, .ion-android-volume-up:before, .ion-android-walk:before, .ion-android-warning:before, .ion-android-watch:before, .ion-android-wifi:before, .ion-aperture:before, .ion-archive:before, .ion-arrow-down-a:before, .ion-arrow-down-b:before, .ion-arrow-down-c:before, .ion-arrow-expand:before, .ion-arrow-graph-down-left:before, .ion-arrow-graph-down-right:before, .ion-arrow-graph-up-left:before, .ion-arrow-graph-up-right:before, .ion-arrow-left-a:before, .ion-arrow-left-b:before, .ion-arrow-left-c:before, .ion-arrow-move:before, .ion-arrow-resize:before, .ion-arrow-return-left:before, .ion-arrow-return-right:before, .ion-arrow-right-a:before, .ion-arrow-right-b:before, .ion-arrow-right-c:before, .ion-arrow-shrink:before, .ion-arrow-swap:before, .ion-arrow-up-a:before, .ion-arrow-up-b:before, .ion-arrow-up-c:before, .ion-asterisk:before, .ion-at:before, .ion-backspace:before, .ion-backspace-outline:before, .ion-bag:before, .ion-battery-charging:before, .ion-battery-empty:before, .ion-battery-full:before, .ion-battery-half:before, .ion-battery-low:before, .ion-beaker:before, .ion-beer:before, .ion-bluetooth:before, .ion-bonfire:before, .ion-bookmark:before, .ion-bowtie:before, .ion-briefcase:before, .ion-bug:before, .ion-calculator:before, .ion-calendar:before, .ion-camera:before, .ion-card:before, .ion-cash:before, .ion-chatbox:before, .ion-chatbox-working:before, .ion-chatboxes:before, .ion-chatbubble:before, .ion-chatbubble-working:before, .ion-chatbubbles:before, .ion-checkmark:before, .ion-checkmark-circled:before, .ion-checkmark-round:before, .ion-chevron-down:before, .ion-chevron-left:before, .ion-chevron-right:before, .ion-chevron-up:before, .ion-clipboard:before, .ion-clock:before, .ion-close:before, .ion-close-circled:before, .ion-close-round:before, .ion-closed-captioning:before, .ion-cloud:before, .ion-code:before, .ion-code-download:before, .ion-code-working:before, .ion-coffee:before, .ion-compass:before, .ion-compose:before, .ion-connection-bars:before, .ion-contrast:before, .ion-crop:before, .ion-cube:before, .ion-disc:before, .ion-document:before, .ion-document-text:before, .ion-drag:before, .ion-earth:before, .ion-easel:before, .ion-edit:before, .ion-egg:before, .ion-eject:before, .ion-email:before, .ion-email-unread:before, .ion-erlenmeyer-flask:before, .ion-erlenmeyer-flask-bubbles:before, .ion-eye:before, .ion-eye-disabled:before, .ion-female:before, .ion-filing:before, .ion-film-marker:before, .ion-fireball:before, .ion-flag:before, .ion-flame:before, .ion-flash:before, .ion-flash-off:before, .ion-folder:before, .ion-fork:before, .ion-fork-repo:before, .ion-forward:before, .ion-funnel:before, .ion-gear-a:before, .ion-gear-b:before, .ion-grid:before, .ion-hammer:before, .ion-happy:before, .ion-happy-outline:before, .ion-headphone:before, .ion-heart:before, .ion-heart-broken:before, .ion-help:before, .ion-help-buoy:before, .ion-help-circled:before, .ion-home:before, .ion-icecream:before, .ion-image:before, .ion-images:before, .ion-information:before, .ion-information-circled:before, .ion-ionic:before, .ion-ios-alarm:before, .ion-ios-alarm-outline:before, .ion-ios-albums:before, .ion-ios-albums-outline:before, .ion-ios-americanfootball:before, .ion-ios-americanfootball-outline:before, .ion-ios-analytics:before, .ion-ios-analytics-outline:before, .ion-ios-arrow-back:before, .ion-ios-arrow-down:before, .ion-ios-arrow-forward:before, .ion-ios-arrow-left:before, .ion-ios-arrow-right:before, .ion-ios-arrow-thin-down:before, .ion-ios-arrow-thin-left:before, .ion-ios-arrow-thin-right:before, .ion-ios-arrow-thin-up:before, .ion-ios-arrow-up:before, .ion-ios-at:before, .ion-ios-at-outline:before, .ion-ios-barcode:before, .ion-ios-barcode-outline:before, .ion-ios-baseball:before, .ion-ios-baseball-outline:before, .ion-ios-basketball:before, .ion-ios-basketball-outline:before, .ion-ios-bell:before, .ion-ios-bell-outline:before, .ion-ios-body:before, .ion-ios-body-outline:before, .ion-ios-bolt:before, .ion-ios-bolt-outline:before, .ion-ios-book:before, .ion-ios-book-outline:before, .ion-ios-bookmarks:before, .ion-ios-bookmarks-outline:before, .ion-ios-box:before, .ion-ios-box-outline:before, .ion-ios-briefcase:before, .ion-ios-briefcase-outline:before, .ion-ios-browsers:before, .ion-ios-browsers-outline:before, .ion-ios-calculator:before, .ion-ios-calculator-outline:before, .ion-ios-calendar:before, .ion-ios-calendar-outline:before, .ion-ios-camera:before, .ion-ios-camera-outline:before, .ion-ios-cart:before, .ion-ios-cart-outline:before, .ion-ios-chatboxes:before, .ion-ios-chatboxes-outline:before, .ion-ios-chatbubble:before, .ion-ios-chatbubble-outline:before, .ion-ios-checkmark:before, .ion-ios-checkmark-empty:before, .ion-ios-checkmark-outline:before, .ion-ios-circle-filled:before, .ion-ios-circle-outline:before, .ion-ios-clock:before, .ion-ios-clock-outline:before, .ion-ios-close:before, .ion-ios-close-empty:before, .ion-ios-close-outline:before, .ion-ios-cloud:before, .ion-ios-cloud-download:before, .ion-ios-cloud-download-outline:before, .ion-ios-cloud-outline:before, .ion-ios-cloud-upload:before, .ion-ios-cloud-upload-outline:before, .ion-ios-cloudy:before, .ion-ios-cloudy-night:before, .ion-ios-cloudy-night-outline:before, .ion-ios-cloudy-outline:before, .ion-ios-cog:before, .ion-ios-cog-outline:before, .ion-ios-color-filter:before, .ion-ios-color-filter-outline:before, .ion-ios-color-wand:before, .ion-ios-color-wand-outline:before, .ion-ios-compose:before, .ion-ios-compose-outline:before, .ion-ios-contact:before, .ion-ios-contact-outline:before, .ion-ios-copy:before, .ion-ios-copy-outline:before, .ion-ios-crop:before, .ion-ios-crop-strong:before, .ion-ios-download:before, .ion-ios-download-outline:before, .ion-ios-drag:before, .ion-ios-email:before, .ion-ios-email-outline:before, .ion-ios-eye:before, .ion-ios-eye-outline:before, .ion-ios-fastforward:before, .ion-ios-fastforward-outline:before, .ion-ios-filing:before, .ion-ios-filing-outline:before, .ion-ios-film:before, .ion-ios-film-outline:before, .ion-ios-flag:before, .ion-ios-flag-outline:before, .ion-ios-flame:before, .ion-ios-flame-outline:before, .ion-ios-flask:before, .ion-ios-flask-outline:before, .ion-ios-flower:before, .ion-ios-flower-outline:before, .ion-ios-folder:before, .ion-ios-folder-outline:before, .ion-ios-football:before, .ion-ios-football-outline:before, .ion-ios-game-controller-a:before, .ion-ios-game-controller-a-outline:before, .ion-ios-game-controller-b:before, .ion-ios-game-controller-b-outline:before, .ion-ios-gear:before, .ion-ios-gear-outline:before, .ion-ios-glasses:before, .ion-ios-glasses-outline:before, .ion-ios-grid-view:before, .ion-ios-grid-view-outline:before, .ion-ios-heart:before, .ion-ios-heart-outline:before, .ion-ios-help:before, .ion-ios-help-empty:before, .ion-ios-help-outline:before, .ion-ios-home:before, .ion-ios-home-outline:before, .ion-ios-infinite:before, .ion-ios-infinite-outline:before, .ion-ios-information:before, .ion-ios-information-empty:before, .ion-ios-information-outline:before, .ion-ios-ionic-outline:before, .ion-ios-keypad:before, .ion-ios-keypad-outline:before, .ion-ios-lightbulb:before, .ion-ios-lightbulb-outline:before, .ion-ios-list:before, .ion-ios-list-outline:before, .ion-ios-location:before, .ion-ios-location-outline:before, .ion-ios-locked:before, .ion-ios-locked-outline:before, .ion-ios-loop:before, .ion-ios-loop-strong:before, .ion-ios-medical:before, .ion-ios-medical-outline:before, .ion-ios-medkit:before, .ion-ios-medkit-outline:before, .ion-ios-mic:before, .ion-ios-mic-off:before, .ion-ios-mic-outline:before, .ion-ios-minus:before, .ion-ios-minus-empty:before, .ion-ios-minus-outline:before, .ion-ios-monitor:before, .ion-ios-monitor-outline:before, .ion-ios-moon:before, .ion-ios-moon-outline:before, .ion-ios-more:before, .ion-ios-more-outline:before, .ion-ios-musical-note:before, .ion-ios-musical-notes:before, .ion-ios-navigate:before, .ion-ios-navigate-outline:before, .ion-ios-nutrition:before, .ion-ios-nutrition-outline:before, .ion-ios-paper:before, .ion-ios-paper-outline:before, .ion-ios-paperplane:before, .ion-ios-paperplane-outline:before, .ion-ios-partlysunny:before, .ion-ios-partlysunny-outline:before, .ion-ios-pause:before, .ion-ios-pause-outline:before, .ion-ios-paw:before, .ion-ios-paw-outline:before, .ion-ios-people:before, .ion-ios-people-outline:before, .ion-ios-person:before, .ion-ios-person-outline:before, .ion-ios-personadd:before, .ion-ios-personadd-outline:before, .ion-ios-photos:before, .ion-ios-photos-outline:before, .ion-ios-pie:before, .ion-ios-pie-outline:before, .ion-ios-pint:before, .ion-ios-pint-outline:before, .ion-ios-play:before, .ion-ios-play-outline:before, .ion-ios-plus:before, .ion-ios-plus-empty:before, .ion-ios-plus-outline:before, .ion-ios-pricetag:before, .ion-ios-pricetag-outline:before, .ion-ios-pricetags:before, .ion-ios-pricetags-outline:before, .ion-ios-printer:before, .ion-ios-printer-outline:before, .ion-ios-pulse:before, .ion-ios-pulse-strong:before, .ion-ios-rainy:before, .ion-ios-rainy-outline:before, .ion-ios-recording:before, .ion-ios-recording-outline:before, .ion-ios-redo:before, .ion-ios-redo-outline:before, .ion-ios-refresh:before, .ion-ios-refresh-empty:before, .ion-ios-refresh-outline:before, .ion-ios-reload:before, .ion-ios-reverse-camera:before, .ion-ios-reverse-camera-outline:before, .ion-ios-rewind:before, .ion-ios-rewind-outline:before, .ion-ios-rose:before, .ion-ios-rose-outline:before, .ion-ios-search:before, .ion-ios-search-strong:before, .ion-ios-settings:before, .ion-ios-settings-strong:before, .ion-ios-shuffle:before, .ion-ios-shuffle-strong:before, .ion-ios-skipbackward:before, .ion-ios-skipbackward-outline:before, .ion-ios-skipforward:before, .ion-ios-skipforward-outline:before, .ion-ios-snowy:before, .ion-ios-speedometer:before, .ion-ios-speedometer-outline:before, .ion-ios-star:before, .ion-ios-star-half:before, .ion-ios-star-outline:before, .ion-ios-stopwatch:before, .ion-ios-stopwatch-outline:before, .ion-ios-sunny:before, .ion-ios-sunny-outline:before, .ion-ios-telephone:before, .ion-ios-telephone-outline:before, .ion-ios-tennisball:before, .ion-ios-tennisball-outline:before, .ion-ios-thunderstorm:before, .ion-ios-thunderstorm-outline:before, .ion-ios-time:before, .ion-ios-time-outline:before, .ion-ios-timer:before, .ion-ios-timer-outline:before, .ion-ios-toggle:before, .ion-ios-toggle-outline:before, .ion-ios-trash:before, .ion-ios-trash-outline:before, .ion-ios-undo:before, .ion-ios-undo-outline:before, .ion-ios-unlocked:before, .ion-ios-unlocked-outline:before, .ion-ios-upload:before, .ion-ios-upload-outline:before, .ion-ios-videocam:before, .ion-ios-videocam-outline:before, .ion-ios-volume-high:before, .ion-ios-volume-low:before, .ion-ios-wineglass:before, .ion-ios-wineglass-outline:before, .ion-ios-world:before, .ion-ios-world-outline:before, .ion-ipad:before, .ion-iphone:before, .ion-ipod:before, .ion-jet:before, .ion-key:before, .ion-knife:before, .ion-laptop:before, .ion-leaf:before, .ion-levels:before, .ion-lightbulb:before, .ion-link:before, .ion-load-a:before, .ion-load-b:before, .ion-load-c:before, .ion-load-d:before, .ion-location:before, .ion-lock-combination:before, .ion-locked:before, .ion-log-in:before, .ion-log-out:before, .ion-loop:before, .ion-magnet:before, .ion-male:before, .ion-man:before, .ion-map:before, .ion-medkit:before, .ion-merge:before, .ion-mic-a:before, .ion-mic-b:before, .ion-mic-c:before, .ion-minus:before, .ion-minus-circled:before, .ion-minus-round:before, .ion-model-s:before, .ion-monitor:before, .ion-more:before, .ion-mouse:before, .ion-music-note:before, .ion-navicon:before, .ion-navicon-round:before, .ion-navigate:before, .ion-network:before, .ion-no-smoking:before, .ion-nuclear:before, .ion-outlet:before, .ion-paintbrush:before, .ion-paintbucket:before, .ion-paper-airplane:before, .ion-paperclip:before, .ion-pause:before, .ion-person:before, .ion-person-add:before, .ion-person-stalker:before, .ion-pie-graph:before, .ion-pin:before, .ion-pinpoint:before, .ion-pizza:before, .ion-plane:before, .ion-planet:before, .ion-play:before, .ion-playstation:before, .ion-plus:before, .ion-plus-circled:before, .ion-plus-round:before, .ion-podium:before, .ion-pound:before, .ion-power:before, .ion-pricetag:before, .ion-pricetags:before, .ion-printer:before, .ion-pull-request:before, .ion-qr-scanner:before, .ion-quote:before, .ion-radio-waves:before, .ion-record:before, .ion-refresh:before, .ion-reply:before, .ion-reply-all:before, .ion-ribbon-a:before, .ion-ribbon-b:before, .ion-sad:before, .ion-sad-outline:before, .ion-scissors:before, .ion-search:before, .ion-settings:before, .ion-share:before, .ion-shuffle:before, .ion-skip-backward:before, .ion-skip-forward:before, .ion-social-android:before, .ion-social-android-outline:before, .ion-social-angular:before, .ion-social-angular-outline:before, .ion-social-apple:before, .ion-social-apple-outline:before, .ion-social-bitcoin:before, .ion-social-bitcoin-outline:before, .ion-social-buffer:before, .ion-social-buffer-outline:before, .ion-social-chrome:before, .ion-social-chrome-outline:before, .ion-social-codepen:before, .ion-social-codepen-outline:before, .ion-social-css3:before, .ion-social-css3-outline:before, .ion-social-designernews:before, .ion-social-designernews-outline:before, .ion-social-dribbble:before, .ion-social-dribbble-outline:before, .ion-social-dropbox:before, .ion-social-dropbox-outline:before, .ion-social-euro:before, .ion-social-euro-outline:before, .ion-social-facebook:before, .ion-social-facebook-outline:before, .ion-social-foursquare:before, .ion-social-foursquare-outline:before, .ion-social-freebsd-devil:before, .ion-social-github:before, .ion-social-github-outline:before, .ion-social-google:before, .ion-social-google-outline:before, .ion-social-googleplus:before, .ion-social-googleplus-outline:before, .ion-social-hackernews:before, .ion-social-hackernews-outline:before, .ion-social-html5:before, .ion-social-html5-outline:before, .ion-social-instagram:before, .ion-social-instagram-outline:before, .ion-social-javascript:before, .ion-social-javascript-outline:before, .ion-social-linkedin:before, .ion-social-linkedin-outline:before, .ion-social-markdown:before, .ion-social-nodejs:before, .ion-social-octocat:before, .ion-social-pinterest:before, .ion-social-pinterest-outline:before, .ion-social-python:before, .ion-social-reddit:before, .ion-social-reddit-outline:before, .ion-social-rss:before, .ion-social-rss-outline:before, .ion-social-sass:before, .ion-social-skype:before, .ion-social-skype-outline:before, .ion-social-snapchat:before, .ion-social-snapchat-outline:before, .ion-social-tumblr:before, .ion-social-tumblr-outline:before, .ion-social-tux:before, .ion-social-twitch:before, .ion-social-twitch-outline:before, .ion-social-twitter:before, .ion-social-twitter-outline:before, .ion-social-usd:before, .ion-social-usd-outline:before, .ion-social-vimeo:before, .ion-social-vimeo-outline:before, .ion-social-whatsapp:before, .ion-social-whatsapp-outline:before, .ion-social-windows:before, .ion-social-windows-outline:before, .ion-social-wordpress:before, .ion-social-wordpress-outline:before, .ion-social-yahoo:before, .ion-social-yahoo-outline:before, .ion-social-yen:before, .ion-social-yen-outline:before, .ion-social-youtube:before, .ion-social-youtube-outline:before, .ion-soup-can:before, .ion-soup-can-outline:before, .ion-speakerphone:before, .ion-speedometer:before, .ion-spoon:before, .ion-star:before, .ion-stats-bars:before, .ion-steam:before, .ion-stop:before, .ion-thermometer:before, .ion-thumbsdown:before, .ion-thumbsup:before, .ion-toggle:before, .ion-toggle-filled:before, .ion-transgender:before, .ion-trash-a:before, .ion-trash-b:before, .ion-trophy:before, .ion-tshirt:before, .ion-tshirt-outline:before, .ion-umbrella:before, .ion-university:before, .ion-unlocked:before, .ion-upload:before, .ion-usb:before, .ion-videocamera:before, .ion-volume-high:before, .ion-volume-low:before, .ion-volume-medium:before, .ion-volume-mute:before, .ion-wand:before, .ion-waterdrop:before, .ion-wifi:before, .ion-wineglass:before, .ion-woman:before, .ion-wrench:before, .ion-xbox:before { display: inline-block; font-family: "Ionicons"; speak: none; font-style: normal; font-weight: normal; font-variant: normal; text-transform: none; text-rendering: auto; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } 15 | 16 | .ion-alert:before { content: "\f101"; } 17 | 18 | .ion-alert-circled:before { content: "\f100"; } 19 | 20 | .ion-android-add:before { content: "\f2c7"; } 21 | 22 | .ion-android-add-circle:before { content: "\f359"; } 23 | 24 | .ion-android-alarm-clock:before { content: "\f35a"; } 25 | 26 | .ion-android-alert:before { content: "\f35b"; } 27 | 28 | .ion-android-apps:before { content: "\f35c"; } 29 | 30 | .ion-android-archive:before { content: "\f2c9"; } 31 | 32 | .ion-android-arrow-back:before { content: "\f2ca"; } 33 | 34 | .ion-android-arrow-down:before { content: "\f35d"; } 35 | 36 | .ion-android-arrow-dropdown:before { content: "\f35f"; } 37 | 38 | .ion-android-arrow-dropdown-circle:before { content: "\f35e"; } 39 | 40 | .ion-android-arrow-dropleft:before { content: "\f361"; } 41 | 42 | .ion-android-arrow-dropleft-circle:before { content: "\f360"; } 43 | 44 | .ion-android-arrow-dropright:before { content: "\f363"; } 45 | 46 | .ion-android-arrow-dropright-circle:before { content: "\f362"; } 47 | 48 | .ion-android-arrow-dropup:before { content: "\f365"; } 49 | 50 | .ion-android-arrow-dropup-circle:before { content: "\f364"; } 51 | 52 | .ion-android-arrow-forward:before { content: "\f30f"; } 53 | 54 | .ion-android-arrow-up:before { content: "\f366"; } 55 | 56 | .ion-android-attach:before { content: "\f367"; } 57 | 58 | .ion-android-bar:before { content: "\f368"; } 59 | 60 | .ion-android-bicycle:before { content: "\f369"; } 61 | 62 | .ion-android-boat:before { content: "\f36a"; } 63 | 64 | .ion-android-bookmark:before { content: "\f36b"; } 65 | 66 | .ion-android-bulb:before { content: "\f36c"; } 67 | 68 | .ion-android-bus:before { content: "\f36d"; } 69 | 70 | .ion-android-calendar:before { content: "\f2d1"; } 71 | 72 | .ion-android-call:before { content: "\f2d2"; } 73 | 74 | .ion-android-camera:before { content: "\f2d3"; } 75 | 76 | .ion-android-cancel:before { content: "\f36e"; } 77 | 78 | .ion-android-car:before { content: "\f36f"; } 79 | 80 | .ion-android-cart:before { content: "\f370"; } 81 | 82 | .ion-android-chat:before { content: "\f2d4"; } 83 | 84 | .ion-android-checkbox:before { content: "\f374"; } 85 | 86 | .ion-android-checkbox-blank:before { content: "\f371"; } 87 | 88 | .ion-android-checkbox-outline:before { content: "\f373"; } 89 | 90 | .ion-android-checkbox-outline-blank:before { content: "\f372"; } 91 | 92 | .ion-android-checkmark-circle:before { content: "\f375"; } 93 | 94 | .ion-android-clipboard:before { content: "\f376"; } 95 | 96 | .ion-android-close:before { content: "\f2d7"; } 97 | 98 | .ion-android-cloud:before { content: "\f37a"; } 99 | 100 | .ion-android-cloud-circle:before { content: "\f377"; } 101 | 102 | .ion-android-cloud-done:before { content: "\f378"; } 103 | 104 | .ion-android-cloud-outline:before { content: "\f379"; } 105 | 106 | .ion-android-color-palette:before { content: "\f37b"; } 107 | 108 | .ion-android-compass:before { content: "\f37c"; } 109 | 110 | .ion-android-contact:before { content: "\f2d8"; } 111 | 112 | .ion-android-contacts:before { content: "\f2d9"; } 113 | 114 | .ion-android-contract:before { content: "\f37d"; } 115 | 116 | .ion-android-create:before { content: "\f37e"; } 117 | 118 | .ion-android-delete:before { content: "\f37f"; } 119 | 120 | .ion-android-desktop:before { content: "\f380"; } 121 | 122 | .ion-android-document:before { content: "\f381"; } 123 | 124 | .ion-android-done:before { content: "\f383"; } 125 | 126 | .ion-android-done-all:before { content: "\f382"; } 127 | 128 | .ion-android-download:before { content: "\f2dd"; } 129 | 130 | .ion-android-drafts:before { content: "\f384"; } 131 | 132 | .ion-android-exit:before { content: "\f385"; } 133 | 134 | .ion-android-expand:before { content: "\f386"; } 135 | 136 | .ion-android-favorite:before { content: "\f388"; } 137 | 138 | .ion-android-favorite-outline:before { content: "\f387"; } 139 | 140 | .ion-android-film:before { content: "\f389"; } 141 | 142 | .ion-android-folder:before { content: "\f2e0"; } 143 | 144 | .ion-android-folder-open:before { content: "\f38a"; } 145 | 146 | .ion-android-funnel:before { content: "\f38b"; } 147 | 148 | .ion-android-globe:before { content: "\f38c"; } 149 | 150 | .ion-android-hand:before { content: "\f2e3"; } 151 | 152 | .ion-android-hangout:before { content: "\f38d"; } 153 | 154 | .ion-android-happy:before { content: "\f38e"; } 155 | 156 | .ion-android-home:before { content: "\f38f"; } 157 | 158 | .ion-android-image:before { content: "\f2e4"; } 159 | 160 | .ion-android-laptop:before { content: "\f390"; } 161 | 162 | .ion-android-list:before { content: "\f391"; } 163 | 164 | .ion-android-locate:before { content: "\f2e9"; } 165 | 166 | .ion-android-lock:before { content: "\f392"; } 167 | 168 | .ion-android-mail:before { content: "\f2eb"; } 169 | 170 | .ion-android-map:before { content: "\f393"; } 171 | 172 | .ion-android-menu:before { content: "\f394"; } 173 | 174 | .ion-android-microphone:before { content: "\f2ec"; } 175 | 176 | .ion-android-microphone-off:before { content: "\f395"; } 177 | 178 | .ion-android-more-horizontal:before { content: "\f396"; } 179 | 180 | .ion-android-more-vertical:before { content: "\f397"; } 181 | 182 | .ion-android-navigate:before { content: "\f398"; } 183 | 184 | .ion-android-notifications:before { content: "\f39b"; } 185 | 186 | .ion-android-notifications-none:before { content: "\f399"; } 187 | 188 | .ion-android-notifications-off:before { content: "\f39a"; } 189 | 190 | .ion-android-open:before { content: "\f39c"; } 191 | 192 | .ion-android-options:before { content: "\f39d"; } 193 | 194 | .ion-android-people:before { content: "\f39e"; } 195 | 196 | .ion-android-person:before { content: "\f3a0"; } 197 | 198 | .ion-android-person-add:before { content: "\f39f"; } 199 | 200 | .ion-android-phone-landscape:before { content: "\f3a1"; } 201 | 202 | .ion-android-phone-portrait:before { content: "\f3a2"; } 203 | 204 | .ion-android-pin:before { content: "\f3a3"; } 205 | 206 | .ion-android-plane:before { content: "\f3a4"; } 207 | 208 | .ion-android-playstore:before { content: "\f2f0"; } 209 | 210 | .ion-android-print:before { content: "\f3a5"; } 211 | 212 | .ion-android-radio-button-off:before { content: "\f3a6"; } 213 | 214 | .ion-android-radio-button-on:before { content: "\f3a7"; } 215 | 216 | .ion-android-refresh:before { content: "\f3a8"; } 217 | 218 | .ion-android-remove:before { content: "\f2f4"; } 219 | 220 | .ion-android-remove-circle:before { content: "\f3a9"; } 221 | 222 | .ion-android-restaurant:before { content: "\f3aa"; } 223 | 224 | .ion-android-sad:before { content: "\f3ab"; } 225 | 226 | .ion-android-search:before { content: "\f2f5"; } 227 | 228 | .ion-android-send:before { content: "\f2f6"; } 229 | 230 | .ion-android-settings:before { content: "\f2f7"; } 231 | 232 | .ion-android-share:before { content: "\f2f8"; } 233 | 234 | .ion-android-share-alt:before { content: "\f3ac"; } 235 | 236 | .ion-android-star:before { content: "\f2fc"; } 237 | 238 | .ion-android-star-half:before { content: "\f3ad"; } 239 | 240 | .ion-android-star-outline:before { content: "\f3ae"; } 241 | 242 | .ion-android-stopwatch:before { content: "\f2fd"; } 243 | 244 | .ion-android-subway:before { content: "\f3af"; } 245 | 246 | .ion-android-sunny:before { content: "\f3b0"; } 247 | 248 | .ion-android-sync:before { content: "\f3b1"; } 249 | 250 | .ion-android-textsms:before { content: "\f3b2"; } 251 | 252 | .ion-android-time:before { content: "\f3b3"; } 253 | 254 | .ion-android-train:before { content: "\f3b4"; } 255 | 256 | .ion-android-unlock:before { content: "\f3b5"; } 257 | 258 | .ion-android-upload:before { content: "\f3b6"; } 259 | 260 | .ion-android-volume-down:before { content: "\f3b7"; } 261 | 262 | .ion-android-volume-mute:before { content: "\f3b8"; } 263 | 264 | .ion-android-volume-off:before { content: "\f3b9"; } 265 | 266 | .ion-android-volume-up:before { content: "\f3ba"; } 267 | 268 | .ion-android-walk:before { content: "\f3bb"; } 269 | 270 | .ion-android-warning:before { content: "\f3bc"; } 271 | 272 | .ion-android-watch:before { content: "\f3bd"; } 273 | 274 | .ion-android-wifi:before { content: "\f305"; } 275 | 276 | .ion-aperture:before { content: "\f313"; } 277 | 278 | .ion-archive:before { content: "\f102"; } 279 | 280 | .ion-arrow-down-a:before { content: "\f103"; } 281 | 282 | .ion-arrow-down-b:before { content: "\f104"; } 283 | 284 | .ion-arrow-down-c:before { content: "\f105"; } 285 | 286 | .ion-arrow-expand:before { content: "\f25e"; } 287 | 288 | .ion-arrow-graph-down-left:before { content: "\f25f"; } 289 | 290 | .ion-arrow-graph-down-right:before { content: "\f260"; } 291 | 292 | .ion-arrow-graph-up-left:before { content: "\f261"; } 293 | 294 | .ion-arrow-graph-up-right:before { content: "\f262"; } 295 | 296 | .ion-arrow-left-a:before { content: "\f106"; } 297 | 298 | .ion-arrow-left-b:before { content: "\f107"; } 299 | 300 | .ion-arrow-left-c:before { content: "\f108"; } 301 | 302 | .ion-arrow-move:before { content: "\f263"; } 303 | 304 | .ion-arrow-resize:before { content: "\f264"; } 305 | 306 | .ion-arrow-return-left:before { content: "\f265"; } 307 | 308 | .ion-arrow-return-right:before { content: "\f266"; } 309 | 310 | .ion-arrow-right-a:before { content: "\f109"; } 311 | 312 | .ion-arrow-right-b:before { content: "\f10a"; } 313 | 314 | .ion-arrow-right-c:before { content: "\f10b"; } 315 | 316 | .ion-arrow-shrink:before { content: "\f267"; } 317 | 318 | .ion-arrow-swap:before { content: "\f268"; } 319 | 320 | .ion-arrow-up-a:before { content: "\f10c"; } 321 | 322 | .ion-arrow-up-b:before { content: "\f10d"; } 323 | 324 | .ion-arrow-up-c:before { content: "\f10e"; } 325 | 326 | .ion-asterisk:before { content: "\f314"; } 327 | 328 | .ion-at:before { content: "\f10f"; } 329 | 330 | .ion-backspace:before { content: "\f3bf"; } 331 | 332 | .ion-backspace-outline:before { content: "\f3be"; } 333 | 334 | .ion-bag:before { content: "\f110"; } 335 | 336 | .ion-battery-charging:before { content: "\f111"; } 337 | 338 | .ion-battery-empty:before { content: "\f112"; } 339 | 340 | .ion-battery-full:before { content: "\f113"; } 341 | 342 | .ion-battery-half:before { content: "\f114"; } 343 | 344 | .ion-battery-low:before { content: "\f115"; } 345 | 346 | .ion-beaker:before { content: "\f269"; } 347 | 348 | .ion-beer:before { content: "\f26a"; } 349 | 350 | .ion-bluetooth:before { content: "\f116"; } 351 | 352 | .ion-bonfire:before { content: "\f315"; } 353 | 354 | .ion-bookmark:before { content: "\f26b"; } 355 | 356 | .ion-bowtie:before { content: "\f3c0"; } 357 | 358 | .ion-briefcase:before { content: "\f26c"; } 359 | 360 | .ion-bug:before { content: "\f2be"; } 361 | 362 | .ion-calculator:before { content: "\f26d"; } 363 | 364 | .ion-calendar:before { content: "\f117"; } 365 | 366 | .ion-camera:before { content: "\f118"; } 367 | 368 | .ion-card:before { content: "\f119"; } 369 | 370 | .ion-cash:before { content: "\f316"; } 371 | 372 | .ion-chatbox:before { content: "\f11b"; } 373 | 374 | .ion-chatbox-working:before { content: "\f11a"; } 375 | 376 | .ion-chatboxes:before { content: "\f11c"; } 377 | 378 | .ion-chatbubble:before { content: "\f11e"; } 379 | 380 | .ion-chatbubble-working:before { content: "\f11d"; } 381 | 382 | .ion-chatbubbles:before { content: "\f11f"; } 383 | 384 | .ion-checkmark:before { content: "\f122"; } 385 | 386 | .ion-checkmark-circled:before { content: "\f120"; } 387 | 388 | .ion-checkmark-round:before { content: "\f121"; } 389 | 390 | .ion-chevron-down:before { content: "\f123"; } 391 | 392 | .ion-chevron-left:before { content: "\f124"; } 393 | 394 | .ion-chevron-right:before { content: "\f125"; } 395 | 396 | .ion-chevron-up:before { content: "\f126"; } 397 | 398 | .ion-clipboard:before { content: "\f127"; } 399 | 400 | .ion-clock:before { content: "\f26e"; } 401 | 402 | .ion-close:before { content: "\f12a"; } 403 | 404 | .ion-close-circled:before { content: "\f128"; } 405 | 406 | .ion-close-round:before { content: "\f129"; } 407 | 408 | .ion-closed-captioning:before { content: "\f317"; } 409 | 410 | .ion-cloud:before { content: "\f12b"; } 411 | 412 | .ion-code:before { content: "\f271"; } 413 | 414 | .ion-code-download:before { content: "\f26f"; } 415 | 416 | .ion-code-working:before { content: "\f270"; } 417 | 418 | .ion-coffee:before { content: "\f272"; } 419 | 420 | .ion-compass:before { content: "\f273"; } 421 | 422 | .ion-compose:before { content: "\f12c"; } 423 | 424 | .ion-connection-bars:before { content: "\f274"; } 425 | 426 | .ion-contrast:before { content: "\f275"; } 427 | 428 | .ion-crop:before { content: "\f3c1"; } 429 | 430 | .ion-cube:before { content: "\f318"; } 431 | 432 | .ion-disc:before { content: "\f12d"; } 433 | 434 | .ion-document:before { content: "\f12f"; } 435 | 436 | .ion-document-text:before { content: "\f12e"; } 437 | 438 | .ion-drag:before { content: "\f130"; } 439 | 440 | .ion-earth:before { content: "\f276"; } 441 | 442 | .ion-easel:before { content: "\f3c2"; } 443 | 444 | .ion-edit:before { content: "\f2bf"; } 445 | 446 | .ion-egg:before { content: "\f277"; } 447 | 448 | .ion-eject:before { content: "\f131"; } 449 | 450 | .ion-email:before { content: "\f132"; } 451 | 452 | .ion-email-unread:before { content: "\f3c3"; } 453 | 454 | .ion-erlenmeyer-flask:before { content: "\f3c5"; } 455 | 456 | .ion-erlenmeyer-flask-bubbles:before { content: "\f3c4"; } 457 | 458 | .ion-eye:before { content: "\f133"; } 459 | 460 | .ion-eye-disabled:before { content: "\f306"; } 461 | 462 | .ion-female:before { content: "\f278"; } 463 | 464 | .ion-filing:before { content: "\f134"; } 465 | 466 | .ion-film-marker:before { content: "\f135"; } 467 | 468 | .ion-fireball:before { content: "\f319"; } 469 | 470 | .ion-flag:before { content: "\f279"; } 471 | 472 | .ion-flame:before { content: "\f31a"; } 473 | 474 | .ion-flash:before { content: "\f137"; } 475 | 476 | .ion-flash-off:before { content: "\f136"; } 477 | 478 | .ion-folder:before { content: "\f139"; } 479 | 480 | .ion-fork:before { content: "\f27a"; } 481 | 482 | .ion-fork-repo:before { content: "\f2c0"; } 483 | 484 | .ion-forward:before { content: "\f13a"; } 485 | 486 | .ion-funnel:before { content: "\f31b"; } 487 | 488 | .ion-gear-a:before { content: "\f13d"; } 489 | 490 | .ion-gear-b:before { content: "\f13e"; } 491 | 492 | .ion-grid:before { content: "\f13f"; } 493 | 494 | .ion-hammer:before { content: "\f27b"; } 495 | 496 | .ion-happy:before { content: "\f31c"; } 497 | 498 | .ion-happy-outline:before { content: "\f3c6"; } 499 | 500 | .ion-headphone:before { content: "\f140"; } 501 | 502 | .ion-heart:before { content: "\f141"; } 503 | 504 | .ion-heart-broken:before { content: "\f31d"; } 505 | 506 | .ion-help:before { content: "\f143"; } 507 | 508 | .ion-help-buoy:before { content: "\f27c"; } 509 | 510 | .ion-help-circled:before { content: "\f142"; } 511 | 512 | .ion-home:before { content: "\f144"; } 513 | 514 | .ion-icecream:before { content: "\f27d"; } 515 | 516 | .ion-image:before { content: "\f147"; } 517 | 518 | .ion-images:before { content: "\f148"; } 519 | 520 | .ion-information:before { content: "\f14a"; } 521 | 522 | .ion-information-circled:before { content: "\f149"; } 523 | 524 | .ion-ionic:before { content: "\f14b"; } 525 | 526 | .ion-ios-alarm:before { content: "\f3c8"; } 527 | 528 | .ion-ios-alarm-outline:before { content: "\f3c7"; } 529 | 530 | .ion-ios-albums:before { content: "\f3ca"; } 531 | 532 | .ion-ios-albums-outline:before { content: "\f3c9"; } 533 | 534 | .ion-ios-americanfootball:before { content: "\f3cc"; } 535 | 536 | .ion-ios-americanfootball-outline:before { content: "\f3cb"; } 537 | 538 | .ion-ios-analytics:before { content: "\f3ce"; } 539 | 540 | .ion-ios-analytics-outline:before { content: "\f3cd"; } 541 | 542 | .ion-ios-arrow-back:before { content: "\f3cf"; } 543 | 544 | .ion-ios-arrow-down:before { content: "\f3d0"; } 545 | 546 | .ion-ios-arrow-forward:before { content: "\f3d1"; } 547 | 548 | .ion-ios-arrow-left:before { content: "\f3d2"; } 549 | 550 | .ion-ios-arrow-right:before { content: "\f3d3"; } 551 | 552 | .ion-ios-arrow-thin-down:before { content: "\f3d4"; } 553 | 554 | .ion-ios-arrow-thin-left:before { content: "\f3d5"; } 555 | 556 | .ion-ios-arrow-thin-right:before { content: "\f3d6"; } 557 | 558 | .ion-ios-arrow-thin-up:before { content: "\f3d7"; } 559 | 560 | .ion-ios-arrow-up:before { content: "\f3d8"; } 561 | 562 | .ion-ios-at:before { content: "\f3da"; } 563 | 564 | .ion-ios-at-outline:before { content: "\f3d9"; } 565 | 566 | .ion-ios-barcode:before { content: "\f3dc"; } 567 | 568 | .ion-ios-barcode-outline:before { content: "\f3db"; } 569 | 570 | .ion-ios-baseball:before { content: "\f3de"; } 571 | 572 | .ion-ios-baseball-outline:before { content: "\f3dd"; } 573 | 574 | .ion-ios-basketball:before { content: "\f3e0"; } 575 | 576 | .ion-ios-basketball-outline:before { content: "\f3df"; } 577 | 578 | .ion-ios-bell:before { content: "\f3e2"; } 579 | 580 | .ion-ios-bell-outline:before { content: "\f3e1"; } 581 | 582 | .ion-ios-body:before { content: "\f3e4"; } 583 | 584 | .ion-ios-body-outline:before { content: "\f3e3"; } 585 | 586 | .ion-ios-bolt:before { content: "\f3e6"; } 587 | 588 | .ion-ios-bolt-outline:before { content: "\f3e5"; } 589 | 590 | .ion-ios-book:before { content: "\f3e8"; } 591 | 592 | .ion-ios-book-outline:before { content: "\f3e7"; } 593 | 594 | .ion-ios-bookmarks:before { content: "\f3ea"; } 595 | 596 | .ion-ios-bookmarks-outline:before { content: "\f3e9"; } 597 | 598 | .ion-ios-box:before { content: "\f3ec"; } 599 | 600 | .ion-ios-box-outline:before { content: "\f3eb"; } 601 | 602 | .ion-ios-briefcase:before { content: "\f3ee"; } 603 | 604 | .ion-ios-briefcase-outline:before { content: "\f3ed"; } 605 | 606 | .ion-ios-browsers:before { content: "\f3f0"; } 607 | 608 | .ion-ios-browsers-outline:before { content: "\f3ef"; } 609 | 610 | .ion-ios-calculator:before { content: "\f3f2"; } 611 | 612 | .ion-ios-calculator-outline:before { content: "\f3f1"; } 613 | 614 | .ion-ios-calendar:before { content: "\f3f4"; } 615 | 616 | .ion-ios-calendar-outline:before { content: "\f3f3"; } 617 | 618 | .ion-ios-camera:before { content: "\f3f6"; } 619 | 620 | .ion-ios-camera-outline:before { content: "\f3f5"; } 621 | 622 | .ion-ios-cart:before { content: "\f3f8"; } 623 | 624 | .ion-ios-cart-outline:before { content: "\f3f7"; } 625 | 626 | .ion-ios-chatboxes:before { content: "\f3fa"; } 627 | 628 | .ion-ios-chatboxes-outline:before { content: "\f3f9"; } 629 | 630 | .ion-ios-chatbubble:before { content: "\f3fc"; } 631 | 632 | .ion-ios-chatbubble-outline:before { content: "\f3fb"; } 633 | 634 | .ion-ios-checkmark:before { content: "\f3ff"; } 635 | 636 | .ion-ios-checkmark-empty:before { content: "\f3fd"; } 637 | 638 | .ion-ios-checkmark-outline:before { content: "\f3fe"; } 639 | 640 | .ion-ios-circle-filled:before { content: "\f400"; } 641 | 642 | .ion-ios-circle-outline:before { content: "\f401"; } 643 | 644 | .ion-ios-clock:before { content: "\f403"; } 645 | 646 | .ion-ios-clock-outline:before { content: "\f402"; } 647 | 648 | .ion-ios-close:before { content: "\f406"; } 649 | 650 | .ion-ios-close-empty:before { content: "\f404"; } 651 | 652 | .ion-ios-close-outline:before { content: "\f405"; } 653 | 654 | .ion-ios-cloud:before { content: "\f40c"; } 655 | 656 | .ion-ios-cloud-download:before { content: "\f408"; } 657 | 658 | .ion-ios-cloud-download-outline:before { content: "\f407"; } 659 | 660 | .ion-ios-cloud-outline:before { content: "\f409"; } 661 | 662 | .ion-ios-cloud-upload:before { content: "\f40b"; } 663 | 664 | .ion-ios-cloud-upload-outline:before { content: "\f40a"; } 665 | 666 | .ion-ios-cloudy:before { content: "\f410"; } 667 | 668 | .ion-ios-cloudy-night:before { content: "\f40e"; } 669 | 670 | .ion-ios-cloudy-night-outline:before { content: "\f40d"; } 671 | 672 | .ion-ios-cloudy-outline:before { content: "\f40f"; } 673 | 674 | .ion-ios-cog:before { content: "\f412"; } 675 | 676 | .ion-ios-cog-outline:before { content: "\f411"; } 677 | 678 | .ion-ios-color-filter:before { content: "\f414"; } 679 | 680 | .ion-ios-color-filter-outline:before { content: "\f413"; } 681 | 682 | .ion-ios-color-wand:before { content: "\f416"; } 683 | 684 | .ion-ios-color-wand-outline:before { content: "\f415"; } 685 | 686 | .ion-ios-compose:before { content: "\f418"; } 687 | 688 | .ion-ios-compose-outline:before { content: "\f417"; } 689 | 690 | .ion-ios-contact:before { content: "\f41a"; } 691 | 692 | .ion-ios-contact-outline:before { content: "\f419"; } 693 | 694 | .ion-ios-copy:before { content: "\f41c"; } 695 | 696 | .ion-ios-copy-outline:before { content: "\f41b"; } 697 | 698 | .ion-ios-crop:before { content: "\f41e"; } 699 | 700 | .ion-ios-crop-strong:before { content: "\f41d"; } 701 | 702 | .ion-ios-download:before { content: "\f420"; } 703 | 704 | .ion-ios-download-outline:before { content: "\f41f"; } 705 | 706 | .ion-ios-drag:before { content: "\f421"; } 707 | 708 | .ion-ios-email:before { content: "\f423"; } 709 | 710 | .ion-ios-email-outline:before { content: "\f422"; } 711 | 712 | .ion-ios-eye:before { content: "\f425"; } 713 | 714 | .ion-ios-eye-outline:before { content: "\f424"; } 715 | 716 | .ion-ios-fastforward:before { content: "\f427"; } 717 | 718 | .ion-ios-fastforward-outline:before { content: "\f426"; } 719 | 720 | .ion-ios-filing:before { content: "\f429"; } 721 | 722 | .ion-ios-filing-outline:before { content: "\f428"; } 723 | 724 | .ion-ios-film:before { content: "\f42b"; } 725 | 726 | .ion-ios-film-outline:before { content: "\f42a"; } 727 | 728 | .ion-ios-flag:before { content: "\f42d"; } 729 | 730 | .ion-ios-flag-outline:before { content: "\f42c"; } 731 | 732 | .ion-ios-flame:before { content: "\f42f"; } 733 | 734 | .ion-ios-flame-outline:before { content: "\f42e"; } 735 | 736 | .ion-ios-flask:before { content: "\f431"; } 737 | 738 | .ion-ios-flask-outline:before { content: "\f430"; } 739 | 740 | .ion-ios-flower:before { content: "\f433"; } 741 | 742 | .ion-ios-flower-outline:before { content: "\f432"; } 743 | 744 | .ion-ios-folder:before { content: "\f435"; } 745 | 746 | .ion-ios-folder-outline:before { content: "\f434"; } 747 | 748 | .ion-ios-football:before { content: "\f437"; } 749 | 750 | .ion-ios-football-outline:before { content: "\f436"; } 751 | 752 | .ion-ios-game-controller-a:before { content: "\f439"; } 753 | 754 | .ion-ios-game-controller-a-outline:before { content: "\f438"; } 755 | 756 | .ion-ios-game-controller-b:before { content: "\f43b"; } 757 | 758 | .ion-ios-game-controller-b-outline:before { content: "\f43a"; } 759 | 760 | .ion-ios-gear:before { content: "\f43d"; } 761 | 762 | .ion-ios-gear-outline:before { content: "\f43c"; } 763 | 764 | .ion-ios-glasses:before { content: "\f43f"; } 765 | 766 | .ion-ios-glasses-outline:before { content: "\f43e"; } 767 | 768 | .ion-ios-grid-view:before { content: "\f441"; } 769 | 770 | .ion-ios-grid-view-outline:before { content: "\f440"; } 771 | 772 | .ion-ios-heart:before { content: "\f443"; } 773 | 774 | .ion-ios-heart-outline:before { content: "\f442"; } 775 | 776 | .ion-ios-help:before { content: "\f446"; } 777 | 778 | .ion-ios-help-empty:before { content: "\f444"; } 779 | 780 | .ion-ios-help-outline:before { content: "\f445"; } 781 | 782 | .ion-ios-home:before { content: "\f448"; } 783 | 784 | .ion-ios-home-outline:before { content: "\f447"; } 785 | 786 | .ion-ios-infinite:before { content: "\f44a"; } 787 | 788 | .ion-ios-infinite-outline:before { content: "\f449"; } 789 | 790 | .ion-ios-information:before { content: "\f44d"; } 791 | 792 | .ion-ios-information-empty:before { content: "\f44b"; } 793 | 794 | .ion-ios-information-outline:before { content: "\f44c"; } 795 | 796 | .ion-ios-ionic-outline:before { content: "\f44e"; } 797 | 798 | .ion-ios-keypad:before { content: "\f450"; } 799 | 800 | .ion-ios-keypad-outline:before { content: "\f44f"; } 801 | 802 | .ion-ios-lightbulb:before { content: "\f452"; } 803 | 804 | .ion-ios-lightbulb-outline:before { content: "\f451"; } 805 | 806 | .ion-ios-list:before { content: "\f454"; } 807 | 808 | .ion-ios-list-outline:before { content: "\f453"; } 809 | 810 | .ion-ios-location:before { content: "\f456"; } 811 | 812 | .ion-ios-location-outline:before { content: "\f455"; } 813 | 814 | .ion-ios-locked:before { content: "\f458"; } 815 | 816 | .ion-ios-locked-outline:before { content: "\f457"; } 817 | 818 | .ion-ios-loop:before { content: "\f45a"; } 819 | 820 | .ion-ios-loop-strong:before { content: "\f459"; } 821 | 822 | .ion-ios-medical:before { content: "\f45c"; } 823 | 824 | .ion-ios-medical-outline:before { content: "\f45b"; } 825 | 826 | .ion-ios-medkit:before { content: "\f45e"; } 827 | 828 | .ion-ios-medkit-outline:before { content: "\f45d"; } 829 | 830 | .ion-ios-mic:before { content: "\f461"; } 831 | 832 | .ion-ios-mic-off:before { content: "\f45f"; } 833 | 834 | .ion-ios-mic-outline:before { content: "\f460"; } 835 | 836 | .ion-ios-minus:before { content: "\f464"; } 837 | 838 | .ion-ios-minus-empty:before { content: "\f462"; } 839 | 840 | .ion-ios-minus-outline:before { content: "\f463"; } 841 | 842 | .ion-ios-monitor:before { content: "\f466"; } 843 | 844 | .ion-ios-monitor-outline:before { content: "\f465"; } 845 | 846 | .ion-ios-moon:before { content: "\f468"; } 847 | 848 | .ion-ios-moon-outline:before { content: "\f467"; } 849 | 850 | .ion-ios-more:before { content: "\f46a"; } 851 | 852 | .ion-ios-more-outline:before { content: "\f469"; } 853 | 854 | .ion-ios-musical-note:before { content: "\f46b"; } 855 | 856 | .ion-ios-musical-notes:before { content: "\f46c"; } 857 | 858 | .ion-ios-navigate:before { content: "\f46e"; } 859 | 860 | .ion-ios-navigate-outline:before { content: "\f46d"; } 861 | 862 | .ion-ios-nutrition:before { content: "\f470"; } 863 | 864 | .ion-ios-nutrition-outline:before { content: "\f46f"; } 865 | 866 | .ion-ios-paper:before { content: "\f472"; } 867 | 868 | .ion-ios-paper-outline:before { content: "\f471"; } 869 | 870 | .ion-ios-paperplane:before { content: "\f474"; } 871 | 872 | .ion-ios-paperplane-outline:before { content: "\f473"; } 873 | 874 | .ion-ios-partlysunny:before { content: "\f476"; } 875 | 876 | .ion-ios-partlysunny-outline:before { content: "\f475"; } 877 | 878 | .ion-ios-pause:before { content: "\f478"; } 879 | 880 | .ion-ios-pause-outline:before { content: "\f477"; } 881 | 882 | .ion-ios-paw:before { content: "\f47a"; } 883 | 884 | .ion-ios-paw-outline:before { content: "\f479"; } 885 | 886 | .ion-ios-people:before { content: "\f47c"; } 887 | 888 | .ion-ios-people-outline:before { content: "\f47b"; } 889 | 890 | .ion-ios-person:before { content: "\f47e"; } 891 | 892 | .ion-ios-person-outline:before { content: "\f47d"; } 893 | 894 | .ion-ios-personadd:before { content: "\f480"; } 895 | 896 | .ion-ios-personadd-outline:before { content: "\f47f"; } 897 | 898 | .ion-ios-photos:before { content: "\f482"; } 899 | 900 | .ion-ios-photos-outline:before { content: "\f481"; } 901 | 902 | .ion-ios-pie:before { content: "\f484"; } 903 | 904 | .ion-ios-pie-outline:before { content: "\f483"; } 905 | 906 | .ion-ios-pint:before { content: "\f486"; } 907 | 908 | .ion-ios-pint-outline:before { content: "\f485"; } 909 | 910 | .ion-ios-play:before { content: "\f488"; } 911 | 912 | .ion-ios-play-outline:before { content: "\f487"; } 913 | 914 | .ion-ios-plus:before { content: "\f48b"; } 915 | 916 | .ion-ios-plus-empty:before { content: "\f489"; } 917 | 918 | .ion-ios-plus-outline:before { content: "\f48a"; } 919 | 920 | .ion-ios-pricetag:before { content: "\f48d"; } 921 | 922 | .ion-ios-pricetag-outline:before { content: "\f48c"; } 923 | 924 | .ion-ios-pricetags:before { content: "\f48f"; } 925 | 926 | .ion-ios-pricetags-outline:before { content: "\f48e"; } 927 | 928 | .ion-ios-printer:before { content: "\f491"; } 929 | 930 | .ion-ios-printer-outline:before { content: "\f490"; } 931 | 932 | .ion-ios-pulse:before { content: "\f493"; } 933 | 934 | .ion-ios-pulse-strong:before { content: "\f492"; } 935 | 936 | .ion-ios-rainy:before { content: "\f495"; } 937 | 938 | .ion-ios-rainy-outline:before { content: "\f494"; } 939 | 940 | .ion-ios-recording:before { content: "\f497"; } 941 | 942 | .ion-ios-recording-outline:before { content: "\f496"; } 943 | 944 | .ion-ios-redo:before { content: "\f499"; } 945 | 946 | .ion-ios-redo-outline:before { content: "\f498"; } 947 | 948 | .ion-ios-refresh:before { content: "\f49c"; } 949 | 950 | .ion-ios-refresh-empty:before { content: "\f49a"; } 951 | 952 | .ion-ios-refresh-outline:before { content: "\f49b"; } 953 | 954 | .ion-ios-reload:before { content: "\f49d"; } 955 | 956 | .ion-ios-reverse-camera:before { content: "\f49f"; } 957 | 958 | .ion-ios-reverse-camera-outline:before { content: "\f49e"; } 959 | 960 | .ion-ios-rewind:before { content: "\f4a1"; } 961 | 962 | .ion-ios-rewind-outline:before { content: "\f4a0"; } 963 | 964 | .ion-ios-rose:before { content: "\f4a3"; } 965 | 966 | .ion-ios-rose-outline:before { content: "\f4a2"; } 967 | 968 | .ion-ios-search:before { content: "\f4a5"; } 969 | 970 | .ion-ios-search-strong:before { content: "\f4a4"; } 971 | 972 | .ion-ios-settings:before { content: "\f4a7"; } 973 | 974 | .ion-ios-settings-strong:before { content: "\f4a6"; } 975 | 976 | .ion-ios-shuffle:before { content: "\f4a9"; } 977 | 978 | .ion-ios-shuffle-strong:before { content: "\f4a8"; } 979 | 980 | .ion-ios-skipbackward:before { content: "\f4ab"; } 981 | 982 | .ion-ios-skipbackward-outline:before { content: "\f4aa"; } 983 | 984 | .ion-ios-skipforward:before { content: "\f4ad"; } 985 | 986 | .ion-ios-skipforward-outline:before { content: "\f4ac"; } 987 | 988 | .ion-ios-snowy:before { content: "\f4ae"; } 989 | 990 | .ion-ios-speedometer:before { content: "\f4b0"; } 991 | 992 | .ion-ios-speedometer-outline:before { content: "\f4af"; } 993 | 994 | .ion-ios-star:before { content: "\f4b3"; } 995 | 996 | .ion-ios-star-half:before { content: "\f4b1"; } 997 | 998 | .ion-ios-star-outline:before { content: "\f4b2"; } 999 | 1000 | .ion-ios-stopwatch:before { content: "\f4b5"; } 1001 | 1002 | .ion-ios-stopwatch-outline:before { content: "\f4b4"; } 1003 | 1004 | .ion-ios-sunny:before { content: "\f4b7"; } 1005 | 1006 | .ion-ios-sunny-outline:before { content: "\f4b6"; } 1007 | 1008 | .ion-ios-telephone:before { content: "\f4b9"; } 1009 | 1010 | .ion-ios-telephone-outline:before { content: "\f4b8"; } 1011 | 1012 | .ion-ios-tennisball:before { content: "\f4bb"; } 1013 | 1014 | .ion-ios-tennisball-outline:before { content: "\f4ba"; } 1015 | 1016 | .ion-ios-thunderstorm:before { content: "\f4bd"; } 1017 | 1018 | .ion-ios-thunderstorm-outline:before { content: "\f4bc"; } 1019 | 1020 | .ion-ios-time:before { content: "\f4bf"; } 1021 | 1022 | .ion-ios-time-outline:before { content: "\f4be"; } 1023 | 1024 | .ion-ios-timer:before { content: "\f4c1"; } 1025 | 1026 | .ion-ios-timer-outline:before { content: "\f4c0"; } 1027 | 1028 | .ion-ios-toggle:before { content: "\f4c3"; } 1029 | 1030 | .ion-ios-toggle-outline:before { content: "\f4c2"; } 1031 | 1032 | .ion-ios-trash:before { content: "\f4c5"; } 1033 | 1034 | .ion-ios-trash-outline:before { content: "\f4c4"; } 1035 | 1036 | .ion-ios-undo:before { content: "\f4c7"; } 1037 | 1038 | .ion-ios-undo-outline:before { content: "\f4c6"; } 1039 | 1040 | .ion-ios-unlocked:before { content: "\f4c9"; } 1041 | 1042 | .ion-ios-unlocked-outline:before { content: "\f4c8"; } 1043 | 1044 | .ion-ios-upload:before { content: "\f4cb"; } 1045 | 1046 | .ion-ios-upload-outline:before { content: "\f4ca"; } 1047 | 1048 | .ion-ios-videocam:before { content: "\f4cd"; } 1049 | 1050 | .ion-ios-videocam-outline:before { content: "\f4cc"; } 1051 | 1052 | .ion-ios-volume-high:before { content: "\f4ce"; } 1053 | 1054 | .ion-ios-volume-low:before { content: "\f4cf"; } 1055 | 1056 | .ion-ios-wineglass:before { content: "\f4d1"; } 1057 | 1058 | .ion-ios-wineglass-outline:before { content: "\f4d0"; } 1059 | 1060 | .ion-ios-world:before { content: "\f4d3"; } 1061 | 1062 | .ion-ios-world-outline:before { content: "\f4d2"; } 1063 | 1064 | .ion-ipad:before { content: "\f1f9"; } 1065 | 1066 | .ion-iphone:before { content: "\f1fa"; } 1067 | 1068 | .ion-ipod:before { content: "\f1fb"; } 1069 | 1070 | .ion-jet:before { content: "\f295"; } 1071 | 1072 | .ion-key:before { content: "\f296"; } 1073 | 1074 | .ion-knife:before { content: "\f297"; } 1075 | 1076 | .ion-laptop:before { content: "\f1fc"; } 1077 | 1078 | .ion-leaf:before { content: "\f1fd"; } 1079 | 1080 | .ion-levels:before { content: "\f298"; } 1081 | 1082 | .ion-lightbulb:before { content: "\f299"; } 1083 | 1084 | .ion-link:before { content: "\f1fe"; } 1085 | 1086 | .ion-load-a:before { content: "\f29a"; } 1087 | 1088 | .ion-load-b:before { content: "\f29b"; } 1089 | 1090 | .ion-load-c:before { content: "\f29c"; } 1091 | 1092 | .ion-load-d:before { content: "\f29d"; } 1093 | 1094 | .ion-location:before { content: "\f1ff"; } 1095 | 1096 | .ion-lock-combination:before { content: "\f4d4"; } 1097 | 1098 | .ion-locked:before { content: "\f200"; } 1099 | 1100 | .ion-log-in:before { content: "\f29e"; } 1101 | 1102 | .ion-log-out:before { content: "\f29f"; } 1103 | 1104 | .ion-loop:before { content: "\f201"; } 1105 | 1106 | .ion-magnet:before { content: "\f2a0"; } 1107 | 1108 | .ion-male:before { content: "\f2a1"; } 1109 | 1110 | .ion-man:before { content: "\f202"; } 1111 | 1112 | .ion-map:before { content: "\f203"; } 1113 | 1114 | .ion-medkit:before { content: "\f2a2"; } 1115 | 1116 | .ion-merge:before { content: "\f33f"; } 1117 | 1118 | .ion-mic-a:before { content: "\f204"; } 1119 | 1120 | .ion-mic-b:before { content: "\f205"; } 1121 | 1122 | .ion-mic-c:before { content: "\f206"; } 1123 | 1124 | .ion-minus:before { content: "\f209"; } 1125 | 1126 | .ion-minus-circled:before { content: "\f207"; } 1127 | 1128 | .ion-minus-round:before { content: "\f208"; } 1129 | 1130 | .ion-model-s:before { content: "\f2c1"; } 1131 | 1132 | .ion-monitor:before { content: "\f20a"; } 1133 | 1134 | .ion-more:before { content: "\f20b"; } 1135 | 1136 | .ion-mouse:before { content: "\f340"; } 1137 | 1138 | .ion-music-note:before { content: "\f20c"; } 1139 | 1140 | .ion-navicon:before { content: "\f20e"; } 1141 | 1142 | .ion-navicon-round:before { content: "\f20d"; } 1143 | 1144 | .ion-navigate:before { content: "\f2a3"; } 1145 | 1146 | .ion-network:before { content: "\f341"; } 1147 | 1148 | .ion-no-smoking:before { content: "\f2c2"; } 1149 | 1150 | .ion-nuclear:before { content: "\f2a4"; } 1151 | 1152 | .ion-outlet:before { content: "\f342"; } 1153 | 1154 | .ion-paintbrush:before { content: "\f4d5"; } 1155 | 1156 | .ion-paintbucket:before { content: "\f4d6"; } 1157 | 1158 | .ion-paper-airplane:before { content: "\f2c3"; } 1159 | 1160 | .ion-paperclip:before { content: "\f20f"; } 1161 | 1162 | .ion-pause:before { content: "\f210"; } 1163 | 1164 | .ion-person:before { content: "\f213"; } 1165 | 1166 | .ion-person-add:before { content: "\f211"; } 1167 | 1168 | .ion-person-stalker:before { content: "\f212"; } 1169 | 1170 | .ion-pie-graph:before { content: "\f2a5"; } 1171 | 1172 | .ion-pin:before { content: "\f2a6"; } 1173 | 1174 | .ion-pinpoint:before { content: "\f2a7"; } 1175 | 1176 | .ion-pizza:before { content: "\f2a8"; } 1177 | 1178 | .ion-plane:before { content: "\f214"; } 1179 | 1180 | .ion-planet:before { content: "\f343"; } 1181 | 1182 | .ion-play:before { content: "\f215"; } 1183 | 1184 | .ion-playstation:before { content: "\f30a"; } 1185 | 1186 | .ion-plus:before { content: "\f218"; } 1187 | 1188 | .ion-plus-circled:before { content: "\f216"; } 1189 | 1190 | .ion-plus-round:before { content: "\f217"; } 1191 | 1192 | .ion-podium:before { content: "\f344"; } 1193 | 1194 | .ion-pound:before { content: "\f219"; } 1195 | 1196 | .ion-power:before { content: "\f2a9"; } 1197 | 1198 | .ion-pricetag:before { content: "\f2aa"; } 1199 | 1200 | .ion-pricetags:before { content: "\f2ab"; } 1201 | 1202 | .ion-printer:before { content: "\f21a"; } 1203 | 1204 | .ion-pull-request:before { content: "\f345"; } 1205 | 1206 | .ion-qr-scanner:before { content: "\f346"; } 1207 | 1208 | .ion-quote:before { content: "\f347"; } 1209 | 1210 | .ion-radio-waves:before { content: "\f2ac"; } 1211 | 1212 | .ion-record:before { content: "\f21b"; } 1213 | 1214 | .ion-refresh:before { content: "\f21c"; } 1215 | 1216 | .ion-reply:before { content: "\f21e"; } 1217 | 1218 | .ion-reply-all:before { content: "\f21d"; } 1219 | 1220 | .ion-ribbon-a:before { content: "\f348"; } 1221 | 1222 | .ion-ribbon-b:before { content: "\f349"; } 1223 | 1224 | .ion-sad:before { content: "\f34a"; } 1225 | 1226 | .ion-sad-outline:before { content: "\f4d7"; } 1227 | 1228 | .ion-scissors:before { content: "\f34b"; } 1229 | 1230 | .ion-search:before { content: "\f21f"; } 1231 | 1232 | .ion-settings:before { content: "\f2ad"; } 1233 | 1234 | .ion-share:before { content: "\f220"; } 1235 | 1236 | .ion-shuffle:before { content: "\f221"; } 1237 | 1238 | .ion-skip-backward:before { content: "\f222"; } 1239 | 1240 | .ion-skip-forward:before { content: "\f223"; } 1241 | 1242 | .ion-social-android:before { content: "\f225"; } 1243 | 1244 | .ion-social-android-outline:before { content: "\f224"; } 1245 | 1246 | .ion-social-angular:before { content: "\f4d9"; } 1247 | 1248 | .ion-social-angular-outline:before { content: "\f4d8"; } 1249 | 1250 | .ion-social-apple:before { content: "\f227"; } 1251 | 1252 | .ion-social-apple-outline:before { content: "\f226"; } 1253 | 1254 | .ion-social-bitcoin:before { content: "\f2af"; } 1255 | 1256 | .ion-social-bitcoin-outline:before { content: "\f2ae"; } 1257 | 1258 | .ion-social-buffer:before { content: "\f229"; } 1259 | 1260 | .ion-social-buffer-outline:before { content: "\f228"; } 1261 | 1262 | .ion-social-chrome:before { content: "\f4db"; } 1263 | 1264 | .ion-social-chrome-outline:before { content: "\f4da"; } 1265 | 1266 | .ion-social-codepen:before { content: "\f4dd"; } 1267 | 1268 | .ion-social-codepen-outline:before { content: "\f4dc"; } 1269 | 1270 | .ion-social-css3:before { content: "\f4df"; } 1271 | 1272 | .ion-social-css3-outline:before { content: "\f4de"; } 1273 | 1274 | .ion-social-designernews:before { content: "\f22b"; } 1275 | 1276 | .ion-social-designernews-outline:before { content: "\f22a"; } 1277 | 1278 | .ion-social-dribbble:before { content: "\f22d"; } 1279 | 1280 | .ion-social-dribbble-outline:before { content: "\f22c"; } 1281 | 1282 | .ion-social-dropbox:before { content: "\f22f"; } 1283 | 1284 | .ion-social-dropbox-outline:before { content: "\f22e"; } 1285 | 1286 | .ion-social-euro:before { content: "\f4e1"; } 1287 | 1288 | .ion-social-euro-outline:before { content: "\f4e0"; } 1289 | 1290 | .ion-social-facebook:before { content: "\f231"; } 1291 | 1292 | .ion-social-facebook-outline:before { content: "\f230"; } 1293 | 1294 | .ion-social-foursquare:before { content: "\f34d"; } 1295 | 1296 | .ion-social-foursquare-outline:before { content: "\f34c"; } 1297 | 1298 | .ion-social-freebsd-devil:before { content: "\f2c4"; } 1299 | 1300 | .ion-social-github:before { content: "\f233"; } 1301 | 1302 | .ion-social-github-outline:before { content: "\f232"; } 1303 | 1304 | .ion-social-google:before { content: "\f34f"; } 1305 | 1306 | .ion-social-google-outline:before { content: "\f34e"; } 1307 | 1308 | .ion-social-googleplus:before { content: "\f235"; } 1309 | 1310 | .ion-social-googleplus-outline:before { content: "\f234"; } 1311 | 1312 | .ion-social-hackernews:before { content: "\f237"; } 1313 | 1314 | .ion-social-hackernews-outline:before { content: "\f236"; } 1315 | 1316 | .ion-social-html5:before { content: "\f4e3"; } 1317 | 1318 | .ion-social-html5-outline:before { content: "\f4e2"; } 1319 | 1320 | .ion-social-instagram:before { content: "\f351"; } 1321 | 1322 | .ion-social-instagram-outline:before { content: "\f350"; } 1323 | 1324 | .ion-social-javascript:before { content: "\f4e5"; } 1325 | 1326 | .ion-social-javascript-outline:before { content: "\f4e4"; } 1327 | 1328 | .ion-social-linkedin:before { content: "\f239"; } 1329 | 1330 | .ion-social-linkedin-outline:before { content: "\f238"; } 1331 | 1332 | .ion-social-markdown:before { content: "\f4e6"; } 1333 | 1334 | .ion-social-nodejs:before { content: "\f4e7"; } 1335 | 1336 | .ion-social-octocat:before { content: "\f4e8"; } 1337 | 1338 | .ion-social-pinterest:before { content: "\f2b1"; } 1339 | 1340 | .ion-social-pinterest-outline:before { content: "\f2b0"; } 1341 | 1342 | .ion-social-python:before { content: "\f4e9"; } 1343 | 1344 | .ion-social-reddit:before { content: "\f23b"; } 1345 | 1346 | .ion-social-reddit-outline:before { content: "\f23a"; } 1347 | 1348 | .ion-social-rss:before { content: "\f23d"; } 1349 | 1350 | .ion-social-rss-outline:before { content: "\f23c"; } 1351 | 1352 | .ion-social-sass:before { content: "\f4ea"; } 1353 | 1354 | .ion-social-skype:before { content: "\f23f"; } 1355 | 1356 | .ion-social-skype-outline:before { content: "\f23e"; } 1357 | 1358 | .ion-social-snapchat:before { content: "\f4ec"; } 1359 | 1360 | .ion-social-snapchat-outline:before { content: "\f4eb"; } 1361 | 1362 | .ion-social-tumblr:before { content: "\f241"; } 1363 | 1364 | .ion-social-tumblr-outline:before { content: "\f240"; } 1365 | 1366 | .ion-social-tux:before { content: "\f2c5"; } 1367 | 1368 | .ion-social-twitch:before { content: "\f4ee"; } 1369 | 1370 | .ion-social-twitch-outline:before { content: "\f4ed"; } 1371 | 1372 | .ion-social-twitter:before { content: "\f243"; } 1373 | 1374 | .ion-social-twitter-outline:before { content: "\f242"; } 1375 | 1376 | .ion-social-usd:before { content: "\f353"; } 1377 | 1378 | .ion-social-usd-outline:before { content: "\f352"; } 1379 | 1380 | .ion-social-vimeo:before { content: "\f245"; } 1381 | 1382 | .ion-social-vimeo-outline:before { content: "\f244"; } 1383 | 1384 | .ion-social-whatsapp:before { content: "\f4f0"; } 1385 | 1386 | .ion-social-whatsapp-outline:before { content: "\f4ef"; } 1387 | 1388 | .ion-social-windows:before { content: "\f247"; } 1389 | 1390 | .ion-social-windows-outline:before { content: "\f246"; } 1391 | 1392 | .ion-social-wordpress:before { content: "\f249"; } 1393 | 1394 | .ion-social-wordpress-outline:before { content: "\f248"; } 1395 | 1396 | .ion-social-yahoo:before { content: "\f24b"; } 1397 | 1398 | .ion-social-yahoo-outline:before { content: "\f24a"; } 1399 | 1400 | .ion-social-yen:before { content: "\f4f2"; } 1401 | 1402 | .ion-social-yen-outline:before { content: "\f4f1"; } 1403 | 1404 | .ion-social-youtube:before { content: "\f24d"; } 1405 | 1406 | .ion-social-youtube-outline:before { content: "\f24c"; } 1407 | 1408 | .ion-soup-can:before { content: "\f4f4"; } 1409 | 1410 | .ion-soup-can-outline:before { content: "\f4f3"; } 1411 | 1412 | .ion-speakerphone:before { content: "\f2b2"; } 1413 | 1414 | .ion-speedometer:before { content: "\f2b3"; } 1415 | 1416 | .ion-spoon:before { content: "\f2b4"; } 1417 | 1418 | .ion-star:before { content: "\f24e"; } 1419 | 1420 | .ion-stats-bars:before { content: "\f2b5"; } 1421 | 1422 | .ion-steam:before { content: "\f30b"; } 1423 | 1424 | .ion-stop:before { content: "\f24f"; } 1425 | 1426 | .ion-thermometer:before { content: "\f2b6"; } 1427 | 1428 | .ion-thumbsdown:before { content: "\f250"; } 1429 | 1430 | .ion-thumbsup:before { content: "\f251"; } 1431 | 1432 | .ion-toggle:before { content: "\f355"; } 1433 | 1434 | .ion-toggle-filled:before { content: "\f354"; } 1435 | 1436 | .ion-transgender:before { content: "\f4f5"; } 1437 | 1438 | .ion-trash-a:before { content: "\f252"; } 1439 | 1440 | .ion-trash-b:before { content: "\f253"; } 1441 | 1442 | .ion-trophy:before { content: "\f356"; } 1443 | 1444 | .ion-tshirt:before { content: "\f4f7"; } 1445 | 1446 | .ion-tshirt-outline:before { content: "\f4f6"; } 1447 | 1448 | .ion-umbrella:before { content: "\f2b7"; } 1449 | 1450 | .ion-university:before { content: "\f357"; } 1451 | 1452 | .ion-unlocked:before { content: "\f254"; } 1453 | 1454 | .ion-upload:before { content: "\f255"; } 1455 | 1456 | .ion-usb:before { content: "\f2b8"; } 1457 | 1458 | .ion-videocamera:before { content: "\f256"; } 1459 | 1460 | .ion-volume-high:before { content: "\f257"; } 1461 | 1462 | .ion-volume-low:before { content: "\f258"; } 1463 | 1464 | .ion-volume-medium:before { content: "\f259"; } 1465 | 1466 | .ion-volume-mute:before { content: "\f25a"; } 1467 | 1468 | .ion-wand:before { content: "\f358"; } 1469 | 1470 | .ion-waterdrop:before { content: "\f25b"; } 1471 | 1472 | .ion-wifi:before { content: "\f25c"; } 1473 | 1474 | .ion-wineglass:before { content: "\f2b9"; } 1475 | 1476 | .ion-woman:before { content: "\f25d"; } 1477 | 1478 | .ion-wrench:before { content: "\f2ba"; } 1479 | 1480 | .ion-xbox:before { content: "\f30c"; } 1481 | -------------------------------------------------------------------------------- /public/assets/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #f0f0f0; 3 | background-size: cover; 4 | background: #e8e8e8; 5 | display: flex; 6 | flex-direction: column; 7 | font-family: arial, sans-serif; 8 | height: 100vh; 9 | margin: 0; 10 | overflow-y: hidden; 11 | padding: 0; } 12 | 13 | #page { 14 | display: flex; 15 | flex-grow: 1; } 16 | 17 | /* ====================== Border Helpers ====================== */ 18 | ._border.top { 19 | border-top: solid; } 20 | 21 | ._border.right { 22 | border-right: solid; } 23 | 24 | ._border.bottom { 25 | border-bottom: solid; } 26 | 27 | ._border.left { 28 | border-left: solid; } 29 | 30 | ._border.thin { 31 | border-width: 1px; } 32 | 33 | ._border.thick { 34 | border-width: 3px; } 35 | 36 | /* ====================== Padding Helpers ====================== */ 37 | .p-none { 38 | padding: 0; } 39 | 40 | .p-two { 41 | padding: 20px; } 42 | 43 | .p-one { 44 | padding: 10px; } 45 | 46 | .p-half { 47 | padding: 5px; } 48 | 49 | .p-quarter { 50 | padding: 2.5px; } 51 | 52 | .ptb-two { 53 | padding-top: 20px; 54 | padding-bottom: 20px; } 55 | 56 | .ptb-one { 57 | padding-top: 10px; 58 | padding-bottom: 10px; } 59 | 60 | .ptb-half { 61 | padding-top: 5px; 62 | padding-bottom: 5px; } 63 | 64 | .ptb-quarter { 65 | padding-top: 2.5px; 66 | padding-bottom: 2.5px; } 67 | 68 | .plr-two { 69 | padding-left: 20px; 70 | padding-right: 20px; } 71 | 72 | .plr-one { 73 | padding-left: 10px; 74 | padding-right: 10px; } 75 | 76 | .plr-half { 77 | padding-left: 5px; 78 | padding-right: 5px; } 79 | 80 | .plr-quarter { 81 | padding-left: 2.5px; 82 | padding-right: 2.5px; } 83 | 84 | .pt-two { 85 | padding-top: 20px; } 86 | 87 | .pt-one { 88 | padding-top: 10px; } 89 | 90 | .pt-half { 91 | padding-top: 5px; } 92 | 93 | .pt-quarter { 94 | padding-top: 2.5px; } 95 | 96 | .pr-two { 97 | padding-right: 20px; } 98 | 99 | .pr-one { 100 | padding-right: 10px; } 101 | 102 | .pr-half { 103 | padding-right: 5px; } 104 | 105 | .pr-quarter { 106 | padding-right: 2.5px; } 107 | 108 | .pb-two { 109 | padding-bottom: 20px; } 110 | 111 | .pb-one { 112 | padding-bottom: 10px; } 113 | 114 | .pb-half { 115 | padding-bottom: 5px; } 116 | 117 | .pb-quarter { 118 | padding-bottom: 2.5px; } 119 | 120 | .pl-two { 121 | padding-left: 20px; } 122 | 123 | .pl-one { 124 | padding-left: 10px; } 125 | 126 | .pl-half { 127 | padding-left: 5px; } 128 | 129 | .pl-quarter { 130 | padding-left: 2.5px; } 131 | 132 | /* ====================== Margin Helpers ====================== */ 133 | .m-none { 134 | margin: 0; } 135 | 136 | .m-two { 137 | margin: 20px; } 138 | 139 | .m-one { 140 | margin: 10px; } 141 | 142 | .m-half { 143 | margin: 5px; } 144 | 145 | .m-quarter { 146 | margin: 2.5px; } 147 | 148 | .mtb-two { 149 | margin-top: 20px; 150 | margin-bottom: 20px; } 151 | 152 | .mtb-one { 153 | margin-top: 10px; 154 | margin-bottom: 10px; } 155 | 156 | .mtb-half { 157 | margin-top: 5px; 158 | margin-bottom: 5px; } 159 | 160 | .mtb-quarter { 161 | margin-top: 2.5px; 162 | margin-bottom: 2.5px; } 163 | 164 | .mlr-two { 165 | margin-left: 20px; 166 | margin-right: 20px; } 167 | 168 | .mlr-one { 169 | margin-left: 10px; 170 | margin-right: 10px; } 171 | 172 | .mlr-half { 173 | margin-left: 5px; 174 | margin-right: 5px; } 175 | 176 | .mlr-quarter { 177 | margin-left: 2.5px; 178 | margin-right: 2.5px; } 179 | 180 | .mt-two { 181 | margin-top: 20px; } 182 | 183 | .mt-one { 184 | margin-top: 10px; } 185 | 186 | .mt-half { 187 | margin-top: 5px; } 188 | 189 | .mt-quarter { 190 | margin-top: 2.5px; } 191 | 192 | .mr-two { 193 | margin-right: 20px; } 194 | 195 | .mr-one { 196 | margin-right: 10px; } 197 | 198 | .mr-half { 199 | margin-right: 5px; } 200 | 201 | .mr-quarter { 202 | margin-right: 2.5px; } 203 | 204 | .mb-two { 205 | margin-bottom: 20px; } 206 | 207 | .mb-one { 208 | margin-bottom: 10px; } 209 | 210 | .mb-half { 211 | margin-bottom: 5px; } 212 | 213 | .mb-quarter { 214 | margin-bottom: 2.5px; } 215 | 216 | .ml-two { 217 | margin-left: 20px; } 218 | 219 | .ml-one { 220 | margin-left: 10px; } 221 | 222 | .ml-half { 223 | margin-left: 5px; } 224 | 225 | .ml-quarter { 226 | margin-left: 2.5px; } 227 | 228 | /* ========= align it ====== */ 229 | .center { 230 | text-align: center; 231 | margin: 0 auto; } 232 | 233 | /*============= DOM HELPERS =============== */ 234 | .hide { 235 | display: none; } 236 | 237 | .no-bullet { 238 | list-style-type: none; } 239 | 240 | /*============= Typography =============== */ 241 | ._text.bold { 242 | font-weight: 800; } 243 | 244 | ._text.small { 245 | font-size: 14px; } 246 | 247 | ._text.medium { 248 | font-size: 16px; } 249 | 250 | ._text.large { 251 | font-size: 18px; } 252 | 253 | ._text.large-2x { 254 | font-size: 22px; } 255 | 256 | ._text.caps { 257 | text-transform: uppercase; } 258 | 259 | ._text.centered { 260 | text-align: center; } 261 | 262 | ._text.italic { 263 | font-style: italic; } 264 | 265 | ._text.underline { 266 | text-decoration: underline; } 267 | 268 | ._icon.x-small { 269 | font-size: 14px; } 270 | 271 | ._icon.small { 272 | font-size: 24px; } 273 | 274 | ._icon.medium { 275 | font-size: 36px; } 276 | 277 | ._icon.large { 278 | font-size: 48px; } 279 | 280 | ._icon.white { 281 | color: #ececec; } 282 | 283 | ._icon.gray { 284 | color: #b5b5b5; } 285 | 286 | .display-none { 287 | display: none !important; } 288 | 289 | .invisible { 290 | visibility: hidden; } 291 | 292 | .modal { 293 | position: fixed; 294 | z-index: 1; 295 | left: 0; 296 | top: 0; 297 | width: 100%; 298 | height: 100%; 299 | background-color: rgba(0, 0, 0, 0.4); 300 | /* Black w/ opacity */ } 301 | 302 | .modal-close { 303 | position: absolute; 304 | top: 0; 305 | right: 0; 306 | padding: 5px; } 307 | 308 | #editor { 309 | display: flex; 310 | align-items: center; 311 | padding: 6% 5% 0; 312 | background: rgba(255, 255, 255, 0); 313 | letter-spacing: 1px; } 314 | 315 | textarea { 316 | display: flex; 317 | margin: 0; 318 | padding: 0; 319 | outline: none; 320 | resize: none; 321 | width: 70%; 322 | margin: 0 auto; 323 | border: 0; 324 | margin: 5% auto; } 325 | @media (min-width: 1000px) { 326 | textarea { 327 | width: 50%; } } 328 | 329 | #menu-open { 330 | position: fixed; 331 | left: 0; 332 | bottom: 0; 333 | margin: 5px; } 334 | 335 | #menu-close { 336 | position: fixed; 337 | left: 0; 338 | bottom: 0; 339 | margin: 5px; 340 | z-index: 999; } 341 | 342 | .button-drawer { 343 | background: rgba(0, 0, 0, 0.6); 344 | bottom: -50px; 345 | display: flex; 346 | flex-direction: row; 347 | height: 45px; 348 | justify-content: space-around; 349 | left: 0; 350 | margin: 0 auto; 351 | position: fixed; 352 | width: 100%; 353 | transition-property: all; 354 | transition-duration: 0.75s; 355 | transition-timing-function: cubic-bezier(0, 1, 0.5, 1); } 356 | .button-drawer.open { 357 | bottom: 0; } 358 | 359 | .button-drawer-style { 360 | color: #e7e7e7; 361 | font-size: 12px; 362 | font-weight: 300; 363 | letter-spacing: 2px; 364 | text-transform: uppercase; 365 | transition-duration: 0.75s; 366 | transition-property: all; 367 | border-bottom: 2px solid transparent; 368 | align-self: center; } 369 | .button-drawer-style.on { 370 | color: #fff; 371 | text-shadow: -1px 1px 3px rgba(208, 208, 208, 0.2), 1px -1px 3px rgba(252, 252, 252, 0.2); 372 | border-bottom: 2px solid white; } 373 | .button-drawer-style.dim { 374 | color: rgba(255, 255, 255, 0.5); } 375 | 376 | .asset-drawer { 377 | background: rgba(0, 0, 0, 0.6); 378 | display: flex; 379 | flex-direction: row; 380 | justify-content: center; 381 | align-items: center; 382 | width: 100%; 383 | position: fixed; 384 | bottom: 45px; 385 | height: 40px; 386 | padding: 5px; 387 | transition-property: all; 388 | transition-duration: 0.75s; 389 | transition-timing-function: cubic-bezier(0, 1, 0.5, 1); } 390 | 391 | .asset-row { 392 | display: flex; 393 | flex-direction: row; } 394 | 395 | #mixer-open { 396 | position: fixed; 397 | right: 0; 398 | bottom: 0; 399 | margin: 5px; 400 | z-index: 999; } 401 | 402 | #mixer-close { 403 | position: fixed; 404 | right: 0; 405 | bottom: 0; 406 | margin: 5px; 407 | z-index: 999; } 408 | 409 | .slider-container { 410 | display: flex; 411 | flex-direction: row; 412 | justify-content: center; 413 | align-items: center; } 414 | 415 | .slider-text { 416 | color: #e7e7e7; 417 | font-size: 12px; 418 | font-weight: 300; 419 | letter-spacing: 2px; 420 | text-transform: uppercase; 421 | padding-right: 15px; 422 | margin-left: -15px; } 423 | 424 | button { 425 | background: none; 426 | border: 0; 427 | font: inherit; 428 | line-height: normal; 429 | outline: none; 430 | overflow: visible; } 431 | 432 | .btn.gourd { 433 | background: #d9d9d9; 434 | border-bottom: 2px solid transparent; 435 | height: 25px; 436 | margin: 0 20px; 437 | padding-top: 5px; 438 | width: 25px; 439 | border-radius: 50%; 440 | font-size: 10px; } 441 | .btn.gourd:hover { 442 | background: #c8c8c8; } 443 | .btn.gourd:active { 444 | background: #b4b4b4; } 445 | .btn.gourd.on { 446 | background: #dbdbdb; 447 | box-shadow: 0 0 5px #ebebeb; } 448 | 449 | #mixer input[type=range] { 450 | -webkit-appearance: none; } 451 | 452 | #mixer input[type=range] { 453 | width: 150px; 454 | height: 5px; 455 | background: #ddd; 456 | border: none; 457 | border-radius: 10px; } 458 | @media (max-width: 500px) { 459 | #mixer input[type=range] { 460 | width: 75px; } } 461 | 462 | #mixer input[type=range]::-webkit-slider-thumb { 463 | -webkit-appearance: none; 464 | border: none; 465 | height: 16px; 466 | width: 16px; 467 | border-radius: 50%; 468 | background: goldenrod; 469 | box-shadow: 0 2px 3px rgba(0, 0, 0, 0.22); } 470 | #mixer input[type=range]::-webkit-slider-thumb:active { 471 | box-shadow: 0 0px 0px transparent; } 472 | 473 | #mixer input[type=range]:focus { 474 | outline: none; } 475 | 476 | #preferences { 477 | display: flex; 478 | justify-content: center; 479 | align-items: center; } 480 | 481 | .preferences { 482 | width: 300px; 483 | height: 200px; 484 | background: white; 485 | box-shadow: 2px 3px 3px rgba(0, 0, 0, 0.3); 486 | display: flex; 487 | flex-direction: column; } 488 | .preferences .header { 489 | background: #464646; 490 | color: #dfe8f4; 491 | display: flex; 492 | justify-content: space-between; 493 | align-items: center; 494 | min-height: 60px; 495 | width: 100%; 496 | text-transform: uppercase; 497 | font-weight: 100; 498 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12); } 499 | .preferences .header h1 { 500 | font-size: 14px; 501 | letter-spacing: 2px; 502 | padding-left: 20px; } 503 | .preferences .header span { 504 | padding-right: 20px; 505 | cursor: pointer; } 506 | .preferences .settings { 507 | width: 100%; 508 | margin: 0 auto; 509 | display: flex; 510 | flex-direction: column; 511 | flex-grow: 1; 512 | font-weight: thin; 513 | font-size: 13px; 514 | justify-content: space-between; } 515 | .preferences .settings .row { 516 | display: flex; 517 | flex-grow: 1; 518 | padding: 0px 20px; 519 | justify-content: space-between; 520 | border-bottom: 1px solid #dedede; 521 | align-items: center; } 522 | .preferences .settings .row:hover { 523 | background: #dfe8f4; } 524 | .preferences .settings .label { 525 | color: #505050; } 526 | .preferences .settings .value { 527 | width: 80px; } 528 | .preferences .settings .value input { 529 | width: inherit; } 530 | -------------------------------------------------------------------------------- /public/assets/fonts/ionicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/public/assets/fonts/ionicons.eot -------------------------------------------------------------------------------- /public/assets/fonts/ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/public/assets/fonts/ionicons.ttf -------------------------------------------------------------------------------- /public/assets/fonts/ionicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teesloane/moonview/c3bf7a511a321a2c69e0a73ef961e9597524ee9c/public/assets/fonts/ionicons.woff -------------------------------------------------------------------------------- /public/assets/js/core/content-update.js: -------------------------------------------------------------------------------- 1 | const electron = require('electron') 2 | const ipcRenderer = electron.ipcRenderer 3 | const el = require('../helpers/dom-elements') 4 | 5 | function markdownUpdate () { 6 | el.editor.addEventListener('input', function (e) { 7 | ipcRenderer.send('update-content', e.target.value) 8 | }) 9 | } 10 | 11 | module.exports = markdownUpdate 12 | -------------------------------------------------------------------------------- /public/assets/js/core/file-io.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const { ipcRenderer } = require('electron') 3 | const { dialog } = require('electron').remote 4 | 5 | let file = { 6 | currentFile: undefined, 7 | 8 | open () { 9 | dialog.showOpenDialog({ 10 | title: 'Open a document', 11 | buttonLabel: 'Open', 12 | filters: [{name: 'Text', extensions: ['txt', 'md']}] 13 | }, function (filesIn) { 14 | if (filesIn === undefined) return 15 | let fileIn = filesIn[0] // must be 1st element of array even though only one item is selected to be opened. 16 | fs.readFile(fileIn, 'utf-8', function (err, data) { 17 | if (err) throw err 18 | document.getElementById('editor').value = data 19 | file.currentFile = fileIn 20 | }) 21 | }) 22 | }, 23 | 24 | saveAs (callback) { 25 | dialog.showSaveDialog({ 26 | title: 'Save your document', 27 | buttonLabel: 'Save', 28 | filters: [{name: 'text', extensions: ['txt', 'md']}] 29 | }, function (fileOut) { 30 | if (fileOut === undefined) return 31 | fs.writeFile(fileOut, document.getElementById('editor').value, function (err) { 32 | file.currentFile = fileOut 33 | if (err) throw err 34 | }) 35 | }) 36 | }, 37 | 38 | save () { 39 | // if file hasn't been saved, run saveAs() 40 | if (file.currentFile === undefined) { 41 | file.saveAs() 42 | } else { 43 | fs.writeFile(file.currentFile.toString(), document.getElementById('editor').value, function (err) { 44 | if (err) throw err 45 | }) 46 | } 47 | }, 48 | 49 | newFile () { 50 | if (this.isUnsaved() || this.hasChanged()) { 51 | this.fileWarning('You have an unsaved file. Save it?', 'Cancel', 'New File', function () { 52 | // noop function - nothing happens on 'cancel'. 53 | }, function () { 54 | // reset the editor for a "new file" 55 | document.getElementById('editor').value = '' 56 | file.currentFile = undefined 57 | }) 58 | } else { 59 | document.getElementById('editor').value = '' 60 | file.currentFile = undefined 61 | } 62 | }, 63 | 64 | // ====================================================== // 65 | // HELPERS FOR CHECKING FOR UNSAVED FILES, warnings, etc. // 66 | fileWarning (Message, OptionA, OptionB, ActionA, ActionB) { 67 | dialog.showMessageBox({ 68 | type: 'warning', 69 | buttons: [OptionA, OptionB], // string 70 | title: 'Unsaved Work', 71 | message: Message 72 | }, function (rdata) { 73 | if (rdata === 0) { 74 | ActionA() 75 | } else if (rdata === 1) { ActionB() } 76 | }) 77 | }, 78 | 79 | hasChanged () { 80 | let editor = document.getElementById('editor') 81 | 82 | if (file.currentFile !== undefined) { 83 | fs.readFile(file.currentFile, 'utf-8', function (err, data) { 84 | if (err) throw err 85 | if (editor.value !== data) return true 86 | }) 87 | } 88 | }, 89 | 90 | editorIsEmpty () { 91 | let editor = document.getElementById('editor') 92 | if (editor.value === '') return true 93 | }, 94 | 95 | isUnsaved () { 96 | let editor = document.getElementById('editor') 97 | if (file.currentFile === undefined && editor.value !== '') { 98 | return true 99 | } 100 | }, 101 | 102 | windowCloseCheck () { 103 | window.onbeforeunload = function (e) { 104 | e.returnValue = false 105 | // window.alert('try to close me'); 106 | if (file.isUnsaved() || file.hasChanged()) { 107 | // prompt - save or just quit? 108 | file.fileWarning('You have unsaveeed work.', 'Save', 'Quit', function () { 109 | // OPTION A - save 110 | file.save() 111 | }, function () { 112 | // OPTION B: Quit. 113 | ipcRenderer.send('quitter') 114 | }) 115 | } else { 116 | // file is saved and no new work has been done: 117 | ipcRenderer.send('quitter') 118 | } 119 | } 120 | } 121 | } 122 | 123 | module.exports = file 124 | -------------------------------------------------------------------------------- /public/assets/js/core/interaction/audio.js: -------------------------------------------------------------------------------- 1 | /* This file builds out invocable logic for all audio related interaction. 2 | Examples: Start / Pause an audio file. Change volume of an audio file. etc. */ 3 | 4 | const tree = require('../../helpers/tree') 5 | const help = require('../../helpers/utilities') 6 | const el = require('../../helpers/dom-elements') 7 | const fs = require('fs') 8 | 9 | let audio = { 10 | 11 | /* Plz refactor me: sometimes not all audio files are queued up. Closure issues? */ 12 | toggleSet (stemsFolder) { 13 | // if audio is playing already, stop it and start the new set. 14 | if (tree.selectedStems.constructor === Array) { 15 | tree.selectedStems.forEach((stem) => { 16 | stem.pause() 17 | }) 18 | } 19 | 20 | // get audio files, filter by .ext, shuffle the array, convert to audio obj's 21 | let audioFiles = help.getDirList(stemsFolder) 22 | audioFiles = help.filterFileTypes(audioFiles, ['.mp3', '.wav']) 23 | audioFiles = help.shuffle(audioFiles) 24 | 25 | // reassign (overwrite) each element to a new instance of the Audio class. 26 | audioFiles.forEach((file, idx, arr) => { arr[idx] = new Audio(file) }) 27 | 28 | // assign to the tree so that the files can be stopped via stopAudio() 29 | tree.selectedStems = audioFiles 30 | 31 | // randomly toggle the audio files when metadata loads; now operating on tree arr 32 | tree.selectedStems[0].addEventListener('loadedmetadata', () => { 33 | for (let i = 0; i < tree.selectedStems.length; i++) { 34 | // on first loop iteration, just play the first audio file in the array. 35 | if (i === 0) { 36 | tree.selectedStems[0].loop = true 37 | tree.selectedStems[0].play() 38 | 39 | /* on every suceeding loop, que the next stem, BUT play it at a random 40 | time during the playing of the first loop -> [i - 1] */ 41 | } else { 42 | // make sure metadata (file duration) is loaded before proceding. 43 | tree.selectedStems[i].addEventListener('loadedmetadata', () => { 44 | var queNext = Math.random() * (tree.selectedStems[i - 1].duration) 45 | 46 | // Closure: Play the next file at the next unique queNext time. 47 | ~(function (idx, delay) { 48 | delay += queNext // sum equals delay so that it staircases new audio. 49 | 50 | setTimeout(function () { 51 | tree.selectedStems[idx].loop = true 52 | tree.selectedStems[idx].play() 53 | }, delay * 1000) // change 1000 to speed up / slow down when next track plays 54 | })(i, queNext) 55 | }) 56 | } 57 | } 58 | }) 59 | 60 | // connect to the volume slider 61 | let audioVolume = document.getElementById('muzak-slider') 62 | audioVolume.addEventListener('change', () => { 63 | // change volume of each stem together. 64 | tree.selectedStems.forEach((stem) => { 65 | stem.volume = audioVolume.value / 100 66 | }) 67 | }) 68 | }, 69 | 70 | stopAudio () { 71 | if (tree.selectedStems !== '') { 72 | tree.selectedStems.forEach((stem) => { 73 | stem.pause() 74 | }) 75 | } 76 | 77 | tree.selectedStems = '' 78 | 79 | el.loopButtons.childNodes.forEach(function (child) { 80 | child.classList.remove('on') 81 | }) 82 | }, 83 | 84 | createButtons () { 85 | let limit = 5 86 | // get the directory of "tracks", filter out non dirs, limit # of buttons can be made. 87 | let stems = help.getDirList(tree.stems) 88 | 89 | stems = stems.filter((dir) => { 90 | return fs.lstatSync(dir).isDirectory() 91 | }) 92 | 93 | if (stems.length > limit) { 94 | stems = stems.slice(limit) 95 | } 96 | 97 | // create button for each folder full of stems. 98 | for (let i = 0; i < stems.length; i++) { 99 | help.createButtons(stems, i, el.loopButtons, i + 1, 'loop', this.toggleSet) 100 | } 101 | 102 | // create a cancel button to stop audio 103 | help.createCancelButton(el.loopCancel, 'loop', this.stopAudio) 104 | } 105 | } 106 | 107 | module.exports = audio 108 | -------------------------------------------------------------------------------- /public/assets/js/core/interaction/back-drop.js: -------------------------------------------------------------------------------- 1 | /* This file builds out invocable logic for all background related interaction. 2 | Examples: Change Moonview's Background image.*/ 3 | const sizeOf = require('image-size') // used for easily calc. img width for bg display type. 4 | const fileUrl = require('file-url') 5 | const help = require('../../helpers/utilities') 6 | const el = require('../../helpers/dom-elements') 7 | const tree = require('../../helpers/tree') 8 | const monoThief = require('../../helpers/monochrome-thief') 9 | 10 | let backDrop = { 11 | toggleBackDrop (image) { 12 | let imageSize = sizeOf(image) 13 | 14 | // Change the image only when loaded. 15 | let img = new Image() 16 | 17 | img.onload = function () { 18 | if (imageSize.width < 1000) { 19 | document.body.style.backgroundSize = 'auto' 20 | document.body.style.backgroundRepeat = 'repeat' 21 | } else { 22 | document.body.style.backgroundSize = 'cover' 23 | document.body.style.backgroundRepeat = 'no-repeat' 24 | } 25 | document.body.style.backgroundImage = `url(${fileUrl(image)})` 26 | 27 | // Get avg brightness of image and change font color accordingly 28 | let brightness = monoThief(img) 29 | if (brightness < 80) { 30 | el.editor.style.color = '#fff' 31 | } else { 32 | el.editor.style.color = 'black' 33 | } 34 | } 35 | 36 | img.src = image 37 | if (img.complete) img.onload() 38 | }, 39 | 40 | setDefaultBackDrop () { 41 | document.body.style.background = tree.defaultBackground 42 | el.editor.style.color = 'black' 43 | 44 | el.backgroundButtons.childNodes.forEach(function (child) { 45 | child.classList.remove('on') 46 | }) 47 | }, 48 | 49 | createButtons () { 50 | // get image files and filter by .extension 51 | let imageFiles = help.getDirList(tree.bg) 52 | imageFiles = help.filterFileTypes(imageFiles, ['.png', '.jpg', '.jpeg']) 53 | 54 | // create buttons forEach file. 55 | for (let i = 0; i < imageFiles.length; i++) { 56 | help.createButtons(imageFiles, i, el.backgroundButtons, i + 1, 'bg', this.toggleBackDrop) 57 | } 58 | 59 | // create a cancel button to set default backDrop image. 60 | help.createCancelButton(el.backgroundCancel, 'background', this.setDefaultBackDrop) 61 | } 62 | 63 | } 64 | 65 | module.exports = backDrop 66 | -------------------------------------------------------------------------------- /public/assets/js/core/interaction/field-recording.js: -------------------------------------------------------------------------------- 1 | /* This file builds out invocable logic for all field recording related interaction. 2 | Examples: Start / Pause a field recording. */ 3 | 4 | const tree = require('../../helpers/tree') 5 | const help = require('../../helpers/utilities') 6 | const el = require('../../helpers/dom-elements') 7 | 8 | let fieldRecording = { 9 | 10 | toggle (file) { 11 | let audio = new Audio(file) 12 | 13 | if (tree.selectedFieldRecording.currentTime > 0) { 14 | tree.selectedFieldRecording.pause() 15 | } 16 | 17 | tree.selectedFieldRecording = audio 18 | tree.selectedFieldRecording.loop = true 19 | tree.selectedFieldRecording.play() 20 | 21 | let fieldVolume = document.getElementById('fieldrecording-slider') 22 | fieldVolume.addEventListener('change', () => { 23 | tree.selectedFieldRecording.volume = fieldVolume.value / 100 24 | }) 25 | }, 26 | 27 | stopAudio () { 28 | if (tree.selectedFieldRecording !== '') { 29 | tree.selectedFieldRecording.pause() 30 | } 31 | 32 | tree.selectedFieldRecording = '' 33 | 34 | el.loopButtons.childNodes.forEach(function (child) { 35 | child.classList.remove('on') 36 | }) 37 | }, 38 | 39 | createButtons () { 40 | // get audio files and filter by .extension 41 | let audioFiles = help.getDirList(tree.fieldRecordings) 42 | audioFiles = help.filterFileTypes(audioFiles, ['.mp3']) 43 | 44 | // create buttons forEach file. 45 | for (let i = 0; i < audioFiles.length; i++) { 46 | help.createButtons(audioFiles, i, el.fieldRecordingButtons, i + 1, 'loop', this.toggle) 47 | } 48 | 49 | // create a cancel button to stop audio 50 | help.createCancelButton(el.fieldRecordingCancel, 'fieldRecording', this.stopAudio) 51 | } 52 | } 53 | 54 | module.exports = fieldRecording 55 | -------------------------------------------------------------------------------- /public/assets/js/core/interaction/font.js: -------------------------------------------------------------------------------- 1 | const tree = require('../../helpers/tree') 2 | const el = require('../../helpers/dom-elements') 3 | const help = require('../../helpers/utilities') 4 | 5 | let font = { 6 | 7 | toggleFont (font) { 8 | // let editor = document.getElementById('editor') 9 | el.editor.style.fontFamily = font 10 | }, 11 | 12 | // create buttons forEach font. 13 | createButtons () { 14 | for (let i = 0; i < tree.fonts.length; i++) { 15 | help.createButtons(tree.fonts, i, el.fontButtons, i + 1, 'font', this.toggleFont) 16 | } 17 | } 18 | 19 | } 20 | 21 | module.exports = font 22 | -------------------------------------------------------------------------------- /public/assets/js/core/menu-ambient.js: -------------------------------------------------------------------------------- 1 | /* Assembles the menu for accessing ambient assets and interacting with: 2 | Imports interactions files (audio, font etc) for creating the buttons. This file also 3 | sets up all the necessary event listeners for interacting with the above buttons */ 4 | 5 | let el = require('../helpers/dom-elements') 6 | const audio = require('./interaction/audio') 7 | const backDrop = require('./interaction/back-drop') 8 | const font = require('./interaction/font') 9 | const fieldRecording = require('./interaction/field-recording.js') 10 | 11 | function createAmbientMenu () { 12 | // create buttons for each interactive ambient asset 13 | audio.createButtons() 14 | backDrop.createButtons() 15 | font.createButtons() 16 | fieldRecording.createButtons() 17 | 18 | mainMenuListeners() 19 | mixerMenuListeners() 20 | } 21 | 22 | // Listeners specific to the ambient menu 23 | function mainMenuListeners () { 24 | el.openMenubar.addEventListener('click', () => { 25 | el.menubar.classList.toggle('open') 26 | el.openMenubar.classList.toggle('display-none') 27 | el.closeMenubar.classList.toggle('display-none') 28 | el.openMixer.classList.add('display-none') 29 | }) 30 | 31 | el.closeMenubar.addEventListener('click', () => { 32 | swapButtons(el.allMenuButtons, 'null') 33 | el.menubar.classList.toggle('open') 34 | el.menubar.classList.remove('extend') 35 | el.openMixer.classList.remove('display-none') 36 | 37 | el.editor.focus() 38 | 39 | // hide the asset drawer if it's open 40 | el.allAssetButtons.forEach((row) => { 41 | row.classList.add('display-none') 42 | }) 43 | 44 | // swap open/close for sidebar buttons 45 | el.openMenubar.classList.toggle('display-none') 46 | el.closeMenubar.classList.toggle('display-none') 47 | el.assetDrawer.classList.add('display-none') 48 | }) 49 | 50 | // event listeners for toggling asset buttons in the second drawer 51 | el.openMuzak.addEventListener('click', () => { 52 | swapButtons(el.allMenuButtons, el.openMuzak) 53 | swapAssets(el.allAssetButtons, el.muzakAssets) 54 | }) 55 | 56 | el.openBackdrop.addEventListener('click', () => { 57 | swapButtons(el.allMenuButtons, el.openBackdrop) 58 | swapAssets(el.allAssetButtons, el.backdropAssets) 59 | }) 60 | 61 | el.openTypeface.addEventListener('click', () => { 62 | swapButtons(el.allMenuButtons, el.openTypeface) 63 | swapAssets(el.allAssetButtons, el.typefaceAssets) 64 | }) 65 | 66 | el.openFieldRecording.addEventListener('click', () => { 67 | swapButtons(el.allMenuButtons, el.openFieldRecording) 68 | swapAssets(el.allAssetButtons, el.fieldRecordingAssets) 69 | }) 70 | 71 | // Dom manipulation to happen when the editor is focused on & menus are open 72 | el.editor.addEventListener('click', () => { 73 | // close any open bottom level menus 74 | el.menubar.classList.remove('open') 75 | el.mixer.classList.remove('open') 76 | 77 | // close the asset drawer if it's open 78 | el.assetDrawer.classList.add('display-none') 79 | 80 | // revert menu icons to what they should be 81 | el.openMenubar.classList.remove('display-none') // show the open menu button 82 | el.closeMenubar.classList.add('display-none') // hide the x for menu. 83 | 84 | el.openMixer.classList.remove('display-none') // show the open for mixer 85 | el.closeMixer.classList.add('display-none') // hide the close button 86 | }) 87 | } 88 | 89 | // listners specific to the audio mixer menu 90 | function mixerMenuListeners () { 91 | // open the menu + hide the other menu 92 | el.openMixer.addEventListener('click', () => { 93 | el.mixer.classList.toggle('open') 94 | el.closeMixer.classList.toggle('display-none') 95 | el.openMixer.classList.toggle('display-none') 96 | 97 | el.openMenubar.classList.toggle('display-none') 98 | }) 99 | 100 | el.closeMixer.addEventListener('click', () => { 101 | el.mixer.classList.toggle('open') 102 | el.closeMixer.classList.toggle('display-none') 103 | el.openMixer.classList.toggle('display-none') 104 | 105 | el.openMenubar.classList.toggle('display-none') 106 | }) 107 | } 108 | 109 | // for swapping which row of media assets are displayed 110 | // (show the music buttons, img buttons etc) 111 | function swapAssets (buttonRow, exception) { 112 | el.assetDrawer.classList.remove('display-none') 113 | el.menubar.classList.add('extend') 114 | buttonRow.forEach((row) => { 115 | if (row === exception) { 116 | row.classList.remove('display-none') 117 | } else { 118 | row.classList.add('display-none') 119 | } 120 | }) 121 | } 122 | 123 | // highlights the currently selected button. 124 | function swapButtons (buttonRow, exception) { 125 | buttonRow.forEach((button) => { 126 | if (button === exception) { 127 | button.classList.add('on') 128 | button.classList.remove('dim') 129 | } else { 130 | button.classList.remove('on') 131 | button.classList.add('dim') 132 | } 133 | }) 134 | } 135 | 136 | module.exports = createAmbientMenu 137 | -------------------------------------------------------------------------------- /public/assets/js/core/menu-app.js: -------------------------------------------------------------------------------- 1 | const {remote, ipcRenderer} = require('electron') 2 | const {Menu} = remote 3 | const file = require('./file-io') 4 | const el = require('../helpers/dom-elements') 5 | 6 | const template = [ 7 | { 8 | label: 'File', 9 | submenu: [ 10 | { 11 | label: 'New', 12 | accelerator: 'CmdOrCtrl+N', 13 | click () { file.newFile() } 14 | }, 15 | { 16 | label: 'Open', 17 | accelerator: 'CmdOrCtrl+O', 18 | click () { file.open() } 19 | }, 20 | { 21 | label: 'Save', 22 | accelerator: 'CmdOrCtrl+S', 23 | click () { file.save() } 24 | }, 25 | { 26 | label: 'Save As', 27 | click () { file.saveAs() } 28 | } 29 | ] 30 | }, 31 | { 32 | label: 'Edit', 33 | submenu: [ 34 | { 35 | label: 'Undo', 36 | accelerator: 'CmdOrCtrl+Z', 37 | role: 'undo' 38 | }, 39 | { 40 | label: 'Redo', 41 | accelerator: 'Shift+CmdOrCtrl+Z', 42 | role: 'redo' 43 | }, 44 | { 45 | type: 'separator' 46 | }, 47 | { 48 | label: 'Cut', 49 | accelerator: 'CmdOrCtrl+X', 50 | role: 'cut' 51 | }, 52 | { 53 | label: 'Copy', 54 | accelerator: 'CmdOrCtrl+C', 55 | role: 'copy' 56 | }, 57 | { 58 | label: 'Paste', 59 | accelerator: 'CmdOrCtrl+V', 60 | role: 'paste' 61 | }, 62 | { 63 | label: 'Select All', 64 | accelerator: 'CmdOrCtrl+A', 65 | role: 'selectall' 66 | }, 67 | { 68 | label: 'Preferences', 69 | accelerator: 'CmdOrCtrl+,', 70 | click () { el.preferences.classList.toggle('display-none') } 71 | } 72 | ] 73 | }, 74 | { 75 | label: 'View', 76 | submenu: [ 77 | { 78 | label: 'Markdown Preview', 79 | accelerator: 'CmdOrCtrl+P', 80 | click (item, focusedWindow) { 81 | ipcRenderer.send('show-preview') 82 | } 83 | }, 84 | { 85 | label: 'Toggle Full Screen', 86 | accelerator: process.platform === 'darwin' ? 'Ctrl+Command+F' : 'F11', 87 | click (item, focusedWindow) { 88 | if (focusedWindow) focusedWindow.setFullScreen(!focusedWindow.isFullScreen()) 89 | } 90 | } 91 | 92 | ] 93 | }, 94 | { 95 | label: 'Window', 96 | role: 'window', 97 | submenu: [ 98 | { 99 | label: 'Minimize', 100 | accelerator: 'CmdOrCtrl+M', 101 | role: 'minimize' 102 | }, 103 | { 104 | label: 'Close', 105 | accelerator: 'CmdOrCtrl+W', 106 | role: 'close' 107 | } 108 | ] 109 | } 110 | 111 | ] 112 | 113 | if (process.platform === 'darwin') { 114 | const name = require('electron').remote.app.getName() 115 | template.unshift({ 116 | label: name, 117 | submenu: [ 118 | { 119 | label: 'About ' + name, 120 | role: 'about' 121 | }, 122 | { 123 | type: 'separator' 124 | }, 125 | { 126 | label: 'Services', 127 | role: 'services', 128 | submenu: [] 129 | }, 130 | { 131 | type: 'separator' 132 | }, 133 | { 134 | label: 'Hide ' + name, 135 | accelerator: 'Command+H', 136 | role: 'hide' 137 | }, 138 | { 139 | label: 'Hide Others', 140 | accelerator: 'Command+Alt+H', 141 | role: 'hideothers' 142 | }, 143 | { 144 | label: 'Show All', 145 | role: 'unhide' 146 | }, 147 | { 148 | type: 'separator' 149 | }, 150 | { 151 | label: 'Quit', 152 | accelerator: 'Command+Q', 153 | click () { 154 | if (file.isUnsaved() || file.hasChanged()) { 155 | // prompt - save or just quit? 156 | file.fileWarning('You have unsaved work.', 'Save', 'Quit', function () { 157 | // OPTION A - save 158 | file.save() 159 | }, function () { 160 | // OPTION B: Quit. 161 | ipcRenderer.send('quitter') 162 | }) 163 | } else { 164 | // file is saved and no new work has been done: 165 | ipcRenderer.send('quitter') 166 | } 167 | } 168 | } 169 | ] 170 | }) 171 | // Window menu. 172 | template[3].submenu.push( 173 | { 174 | type: 'separator' 175 | }, 176 | { 177 | label: 'Bring All to Front', 178 | role: 'front' 179 | } 180 | ) 181 | } 182 | 183 | module.exports = function createAppMenu () { 184 | Menu.setApplicationMenu(Menu.buildFromTemplate(template)) 185 | } 186 | -------------------------------------------------------------------------------- /public/assets/js/core/preview-render.js: -------------------------------------------------------------------------------- 1 | const marked = require('marked') 2 | const { ipcRenderer } = require('electron') 3 | const container = document.getElementById('preview-container') 4 | 5 | ipcRenderer.send('get-content') 6 | 7 | ipcRenderer.on('update-preview', function (e, content) { 8 | container.innerHTML = marked(content) 9 | }) 10 | -------------------------------------------------------------------------------- /public/assets/js/core/settings.js: -------------------------------------------------------------------------------- 1 | const el = require('../helpers/dom-elements') 2 | const defaultSettings = { 3 | fontSize: 13, 4 | editorWidth: 540 5 | } 6 | 7 | let settings = { 8 | 9 | setDefault () { 10 | if (!localStorage.userSettings) { 11 | localStorage.setItem('userSettings', JSON.stringify(defaultSettings)) 12 | this.updateDependencies() 13 | } 14 | 15 | this.updateDependencies() 16 | }, 17 | 18 | // helper to get the settings as an object from local storage 19 | getSettings () { 20 | return JSON.parse(localStorage.getItem('userSettings')) 21 | }, 22 | 23 | updateSettings (attr, value) { 24 | // convert ls to an obj 25 | let settings = this.getSettings() 26 | 27 | // update the settings with the new value 28 | settings[attr] = value 29 | 30 | // store to localStorage 31 | localStorage.setItem('userSettings', JSON.stringify(settings)) 32 | 33 | // update dom stuff dependent on user Settings 34 | this.updateDependencies() 35 | }, 36 | 37 | saveIt: undefined, 38 | 39 | // update dom settings based on changes to prefs window 40 | updateDependencies () { 41 | let userSettings = settings.getSettings() 42 | 43 | el.fontSize.value = userSettings.fontSize 44 | el.editor.style.fontSize = userSettings.fontSize + 'px' 45 | 46 | el.editorWidth.value = userSettings.editorWidth 47 | el.editor.style.width = userSettings.editorWidth + 'px' 48 | 49 | // show the editor width value next to the text 50 | el.editorWidthVal.textContent = userSettings.editorWidth + 'px'; 51 | } 52 | } 53 | 54 | /** ### Preference Window Event Listeners (Change the Settings) ### **/ 55 | 56 | // close the prefs modal 57 | el.closePreferences.addEventListener('click', () => { 58 | el.preferences.classList.toggle('display-none') 59 | }) 60 | 61 | // change font size 62 | el.fontSize.addEventListener('change', () => { 63 | settings.updateSettings('fontSize', el.fontSize.value) 64 | }) 65 | 66 | // change editor width 67 | el.editorWidth.addEventListener('change', () => { 68 | settings.updateSettings('editorWidth', el.editorWidth.value) 69 | }) 70 | 71 | module.exports = settings 72 | -------------------------------------------------------------------------------- /public/assets/js/helpers/dom-elements.js: -------------------------------------------------------------------------------- 1 | const el = { 2 | 3 | editor: document.getElementById('editor'), 4 | 5 | // mounting points for buttons for audio, sounds, bgs etc. 6 | loopButtons: document.getElementById('loop-buttons'), 7 | backgroundButtons: document.getElementById('background-buttons'), 8 | fontButtons: document.getElementById('font-buttons'), 9 | fieldRecordingButtons: document.getElementById('fieldrecording-buttons'), 10 | 11 | loopCancel: document.getElementById('loop-cancel'), 12 | backgroundCancel: document.getElementById('background-cancel'), 13 | fieldRecordingCancel: document.getElementById('fieldrecording-cancel'), 14 | 15 | // sidebar controls / toggles etc 16 | menubar: document.getElementById('button-drawer'), 17 | openMenubar: document.getElementById('menu-open'), 18 | closeMenubar: document.getElementById('menu-close'), 19 | assetDrawer: document.getElementById('asset-drawer'), 20 | 21 | // Drawer toggles 22 | openMuzak: document.getElementById('open-muzak'), 23 | muzakAssets: document.getElementById('muzak-assets'), 24 | 25 | openBackdrop: document.getElementById('open-backdrop'), 26 | backdropAssets: document.getElementById('backdrop-assets'), 27 | 28 | openTypeface: document.getElementById('open-typeface'), 29 | typefaceAssets: document.getElementById('typeface-assets'), 30 | 31 | openFieldRecording: document.getElementById('open-fieldrecording'), 32 | fieldRecordingAssets: document.getElementById('fieldrecording-assets'), 33 | 34 | get allAssetButtons () { 35 | return [this.muzakAssets, this.backdropAssets, this.typefaceAssets, this.fieldRecordingAssets] 36 | }, 37 | 38 | get allMenuButtons () { 39 | return [this.openMuzak, this.openBackdrop, this.openTypeface, this.openFieldRecording] 40 | }, 41 | 42 | // mixer Menu // 43 | openMixer: document.getElementById('mixer-open'), 44 | closeMixer: document.getElementById('mixer-close'), 45 | mixer: document.getElementById('mixer'), 46 | 47 | // Preferences Window // 48 | preferences: document.getElementById('preferences'), 49 | closePreferences: document.getElementById('close-preferences'), 50 | 51 | fontSize: document.getElementById('pref-font-size'), 52 | editorWidth: document.getElementById('pref-editor-width'), 53 | editorWidthVal: document.getElementById('pref-editor-width-val'), 54 | autoSave: document.getElementById('pref-autosave') 55 | } 56 | 57 | module.exports = el 58 | -------------------------------------------------------------------------------- /public/assets/js/helpers/monochrome-thief.js: -------------------------------------------------------------------------------- 1 | module.exports = function (img) { 2 | let colourSum = 0 3 | 4 | let canvas = document.createElement('canvas') 5 | canvas.width = img.width 6 | canvas.height = img.height 7 | 8 | let ctx = canvas.getContext('2d') 9 | ctx.drawImage(img, 0, 0) 10 | 11 | let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height) 12 | let data = imageData.data 13 | let r, g, b, avg 14 | 15 | for (let x = 0, len = data.length; x < len; x += 4) { 16 | r = data[x] 17 | g = data[x + 1] 18 | b = data[x + 2] 19 | 20 | avg = Math.floor((r + g + b) / 3) 21 | colourSum += avg 22 | } 23 | 24 | let brightness = Math.floor(colourSum / (img.width * img.height)) 25 | return brightness 26 | } 27 | -------------------------------------------------------------------------------- /public/assets/js/helpers/tree.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | // todo: get out of this ../../ bs 3 | const _private = '../../../../private' 4 | 5 | const tree = { 6 | audio: path.join(__dirname, _private, '/ambiance/audio'), 7 | bg: path.join(__dirname, _private, '/ambiance/images'), 8 | fieldRecordings: path.join(__dirname, _private, '/ambiance/fieldrecordings'), 9 | fonts: ['arial', 'courier', 'cursive'], 10 | stems: path.join(__dirname, _private, 'ambiance/stems'), 11 | selectedAudio: '', 12 | selectedFieldRecording: '', 13 | selectedStems: '', 14 | defaultBackground: '#f0f0f0', 15 | selectedBackground: '' 16 | } 17 | 18 | module.exports = tree 19 | -------------------------------------------------------------------------------- /public/assets/js/helpers/utilities.js: -------------------------------------------------------------------------------- 1 | /* This file works closely with menu-ambient: it get an array from a directory, 2 | creates buttons based on lists (from a dir), and add event listeners 3 | (with their own respective action) as callbacks */ 4 | 5 | const fs = require('fs') 6 | const path = require('path') 7 | 8 | const utilities = { 9 | 10 | // get a directory of files and format the paths to the files. 11 | getDirList (dir) { 12 | let dirList = fs.readdirSync(dir) 13 | .map((file) => { 14 | return path.resolve(dir, file) 15 | }) 16 | 17 | return dirList 18 | }, 19 | 20 | // oh to use arr.filter() ... 21 | filterFileTypes (files, fileTypes) { 22 | let filteredTypes = [] 23 | 24 | files.forEach((file) => { 25 | fileTypes.forEach((type) => { 26 | if (path.extname(file) === type) filteredTypes.push(file) 27 | }) 28 | }) 29 | 30 | return filteredTypes 31 | }, 32 | 33 | shuffle (array) { 34 | let currentIndex = array.length 35 | let temporaryValue 36 | let randomIndex 37 | 38 | // While there remain elements to shuffle... 39 | while (currentIndex !== 0) { 40 | // Pick a remaining element... 41 | randomIndex = Math.floor(Math.random() * currentIndex) 42 | currentIndex -= 1 43 | 44 | // And swap it with the current element. 45 | temporaryValue = array[currentIndex] 46 | array[currentIndex] = array[randomIndex] 47 | array[randomIndex] = temporaryValue 48 | } 49 | 50 | return array 51 | }, 52 | 53 | /* createButtons is called for each item in a list of assets. 54 | Purpose: Creates buttons users can interact with (playing an audio file, changing a background) 55 | Logic: Uses a closure to apply unique eventListeners to respective buttons. 56 | 57 | Parameters: 58 | assetList: Array Paths to files that will be acted on. (ex: Trigger an audio file) 59 | timesCalled: Int: Prevents fn from proceeding until all the buttons have been created. 60 | mount: String: Where to mount the button in the DOM. 61 | text: String: What text to put in the button. (currently a # from "i" from parental for-loop scope.) 62 | type: String: Provides a unique id to each button. 63 | action: Function: What to do when the button is pressed. 64 | */ 65 | createButtons (assetList, timesCalled, mount, text, type, action) { 66 | // create the button. 67 | mount.innerHTML += `` 68 | 69 | // once all buttons are made, create an array of them. 70 | if (timesCalled + 1 === assetList.length) { 71 | let arr = Array.from(mount.childNodes) // es6 nodelist -> arr. 72 | let assetArray = [] 73 | 74 | // strip non-essential childNodes (ie. non button elements). 75 | arr.forEach((el) => { 76 | if (el.nodeName === 'BUTTON') assetArray.push(el) 77 | }) 78 | 79 | // add an event listener to each item in `list` 80 | for (let i = 0; i < arr.length; i++) { 81 | (function (index) { // closure for unique event listeners. 82 | arr[index].addEventListener('click', () => { 83 | action(assetList[index]) 84 | 85 | // Turn off any button that is 'on', turn 'on' the btn clicked. 86 | arr.forEach((button) => { 87 | if (button.classList.contains('on')) button.classList.remove('on') 88 | arr[index].classList.add('on') 89 | }) 90 | }) 91 | })(i) 92 | } 93 | } 94 | }, 95 | 96 | createCancelButton (mount, type, action) { 97 | mount.innerHTML += `` 98 | mount.addEventListener('click', action) 99 | } 100 | } 101 | 102 | module.exports = utilities 103 | -------------------------------------------------------------------------------- /public/assets/js/setup.js: -------------------------------------------------------------------------------- 1 | const createAppMenu = require('./core/menu-app') 2 | const createAmbientMenu = require('./core/menu-ambient') 3 | const settings = require('./core/settings') 4 | const markdownUpdate = require('./core/content-update') 5 | const file = require('./core/file-io') 6 | 7 | const setup = function () { 8 | settings.setDefault() 9 | createAppMenu() 10 | createAmbientMenu() 11 | markdownUpdate() 12 | 13 | // listen for app close // if things are unsaved run a prompt 14 | file.windowCloseCheck() 15 | } 16 | 17 | setup() 18 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 |