├── .editorconfig ├── .gitignore ├── .prettierrc ├── README.md ├── dev ├── docs │ └── stack.md └── test │ └── helpers.spec.js ├── guide.htm ├── index.htm ├── src ├── assets │ ├── favicon │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon.png │ │ ├── browserconfig.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── manifest.json │ │ ├── mstile-150x150.png │ │ └── safari-pinned-tab.svg │ └── img │ │ ├── guide │ │ ├── cps.png │ │ ├── dark-mode.png │ │ ├── enumerate-chord.png │ │ ├── equal-temperament.png │ │ ├── general-settings.png │ │ ├── harmonic-series.png │ │ ├── isomorphic-settings.png │ │ ├── maxmsp-coll.png │ │ ├── menu-export.png │ │ ├── menu-modify.png │ │ ├── menu-new.png │ │ ├── midi-io-settings.png │ │ ├── preset-scales.png │ │ ├── qwerty-disabled.png │ │ ├── qwerty-enabled.png │ │ ├── random-variance.png │ │ ├── rank-2-temperament.png │ │ ├── reaper-named-notes.png │ │ ├── scale-entry.png │ │ ├── stretch-compress.png │ │ ├── subharmonic-series.png │ │ ├── subset.png │ │ ├── synth-settings.png │ │ ├── tempo-sync-beating.png │ │ └── virtual-keyboard.png │ │ └── scale-workshop-og-image.png ├── css │ ├── style-dark.css │ └── style.css ├── js │ ├── constants.js │ ├── events.js │ ├── exporters.js │ ├── generators.js │ ├── graphics.js │ ├── helpers.js │ ├── keymap.js │ ├── midi │ │ ├── commands.js │ │ ├── constants.js │ │ ├── math.js │ │ ├── midi.js │ │ └── ui.js │ ├── modifiers.js │ ├── scaleworkshop.js │ ├── state │ │ ├── actions-dom.js │ │ ├── actions.js │ │ ├── on-ready.js │ │ ├── reactions-dom.js │ │ ├── reactions.js │ │ └── state.js │ ├── synth.js │ ├── synth │ │ ├── Delay.js │ │ ├── Synth.js │ │ └── Voice.js │ ├── ui.js │ └── user.js └── lib │ ├── bootstrap-3.3.7-dist │ ├── css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap-theme.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ └── js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ └── npm.js │ ├── decimal.js │ ├── eventemitter3.js │ ├── jquery-3.2.1.min.js │ ├── jquery-3.2.1.min.map │ ├── jquery-ui-1.12.1 │ ├── AUTHORS.txt │ ├── LICENSE.txt │ ├── external │ │ └── jquery │ │ │ └── jquery.js │ ├── images │ │ ├── ui-icons_444444_256x240.png │ │ ├── ui-icons_555555_256x240.png │ │ ├── ui-icons_777620_256x240.png │ │ ├── ui-icons_777777_256x240.png │ │ ├── ui-icons_cc0000_256x240.png │ │ └── ui-icons_ffffff_256x240.png │ ├── index.html │ ├── jquery-ui.css │ ├── jquery-ui.js │ ├── jquery-ui.min.css │ ├── jquery-ui.min.js │ ├── jquery-ui.structure.css │ ├── jquery-ui.structure.min.css │ ├── jquery-ui.theme.css │ ├── jquery-ui.theme.min.css │ └── package.json │ ├── jszip.min.js │ ├── ramda-0.27.1.min.js │ ├── socicon │ ├── Read Me.txt │ ├── demo-files │ │ ├── demo.css │ │ └── demo.js │ ├── demo.html │ ├── fonts │ │ ├── Socicon.eot │ │ ├── Socicon.svg │ │ ├── Socicon.ttf │ │ ├── Socicon.woff │ │ └── Socicon.woff2 │ ├── selection.json │ ├── style.css │ ├── style.less │ └── variables.less │ ├── webmidi-3.0.19.iife.min.js │ └── webmidi-3.0.19.iife.min.js.map └── test.html /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js}] 8 | indent_style = space 9 | indent_size = 2 10 | quote_type = single 11 | max_line_length = 100 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "printWidth": 100, 4 | "singleQuote": true, 5 | "trailingComma": "none" 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scale Workshop 2 | 3 | ![Scale Workshop screenshot](https://raw.githubusercontent.com/SeanArchibald/scale-workshop/master/src/assets/img/scale-workshop-og-image.png) 4 | 5 | 6 | ## Description 7 | 8 | [Scale Workshop](http://sevish.com/scaleworkshop/) allows you to design microtonal scales and play them in your web browser. Export your scales for use with VST instruments. Convert Scala files to various tuning formats. 9 | 10 | 11 | ## Frequently Asked Questions 12 | 13 | ### What kinds of microtonal scales are possible with Scale Workshop? 14 | 15 | Scale Workshop can play any kind of microtonal scale, such as equal temperaments, just intonation, historical and traditional scales, non-octave scales, and any arbitrary tunings. The application offers a few methods to generate scales automatically based on parameters you set, or otherwise you can enter your scale data manually. 16 | 17 | ### Can I play and hear my scale? 18 | 19 | Yes, the built-in synth allows you to play your scales within the web browser. If your browser supports web MIDI then you can use a connected MIDI device to play notes. Otherwise you can use your computer keyboard (e.g. a QWERTY keyboard) as an isomorphic keyboard controller to hear your scales. You can also play on a touch device using the 'Touch Keyboard' feature. 20 | 21 | ### Can I use Scale Workshop to tune up other synths? 22 | 23 | Scale Workshop supports any synth that uses Scala (.scl/.kbm) files or AnaMark TUN (.tun) files. It can also export Native Instruments Kontakt tuning scripts, Max/MSP coll tuning tables and Pure Data text tuning tables. 24 | 25 | The Xen Wiki has a [list of microtonal software plugins](https://en.xen.wiki/w/List_of_Microtonal_Software_Plugins) that support Scala and AnaMark files. 26 | 27 | ### How do I enter scale/tuning data manually? 28 | 29 | Scale data should be entered in to the large text field labeled ‘Scale data’. Add each note on its own new line. Cents and ratios are both supported. 30 | 31 | * To specify a ratio, simply write it in the format e.g. `3/2` 32 | * To specify an interval in cents, include a . in the line e.g. `701.9` or `1200.` 33 | * To specify n steps out of m-EDO, write it in the format `n\m` 34 | 35 | No need to enter `0.` or `1/1` on the first line as your scale is automatically assumed to contain this interval. 36 | 37 | The interval on the final line is assumed to be your interval of equivalence (i.e. your octave or pseudo-octave). 38 | 39 | Don't add any other weird data to a line. Don't try to mix decimals with ratios (e.g. `2/1.5`). Scale Workshop will try to gracefully ignore any rubbish that you put in, but it's very possible that weird stuff will happen. 40 | 41 | ### Can I copy and paste the contents of a Scala file (.scl) directly into the 'Scale data' field? 42 | 43 | Scala files contain non-tuning related comments at the top of the file, so Scale Workshop will throw an error if you try to paste them in directly. Instead you can use the ‘Load .scl’ function, which automatically removes those comments for you. Or you can paste the Scala file but remove the comments manually. 44 | 45 | ### Can I convert a TUN file to another format? 46 | 47 | Yes, start by clicking New > Import .TUN and then load your TUN file into Scale Workshop. Then click Export and select your desired output format. Note that Scale Workshop is not a fully compliant AnaMark TUN v2 parser, however it should be able to read TUN files exported by Scala and Scale Workshop. 48 | 49 | ### How do I make my own keyboard mapping? 50 | 51 | Keyboard mappings are not currently supported. You can still export a Scala keyboard mapping file (.kbm) but it will assume a linear mapping. 52 | However you can always use duplicate lines in your tuning in order to skip any keys that you don't want to include, or write your .kbm file manually. 53 | 54 | ### Can I undo/redo? 55 | 56 | Use your browser's Back/Forward navigation buttons to undo/redo changes to your tuning. 57 | 58 | ### How can I share my tunings with a collaborator? 59 | 60 | Use Export > Share scale as URL. The given URL can be copied and pasted to another person. When they open the link they will see a Scale Workshop page with your scale already tuned in. 61 | 62 | ### How can I save my work for later? 63 | 64 | You can bookmark the current page to save your work for later. This works because your tuning data is stored within the bookmarked URL. 65 | 66 | ### When I export a tuning, I get a weird filename, why? 67 | 68 | Exporting a file with the correct filename is not supported in Safari (iOS and macOS). You can try to use Firefox, Chrome or Opera instead. 69 | 70 | ### Can I run this software offline? 71 | 72 | Yes, just download the project from GitHub as a zip file and run index.htm from your web browser. 73 | 74 | ### Can you add a new feature to Scale Workshop? 75 | 76 | Probably! Just add your feature request to the [issue tracker](https://github.com/SeanArchibald/scale-workshop/issues) on GitHub. 77 | 78 | ### I found a bug 79 | 80 | Please [create a bug report](https://github.com/SeanArchibald/scale-workshop/issues) detailing the steps to reproduce the issue. You should also include which web browser and OS you are using. 81 | 82 | 83 | ## Contributing 84 | 85 | Please base any work on develop branch, and pull requests should also be made against develop branch not master. 86 | 87 | 88 | ## Changelog 89 | 90 | ### 1.5 91 | * Feature: output MIDI for real-time microtuning in MIDI synths that support multichannel pitch bend 92 | * Feature: added button for toggling velocity sensing for MIDI in 93 | * Improvement: Virtual Keyboard now works better on desktop, can be closed with the Esc key, keys are highlighted when pressed 94 | * Improvement: user guide documentation is updated 95 | * Improvement: better decimal precision 96 | * Bug fix: typo in Kraig Grady Centaura Harmonic preset scale 97 | * Bug fix: enumerate chord inversion 98 | * Bug fix: Rotate modifier now preserves nonlinear scale data 99 | 100 | ### 1.4.2 101 | * New modifier: Rotate. This allows you to choose an interval from your scale to be the new 1/1. (Known issue: it doesn't work as expected for intervals with decimal notation e.g. `1,5`, `2,0`) 102 | * Bug fixed: synth notes stuck playing quietly in the background 103 | 104 | ### 1.4.1 105 | * Bug fixed: lag on Subset option 106 | * Bug fixed: audio drop out 107 | * Improvement: Graphical ruler shows currently loaded scale 108 | 109 | ### 1.4 110 | * New scale generator: Combination Product Set (CPS) 111 | * New scale modifier: Reduce 112 | * New scale modifier: Sort ascending 113 | * New keyboard keymap: Colemak DH 114 | * Bug fixed in TUN v1 file export (will improve compatibility with Serum, maybe others) 115 | * Improvement: More synth waveforms added 116 | * Improvement: More preset scales added 117 | * Improvement: Updates to the user guide 118 | * Improvement: Reaper Note Name Map provides more options 119 | * Improvement: Rank-2 scale generator will now preserve interval notation (e.g. it will return ratios if you input ratios, return cents if you input cents) 120 | 121 | ### 1.3.2 122 | * Many more preset scales added. Happy exploring! 123 | 124 | ### 1.3.1 125 | * AnaMark TUN export now supports a choice of v1 or v2 as different synths require a certain version. 126 | 127 | ### 1.3 128 | * AnaMark TUN export now contains v1 data only. This should improve compatibility with synths (e.g. Omnisphere and Quanta) 129 | * New feature: Export Korg Minilogue & Monologue tuning formats (.mnlgtuns & .mnlgtuno) 130 | * New feature: Export Soniccouture tuning format (.nka) 131 | * New feature: Export Reaper Note Name Map (.txt) 132 | * Bug fix: AnaMark TUN export is now v1 compliant - fixes compatibility with Spectrasonics Omnisphere 133 | 134 | ### 1.2 135 | * New feature: Approximate scale by harmonics of an arbitrary denominator 136 | * New feature: Approximate scale by subharmonics of an arbitrary numerator 137 | * New feature: Approximate scale to equal divisions 138 | * Improvement: 'Clear scale' function now moved into 'New' menu 139 | * Improvement: 'Mode' renamed to 'Subset' 140 | * Improvement: Various updates to the user guide 141 | * Bug fix: 'Stretch/compress' now works as it should 142 | * Bug fix: 'Tempo-sync beating' now works as it should 143 | 144 | ### 1.1.1 145 | * Improvement: When sharing a Scale Workshop link (on Discord, Facebook, etc.) the site description is now much shorter so takes less space 146 | 147 | ### 1.1 148 | * New feature: Export tuning files for Harmor and Sytrus synths (thanks to Azorlogh) 149 | * Improvement: 'Mode' feature now shows a counter while you input a subset 150 | * Improvement: Include scale URL in a comment within exported TUN, scl, Max/MSP txt and Kontakt script exports (issue #66) 151 | 152 | ### 1.0.4 153 | * New feature: 'Approximate' method for modifying scales can produce rational approximations of your scale 154 | * Improvement: Enumerate Chord method of scale generation now allows for inverted chords (e.g. 4:5:6 inverted would give 10:12:15) 155 | * Improvement: site now automatically redirects to HTTPS on domains known to have valid HTTPS 156 | * Bug fix: changing the main volume before pressing the first note no longer gets ignored 157 | * Bug fix: exported TUN files now has a correct functional tuning section (https://github.com/SeanArchibald/scale-workshop/issues/82) 158 | 159 | ### 1.0.3 160 | * New feature: generate scale from 'Enumerate chord' e.g. `4:5:6:7:8` will result in a scale containing intervals 1/1, 5/4, 3/2, 7/4, 2/1 161 | * New feature: specify an interval in decimal format e.g. `1,5` for a perfect fifth, `2,0` for an octave. 162 | * New feature: export your tuning as a list of Deflemask 'fine tune' effect parameters. The resulting text file is a reference you can use when manually inputting notes into Deflemask chip music tracker. 163 | * Improvement: Colemak keyboard layout support added 164 | * Improvement: when generating rank-2 temperaments, finding MOS scale sizes is now more efficient. 165 | * Bug fix: error when changing main volume before audio initialised 166 | 167 | ### 1.0.2 168 | * MIDI now waits for user input before initializing (issues #56 #57) 169 | * Rank-2 temperament generator now assumes you want all positive/up generators by default (issue #58) 170 | 171 | ### 1.0.1 172 | * Fix stuck notes during MIDI note input 173 | * Fix stuck notes when playing pad synth in Firefox/Safari 174 | 175 | ### 1.0.0 176 | * Stable version 177 | * New modifier added: tempo-sync beating 178 | * Minor bug fixes 179 | 180 | ### 0.9.9 181 | * Added a selection of preset scales 182 | * Fix issue using delay in some situations 183 | * Fix issue stretching/compressing scales in some situations 184 | * Minor interface and user guide improvements 185 | 186 | ### 0.9.8 187 | * Fix .scl import bug 188 | 189 | ### 0.9.7 190 | * Added user guide 191 | * Fix `n\m` style data input 192 | 193 | ### 0.9.6 194 | * Improved modal dialogs on mobile 195 | * Fix regression exporting .tun files 196 | 197 | ### 0.9.5 198 | * Loading the synth is now delayed as much as possible 199 | * Better compatibility for exported Scala files (placeholder description will be used if user doesn't provide a tuning description) 200 | * Improved mode input - you can optionally enter a list of scale degrees from the base note (e.g. 2 4 5 7 9 11 12) 201 | * Stricter validation of tuning data input, improves security 202 | * More default/auto keyboard colour layouts added 203 | 204 | ### 0.9.4 205 | * Import AnaMark .tun files (NOT compliant to the AnaMark v2 spec, but should import tun files generated by Scala and Scale Workshop) 206 | * Dvorak and Programmer Dvorak keyboard layouts are now supported 207 | * Code refactoring and improvements 208 | * Fix: Scale Workshop will no longer prevent keyboard shortcuts from being used 209 | 210 | ### 0.9.3 211 | * Undo/redo function (via browser back/forward navigation) 212 | * Various UI improvements, mostly for phone-sized devices 213 | * Code refactoring and improvements (thanks Lajos) 214 | 215 | ### 0.9.2 216 | * Added key colour customisation 217 | * Added 'About Scale Workshop' screen 218 | * When sharing scale by URL, key colour layout and synth options will now carry across 219 | * When using a menu option that opens a modal dialog, the first field will automatically be selected 220 | * Choice of regional keyboard layout is now remembered across sessions 221 | * Delay time control now shows milliseconds value 222 | 223 | ### 0.9.1 224 | * Improved rank-2 temperament generator. You can now specify how many generators up or down from 1/1 225 | 226 | ### 0.9 227 | * Added virtual keyboard for touch interfaces (experimental) 228 | 229 | ### 0.8.9 230 | * Improved workflow ('Calculate' button removed as the app now responds to scale data changes automatically) 231 | * Improved no-javascript error message 232 | * Fix: Scala .scl file export now preserves ratios instead of converting them to cents 233 | 234 | ### 0.8.8 235 | * Fix stuck notes in Mozilla Firefox (due to differing implementations of the Web Audio API between web browsers, the amplitude envelopes are going to sound slightly different in Firefox) 236 | * Fix blank option shown in 'Line endings format' when using Scale Workshop for the first time 237 | * Fix styling issue with light theme when hovering over top menu option 238 | 239 | ### 0.8.7 240 | 241 | * Basic MIDI input support 242 | * General Settings are now automatically saved and restored across sessions 243 | * Added "Night Mode" dark theme for late night sessions in the workshop 244 | * Added user.js file where you can add your own custom script if needed 245 | 246 | ### 0.8.6 247 | 248 | * Added info tooltips 249 | * URL fix for Xenharmonic Wiki 250 | 251 | ### 0.8.5 252 | 253 | * Added amplitude envelope for synth notes (organ, pad, and percussive presets) 254 | * Added main volume control 255 | * Added keyboard layout setting for international keyboards (English and Hungarian supported) 256 | 257 | ### 0.8.4 258 | 259 | * Added delay effect 260 | * Added 'auto' function for base frequency, which calculates the frequency for the specified MIDI note number assuming 12-EDO A440 261 | * Added option to choose between Microsoft/Unix line endings 262 | * Added indicator to show when Qwerty isomorphic keyboard is active (when typing in a text field, it is inactive) 263 | * Added 'Quiet' button in case things get noisy in the workshop 264 | * Added share scale as URL to email, twitter 265 | * Fix sharing scale as URL - isomorphic mapping 266 | * Removed debug option - debug messages will now be output to the JavaScript console by default. Use `debug = false;` in console to disable 267 | * Improved options menu - options instantly take effect when changed (removed Apply/Save button) 268 | 269 | ### 0.8.3 270 | 271 | * Fix sharing scale as URL - now the qwerty isomorphic mapping is correctly shared 272 | 273 | ### 0.8.2 274 | 275 | * Settings have been moved to the right-side column (desktop) 276 | * Added option to export a list of frequencies in text format readable by Pure Data's [text] object 277 | 278 | ### 0.8.1 279 | 280 | * Choice of waveform for the synth: triangle, sawtooth, square, sine 281 | * Settings menus added - General, Synth and Note Input settings 282 | * Qwerty isomorphic keyboard mapping can be changed in the Note Input settings 283 | * Qwerty isomorphic keyboard mapping is saved when sharing scale by URL 284 | * Currently displayed notes are now highlighted in the tuning data table 285 | * Fix stuck note in FireFox when pressing `/` key 286 | * UI improvement (for large screens): tall columns are now contained within one window and individually scrollable 287 | * Tuning data table is now displayed more compactly to show more info at once 288 | 289 | ### 0.8 290 | 291 | * Synth added: use the QWERTY keys to play current scale 292 | * Export a scale as a URL with the 'Share scale as URL' option 293 | 294 | ### 0.7.1 295 | 296 | * Fix missing line breaks on Notepad and some other text editors 297 | * Improved readme formatting (thanks suhr!) 298 | 299 | ### 0.7.0 300 | 301 | * Scale modifiers added: ‘stretch’, ‘random variance’, ‘mode’ 302 | * Users can now input `n\m` to specify n steps out of m-EDO 303 | * When generating a rank-2 temperament, display the scale sizes which are MOS 304 | * Improve UI for user input, using custom modals instead of JS prompts 305 | * Code refactored to reduce the amount of duplication 306 | * Code is now split up over various js files so it's easier to navigate 307 | * Change logo/favicon to square shape 308 | 309 | ### 0.6 310 | 311 | * Generate rank-2 temperaments 312 | 313 | ### 0.5 314 | 315 | * Fix incorrect base frequency when exporting TUN format and NI Kontakt format 316 | * Export Scala .kbm format 317 | 318 | ### 0.4 319 | 320 | * All dependencies (Bootstrap, jQuery etc.) now included in scaleworkshop directory 321 | * Import Scala .scl format 322 | * Export Scala .scl format 323 | * Export AnaMark TUN format 324 | * Export Native Instruments Kontakt script format 325 | * Export Max/MSP coll format 326 | 327 | ### 0.3 328 | 329 | * Generate equal-tempered tuning 330 | * Generate harmonic series segment tuning 331 | * Generate subharmonic series segment tuning 332 | 333 | ### 0.2 334 | 335 | * Allow tuning data input to be parsed into a frequency table 336 | 337 | ### 0.1 338 | 339 | * Initial version 340 | 341 | 342 | ## Contributors 343 | 344 | * Sevish 345 | * Scott Thompson 346 | * Lajos Mészáros 347 | * Carl Lumma 348 | * Tobia 349 | * Vincenzo Sicurella 350 | * Azorlogh 351 | 352 | 353 | ## License 354 | 355 | Copyright (c) 2017-2019 Sean Archibald 356 | 357 | Permission is hereby granted, free of charge, to any person obtaining a copy 358 | of this software and associated documentation files (the "Software"), to deal 359 | in the Software without restriction, including without limitation the rights 360 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 361 | copies of the Software, and to permit persons to whom the Software is 362 | furnished to do so, subject to the following conditions: 363 | 364 | The above copyright notice and this permission notice shall be included in all 365 | copies or substantial portions of the Software. 366 | 367 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 368 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 369 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 370 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 371 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 372 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 373 | SOFTWARE. 374 | -------------------------------------------------------------------------------- /dev/docs/stack.md: -------------------------------------------------------------------------------- 1 | # Stack 2 | 3 | ## Client 4 | 5 | - Bootstrap 3.3.7 - https://getbootstrap.com/docs/3.3/ 6 | - jQuery 3.2.1 - https://api.jquery.com/ 7 | - jQuery UI 1.12.1 - https://jqueryui.com/ 8 | - Ramda 0.27.1 - https://ramdajs.com/docs/ 9 | - JSZip 3.2.1 - http://stuartk.com/jszip 10 | 11 | ## Testing 12 | 13 | - Mocha - https://mochajs.org/ 14 | - Expect - https://jestjs.io/docs/expect 15 | -------------------------------------------------------------------------------- /src/assets/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /src/assets/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /src/assets/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /src/assets/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #00a300 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/assets/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /src/assets/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /src/assets/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/favicon/favicon.ico -------------------------------------------------------------------------------- /src/assets/favicon/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "icons": [ 4 | { 5 | "src": "/android-chrome-192x192.png", 6 | "sizes": "192x192", 7 | "type": "image/png" 8 | }, 9 | { 10 | "src": "/android-chrome-512x512.png", 11 | "sizes": "512x512", 12 | "type": "image/png" 13 | } 14 | ], 15 | "theme_color": "#ffffff", 16 | "background_color": "#ffffff", 17 | "display": "standalone" 18 | } -------------------------------------------------------------------------------- /src/assets/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /src/assets/favicon/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/guide/cps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/cps.png -------------------------------------------------------------------------------- /src/assets/img/guide/dark-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/dark-mode.png -------------------------------------------------------------------------------- /src/assets/img/guide/enumerate-chord.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/enumerate-chord.png -------------------------------------------------------------------------------- /src/assets/img/guide/equal-temperament.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/equal-temperament.png -------------------------------------------------------------------------------- /src/assets/img/guide/general-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/general-settings.png -------------------------------------------------------------------------------- /src/assets/img/guide/harmonic-series.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/harmonic-series.png -------------------------------------------------------------------------------- /src/assets/img/guide/isomorphic-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/isomorphic-settings.png -------------------------------------------------------------------------------- /src/assets/img/guide/maxmsp-coll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/maxmsp-coll.png -------------------------------------------------------------------------------- /src/assets/img/guide/menu-export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/menu-export.png -------------------------------------------------------------------------------- /src/assets/img/guide/menu-modify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/menu-modify.png -------------------------------------------------------------------------------- /src/assets/img/guide/menu-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/menu-new.png -------------------------------------------------------------------------------- /src/assets/img/guide/midi-io-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/midi-io-settings.png -------------------------------------------------------------------------------- /src/assets/img/guide/preset-scales.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/preset-scales.png -------------------------------------------------------------------------------- /src/assets/img/guide/qwerty-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/qwerty-disabled.png -------------------------------------------------------------------------------- /src/assets/img/guide/qwerty-enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/qwerty-enabled.png -------------------------------------------------------------------------------- /src/assets/img/guide/random-variance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/random-variance.png -------------------------------------------------------------------------------- /src/assets/img/guide/rank-2-temperament.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/rank-2-temperament.png -------------------------------------------------------------------------------- /src/assets/img/guide/reaper-named-notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/reaper-named-notes.png -------------------------------------------------------------------------------- /src/assets/img/guide/scale-entry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/scale-entry.png -------------------------------------------------------------------------------- /src/assets/img/guide/stretch-compress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/stretch-compress.png -------------------------------------------------------------------------------- /src/assets/img/guide/subharmonic-series.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/subharmonic-series.png -------------------------------------------------------------------------------- /src/assets/img/guide/subset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/subset.png -------------------------------------------------------------------------------- /src/assets/img/guide/synth-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/synth-settings.png -------------------------------------------------------------------------------- /src/assets/img/guide/tempo-sync-beating.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/tempo-sync-beating.png -------------------------------------------------------------------------------- /src/assets/img/guide/virtual-keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/guide/virtual-keyboard.png -------------------------------------------------------------------------------- /src/assets/img/scale-workshop-og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/assets/img/scale-workshop-og-image.png -------------------------------------------------------------------------------- /src/css/style-dark.css: -------------------------------------------------------------------------------- 1 | /* "Night Mode" dark theme styles */ 2 | 3 | body.dark { 4 | background-color: #000; 5 | color: #bbb; 6 | } 7 | 8 | .dark p, .dark label { 9 | color: #bbb; 10 | } 11 | 12 | .dark hr { 13 | border-color: #222; 14 | } 15 | 16 | .dark .ui-tooltip { 17 | background-color: #000; 18 | } 19 | 20 | .dark #header-mobile { 21 | background-color: #111; 22 | } 23 | 24 | .dark canvas { 25 | filter: invert(1); 26 | } 27 | 28 | /* Interactable elements */ 29 | 30 | .dark .navbar button, .dark .navbar-inverse .navbar-toggle:focus, .dark .navbar-inverse .navbar-toggle:hover, .dark .navbar-collapse { 31 | background-color: #111; 32 | } 33 | 34 | .dark input:not(.btn), .dark textarea, .dark select { 35 | background-color: #222; 36 | color: #bbb; 37 | border-color: #888; 38 | } 39 | 40 | .dark .input-group-addon { 41 | background-color: #000; 42 | color: #bbb; 43 | } 44 | 45 | .dark .btn-default, .dark .ui-button { 46 | background-color: #222; 47 | color: #bbb; 48 | } 49 | 50 | /* Accordion menu */ 51 | 52 | .dark .ui-accordion-header { 53 | background-color: #222; 54 | color: #bbb; 55 | } 56 | 57 | .dark .ui-accordion-content { 58 | background-color: #000; 59 | color: #bbb; 60 | } 61 | 62 | /* Navbar menu */ 63 | 64 | .dark ul.dropdown-menu { 65 | background-color: #000; 66 | border: 1px solid #444; 67 | border-top: none; 68 | } 69 | 70 | .dark .dropdown-menu > li > a { 71 | color: #bbb; 72 | } 73 | 74 | .dark .dropdown-menu>li>a:focus, .dark .dropdown-menu>li>a:hover { 75 | background-color: #222; 76 | } 77 | 78 | .dark .dropdown-menu .divider { 79 | background-color: #222; 80 | } 81 | 82 | .dark .ui-widget-overlay { 83 | background: black; 84 | } 85 | 86 | /* Modal dialogs */ 87 | 88 | .dark .ui-widget-content { 89 | color: #bbb; 90 | } 91 | 92 | .dark .ui-dialog a { 93 | color: #ddd; 94 | } 95 | 96 | .dark .ui-dialog { 97 | box-shadow: #444 0px 0px 70px; 98 | } 99 | 100 | .dark .ui-dialog, .dark .ui-dialog-titlebar { 101 | background-color: #000; 102 | color: #bbb; 103 | border: none; 104 | border-radius: 0px; 105 | } 106 | 107 | .dark .ui-dialog-titlebar { 108 | border-bottom: 1px solid #888; 109 | } 110 | 111 | .dark button.ui-button.ui-corner-all.ui-widget.ui-button-icon-only.ui-dialog-titlebar-close { /* modal close buttons */ 112 | background: #000; 113 | border: none; 114 | } 115 | 116 | .dark .ui-dialog-buttonpane { 117 | background: #000; 118 | border: none; 119 | } 120 | 121 | .dark .socicon-mail { 122 | color: #bbb; 123 | } 124 | 125 | /* Virtual Keyboard */ 126 | 127 | .dark #virtual-keyboard { 128 | background-color: black; 129 | } 130 | .dark #virtual-keyboard td { 131 | border: 1px solid grey; 132 | } 133 | 134 | /* Tuning Table */ 135 | 136 | .dark #tuning-table th, .dark #tuning-table td { 137 | border-color: #333; 138 | } 139 | 140 | .dark #tuning-table th:hover, .dark #tuning-table tr:hover { 141 | background-color: #222; 142 | } 143 | 144 | .dark #tuning-table tr.warning td { 145 | background-color: #192d37; /*#fcf8e3*/ 146 | } 147 | 148 | .dark #tuning-table tr.info td { 149 | background-color: #4c4823; /*#d9edf7*/ 150 | } 151 | 152 | .dark tr.bg-playnote td { 153 | background-color: #1f4018 !important; /*#dff0d8*/ 154 | } 155 | 156 | /* 157 | * NON-MOBILE 158 | */ 159 | @media (min-width: 768px) { 160 | 161 | .dark .navbar { 162 | background-color: #111; 163 | } 164 | 165 | } 166 | @media (min-width: 992px) { 167 | 168 | 169 | 170 | } 171 | @media (max-width: 991px) { 172 | 173 | 174 | 175 | } 176 | -------------------------------------------------------------------------------- /src/css/style.css: -------------------------------------------------------------------------------- 1 | img, 2 | canvas { 3 | max-width: 100%; 4 | } 5 | 6 | .helpicon { 7 | color: #999; 8 | font-size: 0.9em; 9 | } 10 | 11 | .hidden { 12 | display: none; 13 | } 14 | 15 | .ui-widget-overlay { 16 | position: absolute; 17 | top: 0; 18 | left: 0; 19 | width: 100%; 20 | height: 100%; 21 | background: white; 22 | opacity: 0.5; 23 | } 24 | 25 | /* Make jQuery UI colors match with Bootstrap */ 26 | .ui-state-default, 27 | .ui-widget-content .ui-state-default, 28 | .ui-widget-header .ui-state-default, 29 | .ui-button, 30 | html .ui-button.ui-state-disabled:hover, 31 | html .ui-button.ui-state-disabled:active { 32 | color: black; 33 | background-color: #eee; 34 | border-color: #ccc; 35 | } 36 | .ui-state-active, 37 | .ui-widget-content .ui-state-active, 38 | .ui-widget-header .ui-state-active, 39 | a.ui-button:active, 40 | .ui-button:active, 41 | .ui-button.ui-state-active:hover { 42 | color: black; 43 | background-color: #eee; 44 | border-color: #ccc; 45 | } 46 | 47 | h3.ui-accordion-header { 48 | font-weight: bold; 49 | } 50 | 51 | textarea { 52 | resize: vertical; 53 | } 54 | 55 | /* normal cursor when hovering over navbar */ 56 | .navbar a { 57 | cursor: default; 58 | } 59 | 60 | .navbar { 61 | z-index: 4; 62 | position: fixed; 63 | left: 0px; 64 | top: 0px; 65 | border: none; 66 | width: 100%; 67 | } 68 | 69 | .navbar button { 70 | margin: 0px; 71 | border-radius: unset; 72 | border: none; 73 | background-color: #333; 74 | width: 50px; 75 | } 76 | 77 | .navbar-toggle .icon-bar { 78 | width: 30px; 79 | } 80 | 81 | .navbar-inverse { 82 | background-color: unset; 83 | } 84 | 85 | .navbar-toggle { 86 | padding: 10px; 87 | } 88 | 89 | .navbar-toggle .icon-bar + .icon-bar { 90 | margin-top: 12px; 91 | } 92 | 93 | .navbar-header { 94 | padding-left: 0px !important; 95 | } 96 | 97 | .navbar-collapse { 98 | background-color: #333; 99 | border: none; 100 | width: calc(100vw - 50px); 101 | position: fixed; 102 | top: 0px; 103 | max-height: 100vh; 104 | } 105 | 106 | .navbar-brand { 107 | display: none; 108 | cursor: default; 109 | } 110 | 111 | .nav > li { 112 | width: 49%; 113 | display: inline-block; 114 | } 115 | 116 | .nav > li:hover, 117 | .navbar-brand:hover { 118 | background-color: #222; 119 | } 120 | 121 | .nav > li.open { 122 | width: 100%; 123 | } 124 | 125 | #header-mobile { 126 | background-color: #333; 127 | width: 100%; 128 | height: 50px; 129 | margin-top: -20px; 130 | } 131 | 132 | #header-mobile h1 { 133 | font-size: 12pt; 134 | font-weight: normal; 135 | color: white; 136 | padding: 17px; 137 | } 138 | 139 | body > .container-fluid > .row { 140 | margin-top: 20px; 141 | } 142 | 143 | input#btn_frequency_auto, 144 | input#btn_key_colors_auto { 145 | margin-top: 3px; 146 | } 147 | 148 | #txt_name { 149 | font-size: 1.4em; 150 | background-color: unset; 151 | } 152 | 153 | #col-tuning-table { 154 | padding-left: 0px; 155 | padding-right: 0px; 156 | } 157 | 158 | #tuning-table { 159 | margin-bottom: 4px; 160 | } 161 | 162 | .table-condensed > tbody > tr > td, 163 | .table-condensed > tbody > tr > th, 164 | .table-condensed > tfoot > tr > td, 165 | .table-condensed > tfoot > tr > th, 166 | .table-condensed > thead > tr > td, 167 | .table-condensed > thead > tr > th { 168 | padding: 3px 5px; 169 | } 170 | 171 | tr.bg-playnote td { 172 | background-color: #dff0d8 !important; 173 | } 174 | 175 | #tuning-table td, 176 | #tuning-table th { 177 | text-align: center; 178 | } 179 | 180 | p.social-icons { 181 | text-align: center; 182 | font-size: 1.5em; 183 | } 184 | .social-icons .socicon-twitter { 185 | color: #4da7de; 186 | } 187 | 188 | div#qwerty-indicator { 189 | padding: 1em; 190 | display: none; 191 | } 192 | 193 | #btn_panic { 194 | display: none; 195 | } 196 | 197 | div#splash { 198 | position: absolute; 199 | top: 0; 200 | left: 0; 201 | width: 100vw; 202 | height: 100vh; 203 | background-color: white; 204 | z-index: 10; 205 | display: table; 206 | } 207 | 208 | div#splash-center { 209 | display: table-cell; 210 | vertical-align: middle; 211 | text-align: center; 212 | } 213 | div#splash-center img { 214 | max-width: 70vw; 215 | height: auto; 216 | box-shadow: #aaa 0px 0px 40px; 217 | } 218 | 219 | #modal_load_preset_scale optgroup + optgroup { 220 | margin-top: 1em; 221 | } 222 | 223 | /* Virtual keyboard */ 224 | 225 | #virtual-keyboard { 226 | background-color: white; 227 | position: fixed; 228 | top: 50px; 229 | left: 0; 230 | width: 100vw; 231 | min-width: 500px; /* this stops the keys getting too close together for portrait mobile users */ 232 | height: calc(100vh - 50px); 233 | display: none; 234 | z-index: 2; 235 | } 236 | #virtual-keyboard td { 237 | text-align: center; 238 | vertical-align: middle; 239 | border: 1px solid grey; 240 | font-size: 0.6em; 241 | user-select: none; 242 | cursor: pointer; 243 | } 244 | #virtual-keyboard td p { 245 | pointer-events: none; 246 | word-break: break-word; 247 | line-height: 1.1em; 248 | color: #888; 249 | } 250 | 251 | #virtual-keyboard .key:hover { 252 | background: linear-gradient( 253 | 0deg, 254 | rgba(255, 255, 255, 0) 0%, 255 | rgba(255, 0, 0, 0.5) 50%, 256 | rgba(255, 255, 255, 0) 100% 257 | ); 258 | } 259 | #virtual-keyboard .key.active { 260 | background: linear-gradient( 261 | 0deg, 262 | rgba(0, 0, 0, 0) 0%, 263 | rgba(0, 255, 0, 0.5) 50%, 264 | rgba(0, 0, 0, 0) 100% 265 | ); 266 | } 267 | 268 | /* 269 | * Fullscreen variant for jQueryUI modal widget 270 | */ 271 | .fullscreen-modal { 272 | top: 0px !important; 273 | left: 0px !important; 274 | width: 100vw !important; 275 | height: 100vh !important; 276 | position: fixed; 277 | } 278 | .fullscreen-modal .ui-dialog-buttonpane { 279 | } 280 | 281 | /* 282 | * JQUERY MODAL UI MOBILE-ONLY FIXES 283 | */ 284 | @media (max-width: 420px), /* OR */ (max-height: 420px) { 285 | .ui-dialog { 286 | top: 0px !important; 287 | left: 0px !important; 288 | width: 100vw !important; 289 | max-height: 100vh !important; 290 | position: fixed; 291 | overflow-x: scroll; 292 | } 293 | } 294 | 295 | /* 296 | * NON-MOBILE 297 | */ 298 | @media (min-width: 768px) { 299 | body > .container-fluid > .row { 300 | margin-top: 0px; 301 | } 302 | 303 | div#qwerty-indicator { 304 | display: block; 305 | } 306 | 307 | .col-main { 308 | /* main columns of the Scale Workshop UI */ 309 | height: calc(100vh - 70px); 310 | overflow-y: auto; 311 | } 312 | 313 | #btn_panic { 314 | display: unset; 315 | } 316 | 317 | #virtual-keyboard { 318 | font-size: 0.9em; 319 | height: calc(100vh - 50px); 320 | } 321 | 322 | #tuning-table td.key-color, 323 | #tuning-table th.key-color { 324 | border-left: 1px solid #ddd; 325 | } 326 | 327 | .navbar { 328 | border-radius: 0px; 329 | } 330 | 331 | .navbar { 332 | z-index: 4; 333 | position: relative; 334 | left: unset; 335 | top: unset; 336 | background-color: #222; 337 | border: none; 338 | } 339 | 340 | .navbar-header { 341 | padding-left: 0px !important; 342 | } 343 | 344 | .navbar-collapse { 345 | background-color: unset; 346 | position: unset; 347 | } 348 | 349 | .navbar-brand { 350 | display: block; 351 | } 352 | 353 | .nav > li { 354 | width: unset; 355 | display: block; 356 | } 357 | 358 | .nav > li.open { 359 | width: unset; 360 | } 361 | } 362 | @media (min-width: 992px) { 363 | .col-sub { 364 | /* main columns of the Scale Workshop UI */ 365 | height: calc(100vh - 70px); 366 | overflow-y: auto; 367 | } 368 | 369 | .navbar { 370 | border-radius: 0px; 371 | } 372 | } 373 | @media (max-width: 991px) { 374 | #col-tuning-table { 375 | margin-top: 1em; 376 | padding-left: 0px; 377 | padding-right: 0px; 378 | } 379 | } 380 | 381 | #modal_midi_settings { 382 | user-select: none; 383 | } 384 | #modal_midi_settings .form-group { 385 | margin-top: 20px; 386 | } 387 | #modal_midi_settings .settings .row { 388 | display: flex; 389 | margin: 0; 390 | } 391 | #modal_midi_settings .device { 392 | display: flex; 393 | align-items: center; 394 | } 395 | #modal_midi_settings .checkbox-wrapper { 396 | padding: 0 10px; 397 | } 398 | #modal_midi_settings .device input[type='checkbox'] { 399 | margin: 0; 400 | } 401 | #modal_midi_settings .device h4 { 402 | flex-grow: 1; 403 | font-size: unset; 404 | } 405 | #modal_midi_settings .device h4 label { 406 | margin: 0; 407 | font-weight: unset; 408 | } 409 | #modal_midi_settings .channels { 410 | display: flex; 411 | flex-wrap: wrap; 412 | } 413 | #modal_midi_settings .channels label, #modal_midi_settings .settings label { 414 | font-weight: unset; 415 | } 416 | #modal_midi_settings .device + .device { 417 | margin-top: 2em; 418 | } 419 | #modal_midi_settings .channel { 420 | display: flex; 421 | flex-direction: column; 422 | padding: 0 10px; 423 | align-items: center; 424 | } 425 | #modal_midi_settings .channel input[type='checkbox'] { 426 | margin: 0; 427 | } 428 | -------------------------------------------------------------------------------- /src/js/constants.js: -------------------------------------------------------------------------------- 1 | const LINE_TYPE = { 2 | CENTS: 'cents', 3 | DECIMAL: 'decimal', 4 | RATIO: 'ratio', 5 | N_OF_EDO: 'n of edo', 6 | INVALID: 'invalid' 7 | } 8 | 9 | const SEMITONE_RATIO_IN_12_EDO = Math.pow(2, 1 / 12) 10 | 11 | const MNLG_OCTAVESIZE = 12 12 | const MNLG_SCALESIZE = 128 13 | const MNLG_MAXCENTS = 12800 14 | const MNLG_A_REF = { val: 6900, ind: 69, freq: 440.0 } 15 | const MNLG_C_REF = { val: 6000, ind: 60, freq: 261.6255653 } 16 | 17 | // prettier-ignore 18 | const PRIMES = [ 19 | 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 20 | 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 21 | 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 22 | 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 23 | 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 24 | 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 25 | 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 26 | 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 27 | 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 28 | 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 29 | 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 30 | 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 31 | 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 32 | 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 33 | 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 34 | 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 35 | 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 36 | 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 37 | 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 38 | 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 39 | 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 40 | 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 41 | 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, 42 | 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 43 | 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 44 | 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 45 | 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 46 | 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, 2797, 47 | 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 48 | 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 49 | 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 3083, 3089, 50 | 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 51 | 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299, 52 | 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 53 | 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 54 | 3511, 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 55 | 3607, 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 56 | 3701, 3709, 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 57 | 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 58 | 3907, 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, 59 | 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 4073, 4079, 4091, 4093, 4099, 60 | 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, 61 | 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, 62 | 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 63 | 4409, 4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 64 | 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597, 65 | 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691, 66 | 4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, 4787, 4789, 4793, 4799, 67 | 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 68 | 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 69 | 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 70 | 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189, 5197, 71 | 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, 5281, 5297, 72 | 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 73 | 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, 5449, 5471, 5477, 5479, 5483, 74 | 5501, 5503, 5507, 5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 75 | 5623, 5639, 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, 76 | 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, 77 | 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897, 78 | 5903, 5923, 5927, 5939, 5953, 5981, 5987, 79 | 6007, 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 80 | 6101, 6113, 6121, 6131, 6133, 6143, 6151, 6163, 6173, 6197, 6199, 81 | 6203, 6211, 6217, 6221, 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 82 | 6301, 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397, 83 | 6421, 6427, 6449, 6451, 6469, 6473, 6481, 6491, 84 | 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, 85 | 6607, 6619, 6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691, 86 | 6701, 6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779, 6781, 6791, 6793, 87 | 6803, 6823, 6827, 6829, 6833, 6841, 6857, 6863, 6869, 6871, 6883, 6899, 88 | 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, 89 | 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 90 | 7103, 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 91 | 7207, 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, 92 | 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 93 | 7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 94 | 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589, 7591, 95 | 7603, 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 96 | 7703, 7717, 7723, 7727, 7741, 7753, 7757, 7759, 7789, 7793, 97 | 7817, 7823, 7829, 7841, 7853, 7867, 7873, 7877, 7879, 7883, 98 | 7901, 7907, 7919 99 | ] 100 | -------------------------------------------------------------------------------- /src/js/graphics.js: -------------------------------------------------------------------------------- 1 | /** 2 | * graphics.js 3 | * Functions for rendering of tuning graphics 4 | */ 5 | 6 | // draws a graphic of the scale represented as notches on a horizontal rule 7 | function render_graphic_scale_rule() { 8 | let canvas = document.getElementById('graphic-scale-rule') 9 | let w = canvas.width 10 | let h = canvas.height 11 | let ctx = canvas.getContext('2d') 12 | 13 | // render background 14 | ctx.fillStyle = '#fff' 15 | ctx.fillRect(0, 0, w, h) 16 | 17 | // render a plain horizontal rule 18 | ctx.beginPath() 19 | ctx.moveTo(0, h * 0.5) 20 | ctx.lineTo(w, h * 0.5) 21 | ctx.strokeStyle = '#555' 22 | ctx.lineWidth = 3 23 | ctx.stroke() 24 | 25 | // if scale data exists then add some notches to the rule 26 | if (tuning_table.note_count > 0) { 27 | let equave = tuning_table.tuning_data[tuning_table.note_count - 1] 28 | for (i = 0; i < tuning_table.note_count; i++) { 29 | let pos = 1 + (w - 2) * (Math.log(tuning_table.tuning_data[i]) / Math.log(equave)) 30 | ctx.beginPath() 31 | ctx.moveTo(pos, h * 0.4) 32 | ctx.lineTo(pos, h * 0.6) 33 | ctx.strokeStyle = '#555' 34 | ctx.lineWidth = 3 35 | ctx.stroke() 36 | } 37 | } 38 | } 39 | 40 | // init graphics 41 | render_graphic_scale_rule() 42 | -------------------------------------------------------------------------------- /src/js/keymap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * keymap.js 3 | * International keyboard layouts 4 | */ 5 | 6 | // prettier-ignore 7 | var Layouts = { 8 | // English QWERTY Layout 9 | // 10 | // <\> is placed to the right of <'> because on ISO (EU) variants it's there. 11 | // The ANSI (US) variant places it to the right of <]>, but it's a less useful 12 | // position so it can be ignored. 13 | EN: [ 14 | "1234567890-=", 15 | "QWERTYUIOP[]", 16 | "ASDFGHJKL;'\\", 17 | "ZXCVBNM,./", 18 | ], 19 | 20 | // Hungarian QWERTZ layout 21 | HU: [ 22 | "123456789ñ/=", 23 | "QWERTZUIOP[]", 24 | "ASDFGHJKL;'\\", 25 | "YXCVBNM,.-", 26 | ], 27 | 28 | // Dvorak keyboard 29 | DK: [ 30 | "1234567890-=", 31 | "',.PYFGCRL/@", 32 | "AOEUIDHTNS-\\", 33 | ";QJKXBMWVZ", 34 | ], 35 | 36 | // Programmer Dvorak keyboard 37 | PK: [ 38 | "&7531902468#", 39 | ";,.PYFGCRL/@", 40 | "AOEUIDHTNS-\\", 41 | "'QJKXBMWVZ", 42 | ], 43 | 44 | // Colemak keyboard 45 | CO: [ 46 | "1234567890-=", 47 | "QWFPGJLUY;[]", 48 | "ARSTDHNEIO'\\", 49 | "ZXCVBKM,./" 50 | ], 51 | 52 | // Colemak DH-m keyboard 53 | CO_DH: [ 54 | "1234567890-=", 55 | "QWFPBJLUY;[]\\", 56 | "ARSTGMNEIO'", 57 | "ZXCDVKH,./" 58 | ] 59 | }; 60 | 61 | // Map of irregular keycodes 62 | // 63 | // This website can be used to display the 'which' value for a given key: 64 | // 65 | // https://keycode.info 66 | // 67 | // prettier-ignore 68 | var Keycodes = { 69 | ";": 186, 70 | "=": 187, 71 | ",": 188, 72 | "-": 189, 73 | ".": 190, 74 | "/": 191, 75 | "ñ": 192, 76 | "[": 219, 77 | "\\": 220, 78 | "]": 221, 79 | "'": 222, 80 | "&": 166, 81 | "#": 163, 82 | } 83 | 84 | // Build Keymap from Layouts 85 | var Keymap = {} 86 | for (var id in Layouts) { 87 | Keymap[id] = buildKeymapFromLayout(Layouts[id]) 88 | } 89 | 90 | function buildKeymapFromLayout(rows) { 91 | var map = {} 92 | for (var r = rows.length - 1; r >= 0; r--) { 93 | var row = rows[r] 94 | var rowId = rows.length - r - 2 95 | for (var c = 0; c < row.length; c++) { 96 | var keycode = Keycodes[row.charAt(c)] || row.charCodeAt(c) 97 | map[keycode] = [rowId, c] 98 | } 99 | } 100 | return map 101 | } 102 | -------------------------------------------------------------------------------- /src/js/midi/commands.js: -------------------------------------------------------------------------------- 1 | const setPitchBendLimit = (channel, semitones) => { 2 | return [ 3 | (commands.cc << 4) | (channel - 1), 4 | cc.registeredParameterLSB, 5 | 0, 6 | (commands.cc << 4) | (channel - 1), 7 | cc.registeredParameterMSB, 8 | 0, 9 | (commands.cc << 4) | (channel - 1), 10 | cc.dataEntry, 11 | semitones, 12 | (commands.cc << 4) | (channel - 1), 13 | cc.registeredParameterLSB, 14 | 127, 15 | (commands.cc << 4) | (channel - 1), 16 | cc.registeredParameterMSB, 17 | 127 18 | ] 19 | } 20 | 21 | const pitchBendAmountToDataBytes = (pitchBendAmount) => { 22 | const realValue = pitchBendAmount - pitchBendMin 23 | return [realValue & 0b01111111, (realValue >> 7) & 0b01111111] 24 | } 25 | 26 | const bendPitch = (channel, pitchBendAmount) => { 27 | return [ 28 | ...[(commands.pitchbend << 4) | (channel - 1)], 29 | ...pitchBendAmountToDataBytes(pitchBendAmount) 30 | ] 31 | } 32 | 33 | const noteOn = (channel, note, pitchBendAmount = null, velocity = 127) => { 34 | return [ 35 | ...(pitchBendAmount !== null ? bendPitch(channel, pitchBendAmount) : []), 36 | ...[(commands.noteOn << 4) | (channel - 1), note, velocity] 37 | ] 38 | } 39 | 40 | const noteOff = (channel, note, velocity = 127) => { 41 | return [(commands.noteOff << 4) | (channel - 1), note, velocity] 42 | } 43 | -------------------------------------------------------------------------------- /src/js/midi/constants.js: -------------------------------------------------------------------------------- 1 | const whiteOnlyMap = { 2 | 0: 25, 3 | 2: 26, 4 | 4: 27, 5 | 5: 28, 6 | 7: 29, 7 | 9: 30, 8 | 11: 31, 9 | 12: 32, 10 | 14: 33, 11 | 16: 34, 12 | 17: 35, 13 | 19: 36, 14 | 21: 37, 15 | 23: 38, 16 | 24: 39, 17 | 26: 40, 18 | 28: 41, 19 | 29: 42, 20 | 31: 43, 21 | 33: 44, 22 | 35: 45, 23 | 36: 46, 24 | 38: 47, 25 | 40: 48, 26 | 41: 49, 27 | 43: 50, 28 | 45: 51, 29 | 47: 52, 30 | 48: 53, 31 | 50: 54, 32 | 52: 55, 33 | 53: 56, 34 | 55: 57, 35 | 57: 58, 36 | 59: 59, 37 | 60: 60, 38 | 62: 61, 39 | 64: 62, 40 | 65: 63, 41 | 67: 64, 42 | 69: 65, 43 | 71: 66, 44 | 72: 67, 45 | 74: 68, 46 | 76: 69, 47 | 77: 70, 48 | 79: 71, 49 | 81: 72, 50 | 83: 73, 51 | 84: 74, 52 | 86: 75, 53 | 88: 76, 54 | 89: 77, 55 | 91: 78, 56 | 93: 79, 57 | 95: 80, 58 | 96: 81, 59 | 98: 82, 60 | 100: 83, 61 | 101: 84, 62 | 103: 85, 63 | 105: 86, 64 | 107: 87, 65 | 108: 88, 66 | 110: 89, 67 | 112: 90, 68 | 113: 91, 69 | 115: 92, 70 | 117: 93, 71 | 119: 94, 72 | 120: 95, 73 | 122: 96, 74 | 124: 97, 75 | 125: 98, 76 | 127: 99 77 | } 78 | 79 | // https://www.midi.org/specifications/item/table-1-summary-of-midi-message 80 | const commands = { 81 | noteOn: 0b1001, 82 | noteOff: 0b1000, 83 | aftertouch: 0b1010, 84 | pitchbend: 0b1110, 85 | cc: 0b1011 86 | } 87 | 88 | // https://www.midi.org/specifications/item/table-3-control-change-messages-data-bytes-2 89 | // http://www.nortonmusic.com/midi_cc.html 90 | const cc = { 91 | dataEntry: 6, 92 | sustain: 64, 93 | registeredParameterLSB: 100, 94 | registeredParameterMSB: 101 95 | } 96 | 97 | /// 440Hz A4 98 | const referenceNote = { 99 | frequency: 440, 100 | id: 69 101 | } 102 | 103 | const pitchBendMin = 1 - (1 << 14) / 2 // -8191 104 | const pitchBendMax = (1 << 14) / 2 // 8192 105 | 106 | // settings for MIDI OUT ports 107 | const defaultInputData = { 108 | enabled: true, 109 | channels: [ 110 | { id: 1, enabled: true, pitchBendAmount: 0 }, 111 | { id: 2, enabled: true, pitchBendAmount: 0 }, 112 | { id: 3, enabled: true, pitchBendAmount: 0 }, 113 | { id: 4, enabled: true, pitchBendAmount: 0 }, 114 | { id: 5, enabled: true, pitchBendAmount: 0 }, 115 | { id: 6, enabled: true, pitchBendAmount: 0 }, 116 | { id: 7, enabled: true, pitchBendAmount: 0 }, 117 | { id: 8, enabled: true, pitchBendAmount: 0 }, 118 | { id: 9, enabled: true, pitchBendAmount: 0 }, 119 | { id: 10, enabled: false, pitchBendAmount: 0 }, // drum channel 120 | { id: 11, enabled: true, pitchBendAmount: 0 }, 121 | { id: 12, enabled: true, pitchBendAmount: 0 }, 122 | { id: 13, enabled: true, pitchBendAmount: 0 }, 123 | { id: 14, enabled: true, pitchBendAmount: 0 }, 124 | { id: 15, enabled: true, pitchBendAmount: 0 }, 125 | { id: 16, enabled: true, pitchBendAmount: 0 } 126 | ] 127 | } 128 | 129 | // settings for MIDI IN ports 130 | const defaultOutputData = { 131 | enabled: false, 132 | channels: [ 133 | { id: 1, enabled: true, pitchBendAmount: 0 }, 134 | { id: 2, enabled: false, pitchBendAmount: 0 }, 135 | { id: 3, enabled: false, pitchBendAmount: 0 }, 136 | { id: 4, enabled: false, pitchBendAmount: 0 }, 137 | { id: 5, enabled: false, pitchBendAmount: 0 }, 138 | { id: 6, enabled: false, pitchBendAmount: 0 }, 139 | { id: 7, enabled: false, pitchBendAmount: 0 }, 140 | { id: 8, enabled: false, pitchBendAmount: 0 }, 141 | { id: 9, enabled: false, pitchBendAmount: 0 }, 142 | { id: 10, enabled: false, pitchBendAmount: 0 }, 143 | { id: 11, enabled: false, pitchBendAmount: 0 }, 144 | { id: 12, enabled: false, pitchBendAmount: 0 }, 145 | { id: 13, enabled: false, pitchBendAmount: 0 }, 146 | { id: 14, enabled: false, pitchBendAmount: 0 }, 147 | { id: 15, enabled: false, pitchBendAmount: 0 }, 148 | { id: 16, enabled: false, pitchBendAmount: 0 } 149 | ] 150 | } 151 | 152 | const octaveRatio = 2 153 | const semitonesPerOctave = 12 154 | const maxBendingDistanceInSemitones = 12 155 | const centsPerOctave = 1200 156 | 157 | const middleC = 60 158 | const drumChannel = 10 // when counting from 1 159 | 160 | const allMidiKeys = [...Array(128).keys()] // [0, 1, 2, ..., 127] 161 | 162 | const whiteMidiKeys = Object.keys(whiteOnlyMap).map((id) => parseInt(id)) 163 | 164 | const blackMidiKeys = R.difference(allMidiKeys, whiteMidiKeys) 165 | -------------------------------------------------------------------------------- /src/js/midi/math.js: -------------------------------------------------------------------------------- 1 | const moveNUnits = (ratioOfSymmetry, divisionsPerRatio, n, frequency) => { 2 | // return frequency * ratioOfSymmetry ** (n / divisionsPerRatio) 3 | return Decimal.mul(frequency, Decimal.pow(ratioOfSymmetry, Decimal.div(n, divisionsPerRatio))) 4 | } 5 | 6 | const getDistanceInUnits = (ratioOfSymmetry, divisionsPerRatio, freq2, freq1) => { 7 | // return divisionsPerRatio * Math.log(freq2 / freq1, ratioOfSymmetry) 8 | return Decimal.mul(divisionsPerRatio, Decimal.log(Decimal.div(freq2, freq1), ratioOfSymmetry)) 9 | } 10 | 11 | const moveNSemitones = (n, frequency) => { 12 | return moveNUnits(octaveRatio, semitonesPerOctave, n, frequency) 13 | } 14 | 15 | const getDistanceInSemitones = (freq2, freq1) => { 16 | return getDistanceInUnits(octaveRatio, semitonesPerOctave, freq2, freq1) 17 | } 18 | 19 | const bendingRatio = moveNSemitones(maxBendingDistanceInSemitones, 1) 20 | 21 | const getBendingDistance = (freq2, freq1) => { 22 | return getDistanceInUnits(bendingRatio, pitchBendMax, freq2, freq1) 23 | } 24 | 25 | const getNoteFrequency = (midinote) => { 26 | return moveNSemitones( 27 | Decimal.sub(R.clamp(0, 127, midinote), referenceNote.id), 28 | referenceNote.frequency 29 | ) 30 | } 31 | 32 | const getNoteId = (frequency) => { 33 | return Decimal.floor( 34 | Decimal.add(getDistanceInSemitones(frequency, referenceNote.frequency), referenceNote.id) 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /src/js/midi/midi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * midi.js 3 | * Capture MIDI input for synth 4 | */ 5 | 6 | const deviceChannelInfo = {} 7 | 8 | const getNameFromPort = (port) => { 9 | const { name, version, manufacturer } = port 10 | return `${name} (version ${version}) ${manufacturer}` 11 | } 12 | 13 | class MIDI extends EventEmitter { 14 | constructor() { 15 | super() 16 | 17 | this._ = { 18 | inited: false, 19 | supported: false, 20 | devices: { 21 | inputs: {}, 22 | outputs: {} 23 | }, 24 | whiteOnly: false 25 | } 26 | } 27 | 28 | set whiteOnly(value) { 29 | this._.whiteOnly = value 30 | 31 | allMidiKeys.forEach((note) => { 32 | for (let channel = 1; channel <= 16; channel++) { 33 | this.emit('note off', note, 1, channel) 34 | } 35 | }) 36 | } 37 | 38 | async init() { 39 | if (!this._.inited) { 40 | this._.inited = true 41 | 42 | const enableMidiSupport = (midiAccess) => { 43 | this._.supported = true 44 | 45 | midiAccess.onstatechange = (event) => { 46 | initPort(event.port) 47 | } 48 | 49 | const inputs = midiAccess.inputs.values() 50 | for (let input = inputs.next(); input && !input.done; input = inputs.next()) { 51 | initPort(input.value) 52 | } 53 | 54 | const outputs = midiAccess.outputs.values() 55 | for (let output = outputs.next(); output && !output.done; output = outputs.next()) { 56 | initPort(output.value) 57 | } 58 | } 59 | 60 | const initPort = (port) => { 61 | const { devices } = this._ 62 | 63 | if (port.type === 'input') { 64 | if (!devices.inputs[port.id]) { 65 | devices.inputs[port.id] = { 66 | port, 67 | name: getNameFromPort(port), 68 | ...R.clone(defaultInputData) 69 | } 70 | } 71 | 72 | devices.inputs[port.id].connected = false 73 | if (port.state === 'connected') { 74 | if (port.connection === 'closed') { 75 | port.open() 76 | } else if (port.connection === 'open') { 77 | port.onmidimessage = onMidiMessage(devices.inputs[port.id]) 78 | devices.inputs[port.id].connected = true 79 | } 80 | } 81 | } else if (port.type === 'output') { 82 | if (!devices.outputs[port.id]) { 83 | devices.outputs[port.id] = { 84 | port, 85 | name: getNameFromPort(port), 86 | ...R.clone(defaultOutputData) 87 | } 88 | } 89 | 90 | if (port.state === 'connected') { 91 | if (port.connection === 'closed') { 92 | port.open() 93 | } else if (port.connection === 'open') { 94 | devices.outputs[port.id].connected = true 95 | } 96 | } 97 | } 98 | 99 | this.emit('update') 100 | } 101 | 102 | const onMidiMessage = (device) => (event) => { 103 | if (device.enabled) { 104 | const { whiteOnly } = this._ 105 | const [data, ...params] = event.data 106 | const cmd = data >> 4 107 | const channel = data & 0x0f 108 | 109 | if (device.channels[channel]?.enabled === true) { 110 | switch (cmd) { 111 | case commands.noteOff: 112 | { 113 | const [note, velocity] = params 114 | if (whiteOnly) { 115 | if (whiteMidiKeys.includes(note)) { 116 | this.emit('note off', whiteOnlyMap[note], velocity, channel) 117 | } 118 | } else { 119 | this.emit('note off', note, velocity, channel) 120 | } 121 | } 122 | break 123 | case commands.noteOn: 124 | { 125 | const [note, velocity] = params 126 | if (whiteOnly) { 127 | if (whiteMidiKeys.includes(note)) { 128 | this.emit( 129 | 'note on', 130 | whiteOnlyMap[note], 131 | state.get('midi velocity sensing') ? velocity : 127, 132 | channel 133 | ) 134 | } 135 | } else { 136 | this.emit( 137 | 'note on', 138 | note, 139 | state.get('midi velocity sensing') ? velocity : 127, 140 | channel 141 | ) 142 | } 143 | } 144 | break 145 | case commands.aftertouch: 146 | { 147 | const [note, pressure] = params 148 | if (whiteOnly) { 149 | if (whiteMidiKeys.includes(note)) { 150 | this.emit('aftertouch', whiteOnlyMap[note], (pressure / 128) * 100, channel) 151 | } 152 | } else { 153 | this.emit('aftertouch', note, (pressure / 128) * 100, channel) 154 | } 155 | } 156 | break 157 | case commands.pitchbend: 158 | { 159 | const [low, high] = params 160 | this.emit('pitchbend', (((high << 7) | low) / 0x3fff - 1) * 100) 161 | } 162 | break 163 | case commands.cc: 164 | { 165 | const [cmd, value] = params 166 | 167 | switch (cmd) { 168 | case cc.sustain: 169 | this.emit('sustain', value >= 64) 170 | break 171 | } 172 | } 173 | break 174 | } 175 | } 176 | } 177 | } 178 | 179 | if (navigator.requestMIDIAccess) { 180 | const midiAccess = await navigator.requestMIDIAccess({ sysex: false }) 181 | enableMidiSupport(midiAccess) 182 | this.emit('ready') 183 | } else { 184 | this.emit('blocked') 185 | } 186 | } 187 | } 188 | 189 | toggleDevice(type, deviceId, newValue = null) { 190 | const { devices } = this._ 191 | 192 | const device = devices[`${type}s`][deviceId] 193 | device.enabled = newValue === null ? !device.enabled : newValue 194 | 195 | if (type === 'output') { 196 | if (device.enabled) { 197 | device.channels.forEach((channel) => { 198 | device.port.send(setPitchBendLimit(channel, maxBendingDistanceInSemitones)) 199 | }) 200 | } else { 201 | device.channels.forEach((channel) => { 202 | device.port.send(bendPitch(channel, 0)) 203 | }) 204 | } 205 | } 206 | 207 | this.emit('update') 208 | } 209 | 210 | setDevice(type, deviceId, newValue) { 211 | this.toggleDevice(type, deviceId, newValue) 212 | } 213 | 214 | toggleChannel(type, deviceId, channelId, newValue = null) { 215 | const { devices } = this._ 216 | 217 | const device = devices[`${type}s`][deviceId] 218 | const channel = device.channels.find(({ id }) => id === channelId) 219 | 220 | newValue = newValue === null ? !channel.enabled : newValue 221 | if (channel.enabled !== newValue) { 222 | channel.enabled = newValue 223 | this.emit('update') 224 | } 225 | } 226 | 227 | setChannel(type, deviceId, channelId, newValue) { 228 | this.toggleChannel(type, deviceId, channelId, newValue) 229 | } 230 | 231 | getEnabledOutputs() { 232 | return Object.values(this._.devices.outputs).filter(({ enabled, channels }) => { 233 | return enabled === true && channels.find(({ enabled }) => enabled === true) !== undefined 234 | }) 235 | } 236 | 237 | getLowestEnabledChannel(channels) { 238 | return channels.find(({ enabled }) => enabled === true) 239 | } 240 | 241 | playFrequency(frequency = 0) { 242 | const devices = this.getEnabledOutputs() 243 | 244 | if (devices.length) { 245 | devices.forEach(({ port, channels }) => { 246 | const channel = channels.find(({ enabled }) => enabled === true) 247 | if (!deviceChannelInfo[port.id]) { 248 | deviceChannelInfo[port.id] = {} 249 | } 250 | if (!deviceChannelInfo[port.id][channel]) { 251 | deviceChannelInfo[port.id][channel] = { 252 | pressedNoteIds: [] 253 | } 254 | } 255 | 256 | if (frequency === 0) { 257 | if (deviceChannelInfo[port.id][channel].pressedNoteIds.length) { 258 | port.send( 259 | deviceChannelInfo[port.id][channel].pressedNoteIds.flatMap((noteId) => { 260 | return noteOff(channel, noteId) 261 | }) 262 | ) 263 | 264 | deviceChannelInfo[port.id][channel].pressedNoteIds = [] 265 | } 266 | } else { 267 | const noteId = parseInt(getNoteId(frequency).toString()) 268 | const pitchbendAmount = parseFloat( 269 | getBendingDistance(frequency, getNoteFrequency(noteId)).toString() 270 | ) 271 | 272 | port.send(noteOn(channel, noteId, pitchbendAmount)) 273 | deviceChannelInfo[port.id][channel].pressedNoteIds.push(noteId) 274 | } 275 | }) 276 | } 277 | } 278 | 279 | stopFrequency() { 280 | this.playFrequency(0) 281 | } 282 | 283 | isSupported() { 284 | return this._.supported 285 | } 286 | } 287 | 288 | // ------------------------------------------- 289 | 290 | const midi = new MIDI() 291 | 292 | jQuery(() => { 293 | const midiEnablerBtn = jQuery('#midi-enabler') 294 | 295 | midi 296 | .on('blocked', () => { 297 | midiEnablerBtn 298 | .prop('disabled', false) 299 | .removeClass('btn-success') 300 | .addClass('btn-danger') 301 | .text('off (blocked)') 302 | }) 303 | .on('note on', (note, velocity, channel) => { 304 | synth.noteOn(note, velocity) 305 | }) 306 | .on('note off', (note, velocity, channel) => { 307 | synth.noteOff(note) 308 | }) 309 | .on('update', () => { 310 | if (state.get('midi modal visible')) { 311 | state.set('midi modal visible', true, true) 312 | } 313 | }) 314 | 315 | midiEnablerBtn.on('click', async () => { 316 | await midi.init() 317 | 318 | if (midi.isSupported()) { 319 | state.set('midi enabled', true) 320 | } 321 | }) 322 | }) 323 | -------------------------------------------------------------------------------- /src/js/midi/ui.js: -------------------------------------------------------------------------------- 1 | const MidiChannel = ({ type, deviceId, channelId, enabled }) => { 2 | const template = document.createElement('template') 3 | template.innerHTML = ` 4 |
5 | 8 | 9 |
10 | ` 11 | const content = template.content 12 | content.querySelector('input[type="checkbox"]').addEventListener('change', (e) => { 13 | const isEnabled = e.target.checked 14 | midi.setChannel(type, deviceId, channelId, isEnabled) 15 | }) 16 | return content 17 | } 18 | 19 | const MidiDevice = ({ type, deviceId, name, channels, enabled }) => { 20 | const template = document.createElement('template') 21 | template.innerHTML = ` 22 |
23 |
24 | 25 |
26 |

27 |
28 |
29 | ` 30 | const content = template.content 31 | channels.forEach(({ id, enabled }) => { 32 | content 33 | .querySelector('.channels') 34 | .appendChild(MidiChannel({ type, deviceId, channelId: id, enabled })) 35 | }) 36 | content.getElementById(`${type}--${name}`).addEventListener('change', (e) => { 37 | const isEnabled = e.target.checked 38 | midi.setDevice(type, deviceId, isEnabled) 39 | }) 40 | return content 41 | } 42 | 43 | const renderMidiInputsTo = (container) => { 44 | const { inputs } = midi._.devices 45 | 46 | container.innerHTML = '' 47 | Object.values(inputs).forEach((input) => { 48 | container.appendChild(MidiDevice({ type: 'input', deviceId: input.port.id, ...input })) 49 | }) 50 | } 51 | 52 | const renderMidiOutputsTo = (container) => { 53 | const { outputs } = midi._.devices 54 | 55 | container.innerHTML = '' 56 | Object.values(outputs).forEach((output) => { 57 | container.appendChild(MidiDevice({ type: 'output', deviceId: output.port.id, ...output })) 58 | }) 59 | } 60 | 61 | const renderMidiSettingsTo = (container) => { 62 | const { whiteOnly } = midi._ 63 | const whiteModeSwitch = container.querySelector('#input_midi_whitemode') 64 | whiteModeSwitch.checked = whiteOnly 65 | whiteModeSwitch.addEventListener('change', (e) => { 66 | midi.whiteOnly = e.target.checked 67 | }) 68 | } 69 | -------------------------------------------------------------------------------- /src/js/state/actions-dom.js: -------------------------------------------------------------------------------- 1 | // DOM changes, need to sync with state 2 | 3 | jQuery('#input_range_main_vol').on('input', function () { 4 | state.set('main volume', parseFloat(jQuery(this).val())) 5 | }) 6 | 7 | jQuery('#velocity-toggler').on('click', () => { 8 | state.set('midi velocity sensing', !state.get('midi velocity sensing')) 9 | }) 10 | 11 | // hide virtual keyboard when mobile hamburger menu button is clicked 12 | jQuery('button.navbar-toggle').on('click', () => { 13 | state.set('virtual keyboard visible', false) 14 | state.set('mobile menu visible', !state.get('mobile menu visible')) 15 | }) 16 | 17 | // Touch keyboard (#nav_play) option clicked 18 | jQuery('#nav_play, #launch-kbd').on('click', (event) => { 19 | event.preventDefault() 20 | state.set('virtual keyboard visible', !state.get('virtual keyboard visible')) 21 | }) 22 | -------------------------------------------------------------------------------- /src/js/state/actions.js: -------------------------------------------------------------------------------- 1 | // non-DOM changes, need to sync with state 2 | 3 | document.addEventListener('keydown', (event) => { 4 | if (event.key === 'Escape') { 5 | state.set('virtual keyboard visible', false) 6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /src/js/state/on-ready.js: -------------------------------------------------------------------------------- 1 | // when all event hooks set up the initial change events can fire 2 | 3 | state.ready() 4 | -------------------------------------------------------------------------------- /src/js/state/reactions-dom.js: -------------------------------------------------------------------------------- 1 | // data changed, sync it with the DOM 2 | 3 | state.on('main volume', (value) => { 4 | jQuery('#input_range_main_vol').val(value) 5 | }) 6 | 7 | state.on('midi velocity sensing', (value) => { 8 | const velocityToggleBtn = jQuery('#velocity-toggler') 9 | 10 | if (value) { 11 | velocityToggleBtn.removeClass('btn-basic').addClass('btn-success').text('velocity: on') 12 | } else { 13 | velocityToggleBtn.removeClass('btn-success').addClass('btn-basic').text('velocity: off') 14 | } 15 | }) 16 | 17 | state.on('virtual keyboard visible', (value) => { 18 | if (value) { 19 | touch_kbd_open() 20 | } else { 21 | touch_kbd_close() 22 | } 23 | }) 24 | 25 | state.on('mobile menu visible', (value) => { 26 | if (value) { 27 | jQuery('#mobile-menu').show() 28 | } else { 29 | jQuery('#mobile-menu').hide() 30 | } 31 | }) 32 | 33 | state.on('midi modal visible', (value, ...args) => { 34 | const midiModal = document.getElementById('modal_midi_settings') 35 | if (value) { 36 | renderMidiInputsTo(midiModal.querySelector('.inputs')) 37 | renderMidiOutputsTo(midiModal.querySelector('.outputs')) 38 | renderMidiSettingsTo(midiModal.querySelector('.settings')) 39 | } 40 | }) 41 | 42 | state.on('midi enabled', (value) => { 43 | if (value) { 44 | jQuery('#midi-enabler') 45 | .prop('disabled', true) 46 | .removeClass('btn-danger') 47 | .addClass('btn-success') 48 | .text('on') 49 | } 50 | }) 51 | -------------------------------------------------------------------------------- /src/js/state/reactions.js: -------------------------------------------------------------------------------- 1 | // data changed, handle programmatic reaction - no DOM changes 2 | 3 | state.on('main volume', (newValue) => { 4 | synth.setMainVolume(newValue) 5 | }) 6 | -------------------------------------------------------------------------------- /src/js/state/state.js: -------------------------------------------------------------------------------- 1 | class State extends EventEmitter { 2 | constructor(initialData = {}) { 3 | super() 4 | this.data = initialData 5 | } 6 | get(key) { 7 | return this.data[key] 8 | } 9 | set(key, newValue, forceEmit = false) { 10 | const oldValue = this.data[key] 11 | if (oldValue !== newValue) { 12 | this.data[key] = newValue 13 | this.emit(key, newValue, oldValue) 14 | } else { 15 | if (forceEmit) { 16 | this.emit(key, newValue, oldValue) 17 | } 18 | } 19 | } 20 | ready() { 21 | Object.entries(this.data).forEach(([key, value]) => { 22 | this.emit(key, value) 23 | }) 24 | } 25 | } 26 | 27 | const state = new State({ 28 | 'main volume': 0.8, 29 | 'midi enabled': false, 30 | 'midi velocity sensing': true, 31 | 'virtual keyboard visible': false, 32 | 'mobile menu visible': false, 33 | 'midi modal visible': false 34 | }) 35 | -------------------------------------------------------------------------------- /src/js/synth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * synth.js 3 | * Web audio synth 4 | */ 5 | 6 | const synth = new Synth() 7 | 8 | // keycode_to_midinote() 9 | // it turns a keycode to a MIDI note based on this reference layout: 10 | // 11 | // 1 2 3 4 5 6 7 8 9 0 - = 12 | // Q W E R T Y U I O P [ ] 13 | // A S D F G H J K L ; ' \ 14 | // Z X C V B N M , . / 15 | // 16 | function keycode_to_midinote(keycode) { 17 | // get row/col vals from the keymap 18 | var key = synth.keymap[keycode] 19 | 20 | // return false if there is no note assigned to this key 21 | if (R.isNil(key)) { 22 | return false 23 | } 24 | 25 | var [row, col] = key 26 | return ( 27 | row * synth.isomorphicMapping.vertical + 28 | col * synth.isomorphicMapping.horizontal + 29 | tuning_table['base_midi_note'] 30 | ) 31 | } 32 | 33 | function touch_to_midinote([row, col]) { 34 | if (R.isNil(row) || R.isNil(col)) { 35 | return false 36 | } 37 | 38 | return ( 39 | row * synth.isomorphicMapping.vertical + 40 | col * synth.isomorphicMapping.horizontal + 41 | tuning_table['base_midi_note'] 42 | ) 43 | } 44 | 45 | // is_qwerty_active() 46 | // check if qwerty key playing should be active 47 | // returns true if focus is in safe area for typing 48 | // returns false if focus is on an input or textarea element 49 | function is_qwerty_active() { 50 | jQuery('div#qwerty-indicator').empty() 51 | var focus = document.activeElement.tagName 52 | if (focus == 'TEXTAREA' || focus == 'INPUT') { 53 | jQuery('div#qwerty-indicator').html( 54 | '

Keyboard disabled

Click here to enable QWERTY keyboard playing.

' 55 | ) 56 | return false 57 | } else { 58 | jQuery('div#qwerty-indicator').html( 59 | '

Keyboard enabled

Press QWERTY keys to play current tuning.

' 60 | ) 61 | return true 62 | } 63 | } 64 | 65 | // KEYDOWN -- capture keyboard input 66 | document.addEventListener('keydown', function (event) { 67 | // bail if focus is on an input or textarea element 68 | if (!is_qwerty_active()) { 69 | return false 70 | } 71 | 72 | // bail, if a modifier is pressed alongside the key 73 | if (!isSimpleKeypress(event)) { 74 | return false 75 | } 76 | 77 | const midiNote = keycode_to_midinote(event.which) // midi note number 0-127 78 | const velocity = 100 79 | 80 | if (midiNote !== false) { 81 | event.preventDefault() 82 | synth.noteOn(midiNote, velocity) 83 | } 84 | }) 85 | 86 | // KEYUP -- capture keyboard input 87 | document.addEventListener('keyup', function (event) { 88 | // bail, if a modifier is pressed alongside the key 89 | if (!isSimpleKeypress(event)) { 90 | return false 91 | } 92 | const midiNote = keycode_to_midinote(event.which) 93 | if (midiNote !== false) { 94 | event.preventDefault() 95 | synth.noteOff(midiNote) 96 | } 97 | }) 98 | 99 | // -[virtual keyboard mobile]----------------------------------------------- 100 | 101 | // TODO: multi-touch support; https://stackoverflow.com/a/7236327/1806628 102 | 103 | jQuery('#virtual-keyboard') 104 | .on('touchstart', (e) => { 105 | e.preventDefault() 106 | synth.noteOn(touch_to_midinote(getCoordsFromKey(e.target))) 107 | e.target.classList.add('active') 108 | }) 109 | .on('touchend', (e) => { 110 | e.preventDefault() 111 | synth.noteOff(touch_to_midinote(getCoordsFromKey(e.target))) 112 | e.target.classList.remove('active') 113 | }) 114 | .on('touchcancel', (e) => { 115 | e.preventDefault() 116 | synth.noteOff(touch_to_midinote(getCoordsFromKey(e.target))) 117 | e.target.classList.remove('active') 118 | }) 119 | // .on('touchmove', (e) => { 120 | // e.preventDefault() 121 | // console.log('touchmove', e.target) 122 | // }) 123 | 124 | // -[virtual keyboard desktop]---------------------------------------------- 125 | 126 | const LEFT_MOUSE_BTN = 0 127 | 128 | let isMousePressed = false 129 | 130 | jQuery('#virtual-keyboard') 131 | .on('mousedown', 'td', (e) => { 132 | if (e.button !== LEFT_MOUSE_BTN) { 133 | return 134 | } 135 | 136 | isMousePressed = true 137 | synth.noteOn(touch_to_midinote(getCoordsFromKey(e.target))) 138 | e.target.classList.add('active') 139 | }) 140 | .on('mouseup', 'td', (e) => { 141 | if (e.button !== LEFT_MOUSE_BTN) { 142 | return 143 | } 144 | 145 | isMousePressed = false 146 | synth.noteOff(touch_to_midinote(getCoordsFromKey(e.target))) 147 | e.target.classList.remove('active') 148 | }) 149 | .on('mouseenter', 'td', (e) => { 150 | if (!isMousePressed) { 151 | return 152 | } 153 | 154 | synth.noteOn(touch_to_midinote(getCoordsFromKey(e.target))) 155 | e.target.classList.add('active') 156 | }) 157 | .on('mouseleave', 'td', (e) => { 158 | if (!isMousePressed) { 159 | return 160 | } 161 | 162 | synth.noteOff(touch_to_midinote(getCoordsFromKey(e.target))) 163 | e.target.classList.remove('active') 164 | }) 165 | -------------------------------------------------------------------------------- /src/js/synth/Delay.js: -------------------------------------------------------------------------------- 1 | class Delay { 2 | constructor(synth) { 3 | this.time = 0.3 4 | this.gain = 0.4 5 | this.inited = false 6 | this.synth = synth 7 | } 8 | enable() { 9 | if (this.inited) { 10 | this.panL.connect(this.synth.masterGain) 11 | this.panR.connect(this.synth.masterGain) 12 | } 13 | } 14 | disable() { 15 | if (this.inited) { 16 | this.panL.disconnect(this.synth.masterGain) 17 | this.panR.disconnect(this.synth.masterGain) 18 | } 19 | } 20 | init(audioCtx) { 21 | if (!this.inited) { 22 | this.inited = true 23 | this.channelL = audioCtx.createDelay(5.0) 24 | this.channelR = audioCtx.createDelay(5.0) 25 | this.gainL = audioCtx.createGain(0.8) 26 | this.gainR = audioCtx.createGain(0.8) 27 | // this.lowpassL = audioCtx.createBiquadFilter() 28 | // this.lowpassR = audioCtx.createBiquadFilter() 29 | // this.highpassL = audioCtx.createBiquadFilter() 30 | // this.highpassR = audioCtx.createBiquadFilter() 31 | this.panL = audioCtx.createPanner() 32 | this.panR = audioCtx.createPanner() 33 | 34 | // feedback loop with gain stage 35 | this.channelL.connect(this.gainL) 36 | this.gainL.connect(this.channelR) 37 | this.channelR.connect(this.gainR) 38 | this.gainR.connect(this.channelL) 39 | 40 | // filters 41 | // this.gainL.connect( this.lowpassL ); 42 | // this.gainR.connect( this.lowpassR ); 43 | // this.lowpassL.frequency.value = 6500; 44 | // this.lowpassR.frequency.value = 7000; 45 | // this.lowpassL.Q.value = 0.7; 46 | // this.lowpassR.Q.value = 0.7; 47 | // this.lowpassL.type = 'lowpass'; 48 | // this.lowpassR.type = 'lowpass'; 49 | // this.lowpassL.connect( this.highpassL ); 50 | // this.lowpassR.connect( this.highpassR ); 51 | // this.highpassL.frequency.value = 130; 52 | // this.highpassR.frequency.value = 140; 53 | // this.highpassL.Q.value = 0.7; 54 | // this.highpassR.Q.value = 0.7; 55 | // this.highpassL.type = 'highpass'; 56 | // this.highpassR.type = 'highpass'; 57 | // this.highpassL.connect( this.panL ); 58 | // this.highpassR.connect( this.panR ); 59 | 60 | // panning 61 | this.gainL.connect(this.panL) // if you uncomment the above filters lines, then comment out this line 62 | this.gainR.connect(this.panR) // if you uncomment the above filters lines, then comment out this line 63 | this.panL.setPosition(-1, 0, 0) 64 | this.panR.setPosition(1, 0, 0) 65 | 66 | // setup delay time and gain for delay lines 67 | const now = synth.now() 68 | this.channelL.delayTime.setValueAtTime(this.time, now) 69 | this.channelR.delayTime.setValueAtTime(this.time, now) 70 | this.gainL.gain.setValueAtTime(this.gain, now) 71 | this.gainR.gain.setValueAtTime(this.gain, now) 72 | 73 | // check on init if user has already enabled delay 74 | if (jQuery('#input_checkbox_delay_on').is(':checked')) { 75 | this.enable() 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/js/synth/Synth.js: -------------------------------------------------------------------------------- 1 | class Synth { 2 | constructor() { 3 | this.keymap = Keymap.EN 4 | this.isomorphicMapping = { 5 | vertical: 5, // how many scale degrees as you move up/down by rows 6 | horizontal: 1 // how many scale degrees as you move left/right by cols 7 | } 8 | this.voices = [] 9 | this.midinotes_to_voices = {} // polyphonic voice allocation 10 | this.voices_to_midinotes = {} // polyphonic voice allocation 11 | this.polyphony = !R.isNil(localStorage.getItem('max_polyphony')) 12 | ? localStorage.getItem('max_polyphony') 13 | : 16 14 | this.nextVoice = 0 15 | this.waveform = 'semisine' 16 | this.mainVolume = 0.8 17 | this.inited = false 18 | 19 | this.delay = new Delay(this) 20 | } 21 | 22 | init() { 23 | // only init once 24 | if (!this.inited) { 25 | this.inited = true 26 | 27 | // set up Web Audio API context 28 | this.audioCtx = new (window.AudioContext || window.webkitAudioContext)() 29 | 30 | // set up custom waveforms 31 | this.custom_waveforms = { 32 | warm1: this.audioCtx.createPeriodicWave( 33 | new Float32Array([0, 10, 2, 2, 2, 1, 1, 0.5]), 34 | new Float32Array([0, 0, 0, 0, 0, 0, 0, 0]) 35 | ), 36 | warm2: this.audioCtx.createPeriodicWave( 37 | new Float32Array([0, 10, 5, 3.33, 2, 1]), 38 | new Float32Array([0, 0, 0, 0, 0, 0]) 39 | ), 40 | warm3: this.audioCtx.createPeriodicWave( 41 | new Float32Array([0, 10, 5, 5, 3]), 42 | new Float32Array([0, 0, 0, 0, 0]) 43 | ), 44 | warm4: this.audioCtx.createPeriodicWave( 45 | new Float32Array([0, 10, 2, 2, 1]), 46 | new Float32Array([0, 0, 0, 0, 0]) 47 | ), 48 | octaver: this.audioCtx.createPeriodicWave( 49 | new Float32Array([0, 1000, 500, 0, 333, 0, 0, 0, 250, 0, 0, 0, 0, 0, 0, 0, 166]), 50 | new Float32Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 51 | ), 52 | brightness: this.audioCtx.createPeriodicWave( 53 | new Float32Array([ 54 | 0, 10, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 0.75, 0.5, 0.2, 0.1 55 | ]), 56 | new Float32Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 57 | ), 58 | harmonicbell: this.audioCtx.createPeriodicWave( 59 | new Float32Array([0, 10, 2, 2, 2, 2, 0, 0, 0, 0, 0, 7]), 60 | new Float32Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 61 | ), 62 | template: this.audioCtx.createPeriodicWave( 63 | // first element is DC offset, second element is fundamental, third element is 2nd harmonic, etc. 64 | new Float32Array([0, 1, 0.5, 0.333, 0.25, 0.2, 0.167]), // sine components 65 | new Float32Array([0, 0, 0.0, 0.0, 0.0, 0.0, 0.0]) // cosine components 66 | ) 67 | } 68 | 69 | // DC-blocked semisine 70 | const semisineSineComponents = new Float32Array(64); 71 | const semisineCosineComponents = new Float32Array(64); 72 | for (let n = 1; n < 64; ++n) { 73 | semisineCosineComponents[n] = 1 / (1 - 4*n*n); 74 | } 75 | this.custom_waveforms.semisine = this.audioCtx.createPeriodicWave( 76 | semisineSineComponents, 77 | semisineCosineComponents 78 | ); 79 | 80 | // set up master gain 81 | this.masterGain = this.audioCtx.createGain() 82 | this.masterGain.gain.value = this.mainVolume 83 | 84 | // set up master filter 85 | this.masterLPfilter = this.audioCtx.createBiquadFilter() 86 | this.masterLPfilter.frequency.value = 5000 87 | this.masterLPfilter.Q.value = 1 88 | this.masterLPfilter.type = 'lowpass' 89 | 90 | // connect master gain control > filter > master output 91 | this.masterGain.connect(this.masterLPfilter) 92 | this.masterLPfilter.connect(this.audioCtx.destination) 93 | 94 | // init delay 95 | this.delay.init(this.audioCtx) 96 | 97 | // init free running oscillators 98 | for (i = 0; i < this.polyphony; i++) { 99 | this.voices[i] = new Voice(this.audioCtx) 100 | this.voices[i].bindDelay(this.delay) 101 | this.voices[i].bindSynth(this) 102 | this.voices[i].init() 103 | } 104 | } 105 | } 106 | 107 | setMainVolume(newValue) { 108 | const oldValue = this.mainVolume 109 | if (newValue !== oldValue) { 110 | this.mainVolume = newValue 111 | if (this.inited) { 112 | const now = this.now() 113 | this.masterGain.gain.value = newValue 114 | this.masterGain.gain.setValueAtTime(newValue, now) 115 | } 116 | } 117 | } 118 | 119 | noteOn(midinote, velocity = 127) { 120 | const frequency = tuning_table.freq[midinote] 121 | 122 | if (!R.isNil(frequency)) { 123 | midi.playFrequency(frequency) 124 | 125 | // make sure note triggers only on first input (prevent duplicate notes) 126 | if (R.isNil(this.midinotes_to_voices[midinote])) { 127 | this.init() 128 | 129 | // round robin voice allocation, but skip voices that are still being held 130 | for (i = this.nextVoice; i < this.nextVoice + this.polyphony; i++) { 131 | // if next voice is free, use it 132 | if (R.isNil(this.voices_to_midinotes[(i + 1) % this.polyphony])) { 133 | this.nextVoice = (i + 1) % this.polyphony 134 | break 135 | } 136 | // if no free voices are found when the loop ends, voice stealing will result 137 | } 138 | 139 | // keep track of allocated voices 140 | this.midinotes_to_voices[midinote] = this.nextVoice 141 | this.voices_to_midinotes[this.nextVoice] = midinote 142 | 143 | // trigger note start 144 | this.voices[this.midinotes_to_voices[midinote]].start(frequency, velocity) 145 | 146 | // indicate playing note 147 | jQuery('#tuning-table-row-' + midinote).addClass('bg-playnote') 148 | console.log(this.midinotes_to_voices) 149 | //console.log("Play note " + midinote + " (" + frequency.toFixed(3) + " Hz) velocity " + velocity); 150 | } 151 | } 152 | } 153 | noteOff(midinote) { 154 | midi.stopFrequency() 155 | 156 | if (!R.isNil(this.midinotes_to_voices[midinote])) { 157 | // release the note 158 | this.voices[this.midinotes_to_voices[midinote]].stop() 159 | 160 | // voice allocation 161 | delete this.voices_to_midinotes[this.midinotes_to_voices[midinote]] 162 | delete this.midinotes_to_voices[midinote] 163 | 164 | // indicate stopped note 165 | jQuery('#tuning-table-row-' + midinote).removeClass('bg-playnote') 166 | if (Object.values(this.midinotes_to_voices).length) { 167 | console.log(this.midinotes_to_voices) 168 | } 169 | } 170 | } 171 | 172 | now() { 173 | return this.audioCtx.currentTime 174 | } 175 | 176 | // this function stops all active voices and cuts the delay 177 | panic() { 178 | // show which voices are active (playing) 179 | console.log(this.voices) 180 | 181 | // loop through active voices 182 | for (let i = 0; i < this.polyphony; i++) { 183 | // turn off voice 184 | this.noteOff(this.voices_to_midinotes[i]) 185 | } 186 | 187 | // turn down delay gain 188 | jQuery('#input_range_feedback_gain').val(0) 189 | this.delay.gain = 0 190 | const now = this.now() 191 | this.delay.gainL.gain.setValueAtTime(this.delay.gain, now) 192 | this.delay.gainR.gain.setValueAtTime(this.delay.gain, now) 193 | 194 | midi.stopFrequency() 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/js/synth/Voice.js: -------------------------------------------------------------------------------- 1 | const getEnvelopeByName = (name) => { 2 | const envelope = { 3 | attackTime: 0, 4 | decayTime: 0, 5 | sustain: 1, 6 | releaseTime: 0 7 | } 8 | 9 | switch (name) { 10 | case 'organ': 11 | envelope.attackTime = 0.01 12 | envelope.decayTime = 0.3 13 | envelope.sustain = 0.8 14 | envelope.releaseTime = 0.01 15 | break 16 | case 'pad': 17 | envelope.attackTime = 1 18 | envelope.decayTime = 3 19 | envelope.sustain = 0.5 20 | envelope.releaseTime = 0.7 21 | break 22 | case 'perc-short': 23 | envelope.attackTime = 0.005 24 | envelope.decayTime = 0.3 25 | envelope.sustain = 0.00001 26 | envelope.releaseTime = 0.05 27 | break 28 | case 'perc-medium': 29 | envelope.attackTime = 0.005 30 | envelope.decayTime = 1.5 31 | envelope.sustain = 0.00001 32 | envelope.releaseTime = 0.25 33 | break 34 | case 'perc-long': 35 | envelope.attackTime = 0.01 36 | envelope.decayTime = 8 37 | envelope.sustain = 0.00001 38 | envelope.releaseTime = 0.8 39 | break 40 | } 41 | 42 | return envelope 43 | } 44 | 45 | const getEnvelopeName = () => jQuery('#input_select_synth_amp_env').val() 46 | 47 | // https://github.com/mohayonao/pseudo-audio-param/blob/master/lib/expr.js#L3 48 | function getLinearRampToValueAtTime(t, v0, v1, t0, t1) { 49 | var a 50 | 51 | if (t <= t0) { 52 | return v0 53 | } 54 | if (t1 <= t) { 55 | return v1 56 | } 57 | 58 | a = (t - t0) / (t1 - t0) 59 | 60 | return v0 + a * (v1 - v0) 61 | } 62 | 63 | // https://github.com/mohayonao/pseudo-audio-param/blob/master/lib/expr.js#L18 64 | function getExponentialRampToValueAtTime(t, v0, v1, t0, t1) { 65 | var a 66 | 67 | if (t <= t0) { 68 | return v0 69 | } 70 | if (t1 <= t) { 71 | return v1 72 | } 73 | if (v0 === v1) { 74 | return v0 75 | } 76 | 77 | a = (t - t0) / (t1 - t0) 78 | 79 | if ((0 < v0 && 0 < v1) || (v0 < 0 && v1 < 0)) { 80 | return v0 * Math.pow(v1 / v0, a) 81 | } 82 | 83 | return 0 84 | } 85 | 86 | const interpolateValueAtTime = (minValue, maxValue, envelope, t) => { 87 | // interpolate attack 88 | if (envelope.attackTime > t) { 89 | return getLinearRampToValueAtTime(t, minValue, maxValue, 0, envelope.attackTime) 90 | } 91 | 92 | // interpolate decay 93 | if (envelope.attackTime + envelope.decayTime > t) { 94 | return getExponentialRampToValueAtTime( 95 | t, 96 | maxValue, 97 | maxValue * envelope.sustain, 98 | envelope.attackTime, 99 | envelope.attackTime + envelope.decayTime 100 | ) 101 | } 102 | 103 | // interpolate sustain 104 | return maxValue * envelope.sustain 105 | } 106 | 107 | class Voice { 108 | constructor(audioCtx) { 109 | // set up oscillator 110 | this.vco = audioCtx.createOscillator() 111 | 112 | // set up amplitude envelope generator 113 | this.vca = audioCtx.createGain() 114 | } 115 | 116 | init() { 117 | // timing 118 | const now = this.synth.now() 119 | 120 | this.vca.gain.setValueAtTime(0, now) 121 | 122 | // routing 123 | this.vco.connect(this.vca) 124 | this.vco.start() 125 | this.vca.connect(this.delay.channelL) 126 | this.vca.connect(this.synth.masterGain) 127 | } 128 | 129 | start(frequency, velocity) { 130 | // start timing 131 | const now = this.synth.now() 132 | this.vca.gain._startTime = now 133 | 134 | // tune oscillator to correct frequency 135 | this.vco.frequency.setValueAtTime(frequency, now) 136 | 137 | // set oscillator waveform 138 | switch (this.synth.waveform) { 139 | case 'warm1': 140 | this.vco.setPeriodicWave(synth.custom_waveforms.warm1) 141 | break 142 | case 'warm2': 143 | this.vco.setPeriodicWave(synth.custom_waveforms.warm2) 144 | break 145 | case 'warm3': 146 | this.vco.setPeriodicWave(synth.custom_waveforms.warm3) 147 | break 148 | case 'warm4': 149 | this.vco.setPeriodicWave(synth.custom_waveforms.warm4) 150 | break 151 | case 'octaver': 152 | this.vco.setPeriodicWave(synth.custom_waveforms.octaver) 153 | break 154 | case 'brightness': 155 | this.vco.setPeriodicWave(synth.custom_waveforms.brightness) 156 | break 157 | case 'harmonicbell': 158 | this.vco.setPeriodicWave(synth.custom_waveforms.harmonicbell) 159 | break 160 | case 'semisine': 161 | this.vco.setPeriodicWave(synth.custom_waveforms.semisine) 162 | break 163 | default: 164 | this.vco.type = this.synth.waveform 165 | } 166 | 167 | // get target gain 168 | if (velocity === 0) { 169 | // in exponentialRampToValueAtTime, target gain can't be 0 170 | this.targetGain = 0.00001 171 | } else { 172 | // use velocity to determine target gain 173 | this.targetGain = velocity = (0.2 * velocity) / 127 174 | } 175 | 176 | // get and set amplitude envelope 177 | const envelope = getEnvelopeByName(getEnvelopeName()) 178 | this.attackTime = envelope.attackTime 179 | this.decayTime = envelope.decayTime 180 | this.sustain = envelope.sustain 181 | this.releaseTime = envelope.releaseTime 182 | 183 | // Attack 184 | this.cancelEnvelope(this.vca.gain, now) 185 | this.vca.gain.setValueAtTime(0, now) 186 | this.vca.gain.linearRampToValueAtTime(this.targetGain, now + this.attackTime) 187 | 188 | // Decay & Sustain 189 | this.vca.gain.exponentialRampToValueAtTime( 190 | this.targetGain * this.sustain, 191 | now + this.attackTime + this.decayTime 192 | ) 193 | } 194 | 195 | stop() { 196 | // timing 197 | const now = this.synth.now() 198 | 199 | // Release 200 | this.cancelEnvelope(this.vca.gain, now) 201 | this.vca.gain.setTargetAtTime(0.0, now, this.releaseTime) 202 | } 203 | 204 | // cancels any scheduled envelope changes in a given property's value 205 | cancelEnvelope(property, now) { 206 | // Firefox and Safari do not support cancelAndHoldAtTime 207 | if (isFunction(property.cancelAndHoldAtTime)) { 208 | property.cancelAndHoldAtTime(now) 209 | } else { 210 | property.cancelScheduledValues(now) 211 | property.setValueAtTime( 212 | interpolateValueAtTime( 213 | 0.00001, 214 | this.targetGain, 215 | getEnvelopeByName(getEnvelopeName()), 216 | now - property._startTime 217 | ), 218 | now 219 | ) 220 | } 221 | } 222 | 223 | bindSynth(synth) { 224 | this.synth = synth 225 | } 226 | bindDelay(delay) { 227 | this.delay = delay 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/js/ui.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ui.js 3 | * User interface 4 | */ 5 | 6 | // use jQuery UI tooltips instead of default browser tooltips 7 | jQuery(function () { 8 | jQuery(document).tooltip() 9 | }) 10 | 11 | // set "accordion" settings UI 12 | jQuery(function () { 13 | jQuery('#settings-accordion').accordion({ 14 | collapsible: true, // allow all tabs to be closed 15 | active: false, // start all tabs closed 16 | heightStyle: 'content', // size each section to content 17 | icons: null, // turn off triangle icons 18 | header: '> div > h3' 19 | }) 20 | }) 21 | 22 | function touch_kbd_open() { 23 | // check if scale already set up - we can't use the touch kbd if there is no scale 24 | if (tuning_table['note_count'] == 0) { 25 | alert("Can't open the touch keyboard until you have created or loaded a scale.") 26 | return 27 | } 28 | 29 | // remove info from keys 30 | jQuery('#virtual-keyboard td').each(function (index) { 31 | // clear content of cell 32 | jQuery(this).empty() 33 | // reset any classes that might be on the cell 34 | jQuery(this).attr('class', 'key') 35 | }) 36 | 37 | // display tuning info on virtual keys 38 | jQuery('#virtual-keyboard td').each(function () { 39 | // get the coord data attribute and figure out the midinote 40 | const midinote = touch_to_midinote(getCoordsFromKey(this)) 41 | 42 | // add text to key 43 | //jQuery(this).append("

midi " + midinote + "

"); 44 | //jQuery(this).append("

" + tuning_table['freq'][midinote].toFixed(1) + "
Hz

"); 45 | 46 | // get the number representing this key color, with the first item being 0 47 | var keynum = (midinote - tuning_table['base_midi_note']).mod(key_colors.length) 48 | 49 | // set the color of the key 50 | this.style.backgroundColor = key_colors[keynum] 51 | }) 52 | 53 | state.set('mobile menu visible', false) 54 | 55 | // show the virtual keyboard 56 | jQuery('#virtual-keyboard').slideDown() 57 | } 58 | 59 | function touch_kbd_close() { 60 | // hide the virtual keyboard 61 | jQuery('#virtual-keyboard').slideUp() 62 | } 63 | -------------------------------------------------------------------------------- /src/js/user.js: -------------------------------------------------------------------------------- 1 | /** 2 | * user.js 3 | * Add your own hacks and hotfixes below 4 | */ 5 | 6 | // declare custom variables and functions here 7 | 8 | // any code added within the below function will be called last when the page loads 9 | function run_user_scripts_on_document_ready() {} 10 | -------------------------------------------------------------------------------- /src/lib/bootstrap-3.3.7-dist/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/lib/bootstrap-3.3.7-dist/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/lib/bootstrap-3.3.7-dist/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/lib/bootstrap-3.3.7-dist/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/lib/bootstrap-3.3.7-dist/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/lib/bootstrap-3.3.7-dist/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/lib/bootstrap-3.3.7-dist/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/lib/bootstrap-3.3.7-dist/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/lib/bootstrap-3.3.7-dist/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /src/lib/eventemitter3.js: -------------------------------------------------------------------------------- 1 | // source: https://github.com/primus/eventemitter3/blob/master/index.js 2 | 3 | 'use strict'; 4 | 5 | var has = Object.prototype.hasOwnProperty 6 | , prefix = '~'; 7 | 8 | /** 9 | * Constructor to create a storage for our `EE` objects. 10 | * An `Events` instance is a plain object whose properties are event names. 11 | * 12 | * @constructor 13 | * @private 14 | */ 15 | function Events() {} 16 | 17 | // 18 | // We try to not inherit from `Object.prototype`. In some engines creating an 19 | // instance in this way is faster than calling `Object.create(null)` directly. 20 | // If `Object.create(null)` is not supported we prefix the event names with a 21 | // character to make sure that the built-in object properties are not 22 | // overridden or used as an attack vector. 23 | // 24 | if (Object.create) { 25 | Events.prototype = Object.create(null); 26 | 27 | // 28 | // This hack is needed because the `__proto__` property is still inherited in 29 | // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. 30 | // 31 | if (!new Events().__proto__) prefix = false; 32 | } 33 | 34 | /** 35 | * Representation of a single event listener. 36 | * 37 | * @param {Function} fn The listener function. 38 | * @param {*} context The context to invoke the listener with. 39 | * @param {Boolean} [once=false] Specify if the listener is a one-time listener. 40 | * @constructor 41 | * @private 42 | */ 43 | function EE(fn, context, once) { 44 | this.fn = fn; 45 | this.context = context; 46 | this.once = once || false; 47 | } 48 | 49 | /** 50 | * Add a listener for a given event. 51 | * 52 | * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. 53 | * @param {(String|Symbol)} event The event name. 54 | * @param {Function} fn The listener function. 55 | * @param {*} context The context to invoke the listener with. 56 | * @param {Boolean} once Specify if the listener is a one-time listener. 57 | * @returns {EventEmitter} 58 | * @private 59 | */ 60 | function addListener(emitter, event, fn, context, once) { 61 | if (typeof fn !== 'function') { 62 | throw new TypeError('The listener must be a function'); 63 | } 64 | 65 | var listener = new EE(fn, context || emitter, once) 66 | , evt = prefix ? prefix + event : event; 67 | 68 | if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; 69 | else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); 70 | else emitter._events[evt] = [emitter._events[evt], listener]; 71 | 72 | return emitter; 73 | } 74 | 75 | /** 76 | * Clear event by name. 77 | * 78 | * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. 79 | * @param {(String|Symbol)} evt The Event name. 80 | * @private 81 | */ 82 | function clearEvent(emitter, evt) { 83 | if (--emitter._eventsCount === 0) emitter._events = new Events(); 84 | else delete emitter._events[evt]; 85 | } 86 | 87 | /** 88 | * Minimal `EventEmitter` interface that is molded against the Node.js 89 | * `EventEmitter` interface. 90 | * 91 | * @constructor 92 | * @public 93 | */ 94 | function EventEmitter() { 95 | this._events = new Events(); 96 | this._eventsCount = 0; 97 | } 98 | 99 | /** 100 | * Return an array listing the events for which the emitter has registered 101 | * listeners. 102 | * 103 | * @returns {Array} 104 | * @public 105 | */ 106 | EventEmitter.prototype.eventNames = function eventNames() { 107 | var names = [] 108 | , events 109 | , name; 110 | 111 | if (this._eventsCount === 0) return names; 112 | 113 | for (name in (events = this._events)) { 114 | if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); 115 | } 116 | 117 | if (Object.getOwnPropertySymbols) { 118 | return names.concat(Object.getOwnPropertySymbols(events)); 119 | } 120 | 121 | return names; 122 | }; 123 | 124 | /** 125 | * Return the listeners registered for a given event. 126 | * 127 | * @param {(String|Symbol)} event The event name. 128 | * @returns {Array} The registered listeners. 129 | * @public 130 | */ 131 | EventEmitter.prototype.listeners = function listeners(event) { 132 | var evt = prefix ? prefix + event : event 133 | , handlers = this._events[evt]; 134 | 135 | if (!handlers) return []; 136 | if (handlers.fn) return [handlers.fn]; 137 | 138 | for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { 139 | ee[i] = handlers[i].fn; 140 | } 141 | 142 | return ee; 143 | }; 144 | 145 | /** 146 | * Return the number of listeners listening to a given event. 147 | * 148 | * @param {(String|Symbol)} event The event name. 149 | * @returns {Number} The number of listeners. 150 | * @public 151 | */ 152 | EventEmitter.prototype.listenerCount = function listenerCount(event) { 153 | var evt = prefix ? prefix + event : event 154 | , listeners = this._events[evt]; 155 | 156 | if (!listeners) return 0; 157 | if (listeners.fn) return 1; 158 | return listeners.length; 159 | }; 160 | 161 | /** 162 | * Calls each of the listeners registered for a given event. 163 | * 164 | * @param {(String|Symbol)} event The event name. 165 | * @returns {Boolean} `true` if the event had listeners, else `false`. 166 | * @public 167 | */ 168 | EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { 169 | var evt = prefix ? prefix + event : event; 170 | 171 | if (!this._events[evt]) return false; 172 | 173 | var listeners = this._events[evt] 174 | , len = arguments.length 175 | , args 176 | , i; 177 | 178 | if (listeners.fn) { 179 | if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); 180 | 181 | switch (len) { 182 | case 1: return listeners.fn.call(listeners.context), true; 183 | case 2: return listeners.fn.call(listeners.context, a1), true; 184 | case 3: return listeners.fn.call(listeners.context, a1, a2), true; 185 | case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; 186 | case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; 187 | case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; 188 | } 189 | 190 | for (i = 1, args = new Array(len -1); i < len; i++) { 191 | args[i - 1] = arguments[i]; 192 | } 193 | 194 | listeners.fn.apply(listeners.context, args); 195 | } else { 196 | var length = listeners.length 197 | , j; 198 | 199 | for (i = 0; i < length; i++) { 200 | if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); 201 | 202 | switch (len) { 203 | case 1: listeners[i].fn.call(listeners[i].context); break; 204 | case 2: listeners[i].fn.call(listeners[i].context, a1); break; 205 | case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; 206 | case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; 207 | default: 208 | if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { 209 | args[j - 1] = arguments[j]; 210 | } 211 | 212 | listeners[i].fn.apply(listeners[i].context, args); 213 | } 214 | } 215 | } 216 | 217 | return true; 218 | }; 219 | 220 | /** 221 | * Add a listener for a given event. 222 | * 223 | * @param {(String|Symbol)} event The event name. 224 | * @param {Function} fn The listener function. 225 | * @param {*} [context=this] The context to invoke the listener with. 226 | * @returns {EventEmitter} `this`. 227 | * @public 228 | */ 229 | EventEmitter.prototype.on = function on(event, fn, context) { 230 | return addListener(this, event, fn, context, false); 231 | }; 232 | 233 | /** 234 | * Add a one-time listener for a given event. 235 | * 236 | * @param {(String|Symbol)} event The event name. 237 | * @param {Function} fn The listener function. 238 | * @param {*} [context=this] The context to invoke the listener with. 239 | * @returns {EventEmitter} `this`. 240 | * @public 241 | */ 242 | EventEmitter.prototype.once = function once(event, fn, context) { 243 | return addListener(this, event, fn, context, true); 244 | }; 245 | 246 | /** 247 | * Remove the listeners of a given event. 248 | * 249 | * @param {(String|Symbol)} event The event name. 250 | * @param {Function} fn Only remove the listeners that match this function. 251 | * @param {*} context Only remove the listeners that have this context. 252 | * @param {Boolean} once Only remove one-time listeners. 253 | * @returns {EventEmitter} `this`. 254 | * @public 255 | */ 256 | EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { 257 | var evt = prefix ? prefix + event : event; 258 | 259 | if (!this._events[evt]) return this; 260 | if (!fn) { 261 | clearEvent(this, evt); 262 | return this; 263 | } 264 | 265 | var listeners = this._events[evt]; 266 | 267 | if (listeners.fn) { 268 | if ( 269 | listeners.fn === fn && 270 | (!once || listeners.once) && 271 | (!context || listeners.context === context) 272 | ) { 273 | clearEvent(this, evt); 274 | } 275 | } else { 276 | for (var i = 0, events = [], length = listeners.length; i < length; i++) { 277 | if ( 278 | listeners[i].fn !== fn || 279 | (once && !listeners[i].once) || 280 | (context && listeners[i].context !== context) 281 | ) { 282 | events.push(listeners[i]); 283 | } 284 | } 285 | 286 | // 287 | // Reset the array, or remove it completely if we have no more listeners. 288 | // 289 | if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; 290 | else clearEvent(this, evt); 291 | } 292 | 293 | return this; 294 | }; 295 | 296 | /** 297 | * Remove all listeners, or those of the specified event. 298 | * 299 | * @param {(String|Symbol)} [event] The event name. 300 | * @returns {EventEmitter} `this`. 301 | * @public 302 | */ 303 | EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { 304 | var evt; 305 | 306 | if (event) { 307 | evt = prefix ? prefix + event : event; 308 | if (this._events[evt]) clearEvent(this, evt); 309 | } else { 310 | this._events = new Events(); 311 | this._eventsCount = 0; 312 | } 313 | 314 | return this; 315 | }; 316 | 317 | // 318 | // Alias methods names because people roll like that. 319 | // 320 | EventEmitter.prototype.off = EventEmitter.prototype.removeListener; 321 | EventEmitter.prototype.addListener = EventEmitter.prototype.on; 322 | 323 | // 324 | // Expose the prefix. 325 | // 326 | EventEmitter.prefixed = prefix; 327 | 328 | // 329 | // Allow `EventEmitter` to be imported as module namespace. 330 | // 331 | EventEmitter.EventEmitter = EventEmitter; 332 | 333 | // 334 | // Expose the module. 335 | // 336 | if ('undefined' !== typeof module) { 337 | module.exports = EventEmitter; 338 | } 339 | -------------------------------------------------------------------------------- /src/lib/jquery-ui-1.12.1/AUTHORS.txt: -------------------------------------------------------------------------------- 1 | Authors ordered by first contribution 2 | A list of current team members is available at http://jqueryui.com/about 3 | 4 | Paul Bakaus 5 | Richard Worth 6 | Yehuda Katz 7 | Sean Catchpole 8 | John Resig 9 | Tane Piper 10 | Dmitri Gaskin 11 | Klaus Hartl 12 | Stefan Petre 13 | Gilles van den Hoven 14 | Micheil Bryan Smith 15 | Jörn Zaefferer 16 | Marc Grabanski 17 | Keith Wood 18 | Brandon Aaron 19 | Scott González 20 | Eduardo Lundgren 21 | Aaron Eisenberger 22 | Joan Piedra 23 | Bruno Basto 24 | Remy Sharp 25 | Bohdan Ganicky 26 | David Bolter 27 | Chi Cheng 28 | Ca-Phun Ung 29 | Ariel Flesler 30 | Maggie Wachs 31 | Scott Jehl 32 | Todd Parker 33 | Andrew Powell 34 | Brant Burnett 35 | Douglas Neiner 36 | Paul Irish 37 | Ralph Whitbeck 38 | Thibault Duplessis 39 | Dominique Vincent 40 | Jack Hsu 41 | Adam Sontag 42 | Carl Fürstenberg 43 | Kevin Dalman 44 | Alberto Fernández Capel 45 | Jacek Jędrzejewski (http://jacek.jedrzejewski.name) 46 | Ting Kuei 47 | Samuel Cormier-Iijima 48 | Jon Palmer 49 | Ben Hollis 50 | Justin MacCarthy 51 | Eyal Kobrigo 52 | Tiago Freire 53 | Diego Tres 54 | Holger Rüprich 55 | Ziling Zhao 56 | Mike Alsup 57 | Robson Braga Araujo 58 | Pierre-Henri Ausseil 59 | Christopher McCulloh 60 | Andrew Newcomb 61 | Lim Chee Aun 62 | Jorge Barreiro 63 | Daniel Steigerwald 64 | John Firebaugh 65 | John Enters 66 | Andrey Kapitcyn 67 | Dmitry Petrov 68 | Eric Hynds 69 | Chairat Sunthornwiphat 70 | Josh Varner 71 | Stéphane Raimbault 72 | Jay Merrifield 73 | J. Ryan Stinnett 74 | Peter Heiberg 75 | Alex Dovenmuehle 76 | Jamie Gegerson 77 | Raymond Schwartz 78 | Phillip Barnes 79 | Kyle Wilkinson 80 | Khaled AlHourani 81 | Marian Rudzynski 82 | Jean-Francois Remy 83 | Doug Blood 84 | Filippo Cavallarin 85 | Heiko Henning 86 | Aliaksandr Rahalevich 87 | Mario Visic 88 | Xavi Ramirez 89 | Max Schnur 90 | Saji Nediyanchath 91 | Corey Frang 92 | Aaron Peterson 93 | Ivan Peters 94 | Mohamed Cherif Bouchelaghem 95 | Marcos Sousa 96 | Michael DellaNoce 97 | George Marshall 98 | Tobias Brunner 99 | Martin Solli 100 | David Petersen 101 | Dan Heberden 102 | William Kevin Manire 103 | Gilmore Davidson 104 | Michael Wu 105 | Adam Parod 106 | Guillaume Gautreau 107 | Marcel Toele 108 | Dan Streetman 109 | Matt Hoskins 110 | Giovanni Giacobbi 111 | Kyle Florence 112 | Pavol Hluchý 113 | Hans Hillen 114 | Mark Johnson 115 | Trey Hunner 116 | Shane Whittet 117 | Edward A Faulkner 118 | Adam Baratz 119 | Kato Kazuyoshi 120 | Eike Send 121 | Kris Borchers 122 | Eddie Monge 123 | Israel Tsadok 124 | Carson McDonald 125 | Jason Davies 126 | Garrison Locke 127 | David Murdoch 128 | Benjamin Scott Boyle 129 | Jesse Baird 130 | Jonathan Vingiano 131 | Dylan Just 132 | Hiroshi Tomita 133 | Glenn Goodrich 134 | Tarafder Ashek-E-Elahi 135 | Ryan Neufeld 136 | Marc Neuwirth 137 | Philip Graham 138 | Benjamin Sterling 139 | Wesley Walser 140 | Kouhei Sutou 141 | Karl Kirch 142 | Chris Kelly 143 | Jason Oster 144 | Felix Nagel 145 | Alexander Polomoshnov 146 | David Leal 147 | Igor Milla 148 | Dave Methvin 149 | Florian Gutmann 150 | Marwan Al Jubeh 151 | Milan Broum 152 | Sebastian Sauer 153 | Gaëtan Muller 154 | Michel Weimerskirch 155 | William Griffiths 156 | Stojce Slavkovski 157 | David Soms 158 | David De Sloovere 159 | Michael P. Jung 160 | Shannon Pekary 161 | Dan Wellman 162 | Matthew Edward Hutton 163 | James Khoury 164 | Rob Loach 165 | Alberto Monteiro 166 | Alex Rhea 167 | Krzysztof Rosiński 168 | Ryan Olton 169 | Genie <386@mail.com> 170 | Rick Waldron 171 | Ian Simpson 172 | Lev Kitsis 173 | TJ VanToll 174 | Justin Domnitz 175 | Douglas Cerna 176 | Bert ter Heide 177 | Jasvir Nagra 178 | Yuriy Khabarov <13real008@gmail.com> 179 | Harri Kilpiö 180 | Lado Lomidze 181 | Amir E. Aharoni 182 | Simon Sattes 183 | Jo Liss 184 | Guntupalli Karunakar 185 | Shahyar Ghobadpour 186 | Lukasz Lipinski 187 | Timo Tijhof 188 | Jason Moon 189 | Martin Frost 190 | Eneko Illarramendi 191 | EungJun Yi 192 | Courtland Allen 193 | Viktar Varvanovich 194 | Danny Trunk 195 | Pavel Stetina 196 | Michael Stay 197 | Steven Roussey 198 | Michael Hollis 199 | Lee Rowlands 200 | Timmy Willison 201 | Karl Swedberg 202 | Baoju Yuan 203 | Maciej Mroziński 204 | Luis Dalmolin 205 | Mark Aaron Shirley 206 | Martin Hoch 207 | Jiayi Yang 208 | Philipp Benjamin Köppchen 209 | Sindre Sorhus 210 | Bernhard Sirlinger 211 | Jared A. Scheel 212 | Rafael Xavier de Souza 213 | John Chen 214 | Robert Beuligmann 215 | Dale Kocian 216 | Mike Sherov 217 | Andrew Couch 218 | Marc-Andre Lafortune 219 | Nate Eagle 220 | David Souther 221 | Mathias Stenbom 222 | Sergey Kartashov 223 | Avinash R 224 | Ethan Romba 225 | Cory Gackenheimer 226 | Juan Pablo Kaniefsky 227 | Roman Salnikov 228 | Anika Henke 229 | Samuel Bovée 230 | Fabrício Matté 231 | Viktor Kojouharov 232 | Pawel Maruszczyk (http://hrabstwo.net) 233 | Pavel Selitskas 234 | Bjørn Johansen 235 | Matthieu Penant 236 | Dominic Barnes 237 | David Sullivan 238 | Thomas Jaggi 239 | Vahid Sohrabloo 240 | Travis Carden 241 | Bruno M. Custódio 242 | Nathanael Silverman 243 | Christian Wenz 244 | Steve Urmston 245 | Zaven Muradyan 246 | Woody Gilk 247 | Zbigniew Motyka 248 | Suhail Alkowaileet 249 | Toshi MARUYAMA 250 | David Hansen 251 | Brian Grinstead 252 | Christian Klammer 253 | Steven Luscher 254 | Gan Eng Chin 255 | Gabriel Schulhof 256 | Alexander Schmitz 257 | Vilhjálmur Skúlason 258 | Siebrand Mazeland 259 | Mohsen Ekhtiari 260 | Pere Orga 261 | Jasper de Groot 262 | Stephane Deschamps 263 | Jyoti Deka 264 | Andrei Picus 265 | Ondrej Novy 266 | Jacob McCutcheon 267 | Monika Piotrowicz 268 | Imants Horsts 269 | Eric Dahl 270 | Dave Stein 271 | Dylan Barrell 272 | Daniel DeGroff 273 | Michael Wiencek 274 | Thomas Meyer 275 | Ruslan Yakhyaev 276 | Brian J. Dowling 277 | Ben Higgins 278 | Yermo Lamers 279 | Patrick Stapleton 280 | Trisha Crowley 281 | Usman Akeju 282 | Rodrigo Menezes 283 | Jacques Perrault 284 | Frederik Elvhage 285 | Will Holley 286 | Uri Gilad 287 | Richard Gibson 288 | Simen Bekkhus 289 | Chen Eshchar 290 | Bruno Pérel 291 | Mohammed Alshehri 292 | Lisa Seacat DeLuca 293 | Anne-Gaelle Colom 294 | Adam Foster 295 | Luke Page 296 | Daniel Owens 297 | Michael Orchard 298 | Marcus Warren 299 | Nils Heuermann 300 | Marco Ziech 301 | Patricia Juarez 302 | Ben Mosher 303 | Ablay Keldibek 304 | Thomas Applencourt 305 | Jiabao Wu 306 | Eric Lee Carraway 307 | Victor Homyakov 308 | Myeongjin Lee 309 | Liran Sharir 310 | Weston Ruter 311 | Mani Mishra 312 | Hannah Methvin 313 | Leonardo Balter 314 | Benjamin Albert 315 | Michał Gołębiowski 316 | Alyosha Pushak 317 | Fahad Ahmad 318 | Matt Brundage 319 | Francesc Baeta 320 | Piotr Baran 321 | Mukul Hase 322 | Konstantin Dinev 323 | Rand Scullard 324 | Dan Strohl 325 | Maksim Ryzhikov 326 | Amine HADDAD 327 | Amanpreet Singh 328 | Alexey Balchunas 329 | Peter Kehl 330 | Peter Dave Hello 331 | Johannes Schäfer 332 | Ville Skyttä 333 | Ryan Oriecuia 334 | -------------------------------------------------------------------------------- /src/lib/jquery-ui-1.12.1/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright jQuery Foundation and other contributors, https://jquery.org/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery-ui 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | Copyright and related rights for sample code are waived via CC0. Sample 34 | code is defined as all source code contained within the demos directory. 35 | 36 | CC0: http://creativecommons.org/publicdomain/zero/1.0/ 37 | 38 | ==== 39 | 40 | All files located in the node_modules and external directories are 41 | externally maintained libraries used by this software which have their 42 | own licenses; we recommend you read them, as their terms may differ from 43 | the terms above. 44 | -------------------------------------------------------------------------------- /src/lib/jquery-ui-1.12.1/images/ui-icons_444444_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/lib/jquery-ui-1.12.1/images/ui-icons_444444_256x240.png -------------------------------------------------------------------------------- /src/lib/jquery-ui-1.12.1/images/ui-icons_555555_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/lib/jquery-ui-1.12.1/images/ui-icons_555555_256x240.png -------------------------------------------------------------------------------- /src/lib/jquery-ui-1.12.1/images/ui-icons_777620_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/lib/jquery-ui-1.12.1/images/ui-icons_777620_256x240.png -------------------------------------------------------------------------------- /src/lib/jquery-ui-1.12.1/images/ui-icons_777777_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/lib/jquery-ui-1.12.1/images/ui-icons_777777_256x240.png -------------------------------------------------------------------------------- /src/lib/jquery-ui-1.12.1/images/ui-icons_cc0000_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/lib/jquery-ui-1.12.1/images/ui-icons_cc0000_256x240.png -------------------------------------------------------------------------------- /src/lib/jquery-ui-1.12.1/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/lib/jquery-ui-1.12.1/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /src/lib/jquery-ui-1.12.1/jquery-ui.structure.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.12.1 - 2016-09-14 2 | * http://jqueryui.com 3 | * Copyright jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin:2px 0 0 0;padding:.5em .5em .5em .7em;font-size:100%}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-button{padding:.4em 1em;display:inline-block;position:relative;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2em;box-sizing:border-box;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-button-icon-only{text-indent:0}.ui-button-icon-only .ui-icon{position:absolute;top:50%;left:50%;margin-top:-8px;margin-left:-8px}.ui-button.ui-icon-notext .ui-icon{padding:0;width:2.1em;height:2.1em;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-icon-notext .ui-icon{width:auto;height:auto;text-indent:0;white-space:normal;padding:.4em 1em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-controlgroup{vertical-align:middle;display:inline-block}.ui-controlgroup > .ui-controlgroup-item{float:left;margin-left:0;margin-right:0}.ui-controlgroup > .ui-controlgroup-item:focus,.ui-controlgroup > .ui-controlgroup-item.ui-visual-focus{z-index:9999}.ui-controlgroup-vertical > .ui-controlgroup-item{display:block;float:none;width:100%;margin-top:0;margin-bottom:0;text-align:left}.ui-controlgroup-vertical .ui-controlgroup-item{box-sizing:border-box}.ui-controlgroup .ui-controlgroup-label{padding:.4em 1em}.ui-controlgroup .ui-controlgroup-label span{font-size:80%}.ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item{border-left:none}.ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item{border-top:none}.ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content{border-right:none}.ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content{border-bottom:none}.ui-controlgroup-vertical .ui-spinner-input{width:75%;width:calc( 100% - 2.4em )}.ui-controlgroup-vertical .ui-spinner .ui-spinner-up{border-top-style:solid}.ui-checkboxradio-label .ui-icon-background{box-shadow:inset 1px 1px 1px #ccc;border-radius:.12em;border:none}.ui-checkboxradio-radio-label .ui-icon-background{width:16px;height:16px;border-radius:1em;overflow:visible;border:none}.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon,.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon{background-image:none;width:8px;height:8px;border-width:4px;border-style:solid}.ui-checkboxradio-disabled{pointer-events:none}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:45%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-datepicker .ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat;left:.5em;top:.3em}.ui-dialog{position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-n{height:2px;top:0}.ui-dialog .ui-resizable-e{width:2px;right:0}.ui-dialog .ui-resizable-s{height:2px;bottom:0}.ui-dialog .ui-resizable-w{width:2px;left:0}.ui-dialog .ui-resizable-se,.ui-dialog .ui-resizable-sw,.ui-dialog .ui-resizable-ne,.ui-dialog .ui-resizable-nw{width:7px;height:7px}.ui-dialog .ui-resizable-se{right:0;bottom:0}.ui-dialog .ui-resizable-sw{left:0;bottom:0}.ui-dialog .ui-resizable-ne{right:0;top:0}.ui-dialog .ui-resizable-nw{left:0;top:0}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:bold;line-height:1.5;padding:2px 0.4em;margin:0.5em 0 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-text{display:block;margin-right:20px;overflow:hidden;text-overflow:ellipsis}.ui-selectmenu-button.ui-button{text-align:left;white-space:nowrap;width:14em}.ui-selectmenu-icon.ui-icon{float:right;margin-top:0}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:.222em 0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:2em}.ui-spinner-button{width:1.6em;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top-style:none;border-bottom-style:none;border-right-style:none}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px}body .ui-tooltip{border-width:2px} -------------------------------------------------------------------------------- /src/lib/jquery-ui-1.12.1/jquery-ui.theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI CSS Framework 1.12.1 3 | * http://jqueryui.com 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license. 7 | * http://jquery.org/license 8 | * 9 | * http://api.jqueryui.com/category/theming/ 10 | * 11 | * To view and modify this theme, visit http://jqueryui.com/themeroller/?bgShadowXPos=&bgOverlayXPos=&bgErrorXPos=&bgHighlightXPos=&bgContentXPos=&bgHeaderXPos=&bgActiveXPos=&bgHoverXPos=&bgDefaultXPos=&bgShadowYPos=&bgOverlayYPos=&bgErrorYPos=&bgHighlightYPos=&bgContentYPos=&bgHeaderYPos=&bgActiveYPos=&bgHoverYPos=&bgDefaultYPos=&bgShadowRepeat=&bgOverlayRepeat=&bgErrorRepeat=&bgHighlightRepeat=&bgContentRepeat=&bgHeaderRepeat=&bgActiveRepeat=&bgHoverRepeat=&bgDefaultRepeat=&iconsHover=url(%22images%2Fui-icons_555555_256x240.png%22)&iconsHighlight=url(%22images%2Fui-icons_777620_256x240.png%22)&iconsHeader=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsError=url(%22images%2Fui-icons_cc0000_256x240.png%22)&iconsDefault=url(%22images%2Fui-icons_777777_256x240.png%22)&iconsContent=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsActive=url(%22images%2Fui-icons_ffffff_256x240.png%22)&bgImgUrlShadow=&bgImgUrlOverlay=&bgImgUrlHover=&bgImgUrlHighlight=&bgImgUrlHeader=&bgImgUrlError=&bgImgUrlDefault=&bgImgUrlContent=&bgImgUrlActive=&opacityFilterShadow=Alpha(Opacity%3D30)&opacityFilterOverlay=Alpha(Opacity%3D30)&opacityShadowPerc=30&opacityOverlayPerc=30&iconColorHover=%23555555&iconColorHighlight=%23777620&iconColorHeader=%23444444&iconColorError=%23cc0000&iconColorDefault=%23777777&iconColorContent=%23444444&iconColorActive=%23ffffff&bgImgOpacityShadow=0&bgImgOpacityOverlay=0&bgImgOpacityError=95&bgImgOpacityHighlight=55&bgImgOpacityContent=75&bgImgOpacityHeader=75&bgImgOpacityActive=65&bgImgOpacityHover=75&bgImgOpacityDefault=75&bgTextureShadow=flat&bgTextureOverlay=flat&bgTextureError=flat&bgTextureHighlight=flat&bgTextureContent=flat&bgTextureHeader=flat&bgTextureActive=flat&bgTextureHover=flat&bgTextureDefault=flat&cornerRadius=3px&fwDefault=normal&ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&cornerRadiusShadow=8px&thicknessShadow=5px&offsetLeftShadow=0px&offsetTopShadow=0px&opacityShadow=.3&bgColorShadow=%23666666&opacityOverlay=.3&bgColorOverlay=%23aaaaaa&fcError=%235f3f3f&borderColorError=%23f1a899&bgColorError=%23fddfdf&fcHighlight=%23777620&borderColorHighlight=%23dad55e&bgColorHighlight=%23fffa90&fcContent=%23333333&borderColorContent=%23dddddd&bgColorContent=%23ffffff&fcHeader=%23333333&borderColorHeader=%23dddddd&bgColorHeader=%23e9e9e9&fcActive=%23ffffff&borderColorActive=%23003eff&bgColorActive=%23007fff&fcHover=%232b2b2b&borderColorHover=%23cccccc&bgColorHover=%23ededed&fcDefault=%23454545&borderColorDefault=%23c5c5c5&bgColorDefault=%23f6f6f6 12 | */ 13 | 14 | 15 | /* Component containers 16 | ----------------------------------*/ 17 | .ui-widget { 18 | font-family: Arial,Helvetica,sans-serif; 19 | font-size: 1em; 20 | } 21 | .ui-widget .ui-widget { 22 | font-size: 1em; 23 | } 24 | .ui-widget input, 25 | .ui-widget select, 26 | .ui-widget textarea, 27 | .ui-widget button { 28 | font-family: Arial,Helvetica,sans-serif; 29 | font-size: 1em; 30 | } 31 | .ui-widget.ui-widget-content { 32 | border: 1px solid #c5c5c5; 33 | } 34 | .ui-widget-content { 35 | border: 1px solid #dddddd; 36 | background: #ffffff; 37 | color: #333333; 38 | } 39 | .ui-widget-content a { 40 | color: #333333; 41 | } 42 | .ui-widget-header { 43 | border: 1px solid #dddddd; 44 | background: #e9e9e9; 45 | color: #333333; 46 | font-weight: bold; 47 | } 48 | .ui-widget-header a { 49 | color: #333333; 50 | } 51 | 52 | /* Interaction states 53 | ----------------------------------*/ 54 | .ui-state-default, 55 | .ui-widget-content .ui-state-default, 56 | .ui-widget-header .ui-state-default, 57 | .ui-button, 58 | 59 | /* We use html here because we need a greater specificity to make sure disabled 60 | works properly when clicked or hovered */ 61 | html .ui-button.ui-state-disabled:hover, 62 | html .ui-button.ui-state-disabled:active { 63 | border: 1px solid #c5c5c5; 64 | background: #f6f6f6; 65 | font-weight: normal; 66 | color: #454545; 67 | } 68 | .ui-state-default a, 69 | .ui-state-default a:link, 70 | .ui-state-default a:visited, 71 | a.ui-button, 72 | a:link.ui-button, 73 | a:visited.ui-button, 74 | .ui-button { 75 | color: #454545; 76 | text-decoration: none; 77 | } 78 | .ui-state-hover, 79 | .ui-widget-content .ui-state-hover, 80 | .ui-widget-header .ui-state-hover, 81 | .ui-state-focus, 82 | .ui-widget-content .ui-state-focus, 83 | .ui-widget-header .ui-state-focus, 84 | .ui-button:hover, 85 | .ui-button:focus { 86 | border: 1px solid #cccccc; 87 | background: #ededed; 88 | font-weight: normal; 89 | color: #2b2b2b; 90 | } 91 | .ui-state-hover a, 92 | .ui-state-hover a:hover, 93 | .ui-state-hover a:link, 94 | .ui-state-hover a:visited, 95 | .ui-state-focus a, 96 | .ui-state-focus a:hover, 97 | .ui-state-focus a:link, 98 | .ui-state-focus a:visited, 99 | a.ui-button:hover, 100 | a.ui-button:focus { 101 | color: #2b2b2b; 102 | text-decoration: none; 103 | } 104 | 105 | .ui-visual-focus { 106 | box-shadow: 0 0 3px 1px rgb(94, 158, 214); 107 | } 108 | .ui-state-active, 109 | .ui-widget-content .ui-state-active, 110 | .ui-widget-header .ui-state-active, 111 | a.ui-button:active, 112 | .ui-button:active, 113 | .ui-button.ui-state-active:hover { 114 | border: 1px solid #003eff; 115 | background: #007fff; 116 | font-weight: normal; 117 | color: #ffffff; 118 | } 119 | .ui-icon-background, 120 | .ui-state-active .ui-icon-background { 121 | border: #003eff; 122 | background-color: #ffffff; 123 | } 124 | .ui-state-active a, 125 | .ui-state-active a:link, 126 | .ui-state-active a:visited { 127 | color: #ffffff; 128 | text-decoration: none; 129 | } 130 | 131 | /* Interaction Cues 132 | ----------------------------------*/ 133 | .ui-state-highlight, 134 | .ui-widget-content .ui-state-highlight, 135 | .ui-widget-header .ui-state-highlight { 136 | border: 1px solid #dad55e; 137 | background: #fffa90; 138 | color: #777620; 139 | } 140 | .ui-state-checked { 141 | border: 1px solid #dad55e; 142 | background: #fffa90; 143 | } 144 | .ui-state-highlight a, 145 | .ui-widget-content .ui-state-highlight a, 146 | .ui-widget-header .ui-state-highlight a { 147 | color: #777620; 148 | } 149 | .ui-state-error, 150 | .ui-widget-content .ui-state-error, 151 | .ui-widget-header .ui-state-error { 152 | border: 1px solid #f1a899; 153 | background: #fddfdf; 154 | color: #5f3f3f; 155 | } 156 | .ui-state-error a, 157 | .ui-widget-content .ui-state-error a, 158 | .ui-widget-header .ui-state-error a { 159 | color: #5f3f3f; 160 | } 161 | .ui-state-error-text, 162 | .ui-widget-content .ui-state-error-text, 163 | .ui-widget-header .ui-state-error-text { 164 | color: #5f3f3f; 165 | } 166 | .ui-priority-primary, 167 | .ui-widget-content .ui-priority-primary, 168 | .ui-widget-header .ui-priority-primary { 169 | font-weight: bold; 170 | } 171 | .ui-priority-secondary, 172 | .ui-widget-content .ui-priority-secondary, 173 | .ui-widget-header .ui-priority-secondary { 174 | opacity: .7; 175 | filter:Alpha(Opacity=70); /* support: IE8 */ 176 | font-weight: normal; 177 | } 178 | .ui-state-disabled, 179 | .ui-widget-content .ui-state-disabled, 180 | .ui-widget-header .ui-state-disabled { 181 | opacity: .35; 182 | filter:Alpha(Opacity=35); /* support: IE8 */ 183 | background-image: none; 184 | } 185 | .ui-state-disabled .ui-icon { 186 | filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */ 187 | } 188 | 189 | /* Icons 190 | ----------------------------------*/ 191 | 192 | /* states and images */ 193 | .ui-icon { 194 | width: 16px; 195 | height: 16px; 196 | } 197 | .ui-icon, 198 | .ui-widget-content .ui-icon { 199 | background-image: url("images/ui-icons_444444_256x240.png"); 200 | } 201 | .ui-widget-header .ui-icon { 202 | background-image: url("images/ui-icons_444444_256x240.png"); 203 | } 204 | .ui-state-hover .ui-icon, 205 | .ui-state-focus .ui-icon, 206 | .ui-button:hover .ui-icon, 207 | .ui-button:focus .ui-icon { 208 | background-image: url("images/ui-icons_555555_256x240.png"); 209 | } 210 | .ui-state-active .ui-icon, 211 | .ui-button:active .ui-icon { 212 | background-image: url("images/ui-icons_ffffff_256x240.png"); 213 | } 214 | .ui-state-highlight .ui-icon, 215 | .ui-button .ui-state-highlight.ui-icon { 216 | background-image: url("images/ui-icons_777620_256x240.png"); 217 | } 218 | .ui-state-error .ui-icon, 219 | .ui-state-error-text .ui-icon { 220 | background-image: url("images/ui-icons_cc0000_256x240.png"); 221 | } 222 | .ui-button .ui-icon { 223 | background-image: url("images/ui-icons_777777_256x240.png"); 224 | } 225 | 226 | /* positioning */ 227 | .ui-icon-blank { background-position: 16px 16px; } 228 | .ui-icon-caret-1-n { background-position: 0 0; } 229 | .ui-icon-caret-1-ne { background-position: -16px 0; } 230 | .ui-icon-caret-1-e { background-position: -32px 0; } 231 | .ui-icon-caret-1-se { background-position: -48px 0; } 232 | .ui-icon-caret-1-s { background-position: -65px 0; } 233 | .ui-icon-caret-1-sw { background-position: -80px 0; } 234 | .ui-icon-caret-1-w { background-position: -96px 0; } 235 | .ui-icon-caret-1-nw { background-position: -112px 0; } 236 | .ui-icon-caret-2-n-s { background-position: -128px 0; } 237 | .ui-icon-caret-2-e-w { background-position: -144px 0; } 238 | .ui-icon-triangle-1-n { background-position: 0 -16px; } 239 | .ui-icon-triangle-1-ne { background-position: -16px -16px; } 240 | .ui-icon-triangle-1-e { background-position: -32px -16px; } 241 | .ui-icon-triangle-1-se { background-position: -48px -16px; } 242 | .ui-icon-triangle-1-s { background-position: -65px -16px; } 243 | .ui-icon-triangle-1-sw { background-position: -80px -16px; } 244 | .ui-icon-triangle-1-w { background-position: -96px -16px; } 245 | .ui-icon-triangle-1-nw { background-position: -112px -16px; } 246 | .ui-icon-triangle-2-n-s { background-position: -128px -16px; } 247 | .ui-icon-triangle-2-e-w { background-position: -144px -16px; } 248 | .ui-icon-arrow-1-n { background-position: 0 -32px; } 249 | .ui-icon-arrow-1-ne { background-position: -16px -32px; } 250 | .ui-icon-arrow-1-e { background-position: -32px -32px; } 251 | .ui-icon-arrow-1-se { background-position: -48px -32px; } 252 | .ui-icon-arrow-1-s { background-position: -65px -32px; } 253 | .ui-icon-arrow-1-sw { background-position: -80px -32px; } 254 | .ui-icon-arrow-1-w { background-position: -96px -32px; } 255 | .ui-icon-arrow-1-nw { background-position: -112px -32px; } 256 | .ui-icon-arrow-2-n-s { background-position: -128px -32px; } 257 | .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } 258 | .ui-icon-arrow-2-e-w { background-position: -160px -32px; } 259 | .ui-icon-arrow-2-se-nw { background-position: -176px -32px; } 260 | .ui-icon-arrowstop-1-n { background-position: -192px -32px; } 261 | .ui-icon-arrowstop-1-e { background-position: -208px -32px; } 262 | .ui-icon-arrowstop-1-s { background-position: -224px -32px; } 263 | .ui-icon-arrowstop-1-w { background-position: -240px -32px; } 264 | .ui-icon-arrowthick-1-n { background-position: 1px -48px; } 265 | .ui-icon-arrowthick-1-ne { background-position: -16px -48px; } 266 | .ui-icon-arrowthick-1-e { background-position: -32px -48px; } 267 | .ui-icon-arrowthick-1-se { background-position: -48px -48px; } 268 | .ui-icon-arrowthick-1-s { background-position: -64px -48px; } 269 | .ui-icon-arrowthick-1-sw { background-position: -80px -48px; } 270 | .ui-icon-arrowthick-1-w { background-position: -96px -48px; } 271 | .ui-icon-arrowthick-1-nw { background-position: -112px -48px; } 272 | .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } 273 | .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } 274 | .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } 275 | .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } 276 | .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } 277 | .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } 278 | .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } 279 | .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } 280 | .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } 281 | .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } 282 | .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } 283 | .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } 284 | .ui-icon-arrowreturn-1-w { background-position: -64px -64px; } 285 | .ui-icon-arrowreturn-1-n { background-position: -80px -64px; } 286 | .ui-icon-arrowreturn-1-e { background-position: -96px -64px; } 287 | .ui-icon-arrowreturn-1-s { background-position: -112px -64px; } 288 | .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } 289 | .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } 290 | .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } 291 | .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } 292 | .ui-icon-arrow-4 { background-position: 0 -80px; } 293 | .ui-icon-arrow-4-diag { background-position: -16px -80px; } 294 | .ui-icon-extlink { background-position: -32px -80px; } 295 | .ui-icon-newwin { background-position: -48px -80px; } 296 | .ui-icon-refresh { background-position: -64px -80px; } 297 | .ui-icon-shuffle { background-position: -80px -80px; } 298 | .ui-icon-transfer-e-w { background-position: -96px -80px; } 299 | .ui-icon-transferthick-e-w { background-position: -112px -80px; } 300 | .ui-icon-folder-collapsed { background-position: 0 -96px; } 301 | .ui-icon-folder-open { background-position: -16px -96px; } 302 | .ui-icon-document { background-position: -32px -96px; } 303 | .ui-icon-document-b { background-position: -48px -96px; } 304 | .ui-icon-note { background-position: -64px -96px; } 305 | .ui-icon-mail-closed { background-position: -80px -96px; } 306 | .ui-icon-mail-open { background-position: -96px -96px; } 307 | .ui-icon-suitcase { background-position: -112px -96px; } 308 | .ui-icon-comment { background-position: -128px -96px; } 309 | .ui-icon-person { background-position: -144px -96px; } 310 | .ui-icon-print { background-position: -160px -96px; } 311 | .ui-icon-trash { background-position: -176px -96px; } 312 | .ui-icon-locked { background-position: -192px -96px; } 313 | .ui-icon-unlocked { background-position: -208px -96px; } 314 | .ui-icon-bookmark { background-position: -224px -96px; } 315 | .ui-icon-tag { background-position: -240px -96px; } 316 | .ui-icon-home { background-position: 0 -112px; } 317 | .ui-icon-flag { background-position: -16px -112px; } 318 | .ui-icon-calendar { background-position: -32px -112px; } 319 | .ui-icon-cart { background-position: -48px -112px; } 320 | .ui-icon-pencil { background-position: -64px -112px; } 321 | .ui-icon-clock { background-position: -80px -112px; } 322 | .ui-icon-disk { background-position: -96px -112px; } 323 | .ui-icon-calculator { background-position: -112px -112px; } 324 | .ui-icon-zoomin { background-position: -128px -112px; } 325 | .ui-icon-zoomout { background-position: -144px -112px; } 326 | .ui-icon-search { background-position: -160px -112px; } 327 | .ui-icon-wrench { background-position: -176px -112px; } 328 | .ui-icon-gear { background-position: -192px -112px; } 329 | .ui-icon-heart { background-position: -208px -112px; } 330 | .ui-icon-star { background-position: -224px -112px; } 331 | .ui-icon-link { background-position: -240px -112px; } 332 | .ui-icon-cancel { background-position: 0 -128px; } 333 | .ui-icon-plus { background-position: -16px -128px; } 334 | .ui-icon-plusthick { background-position: -32px -128px; } 335 | .ui-icon-minus { background-position: -48px -128px; } 336 | .ui-icon-minusthick { background-position: -64px -128px; } 337 | .ui-icon-close { background-position: -80px -128px; } 338 | .ui-icon-closethick { background-position: -96px -128px; } 339 | .ui-icon-key { background-position: -112px -128px; } 340 | .ui-icon-lightbulb { background-position: -128px -128px; } 341 | .ui-icon-scissors { background-position: -144px -128px; } 342 | .ui-icon-clipboard { background-position: -160px -128px; } 343 | .ui-icon-copy { background-position: -176px -128px; } 344 | .ui-icon-contact { background-position: -192px -128px; } 345 | .ui-icon-image { background-position: -208px -128px; } 346 | .ui-icon-video { background-position: -224px -128px; } 347 | .ui-icon-script { background-position: -240px -128px; } 348 | .ui-icon-alert { background-position: 0 -144px; } 349 | .ui-icon-info { background-position: -16px -144px; } 350 | .ui-icon-notice { background-position: -32px -144px; } 351 | .ui-icon-help { background-position: -48px -144px; } 352 | .ui-icon-check { background-position: -64px -144px; } 353 | .ui-icon-bullet { background-position: -80px -144px; } 354 | .ui-icon-radio-on { background-position: -96px -144px; } 355 | .ui-icon-radio-off { background-position: -112px -144px; } 356 | .ui-icon-pin-w { background-position: -128px -144px; } 357 | .ui-icon-pin-s { background-position: -144px -144px; } 358 | .ui-icon-play { background-position: 0 -160px; } 359 | .ui-icon-pause { background-position: -16px -160px; } 360 | .ui-icon-seek-next { background-position: -32px -160px; } 361 | .ui-icon-seek-prev { background-position: -48px -160px; } 362 | .ui-icon-seek-end { background-position: -64px -160px; } 363 | .ui-icon-seek-start { background-position: -80px -160px; } 364 | /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ 365 | .ui-icon-seek-first { background-position: -80px -160px; } 366 | .ui-icon-stop { background-position: -96px -160px; } 367 | .ui-icon-eject { background-position: -112px -160px; } 368 | .ui-icon-volume-off { background-position: -128px -160px; } 369 | .ui-icon-volume-on { background-position: -144px -160px; } 370 | .ui-icon-power { background-position: 0 -176px; } 371 | .ui-icon-signal-diag { background-position: -16px -176px; } 372 | .ui-icon-signal { background-position: -32px -176px; } 373 | .ui-icon-battery-0 { background-position: -48px -176px; } 374 | .ui-icon-battery-1 { background-position: -64px -176px; } 375 | .ui-icon-battery-2 { background-position: -80px -176px; } 376 | .ui-icon-battery-3 { background-position: -96px -176px; } 377 | .ui-icon-circle-plus { background-position: 0 -192px; } 378 | .ui-icon-circle-minus { background-position: -16px -192px; } 379 | .ui-icon-circle-close { background-position: -32px -192px; } 380 | .ui-icon-circle-triangle-e { background-position: -48px -192px; } 381 | .ui-icon-circle-triangle-s { background-position: -64px -192px; } 382 | .ui-icon-circle-triangle-w { background-position: -80px -192px; } 383 | .ui-icon-circle-triangle-n { background-position: -96px -192px; } 384 | .ui-icon-circle-arrow-e { background-position: -112px -192px; } 385 | .ui-icon-circle-arrow-s { background-position: -128px -192px; } 386 | .ui-icon-circle-arrow-w { background-position: -144px -192px; } 387 | .ui-icon-circle-arrow-n { background-position: -160px -192px; } 388 | .ui-icon-circle-zoomin { background-position: -176px -192px; } 389 | .ui-icon-circle-zoomout { background-position: -192px -192px; } 390 | .ui-icon-circle-check { background-position: -208px -192px; } 391 | .ui-icon-circlesmall-plus { background-position: 0 -208px; } 392 | .ui-icon-circlesmall-minus { background-position: -16px -208px; } 393 | .ui-icon-circlesmall-close { background-position: -32px -208px; } 394 | .ui-icon-squaresmall-plus { background-position: -48px -208px; } 395 | .ui-icon-squaresmall-minus { background-position: -64px -208px; } 396 | .ui-icon-squaresmall-close { background-position: -80px -208px; } 397 | .ui-icon-grip-dotted-vertical { background-position: 0 -224px; } 398 | .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } 399 | .ui-icon-grip-solid-vertical { background-position: -32px -224px; } 400 | .ui-icon-grip-solid-horizontal { background-position: -48px -224px; } 401 | .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } 402 | .ui-icon-grip-diagonal-se { background-position: -80px -224px; } 403 | 404 | 405 | /* Misc visuals 406 | ----------------------------------*/ 407 | 408 | /* Corner radius */ 409 | .ui-corner-all, 410 | .ui-corner-top, 411 | .ui-corner-left, 412 | .ui-corner-tl { 413 | border-top-left-radius: 3px; 414 | } 415 | .ui-corner-all, 416 | .ui-corner-top, 417 | .ui-corner-right, 418 | .ui-corner-tr { 419 | border-top-right-radius: 3px; 420 | } 421 | .ui-corner-all, 422 | .ui-corner-bottom, 423 | .ui-corner-left, 424 | .ui-corner-bl { 425 | border-bottom-left-radius: 3px; 426 | } 427 | .ui-corner-all, 428 | .ui-corner-bottom, 429 | .ui-corner-right, 430 | .ui-corner-br { 431 | border-bottom-right-radius: 3px; 432 | } 433 | 434 | /* Overlays */ 435 | .ui-widget-overlay { 436 | background: #aaaaaa; 437 | opacity: .003; 438 | filter: Alpha(Opacity=.3); /* support: IE8 */ 439 | } 440 | .ui-widget-shadow { 441 | -webkit-box-shadow: 0px 0px 5px #666666; 442 | box-shadow: 0px 0px 5px #666666; 443 | } 444 | -------------------------------------------------------------------------------- /src/lib/jquery-ui-1.12.1/jquery-ui.theme.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.12.1 - 2016-09-14 2 | * http://jqueryui.com 3 | * Copyright jQuery Foundation and other contributors; Licensed MIT */ 4 | 5 | .ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #c5c5c5}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:bold}.ui-widget-header a{color:#333}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:normal;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #ccc;background:#ededed;font-weight:normal;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#2b2b2b;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #003eff;background:#007fff;font-weight:normal;color:#fff}.ui-icon-background,.ui-state-active .ui-icon-background{border:#003eff;background-color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-checked{border:1px solid #dad55e;background:#fffa90}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon{background-image:url("images/ui-icons_555555_256x240.png")}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_777620_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cc0000_256x240.png")}.ui-button .ui-icon{background-image:url("images/ui-icons_777777_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:.003;filter:Alpha(Opacity=.3)}.ui-widget-shadow{-webkit-box-shadow:0 0 5px #666;box-shadow:0 0 5px #666} -------------------------------------------------------------------------------- /src/lib/jquery-ui-1.12.1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-ui", 3 | "title": "jQuery UI", 4 | "description": "A curated set of user interface interactions, effects, widgets, and themes built on top of the jQuery JavaScript Library.", 5 | "version": "1.12.1", 6 | "homepage": "http://jqueryui.com", 7 | "author": { 8 | "name": "jQuery Foundation and other contributors", 9 | "url": "https://github.com/jquery/jquery-ui/blob/1.12.1/AUTHORS.txt" 10 | }, 11 | "main": "ui/widget.js", 12 | "maintainers": [ 13 | { 14 | "name": "Scott González", 15 | "email": "scott.gonzalez@gmail.com", 16 | "url": "http://scottgonzalez.com" 17 | }, 18 | { 19 | "name": "Jörn Zaefferer", 20 | "email": "joern.zaefferer@gmail.com", 21 | "url": "http://bassistance.de" 22 | }, 23 | { 24 | "name": "Mike Sherov", 25 | "email": "mike.sherov@gmail.com", 26 | "url": "http://mike.sherov.com" 27 | }, 28 | { 29 | "name": "TJ VanToll", 30 | "email": "tj.vantoll@gmail.com", 31 | "url": "http://tjvantoll.com" 32 | }, 33 | { 34 | "name": "Felix Nagel", 35 | "email": "info@felixnagel.com", 36 | "url": "http://www.felixnagel.com" 37 | }, 38 | { 39 | "name": "Alex Schmitz", 40 | "email": "arschmitz@gmail.com", 41 | "url": "https://github.com/arschmitz" 42 | } 43 | ], 44 | "repository": { 45 | "type": "git", 46 | "url": "git://github.com/jquery/jquery-ui.git" 47 | }, 48 | "bugs": "https://bugs.jqueryui.com/", 49 | "license": "MIT", 50 | "scripts": { 51 | "test": "grunt" 52 | }, 53 | "dependencies": {}, 54 | "devDependencies": { 55 | "commitplease": "2.3.0", 56 | "grunt": "0.4.5", 57 | "grunt-bowercopy": "1.2.4", 58 | "grunt-cli": "0.1.13", 59 | "grunt-compare-size": "0.4.0", 60 | "grunt-contrib-concat": "0.5.1", 61 | "grunt-contrib-csslint": "0.5.0", 62 | "grunt-contrib-jshint": "0.12.0", 63 | "grunt-contrib-qunit": "1.0.1", 64 | "grunt-contrib-requirejs": "0.4.4", 65 | "grunt-contrib-uglify": "0.11.1", 66 | "grunt-git-authors": "3.1.0", 67 | "grunt-html": "6.0.0", 68 | "grunt-jscs": "2.1.0", 69 | "load-grunt-tasks": "3.4.0", 70 | "rimraf": "2.5.1", 71 | "testswarm": "1.1.0" 72 | }, 73 | "keywords": [] 74 | } 75 | -------------------------------------------------------------------------------- /src/lib/socicon/Read Me.txt: -------------------------------------------------------------------------------- 1 | Open *demo.html* to see a list of all the glyphs in your font along with their codes/ligatures. 2 | 3 | To use the generated font in desktop programs, you can install the TTF font. In order to copy the character associated with each icon, refer to the text box at the bottom right corner of each glyph in demo.html. The character inside this text box may be invisible; but it can still be copied. See this guide for more info: https://icomoon.io/#docs/local-fonts 4 | 5 | You won't need any of the files located under the *demo-files* directory when including the generated font in your own projects. 6 | 7 | You can import *selection.json* back to the IcoMoon app using the *Import Icons* button (or via Main Menu → Manage Projects) to retrieve your icon selection. 8 | -------------------------------------------------------------------------------- /src/lib/socicon/demo-files/demo.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 0; 3 | margin: 0; 4 | font-family: sans-serif; 5 | font-size: 1em; 6 | line-height: 1.5; 7 | color: #555; 8 | background: #fff; 9 | } 10 | h1 { 11 | font-size: 1.5em; 12 | font-weight: normal; 13 | } 14 | small { 15 | font-size: .66666667em; 16 | } 17 | a { 18 | color: #e74c3c; 19 | text-decoration: none; 20 | } 21 | a:hover, a:focus { 22 | box-shadow: 0 1px #e74c3c; 23 | } 24 | .bshadow0, input { 25 | box-shadow: inset 0 -2px #e7e7e7; 26 | } 27 | input:hover { 28 | box-shadow: inset 0 -2px #ccc; 29 | } 30 | input, fieldset { 31 | font-family: sans-serif; 32 | font-size: 1em; 33 | margin: 0; 34 | padding: 0; 35 | border: 0; 36 | } 37 | input { 38 | color: inherit; 39 | line-height: 1.5; 40 | height: 1.5em; 41 | padding: .25em 0; 42 | } 43 | input:focus { 44 | outline: none; 45 | box-shadow: inset 0 -2px #449fdb; 46 | } 47 | .glyph { 48 | font-size: 16px; 49 | width: 15em; 50 | padding-bottom: 1em; 51 | margin-right: 4em; 52 | margin-bottom: 1em; 53 | float: left; 54 | overflow: hidden; 55 | } 56 | .liga { 57 | width: 80%; 58 | width: calc(100% - 2.5em); 59 | } 60 | .talign-right { 61 | text-align: right; 62 | } 63 | .talign-center { 64 | text-align: center; 65 | } 66 | .bgc1 { 67 | background: #f1f1f1; 68 | } 69 | .fgc1 { 70 | color: #999; 71 | } 72 | .fgc0 { 73 | color: #000; 74 | } 75 | p { 76 | margin-top: 1em; 77 | margin-bottom: 1em; 78 | } 79 | .mvm { 80 | margin-top: .75em; 81 | margin-bottom: .75em; 82 | } 83 | .mtn { 84 | margin-top: 0; 85 | } 86 | .mtl, .mal { 87 | margin-top: 1.5em; 88 | } 89 | .mbl, .mal { 90 | margin-bottom: 1.5em; 91 | } 92 | .mal, .mhl { 93 | margin-left: 1.5em; 94 | margin-right: 1.5em; 95 | } 96 | .mhmm { 97 | margin-left: 1em; 98 | margin-right: 1em; 99 | } 100 | .mls { 101 | margin-left: .25em; 102 | } 103 | .ptl { 104 | padding-top: 1.5em; 105 | } 106 | .pbs, .pvs { 107 | padding-bottom: .25em; 108 | } 109 | .pvs, .pts { 110 | padding-top: .25em; 111 | } 112 | .unit { 113 | float: left; 114 | } 115 | .unitRight { 116 | float: right; 117 | } 118 | .size1of2 { 119 | width: 50%; 120 | } 121 | .size1of1 { 122 | width: 100%; 123 | } 124 | .clearfix:before, .clearfix:after { 125 | content: " "; 126 | display: table; 127 | } 128 | .clearfix:after { 129 | clear: both; 130 | } 131 | .hidden-true { 132 | display: none; 133 | } 134 | .textbox0 { 135 | width: 3em; 136 | background: #f1f1f1; 137 | padding: .25em .5em; 138 | line-height: 1.5; 139 | height: 1.5em; 140 | } 141 | #testDrive { 142 | display: block; 143 | padding-top: 24px; 144 | line-height: 1.5; 145 | } 146 | .fs0 { 147 | font-size: 16px; 148 | } 149 | .fs1 { 150 | font-size: 32px; 151 | } 152 | 153 | -------------------------------------------------------------------------------- /src/lib/socicon/demo-files/demo.js: -------------------------------------------------------------------------------- 1 | if (!('boxShadow' in document.body.style)) { 2 | document.body.setAttribute('class', 'noBoxShadow'); 3 | } 4 | 5 | document.body.addEventListener("click", function(e) { 6 | var target = e.target; 7 | if (target.tagName === "INPUT" && 8 | target.getAttribute('class').indexOf('liga') === -1) { 9 | target.select(); 10 | } 11 | }); 12 | 13 | (function() { 14 | var fontSize = document.getElementById('fontSize'), 15 | testDrive = document.getElementById('testDrive'), 16 | testText = document.getElementById('testText'); 17 | function updateTest() { 18 | testDrive.innerHTML = testText.value || String.fromCharCode(160); 19 | if (window.icomoonLiga) { 20 | window.icomoonLiga(testDrive); 21 | } 22 | } 23 | function updateSize() { 24 | testDrive.style.fontSize = fontSize.value + 'px'; 25 | } 26 | fontSize.addEventListener('change', updateSize, false); 27 | testText.addEventListener('input', updateTest, false); 28 | testText.addEventListener('change', updateTest, false); 29 | updateSize(); 30 | }()); 31 | -------------------------------------------------------------------------------- /src/lib/socicon/fonts/Socicon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/lib/socicon/fonts/Socicon.eot -------------------------------------------------------------------------------- /src/lib/socicon/fonts/Socicon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/lib/socicon/fonts/Socicon.ttf -------------------------------------------------------------------------------- /src/lib/socicon/fonts/Socicon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/lib/socicon/fonts/Socicon.woff -------------------------------------------------------------------------------- /src/lib/socicon/fonts/Socicon.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeanArchibald/scale-workshop/f84432ca94e3832c39999d108df4aa16afedf0d2/src/lib/socicon/fonts/Socicon.woff2 -------------------------------------------------------------------------------- /src/lib/socicon/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Socicon'; 3 | src: url('fonts/Socicon.eot?484r1f'); 4 | src: url('fonts/Socicon.eot?484r1f#iefix') format('embedded-opentype'), 5 | url('fonts/Socicon.woff2?484r1f') format('woff2'), 6 | url('fonts/Socicon.ttf?484r1f') format('truetype'), 7 | url('fonts/Socicon.woff?484r1f') format('woff'), 8 | url('fonts/Socicon.svg?484r1f#Socicon') format('svg'); 9 | font-weight: normal; 10 | font-style: normal; 11 | } 12 | 13 | [class^="socicon-"], [class*=" socicon-"] { 14 | /* use !important to prevent issues with browser extensions that change fonts */ 15 | font-family: 'Socicon' !important; 16 | speak: none; 17 | font-style: normal; 18 | font-weight: normal; 19 | font-variant: normal; 20 | text-transform: none; 21 | line-height: 1; 22 | 23 | /* Better Font Rendering =========== */ 24 | -webkit-font-smoothing: antialiased; 25 | -moz-osx-font-smoothing: grayscale; 26 | } 27 | 28 | .socicon-internet:before { 29 | content: "\e957"; 30 | } 31 | .socicon-moddb:before { 32 | content: "\e94b"; 33 | } 34 | .socicon-indiedb:before { 35 | content: "\e94c"; 36 | } 37 | .socicon-traxsource:before { 38 | content: "\e94d"; 39 | } 40 | .socicon-gamefor:before { 41 | content: "\e94e"; 42 | } 43 | .socicon-pixiv:before { 44 | content: "\e94f"; 45 | } 46 | .socicon-myanimelist:before { 47 | content: "\e950"; 48 | } 49 | .socicon-blackberry:before { 50 | content: "\e951"; 51 | } 52 | .socicon-wickr:before { 53 | content: "\e952"; 54 | } 55 | .socicon-spip:before { 56 | content: "\e953"; 57 | } 58 | .socicon-napster:before { 59 | content: "\e954"; 60 | } 61 | .socicon-beatport:before { 62 | content: "\e955"; 63 | } 64 | .socicon-hackerone:before { 65 | content: "\e956"; 66 | } 67 | .socicon-hackernews:before { 68 | content: "\e946"; 69 | } 70 | .socicon-smashwords:before { 71 | content: "\e947"; 72 | } 73 | .socicon-kobo:before { 74 | content: "\e948"; 75 | } 76 | .socicon-bookbub:before { 77 | content: "\e949"; 78 | } 79 | .socicon-mailru:before { 80 | content: "\e94a"; 81 | } 82 | .socicon-gitlab:before { 83 | content: "\e945"; 84 | } 85 | .socicon-instructables:before { 86 | content: "\e944"; 87 | } 88 | .socicon-portfolio:before { 89 | content: "\e943"; 90 | } 91 | .socicon-codered:before { 92 | content: "\e940"; 93 | } 94 | .socicon-origin:before { 95 | content: "\e941"; 96 | } 97 | .socicon-nextdoor:before { 98 | content: "\e942"; 99 | } 100 | .socicon-udemy:before { 101 | content: "\e93f"; 102 | } 103 | .socicon-livemaster:before { 104 | content: "\e93e"; 105 | } 106 | .socicon-crunchbase:before { 107 | content: "\e93b"; 108 | } 109 | .socicon-homefy:before { 110 | content: "\e93c"; 111 | } 112 | .socicon-calendly:before { 113 | content: "\e93d"; 114 | } 115 | .socicon-realtor:before { 116 | content: "\e90f"; 117 | } 118 | .socicon-tidal:before { 119 | content: "\e910"; 120 | } 121 | .socicon-qobuz:before { 122 | content: "\e911"; 123 | } 124 | .socicon-natgeo:before { 125 | content: "\e912"; 126 | } 127 | .socicon-mastodon:before { 128 | content: "\e913"; 129 | } 130 | .socicon-unsplash:before { 131 | content: "\e914"; 132 | } 133 | .socicon-homeadvisor:before { 134 | content: "\e915"; 135 | } 136 | .socicon-angieslist:before { 137 | content: "\e916"; 138 | } 139 | .socicon-codepen:before { 140 | content: "\e917"; 141 | } 142 | .socicon-slack:before { 143 | content: "\e918"; 144 | } 145 | .socicon-openaigym:before { 146 | content: "\e919"; 147 | } 148 | .socicon-logmein:before { 149 | content: "\e91a"; 150 | } 151 | .socicon-fiverr:before { 152 | content: "\e91b"; 153 | } 154 | .socicon-gotomeeting:before { 155 | content: "\e91c"; 156 | } 157 | .socicon-aliexpress:before { 158 | content: "\e91d"; 159 | } 160 | .socicon-guru:before { 161 | content: "\e91e"; 162 | } 163 | .socicon-appstore:before { 164 | content: "\e91f"; 165 | } 166 | .socicon-homes:before { 167 | content: "\e920"; 168 | } 169 | .socicon-zoom:before { 170 | content: "\e921"; 171 | } 172 | .socicon-alibaba:before { 173 | content: "\e922"; 174 | } 175 | .socicon-craigslist:before { 176 | content: "\e923"; 177 | } 178 | .socicon-wix:before { 179 | content: "\e924"; 180 | } 181 | .socicon-redfin:before { 182 | content: "\e925"; 183 | } 184 | .socicon-googlecalendar:before { 185 | content: "\e926"; 186 | } 187 | .socicon-shopify:before { 188 | content: "\e927"; 189 | } 190 | .socicon-freelancer:before { 191 | content: "\e928"; 192 | } 193 | .socicon-seedrs:before { 194 | content: "\e929"; 195 | } 196 | .socicon-bing:before { 197 | content: "\e92a"; 198 | } 199 | .socicon-doodle:before { 200 | content: "\e92b"; 201 | } 202 | .socicon-bonanza:before { 203 | content: "\e92c"; 204 | } 205 | .socicon-squarespace:before { 206 | content: "\e92d"; 207 | } 208 | .socicon-toptal:before { 209 | content: "\e92e"; 210 | } 211 | .socicon-gust:before { 212 | content: "\e92f"; 213 | } 214 | .socicon-ask:before { 215 | content: "\e930"; 216 | } 217 | .socicon-trulia:before { 218 | content: "\e931"; 219 | } 220 | .socicon-loomly:before { 221 | content: "\e932"; 222 | } 223 | .socicon-ghost:before { 224 | content: "\e933"; 225 | } 226 | .socicon-upwork:before { 227 | content: "\e934"; 228 | } 229 | .socicon-fundable:before { 230 | content: "\e935"; 231 | } 232 | .socicon-booking:before { 233 | content: "\e936"; 234 | } 235 | .socicon-googlemaps:before { 236 | content: "\e937"; 237 | } 238 | .socicon-zillow:before { 239 | content: "\e938"; 240 | } 241 | .socicon-niconico:before { 242 | content: "\e939"; 243 | } 244 | .socicon-toneden:before { 245 | content: "\e93a"; 246 | } 247 | .socicon-augment:before { 248 | content: "\e908"; 249 | } 250 | .socicon-bitbucket:before { 251 | content: "\e909"; 252 | } 253 | .socicon-fyuse:before { 254 | content: "\e90a"; 255 | } 256 | .socicon-yt-gaming:before { 257 | content: "\e90b"; 258 | } 259 | .socicon-sketchfab:before { 260 | content: "\e90c"; 261 | } 262 | .socicon-mobcrush:before { 263 | content: "\e90d"; 264 | } 265 | .socicon-microsoft:before { 266 | content: "\e90e"; 267 | } 268 | .socicon-pandora:before { 269 | content: "\e907"; 270 | } 271 | .socicon-messenger:before { 272 | content: "\e906"; 273 | } 274 | .socicon-gamewisp:before { 275 | content: "\e905"; 276 | } 277 | .socicon-bloglovin:before { 278 | content: "\e904"; 279 | } 280 | .socicon-tunein:before { 281 | content: "\e903"; 282 | } 283 | .socicon-gamejolt:before { 284 | content: "\e901"; 285 | } 286 | .socicon-trello:before { 287 | content: "\e902"; 288 | } 289 | .socicon-spreadshirt:before { 290 | content: "\e900"; 291 | } 292 | .socicon-500px:before { 293 | content: "\e000"; 294 | } 295 | .socicon-8tracks:before { 296 | content: "\e001"; 297 | } 298 | .socicon-airbnb:before { 299 | content: "\e002"; 300 | } 301 | .socicon-alliance:before { 302 | content: "\e003"; 303 | } 304 | .socicon-amazon:before { 305 | content: "\e004"; 306 | } 307 | .socicon-amplement:before { 308 | content: "\e005"; 309 | } 310 | .socicon-android:before { 311 | content: "\e006"; 312 | } 313 | .socicon-angellist:before { 314 | content: "\e007"; 315 | } 316 | .socicon-apple:before { 317 | content: "\e008"; 318 | } 319 | .socicon-appnet:before { 320 | content: "\e009"; 321 | } 322 | .socicon-baidu:before { 323 | content: "\e00a"; 324 | } 325 | .socicon-bandcamp:before { 326 | content: "\e00b"; 327 | } 328 | .socicon-battlenet:before { 329 | content: "\e00c"; 330 | } 331 | .socicon-mixer:before { 332 | content: "\e00d"; 333 | } 334 | .socicon-bebee:before { 335 | content: "\e00e"; 336 | } 337 | .socicon-bebo:before { 338 | content: "\e00f"; 339 | } 340 | .socicon-behance:before { 341 | content: "\e010"; 342 | } 343 | .socicon-blizzard:before { 344 | content: "\e011"; 345 | } 346 | .socicon-blogger:before { 347 | content: "\e012"; 348 | } 349 | .socicon-buffer:before { 350 | content: "\e013"; 351 | } 352 | .socicon-chrome:before { 353 | content: "\e014"; 354 | } 355 | .socicon-coderwall:before { 356 | content: "\e015"; 357 | } 358 | .socicon-curse:before { 359 | content: "\e016"; 360 | } 361 | .socicon-dailymotion:before { 362 | content: "\e017"; 363 | } 364 | .socicon-deezer:before { 365 | content: "\e018"; 366 | } 367 | .socicon-delicious:before { 368 | content: "\e019"; 369 | } 370 | .socicon-deviantart:before { 371 | content: "\e01a"; 372 | } 373 | .socicon-diablo:before { 374 | content: "\e01b"; 375 | } 376 | .socicon-digg:before { 377 | content: "\e01c"; 378 | } 379 | .socicon-discord:before { 380 | content: "\e01d"; 381 | } 382 | .socicon-disqus:before { 383 | content: "\e01e"; 384 | } 385 | .socicon-douban:before { 386 | content: "\e01f"; 387 | } 388 | .socicon-draugiem:before { 389 | content: "\e020"; 390 | } 391 | .socicon-dribbble:before { 392 | content: "\e021"; 393 | } 394 | .socicon-drupal:before { 395 | content: "\e022"; 396 | } 397 | .socicon-ebay:before { 398 | content: "\e023"; 399 | } 400 | .socicon-ello:before { 401 | content: "\e024"; 402 | } 403 | .socicon-endomodo:before { 404 | content: "\e025"; 405 | } 406 | .socicon-envato:before { 407 | content: "\e026"; 408 | } 409 | .socicon-etsy:before { 410 | content: "\e027"; 411 | } 412 | .socicon-facebook:before { 413 | content: "\e028"; 414 | } 415 | .socicon-feedburner:before { 416 | content: "\e029"; 417 | } 418 | .socicon-filmweb:before { 419 | content: "\e02a"; 420 | } 421 | .socicon-firefox:before { 422 | content: "\e02b"; 423 | } 424 | .socicon-flattr:before { 425 | content: "\e02c"; 426 | } 427 | .socicon-flickr:before { 428 | content: "\e02d"; 429 | } 430 | .socicon-formulr:before { 431 | content: "\e02e"; 432 | } 433 | .socicon-forrst:before { 434 | content: "\e02f"; 435 | } 436 | .socicon-foursquare:before { 437 | content: "\e030"; 438 | } 439 | .socicon-friendfeed:before { 440 | content: "\e031"; 441 | } 442 | .socicon-github:before { 443 | content: "\e032"; 444 | } 445 | .socicon-goodreads:before { 446 | content: "\e033"; 447 | } 448 | .socicon-google:before { 449 | content: "\e034"; 450 | } 451 | .socicon-googlescholar:before { 452 | content: "\e035"; 453 | } 454 | .socicon-googlegroups:before { 455 | content: "\e036"; 456 | } 457 | .socicon-googlephotos:before { 458 | content: "\e037"; 459 | } 460 | .socicon-googleplus:before { 461 | content: "\e038"; 462 | } 463 | .socicon-grooveshark:before { 464 | content: "\e039"; 465 | } 466 | .socicon-hackerrank:before { 467 | content: "\e03a"; 468 | } 469 | .socicon-hearthstone:before { 470 | content: "\e03b"; 471 | } 472 | .socicon-hellocoton:before { 473 | content: "\e03c"; 474 | } 475 | .socicon-heroes:before { 476 | content: "\e03d"; 477 | } 478 | .socicon-smashcast:before { 479 | content: "\e03e"; 480 | } 481 | .socicon-horde:before { 482 | content: "\e03f"; 483 | } 484 | .socicon-houzz:before { 485 | content: "\e040"; 486 | } 487 | .socicon-icq:before { 488 | content: "\e041"; 489 | } 490 | .socicon-identica:before { 491 | content: "\e042"; 492 | } 493 | .socicon-imdb:before { 494 | content: "\e043"; 495 | } 496 | .socicon-instagram:before { 497 | content: "\e044"; 498 | } 499 | .socicon-issuu:before { 500 | content: "\e045"; 501 | } 502 | .socicon-istock:before { 503 | content: "\e046"; 504 | } 505 | .socicon-itunes:before { 506 | content: "\e047"; 507 | } 508 | .socicon-keybase:before { 509 | content: "\e048"; 510 | } 511 | .socicon-lanyrd:before { 512 | content: "\e049"; 513 | } 514 | .socicon-lastfm:before { 515 | content: "\e04a"; 516 | } 517 | .socicon-line:before { 518 | content: "\e04b"; 519 | } 520 | .socicon-linkedin:before { 521 | content: "\e04c"; 522 | } 523 | .socicon-livejournal:before { 524 | content: "\e04d"; 525 | } 526 | .socicon-lyft:before { 527 | content: "\e04e"; 528 | } 529 | .socicon-macos:before { 530 | content: "\e04f"; 531 | } 532 | .socicon-mail:before { 533 | content: "\e050"; 534 | } 535 | .socicon-medium:before { 536 | content: "\e051"; 537 | } 538 | .socicon-meetup:before { 539 | content: "\e052"; 540 | } 541 | .socicon-mixcloud:before { 542 | content: "\e053"; 543 | } 544 | .socicon-modelmayhem:before { 545 | content: "\e054"; 546 | } 547 | .socicon-mumble:before { 548 | content: "\e055"; 549 | } 550 | .socicon-myspace:before { 551 | content: "\e056"; 552 | } 553 | .socicon-newsvine:before { 554 | content: "\e057"; 555 | } 556 | .socicon-nintendo:before { 557 | content: "\e058"; 558 | } 559 | .socicon-npm:before { 560 | content: "\e059"; 561 | } 562 | .socicon-odnoklassniki:before { 563 | content: "\e05a"; 564 | } 565 | .socicon-openid:before { 566 | content: "\e05b"; 567 | } 568 | .socicon-opera:before { 569 | content: "\e05c"; 570 | } 571 | .socicon-outlook:before { 572 | content: "\e05d"; 573 | } 574 | .socicon-overwatch:before { 575 | content: "\e05e"; 576 | } 577 | .socicon-patreon:before { 578 | content: "\e05f"; 579 | } 580 | .socicon-paypal:before { 581 | content: "\e060"; 582 | } 583 | .socicon-periscope:before { 584 | content: "\e061"; 585 | } 586 | .socicon-persona:before { 587 | content: "\e062"; 588 | } 589 | .socicon-pinterest:before { 590 | content: "\e063"; 591 | } 592 | .socicon-play:before { 593 | content: "\e064"; 594 | } 595 | .socicon-player:before { 596 | content: "\e065"; 597 | } 598 | .socicon-playstation:before { 599 | content: "\e066"; 600 | } 601 | .socicon-pocket:before { 602 | content: "\e067"; 603 | } 604 | .socicon-qq:before { 605 | content: "\e068"; 606 | } 607 | .socicon-quora:before { 608 | content: "\e069"; 609 | } 610 | .socicon-raidcall:before { 611 | content: "\e06a"; 612 | } 613 | .socicon-ravelry:before { 614 | content: "\e06b"; 615 | } 616 | .socicon-reddit:before { 617 | content: "\e06c"; 618 | } 619 | .socicon-renren:before { 620 | content: "\e06d"; 621 | } 622 | .socicon-researchgate:before { 623 | content: "\e06e"; 624 | } 625 | .socicon-residentadvisor:before { 626 | content: "\e06f"; 627 | } 628 | .socicon-reverbnation:before { 629 | content: "\e070"; 630 | } 631 | .socicon-rss:before { 632 | content: "\e071"; 633 | } 634 | .socicon-sharethis:before { 635 | content: "\e072"; 636 | } 637 | .socicon-skype:before { 638 | content: "\e073"; 639 | } 640 | .socicon-slideshare:before { 641 | content: "\e074"; 642 | } 643 | .socicon-smugmug:before { 644 | content: "\e075"; 645 | } 646 | .socicon-snapchat:before { 647 | content: "\e076"; 648 | } 649 | .socicon-songkick:before { 650 | content: "\e077"; 651 | } 652 | .socicon-soundcloud:before { 653 | content: "\e078"; 654 | } 655 | .socicon-spotify:before { 656 | content: "\e079"; 657 | } 658 | .socicon-stackexchange:before { 659 | content: "\e07a"; 660 | } 661 | .socicon-stackoverflow:before { 662 | content: "\e07b"; 663 | } 664 | .socicon-starcraft:before { 665 | content: "\e07c"; 666 | } 667 | .socicon-stayfriends:before { 668 | content: "\e07d"; 669 | } 670 | .socicon-steam:before { 671 | content: "\e07e"; 672 | } 673 | .socicon-storehouse:before { 674 | content: "\e07f"; 675 | } 676 | .socicon-strava:before { 677 | content: "\e080"; 678 | } 679 | .socicon-streamjar:before { 680 | content: "\e081"; 681 | } 682 | .socicon-stumbleupon:before { 683 | content: "\e082"; 684 | } 685 | .socicon-swarm:before { 686 | content: "\e083"; 687 | } 688 | .socicon-teamspeak:before { 689 | content: "\e084"; 690 | } 691 | .socicon-teamviewer:before { 692 | content: "\e085"; 693 | } 694 | .socicon-technorati:before { 695 | content: "\e086"; 696 | } 697 | .socicon-telegram:before { 698 | content: "\e087"; 699 | } 700 | .socicon-tripadvisor:before { 701 | content: "\e088"; 702 | } 703 | .socicon-tripit:before { 704 | content: "\e089"; 705 | } 706 | .socicon-triplej:before { 707 | content: "\e08a"; 708 | } 709 | .socicon-tumblr:before { 710 | content: "\e08b"; 711 | } 712 | .socicon-twitch:before { 713 | content: "\e08c"; 714 | } 715 | .socicon-twitter:before { 716 | content: "\e08d"; 717 | } 718 | .socicon-uber:before { 719 | content: "\e08e"; 720 | } 721 | .socicon-ventrilo:before { 722 | content: "\e08f"; 723 | } 724 | .socicon-viadeo:before { 725 | content: "\e090"; 726 | } 727 | .socicon-viber:before { 728 | content: "\e091"; 729 | } 730 | .socicon-viewbug:before { 731 | content: "\e092"; 732 | } 733 | .socicon-vimeo:before { 734 | content: "\e093"; 735 | } 736 | .socicon-vine:before { 737 | content: "\e094"; 738 | } 739 | .socicon-vkontakte:before { 740 | content: "\e095"; 741 | } 742 | .socicon-warcraft:before { 743 | content: "\e096"; 744 | } 745 | .socicon-wechat:before { 746 | content: "\e097"; 747 | } 748 | .socicon-weibo:before { 749 | content: "\e098"; 750 | } 751 | .socicon-whatsapp:before { 752 | content: "\e099"; 753 | } 754 | .socicon-wikipedia:before { 755 | content: "\e09a"; 756 | } 757 | .socicon-windows:before { 758 | content: "\e09b"; 759 | } 760 | .socicon-wordpress:before { 761 | content: "\e09c"; 762 | } 763 | .socicon-wykop:before { 764 | content: "\e09d"; 765 | } 766 | .socicon-xbox:before { 767 | content: "\e09e"; 768 | } 769 | .socicon-xing:before { 770 | content: "\e09f"; 771 | } 772 | .socicon-yahoo:before { 773 | content: "\e0a0"; 774 | } 775 | .socicon-yammer:before { 776 | content: "\e0a1"; 777 | } 778 | .socicon-yandex:before { 779 | content: "\e0a2"; 780 | } 781 | .socicon-yelp:before { 782 | content: "\e0a3"; 783 | } 784 | .socicon-younow:before { 785 | content: "\e0a4"; 786 | } 787 | .socicon-youtube:before { 788 | content: "\e0a5"; 789 | } 790 | .socicon-zapier:before { 791 | content: "\e0a6"; 792 | } 793 | .socicon-zerply:before { 794 | content: "\e0a7"; 795 | } 796 | .socicon-zomato:before { 797 | content: "\e0a8"; 798 | } 799 | .socicon-zynga:before { 800 | content: "\e0a9"; 801 | } 802 | -------------------------------------------------------------------------------- /src/lib/socicon/variables.less: -------------------------------------------------------------------------------- 1 | @icomoon-font-path: "fonts"; 2 | 3 | @socicon-internet: "\e957"; 4 | @socicon-moddb: "\e94b"; 5 | @socicon-indiedb: "\e94c"; 6 | @socicon-traxsource: "\e94d"; 7 | @socicon-gamefor: "\e94e"; 8 | @socicon-pixiv: "\e94f"; 9 | @socicon-myanimelist: "\e950"; 10 | @socicon-blackberry: "\e951"; 11 | @socicon-wickr: "\e952"; 12 | @socicon-spip: "\e953"; 13 | @socicon-napster: "\e954"; 14 | @socicon-beatport: "\e955"; 15 | @socicon-hackerone: "\e956"; 16 | @socicon-hackernews: "\e946"; 17 | @socicon-smashwords: "\e947"; 18 | @socicon-kobo: "\e948"; 19 | @socicon-bookbub: "\e949"; 20 | @socicon-mailru: "\e94a"; 21 | @socicon-gitlab: "\e945"; 22 | @socicon-instructables: "\e944"; 23 | @socicon-portfolio: "\e943"; 24 | @socicon-codered: "\e940"; 25 | @socicon-origin: "\e941"; 26 | @socicon-nextdoor: "\e942"; 27 | @socicon-udemy: "\e93f"; 28 | @socicon-livemaster: "\e93e"; 29 | @socicon-crunchbase: "\e93b"; 30 | @socicon-homefy: "\e93c"; 31 | @socicon-calendly: "\e93d"; 32 | @socicon-realtor: "\e90f"; 33 | @socicon-tidal: "\e910"; 34 | @socicon-qobuz: "\e911"; 35 | @socicon-natgeo: "\e912"; 36 | @socicon-mastodon: "\e913"; 37 | @socicon-unsplash: "\e914"; 38 | @socicon-homeadvisor: "\e915"; 39 | @socicon-angieslist: "\e916"; 40 | @socicon-codepen: "\e917"; 41 | @socicon-slack: "\e918"; 42 | @socicon-openaigym: "\e919"; 43 | @socicon-logmein: "\e91a"; 44 | @socicon-fiverr: "\e91b"; 45 | @socicon-gotomeeting: "\e91c"; 46 | @socicon-aliexpress: "\e91d"; 47 | @socicon-guru: "\e91e"; 48 | @socicon-appstore: "\e91f"; 49 | @socicon-homes: "\e920"; 50 | @socicon-zoom: "\e921"; 51 | @socicon-alibaba: "\e922"; 52 | @socicon-craigslist: "\e923"; 53 | @socicon-wix: "\e924"; 54 | @socicon-redfin: "\e925"; 55 | @socicon-googlecalendar: "\e926"; 56 | @socicon-shopify: "\e927"; 57 | @socicon-freelancer: "\e928"; 58 | @socicon-seedrs: "\e929"; 59 | @socicon-bing: "\e92a"; 60 | @socicon-doodle: "\e92b"; 61 | @socicon-bonanza: "\e92c"; 62 | @socicon-squarespace: "\e92d"; 63 | @socicon-toptal: "\e92e"; 64 | @socicon-gust: "\e92f"; 65 | @socicon-ask: "\e930"; 66 | @socicon-trulia: "\e931"; 67 | @socicon-loomly: "\e932"; 68 | @socicon-ghost: "\e933"; 69 | @socicon-upwork: "\e934"; 70 | @socicon-fundable: "\e935"; 71 | @socicon-booking: "\e936"; 72 | @socicon-googlemaps: "\e937"; 73 | @socicon-zillow: "\e938"; 74 | @socicon-niconico: "\e939"; 75 | @socicon-toneden: "\e93a"; 76 | @socicon-augment: "\e908"; 77 | @socicon-bitbucket: "\e909"; 78 | @socicon-fyuse: "\e90a"; 79 | @socicon-yt-gaming: "\e90b"; 80 | @socicon-sketchfab: "\e90c"; 81 | @socicon-mobcrush: "\e90d"; 82 | @socicon-microsoft: "\e90e"; 83 | @socicon-pandora: "\e907"; 84 | @socicon-messenger: "\e906"; 85 | @socicon-gamewisp: "\e905"; 86 | @socicon-bloglovin: "\e904"; 87 | @socicon-tunein: "\e903"; 88 | @socicon-gamejolt: "\e901"; 89 | @socicon-trello: "\e902"; 90 | @socicon-spreadshirt: "\e900"; 91 | @socicon-500px: "\e000"; 92 | @socicon-8tracks: "\e001"; 93 | @socicon-airbnb: "\e002"; 94 | @socicon-alliance: "\e003"; 95 | @socicon-amazon: "\e004"; 96 | @socicon-amplement: "\e005"; 97 | @socicon-android: "\e006"; 98 | @socicon-angellist: "\e007"; 99 | @socicon-apple: "\e008"; 100 | @socicon-appnet: "\e009"; 101 | @socicon-baidu: "\e00a"; 102 | @socicon-bandcamp: "\e00b"; 103 | @socicon-battlenet: "\e00c"; 104 | @socicon-mixer: "\e00d"; 105 | @socicon-bebee: "\e00e"; 106 | @socicon-bebo: "\e00f"; 107 | @socicon-behance: "\e010"; 108 | @socicon-blizzard: "\e011"; 109 | @socicon-blogger: "\e012"; 110 | @socicon-buffer: "\e013"; 111 | @socicon-chrome: "\e014"; 112 | @socicon-coderwall: "\e015"; 113 | @socicon-curse: "\e016"; 114 | @socicon-dailymotion: "\e017"; 115 | @socicon-deezer: "\e018"; 116 | @socicon-delicious: "\e019"; 117 | @socicon-deviantart: "\e01a"; 118 | @socicon-diablo: "\e01b"; 119 | @socicon-digg: "\e01c"; 120 | @socicon-discord: "\e01d"; 121 | @socicon-disqus: "\e01e"; 122 | @socicon-douban: "\e01f"; 123 | @socicon-draugiem: "\e020"; 124 | @socicon-dribbble: "\e021"; 125 | @socicon-drupal: "\e022"; 126 | @socicon-ebay: "\e023"; 127 | @socicon-ello: "\e024"; 128 | @socicon-endomodo: "\e025"; 129 | @socicon-envato: "\e026"; 130 | @socicon-etsy: "\e027"; 131 | @socicon-facebook: "\e028"; 132 | @socicon-feedburner: "\e029"; 133 | @socicon-filmweb: "\e02a"; 134 | @socicon-firefox: "\e02b"; 135 | @socicon-flattr: "\e02c"; 136 | @socicon-flickr: "\e02d"; 137 | @socicon-formulr: "\e02e"; 138 | @socicon-forrst: "\e02f"; 139 | @socicon-foursquare: "\e030"; 140 | @socicon-friendfeed: "\e031"; 141 | @socicon-github: "\e032"; 142 | @socicon-goodreads: "\e033"; 143 | @socicon-google: "\e034"; 144 | @socicon-googlescholar: "\e035"; 145 | @socicon-googlegroups: "\e036"; 146 | @socicon-googlephotos: "\e037"; 147 | @socicon-googleplus: "\e038"; 148 | @socicon-grooveshark: "\e039"; 149 | @socicon-hackerrank: "\e03a"; 150 | @socicon-hearthstone: "\e03b"; 151 | @socicon-hellocoton: "\e03c"; 152 | @socicon-heroes: "\e03d"; 153 | @socicon-smashcast: "\e03e"; 154 | @socicon-horde: "\e03f"; 155 | @socicon-houzz: "\e040"; 156 | @socicon-icq: "\e041"; 157 | @socicon-identica: "\e042"; 158 | @socicon-imdb: "\e043"; 159 | @socicon-instagram: "\e044"; 160 | @socicon-issuu: "\e045"; 161 | @socicon-istock: "\e046"; 162 | @socicon-itunes: "\e047"; 163 | @socicon-keybase: "\e048"; 164 | @socicon-lanyrd: "\e049"; 165 | @socicon-lastfm: "\e04a"; 166 | @socicon-line: "\e04b"; 167 | @socicon-linkedin: "\e04c"; 168 | @socicon-livejournal: "\e04d"; 169 | @socicon-lyft: "\e04e"; 170 | @socicon-macos: "\e04f"; 171 | @socicon-mail: "\e050"; 172 | @socicon-medium: "\e051"; 173 | @socicon-meetup: "\e052"; 174 | @socicon-mixcloud: "\e053"; 175 | @socicon-modelmayhem: "\e054"; 176 | @socicon-mumble: "\e055"; 177 | @socicon-myspace: "\e056"; 178 | @socicon-newsvine: "\e057"; 179 | @socicon-nintendo: "\e058"; 180 | @socicon-npm: "\e059"; 181 | @socicon-odnoklassniki: "\e05a"; 182 | @socicon-openid: "\e05b"; 183 | @socicon-opera: "\e05c"; 184 | @socicon-outlook: "\e05d"; 185 | @socicon-overwatch: "\e05e"; 186 | @socicon-patreon: "\e05f"; 187 | @socicon-paypal: "\e060"; 188 | @socicon-periscope: "\e061"; 189 | @socicon-persona: "\e062"; 190 | @socicon-pinterest: "\e063"; 191 | @socicon-play: "\e064"; 192 | @socicon-player: "\e065"; 193 | @socicon-playstation: "\e066"; 194 | @socicon-pocket: "\e067"; 195 | @socicon-qq: "\e068"; 196 | @socicon-quora: "\e069"; 197 | @socicon-raidcall: "\e06a"; 198 | @socicon-ravelry: "\e06b"; 199 | @socicon-reddit: "\e06c"; 200 | @socicon-renren: "\e06d"; 201 | @socicon-researchgate: "\e06e"; 202 | @socicon-residentadvisor: "\e06f"; 203 | @socicon-reverbnation: "\e070"; 204 | @socicon-rss: "\e071"; 205 | @socicon-sharethis: "\e072"; 206 | @socicon-skype: "\e073"; 207 | @socicon-slideshare: "\e074"; 208 | @socicon-smugmug: "\e075"; 209 | @socicon-snapchat: "\e076"; 210 | @socicon-songkick: "\e077"; 211 | @socicon-soundcloud: "\e078"; 212 | @socicon-spotify: "\e079"; 213 | @socicon-stackexchange: "\e07a"; 214 | @socicon-stackoverflow: "\e07b"; 215 | @socicon-starcraft: "\e07c"; 216 | @socicon-stayfriends: "\e07d"; 217 | @socicon-steam: "\e07e"; 218 | @socicon-storehouse: "\e07f"; 219 | @socicon-strava: "\e080"; 220 | @socicon-streamjar: "\e081"; 221 | @socicon-stumbleupon: "\e082"; 222 | @socicon-swarm: "\e083"; 223 | @socicon-teamspeak: "\e084"; 224 | @socicon-teamviewer: "\e085"; 225 | @socicon-technorati: "\e086"; 226 | @socicon-telegram: "\e087"; 227 | @socicon-tripadvisor: "\e088"; 228 | @socicon-tripit: "\e089"; 229 | @socicon-triplej: "\e08a"; 230 | @socicon-tumblr: "\e08b"; 231 | @socicon-twitch: "\e08c"; 232 | @socicon-twitter: "\e08d"; 233 | @socicon-uber: "\e08e"; 234 | @socicon-ventrilo: "\e08f"; 235 | @socicon-viadeo: "\e090"; 236 | @socicon-viber: "\e091"; 237 | @socicon-viewbug: "\e092"; 238 | @socicon-vimeo: "\e093"; 239 | @socicon-vine: "\e094"; 240 | @socicon-vkontakte: "\e095"; 241 | @socicon-warcraft: "\e096"; 242 | @socicon-wechat: "\e097"; 243 | @socicon-weibo: "\e098"; 244 | @socicon-whatsapp: "\e099"; 245 | @socicon-wikipedia: "\e09a"; 246 | @socicon-windows: "\e09b"; 247 | @socicon-wordpress: "\e09c"; 248 | @socicon-wykop: "\e09d"; 249 | @socicon-xbox: "\e09e"; 250 | @socicon-xing: "\e09f"; 251 | @socicon-yahoo: "\e0a0"; 252 | @socicon-yammer: "\e0a1"; 253 | @socicon-yandex: "\e0a2"; 254 | @socicon-yelp: "\e0a3"; 255 | @socicon-younow: "\e0a4"; 256 | @socicon-youtube: "\e0a5"; 257 | @socicon-zapier: "\e0a6"; 258 | @socicon-zerply: "\e0a7"; 259 | @socicon-zomato: "\e0a8"; 260 | @socicon-zynga: "\e0a9"; 261 | 262 | -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tests for Scale Workshop 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | --------------------------------------------------------------------------------