Compare commits

...
234 Commits
Author SHA1 Message Date
MightyPorkandGitHub e125aec459 Merge pull request #262 from schneemaier/patch-1
Update flash.sh
2018-09-05 13:40:38 +02:00
schneemaierandGitHub ee61b38026 Update flash.sh
The original code was not working if port and baud was not exported as separate environmentak varaibles.
2018-09-05 11:22:25 +02:00
MightyPork 7e4d25272a Support for the new GPIO control web interface 2018-08-19 11:59:10 +02:00
MightyPork d3130c43da API for writing, toggling, pulsing and reading GPIO pins 2018-08-18 22:11:52 +02:00
MightyPork 70020581fd bump frontend revision 2018-08-18 18:26:22 +02:00
MightyPork 8cc9ae8df4 Merge branch 'patch-clickbuttons' 2018-01-21 14:08:55 +01:00
MightyPork 6971fe2036 v bump 2018-01-21 14:08:38 +01:00
MightyPork 9f97d5c9d1 bugfix for the clickbuttons not working 2018-01-21 14:07:54 +01:00
MightyPork 5343d44ea8 bump the frontend v for the hu patch 2017-11-27 21:33:56 +01:00
MightyPork 76fbf5d65f Merge branch 'work' 2017-11-19 23:07:24 +01:00
MightyPork 6b4013f779 v bump 2017-11-19 22:55:54 +01:00
MightyPork 0438f88c83 update frontend ref 2017-11-12 12:57:31 +01:00
MightyPork d0d7200e57 Merge branch 'double-lines' into work 2017-11-12 12:57:04 +01:00
MightyPork 78aaa949a3 support double lines 2017-11-12 11:40:36 +01:00
MightyPork 5dbb592946 added OSC for color buttons 2017-10-30 21:25:56 +01:00
MightyPork d9aa0418fa updated protocol, added button themeing and font conf 2017-10-30 20:47:41 +01:00
MightyPork 9008f2e1af ref upd 2017-10-26 00:57:40 +02:00
MightyPork bc240e676b added hu to build script 2017-10-26 00:51:51 +02:00
MightyPork 3af928b39d version bump for pre-release build 2017-10-26 00:45:43 +02:00
MightyPork ead9120ae8 ref update 2017-10-26 00:41:24 +02:00
MightyPork dca44963e8 add new OSC and configurable button count 2017-10-26 00:31:10 +02:00
MightyPork 3c12efe28f updated submodule ref 2017-10-25 23:24:07 +02:00
MightyPork e98588b317 Fix broken validation for bm* 2017-10-23 23:49:19 +02:00
MightyPork 0e6c297fe4 be smarter about detecting changes in loaded ini 2017-10-23 23:24:20 +02:00
MightyPork 3e4181f18e Merge branch 'xmacro-config' into work 2017-10-23 22:43:34 +02:00
MightyPork 2f4cd08c60 import now fully working 2017-10-23 22:43:04 +02:00
MightyPork ba2b73a287 ini upload and parse (no loading to conf yet) 2017-10-23 01:40:56 +02:00
MightyPork 4e285694d1 ini export! 2017-10-22 23:41:07 +02:00
MightyPork 9700465927 fixed many bugs 2017-10-22 22:23:09 +02:00
MightyPork 1ab4101ac0 use xmacros for terminal config 2017-10-22 20:30:24 +02:00
MightyPork f0bc70553a use xmacros for system 2017-10-22 17:55:52 +02:00
MightyPork 3eac0ee794 fmt again 2017-10-22 16:50:45 +02:00
MightyPork 2c80b59d71 formatting 2017-10-22 16:46:28 +02:00
MightyPork 9146c9880a slightly cleaner XTABLE_WIFI 2017-10-22 16:45:45 +02:00
MightyPork cabd42f2b4 base of xmarco-based config, used for wifi and network 2017-10-22 16:34:00 +02:00
MightyPork fbd4693035 Merge branch 'master' into work 2017-10-08 13:26:40 +02:00
MightyPork c5670c09bb update frontend ref 2017-10-08 12:57:36 +02:00
MightyPork 4e74d1cd79 updated front-end ref 2017-10-08 12:54:26 +02:00
MightyPork 4c8360dbb5 Merge remote-tracking branch 'origin/work' 2017-10-08 12:35:21 +02:00
MightyPork c0adc253b4 add cgi for setting background image 2017-10-08 12:34:50 +02:00
MightyPork be3e06aac1 version bump 2017-10-08 11:21:11 +02:00
MightyPork 992c927490 Merge remote-tracking branch 'origin/work' 2017-10-08 11:16:44 +02:00
MightyPork ef3b02e68c added new chars to cp 1 2017-10-07 00:46:19 +02:00
MightyPork 1c556457c7 Merge branch 'new-codepages' into work 2017-10-07 00:15:34 +02:00
MightyPork e49a8b0adf added codepages 2 and 3 2017-10-06 23:25:01 +02:00
MightyPork 81c3e426e7 added quarter blocks to codepage 1 2017-10-06 22:28:36 +02:00
MightyPorkandGitHub c90de79a34 Update README.md 2017-10-06 00:12:28 +02:00
MightyPork df404f5f63 2.1.1 - patched cslang 2017-10-04 21:47:32 +02:00
MightyPork 00153656f1 flip front-end back to "work" 2017-10-04 00:22:08 +02:00
MightyPork aa920aee27 Merge branch 'master' into work 2017-10-04 00:21:45 +02:00
MightyPork 7b8c936559 Version bump 2017-10-04 00:13:14 +02:00
MightyPork 17c18ed1c9 update front-end to latest master 2017-10-04 00:12:47 +02:00
MightyPork 274e3b359b Merge branch 'work' 2017-10-04 00:11:26 +02:00
MightyPork 73c98eb2d1 some logging adjustments 2017-10-04 00:10:54 +02:00
MightyPork 022e882001 added IP to answerback message 2017-10-03 22:16:07 +02:00
MightyPork dc08512397 one timezone is probably enough in the release readme 2017-10-03 20:58:00 +02:00
MightyPork 6c1ab28e75 improved build script, automatic version fetch from source 2017-10-03 20:30:53 +02:00
MightyPork 2796709624 version bump to beta2 2017-10-03 01:55:46 +02:00
MightyPork d2edcd7fd5 added new release system 2017-10-03 00:59:21 +02:00
MightyPork 76272530ec improved ENQ response, now includes hashes and ID. Added ID to boot msg 2017-10-02 22:55:56 +02:00
MightyPork a514601897 use async buffer for apars respond other than http 2017-10-02 22:20:38 +02:00
MightyPork 935e05a56a Disable Rx pin pull-up, caused malfunction with cheap adapters 2017-10-02 19:51:58 +02:00
MightyPork a72e2bed63 makefile improvements 2017-10-02 03:18:43 +02:00
MightyPork 679f0adbae Merge branch 'http-comm' into work 2017-10-02 01:40:45 +02:00
MightyPork 791f6e716f finished basic d2d and external, adde flags etc 2017-10-02 01:40:26 +02:00
MightyPork 9a5bc0a274 Add codename 2017-10-01 22:56:13 +02:00
MightyPork dca02976a7 Fixed a horrible MegaBug and added HTTP message sending + D2D sending (no Rx yet) 2017-10-01 19:46:14 +02:00
MightyPork 6665b5c183 CAN and SUB correctly abort current sequence, including string. Start of adding PM's 2017-10-01 14:09:39 +02:00
MightyPork 55afcca955 fix incorrect DCS command handling 2017-10-01 13:47:22 +02:00
MightyPork 7ffc947f1a added client + testing command ESC#7 2017-10-01 12:54:52 +02:00
MightyPork 0eeed9c7d9 add %debugbar% to terminal page 2017-10-01 03:42:15 +02:00
MightyPork 253473daed version bump for next rel 2017-10-01 03:35:52 +02:00
MightyPork 9321cd4ebb made it survive idiots cat'ing image files etc 2017-10-01 03:35:31 +02:00
MightyPork faffa68794 use binary messages and fix bugs in utf sanitizer 2017-10-01 03:23:04 +02:00
MightyPork a6ee808a1d add SKIP tag to defines 2017-09-30 22:59:46 +02:00
MightyPork 5150cc32eb align serializer with front-end changes 2017-09-30 22:57:40 +02:00
MightyPork 42d5f6bc89 decopt 12 configurable 2017-09-30 19:11:34 +02:00
MightyPork d2fe37403f Fix htop display bugs - wrong serialization 2017-09-30 17:48:42 +02:00
MightyPork f6c2b06d8a notify about mouse attrs change 2017-09-30 15:10:52 +02:00
MightyPork b6c5648690 allow zero redraw delay 2017-09-30 15:02:19 +02:00
MightyPork 062b0e6188 fix messed up bg color encoding 2017-09-30 13:52:28 +02:00
MightyPork c83b68405f Merge branch 'partial-updates' into work 2017-09-30 02:55:54 +02:00
MightyPork 97552b30c3 fix some bugs + optimize stuff 2017-09-30 02:40:24 +02:00
MightyPork 1c23e79c59 partial update, may be a bit buggy in places 2017-09-30 01:18:03 +02:00
MightyPork b70c65868b dump context and stop listening for a bit on rx of bad UTF 2017-09-29 21:57:20 +02:00
MightyPork 1614194d76 Removed one NOTIFY_LOCK() that shouldn't have been there 2017-09-29 20:49:52 +02:00
MightyPork 7bca46384e Merge branch 'new-message-format' into work 2017-09-28 23:11:28 +02:00
MightyPork 03c7f4d0ad removed bugs 2017-09-28 22:09:30 +02:00
MightyPork da6c5bf5be re-arranged attr bits and changed string encoding for labels and title to use terminator rather than byte length 2017-09-28 21:50:51 +02:00
MightyPork 2e0648be8e re-ordered cell attr bits for shorter serialize word length 2017-09-28 21:13:10 +02:00
MightyPork 404ccaeb20 new message format 2017-09-28 20:58:45 +02:00
MightyPork 7300e0003c Merge branch 'master' into work 2017-09-26 17:42:55 +02:00
MightyPork 8c55202b42 added exampe of the main config file 2017-09-26 17:41:49 +02:00
MightyPork 65977bfbae Overclock, fix buffer overflow causing total lock-up, small tweaks
removed the main make config file from VCS so it can be easier edited
added overclocking option to enable 160 MHz mode
2017-09-26 17:41:24 +02:00
MightyPork 74d52a0c61 add confirmation messages to form saving 2017-09-26 15:01:22 +02:00
MightyPork 0fae510207 change version to 2.0.0 to indicate the breaking change 2017-09-26 13:43:08 +02:00
MightyPork 1a8d3cb332 show real sdk version, make tpw setting actually work 2017-09-26 11:30:58 +02:00
MightyPork ca5a7e3de9 Merge remote-tracking branch 'origin/work' 2017-09-26 10:53:35 +02:00
MightyPork fa5489ccfc support for truecolor default colors 2017-09-25 01:53:59 +02:00
MightyPork 502babf7b0 updated front-end ref 2017-09-24 20:50:01 +02:00
MightyPork 47f6cd38cd Merge branch 'utf-removal-fix' into work 2017-09-24 20:36:38 +02:00
MightyPork 0f10d84a59 disable non-error ansi logging 2017-09-24 20:36:09 +02:00
MightyPork cb71586138 remove spammy debug 2017-09-24 20:35:55 +02:00
MightyPork 58bd27a082 FIXED A BUG 2017-09-24 20:19:40 +02:00
MightyPork 7eb1bdeec6 fixed off-by one etc errors in the rewritten functions, fixed a lag due to heartbeat packet 2017-09-24 18:16:21 +02:00
MightyPork 674e89c8c3 fix a bug with backspace when cursor is hanging 2017-09-24 14:57:10 +02:00
MightyPork 62a0925586 fix stuff not removed from utf cache in some operations 2017-09-24 14:17:41 +02:00
MightyPork 4f4ea95c06 Merge branch 'offscale-bg' into work 2017-09-24 03:03:52 +02:00
MightyPork 2ede271c65 removed obsolete microoptimization from parser repeat code 2017-09-24 03:02:28 +02:00
MightyPork 692b2c4d11 fix serializer being completely broken 2017-09-24 00:17:58 +02:00
MightyPork 948bcb62ff Backend changes for offscale default fg and bg + future-proofing
- 16-bit attribs
- added attribs FG and BG, if not set, default colors
- added attrib INVERT, removed server-side color flipping
- added screen attrib REVERSE_VIDEO (can't be done server-side if we want it to work with off-scale colors)
- changed Attribs encoding to 3B
- removed old migrations, made useless by the flash wipe caused by adding Admin PW section
- some cleaning in CSI parser
- removed function to set SGR inverse separately (now use normal SGR setter)
- added functions to set fg and bg to defaults
2017-09-23 20:50:37 +02:00
MightyPork 7607b639b0 Merge branch 'passwords-stuff' into work 2017-09-23 02:14:27 +02:00
MightyPork 2e8ec41115 lib bump 2017-09-23 02:13:02 +02:00
MightyPork 80e8e4f7da added username config for basic auth 2017-09-23 01:59:53 +02:00
MightyPork 357a9d43e3 password changing, changed default pw to "adminpw", added settings revert if validation fails 2017-09-23 01:33:55 +02:00
MightyPork a464a73a0e cgi form handlers for setting password stuff 2017-09-22 01:05:13 +02:00
MightyPork 38b3ce2dc8 cgi handler for setting passwords (not tested) 2017-09-22 01:00:52 +02:00
MightyPork 37de3a095d configurable pw lock (not yet tested and not connected to front-end) 2017-09-22 00:48:57 +02:00
MightyPork 82961568b8 increased sysconbf allocated flash size (safe now bc we alrady have a breaking change that wipes old settings) 2017-09-22 00:33:03 +02:00
MightyPork 3849e161e5 add pwlock settings to sysconf 2017-09-22 00:30:16 +02:00
MightyPork 43241c2679 admin pw now lives in a separate config block. BREAKING CHANGE! will wipe old settings :( 2017-09-22 00:22:46 +02:00
MightyPorkandGitHub 3bfd51061e added browser support list 2017-09-19 01:44:52 +02:00
MightyPork 781cf8825b remove theme max num chekc to allow adding themes purely by front-end 2017-09-18 01:58:15 +02:00
MightyPork 079b522540 implemented focus tracking reports 2017-09-17 20:18:57 +02:00
MightyPork 471aa95c34 add timezone to the about page 2017-09-17 19:18:56 +02:00
MightyPork ac56b1dc61 added timezone to bootlog 2017-09-17 19:14:24 +02:00
MightyPork 82fd9e5ad1 raise utf8 cache to max of 160 characters, re-enable buttons by default 2017-09-17 19:10:16 +02:00
MightyPork 92a454a2cf renamed input debug log funcs to imp_ to avoid build collision if DEBUG_WS is enabled also 2017-09-16 01:46:41 +02:00
MightyPork 0012c92c4a added front-end patch 2017-09-14 23:24:35 +02:00
MightyPorkandGitHub 0e8af5712b add info about the mailing list 2017-09-14 01:34:47 +02:00
MightyPork 51e97232c5 version bump to 1.1.1 2017-09-14 00:16:08 +02:00
MightyPork 7e2491cd7b fix broken urxvt mouse reports 2017-09-14 00:11:43 +02:00
MightyPork cd79d5cf25 fix #172 2017-09-13 23:32:18 +02:00
MightyPork 184c772b57 update frontend ref 2017-09-13 02:30:34 +02:00
MightyPork 6e2978492b oops messed up front-end 2017-09-13 02:22:54 +02:00
MightyPork 0d0246b12b updated branch cfg in .gitmodules 2017-09-13 01:40:33 +02:00
MightyPork 62888d4473 update frontend to use branch master 2017-09-13 01:33:10 +02:00
MightyPork 3f0b6f2d26 fix bad submodue ref 2017-09-13 01:21:45 +02:00
MightyPork 5846d46e0c Merge remote-tracking branch 'origin/work' 2017-09-13 01:20:34 +02:00
MightyPork 1454987fcf Merge branch 'work' of github.com:espterm/espterm-firmware into work 2017-09-13 01:12:13 +02:00
MightyPork 2ba04b92d7 frontend update + fixes 2017-09-13 01:12:07 +02:00
MightyPorkandGitHub 6a7493a7be Update README.md 2017-09-12 15:47:15 +02:00
MightyPork 565b2113a5 submodue update - cache busting 2017-09-12 00:53:32 +02:00
MightyPork 128515a9a7 git hashes on about page 2017-09-12 00:26:43 +02:00
MightyPork 038c3b6466 update build script 2017-09-11 22:59:26 +02:00
MightyPork fa5953a231 front-end update 2017-09-11 15:05:29 +02:00
MightyPork 5aef8a0f11 Fix js error prevending wifi scanning 2017-09-11 12:36:23 +02:00
MightyPork 356c4557bb change default size back to 26x10 so it makes better first impression on a phone 2017-09-11 11:34:26 +02:00
MightyPork 5ff9d17850 adjusted defaults (disabled buttons by default so we dont confuse users with something they dont expect) 2017-09-11 05:09:22 +02:00
MightyPork ccbb1886af two hundred and fifty six colors, ladies and gentlemen!! 2017-09-11 04:27:50 +02:00
MightyPork 65d177f149 shortened utf cache & added missing flash storage attr 2017-09-11 03:26:57 +02:00
MightyPork 7cf82753ff bugfixes 2017-09-11 03:15:20 +02:00
MightyPork c4d399ccb0 added utf8 cache implementation, not yet used 2017-09-11 02:07:17 +02:00
MightyPork 521bc63c22 submodule ref 2017-09-11 00:58:49 +02:00
MightyPork b02aebf8c2 repeat character command 2017-09-11 00:53:42 +02:00
MightyPork 4c2421121f moved funcs to flash to make it build again, reduced logging buffer, disabled some logging; add missing files 2017-09-11 00:08:50 +02:00
MightyPork 6e1173cfcf make canvas branch be the default now 2017-09-10 14:05:02 +02:00
MightyPork b0992702b9 Added MPL license file 2017-09-10 10:48:58 +02:00
MightyPork 1742f4604a small cleaning 2017-09-10 10:15:35 +02:00
MightyPork 10fafd7c23 upse capital G for consistency 2017-09-10 02:24:50 +02:00
MightyPork c838883e69 back-end for #123 2017-09-10 02:23:57 +02:00
MightyPork 384372e6c8 backend support for SGR overline, bit 7 of attrib byte 2017-09-10 02:13:34 +02:00
MightyPork 74c7628474 groundwork for 256 color parsing, added conceal 2017-09-10 02:02:43 +02:00
MightyPork bd43c4d9c3 prevent compressor blowing up short repetitions 2017-09-10 01:28:30 +02:00
MightyPork 2e004b6048 changes to terminal settings now have immediate effect via notify 2017-09-10 00:43:31 +02:00
MightyPork ab921d2523 fix newline mode, add backend for cursor and crlf config in terminal config page 2017-09-09 23:51:26 +02:00
MightyPork 9ac5143228 fix compile error - missing declaration 2017-09-09 23:08:48 +02:00
MightyPork 2bea6397c6 cursor shape CSI and cursor blink toggle 2017-09-09 23:06:58 +02:00
MightyPork ea97752867 implemented saving to alternate buffer (except screen content) 2017-09-09 22:27:29 +02:00
MightyPork a12dcfc86f update github repo link 2017-09-09 20:59:17 +02:00
MightyPork b89db56c48 fix build script and switch default submodule to use master branch 2017-09-09 19:52:37 +02:00
MightyPork 6b603a1a8c Delete old html files & moved front-end to a submodule 2017-09-09 19:42:29 +02:00
cpsdqs 0caadb6f41 Actually fix selection not working
by just adding the selection colors to the palette
2017-09-09 16:20:47 +02:00
MightyPork ff5aa4c12c Merge branch 'canvas' of github.com:MightyPork/ESPTerm into canvas 2017-09-09 16:11:36 +02:00
MightyPork 0967d7f94a fixed bad submodule revision 2017-09-09 16:11:30 +02:00
cpsdqs 26e385b4c0 Fix selection not working 2017-09-09 16:10:10 +02:00
cpsdqs 5cc3003e47 Only redraw changed cells (huge performance boost!)
also add semicolons
2017-09-09 16:01:36 +02:00
cpsdqs 3e6187dba9 Add resize button 2017-09-09 15:15:11 +02:00
cpsdqs 7407419e56 Use proper soft keyboard events
...or as close as it gets because the way input events work on mobile is awful.
2017-09-09 14:55:54 +02:00
MightyPork 93824964ed gitignore update 2017-09-09 14:45:05 +02:00
cpsdqsandMightyPork abecf0d4ce Add fitIntoWidth/Height 2017-09-09 14:38:03 +02:00
cpsdqsandMightyPork d6d985a172 Make operators more readable
(it'll be minified anyway)
2017-09-09 14:38:03 +02:00
cpsdqsandMightyPork 71cab02b3e Set to ctx.font as little as possible to improve Firefox performance 2017-09-09 14:38:03 +02:00
MightyPork 3926776adc Fixed the stupid unicode bug 2017-09-09 14:38:03 +02:00
MightyPork 3b7cd0a42e yarn lock 2017-09-09 14:38:03 +02:00
cpsdqsandMightyPork f85bcb3ef6 Use local sass instead of mysterious global CLI 2017-09-09 14:38:03 +02:00
cpsdqsandMightyPork 0f9809c290 Prevent space from scrolling 2017-09-09 14:38:03 +02:00
MightyPork 2d22d5cff1 fix firmware build scripts & disable yuicompressor 2017-09-09 14:38:03 +02:00
cpsdqsandMightyPork ab2dc10e35 Add Menlo to monospace font stack
lest it be Courier on iOS
2017-09-09 14:38:03 +02:00
cpsdqsandMightyPork 34b72625a5 Add touch selection 2017-09-09 14:38:03 +02:00
MightyPork a3567ec338 nicer underlines & allow underline + strike together 2017-09-09 14:38:03 +02:00
cpsdqsandMightyPork 7576cd859a Add floating soft keyboard input 2017-09-09 14:38:03 +02:00
cpsdqsandMightyPork d82c33d774 Fix misaligned characters(?) 2017-09-09 14:38:03 +02:00
cpsdqsandMightyPork d57ed19c42 Add events to TermScreen; use correct cursor 2017-09-09 14:38:03 +02:00
cpsdqsandMightyPork bee99b4c55 Add babel-minify to packjs 2017-09-09 14:38:03 +02:00
MightyPork fe44927a62 cleaning and jslint syntax fixes 2017-09-09 14:35:37 +02:00
cpsdqsandMightyPork 8f81616ca8 Add hanging cursor 2017-09-09 14:33:50 +02:00
cpsdqsandMightyPork 15f539f4d4 Add mouse events, ⌃⇧C to copy, alt-select override
Also prevent body scroll when the terminal is scrollable
2017-09-09 14:33:50 +02:00
cpsdqsandMightyPork 3b6dfbda49 Add selection, other themes, cursor styles
and fix some issues
2017-09-09 14:33:50 +02:00
cpsdqsandMightyPork dd08290587 Rewrite term_screen with canvas 2017-09-09 14:33:50 +02:00
MightyPork b8ed46cbea removed some generated junk 2017-09-09 03:47:36 +02:00
MightyPork 8d7419b666 You wouldn't believe this simple trick that freed 10 kB of RAM! 2017-09-09 03:45:41 +02:00
MightyPork 830ec073bf fix #109 2017-09-09 03:03:12 +02:00
MightyPork 7d6cb2b660 screen serialize speedup + fix bad utf encode 2017-09-09 01:57:15 +02:00
MightyPork 1d6d6ee228 Implement initial load through websocket to cut one unnecessary HTTP GET that caused conn pool overflow. Increased reload / retry delays to reduce server spamming by poorly loaded client. 2017-09-08 23:53:23 +02:00
MightyPork a20ec8a49e version bmup 2017-09-07 22:49:38 +02:00
MightyPork 831ebe3b54 Fix broken codepage 1 2017-09-07 22:44:56 +02:00
MightyPork ea3c815a39 added script to auto-deploy demo; fixes for Edge 2017-09-07 22:42:34 +02:00
MightyPork 85f707401d Updated README 2017-09-07 18:28:23 +02:00
MightyPork 0dbfbf707f removed doc and updated readme 2017-09-07 18:15:06 +02:00
MightyPork a18b1d726e fix bad defaumt msg causing corrupt buttons 2017-09-07 16:40:45 +02:00
MightyPork f8e3454250 Modified HTML pages to allow building Demo version 2017-09-07 16:31:15 +02:00
MightyPork 184ef1ec36 change version to 1.0.0 2017-09-07 02:39:24 +02:00
MightyPork 99b07399b4 added back About link under terminal 2017-09-07 02:32:24 +02:00
MightyPork e46abe175f reduced buffers, not really needed 2017-09-07 02:27:05 +02:00
MightyPork 48cd9c1a2a fine-tuned upload system 2017-09-07 02:18:28 +02:00
MightyPork 07d1a3d3b4 remove some less useful mimes from matcher 2017-09-07 00:31:53 +02:00
MightyPork 44ea1e5b49 Upload modal 2017-09-07 00:29:06 +02:00
MightyPork 6fdb23531b implemented backindex 2017-09-06 21:35:48 +02:00
MightyPork 116201e232 Fixed a couple bugs and cleaned up the parser callbacks file for CSI 2017-09-06 20:54:20 +02:00
MightyPork a78830f805 Paste button 2017-09-06 17:27:33 +02:00
MightyPork 3f198f8360 fix parser bug with bell & broken mouse tracking sgr reporting for movement with button 2017-09-06 17:08:19 +02:00
MightyPork f693373a34 button to force screen reset 2017-09-06 16:31:48 +02:00
MightyPork 0af62f47e6 configurable button codes 2017-09-06 16:13:14 +02:00
MightyPork a673843a63 cleaning 2017-09-06 14:48:01 +02:00
MightyPork 023a2db01a off by one 2017-09-06 14:46:39 +02:00
MightyPork 979f956e7e fixed regression - buttons not sending commands 2017-09-06 14:45:34 +02:00
MightyPork 5f6566a6fd implemented loopback option SRM 2017-09-06 14:44:42 +02:00
MightyPork 6d98d2c6da removed outline from term buttons 2017-09-06 14:24:02 +02:00
MightyPork 9fa9cb0ca2 vers = 8.0.0 in preparation for next release 2017-09-06 14:22:30 +02:00
MightyPork ba9c757cdc help page improvements, opt to hide botnav and buttons, DECOPT for those: 800 and 801, charset tables added to help page, terminal css overhaul to avoid Black Lines 2017-09-06 14:21:38 +02:00
MightyPork d2b2b89593 improved the help page + added markdown style html formatting processor) 2017-09-05 21:45:12 +02:00
MightyPorkandGitHub 6aa4747d3e Mouse tracking explained 2017-09-05 17:52:13 +02:00
MightyPorkandGitHub 5eec7d2bf6 added a table with nomenclature 2017-09-05 16:54:55 +02:00
248 changed files with 8039 additions and 21263 deletions
+6 -1
View File
@@ -1,4 +1,5 @@
html/
html_demo/
build/
firmware/
espfs/mkespfsimage/*.o
@@ -10,10 +11,14 @@ espfs/espfstest/espfstest
html_compressed/
ldscript_memspecific.ld
eagle.app.sym
node_modules
esphttpdconfig.mk
# Garbage added by CLion
.idea/
cmake-build-debug/
cmake-build-debug*/
.sass-cache
*.map
!.gitignore
+5
View File
@@ -1,3 +1,8 @@
[submodule "libesphttpd"]
path = libesphttpd
url = git@github.com:MightyPork/libesphttpd.git
branch = master
[submodule "front-end"]
path = front-end
url = git@github.com:espterm/espterm-front-end.git
branch = master
+44 -7
View File
@@ -56,6 +56,8 @@ set(SOURCE_FILES
libesphttpd/util/cgiwebsocket.c
libesphttpd/util/cgiflash.c
libesphttpd/util/captdns.c
libesphttpd/esphttpclient/httpclient.c
libesphttpd/include/httpclient.h
esp_iot_sdk_v1.5.2/include/user_interface.h
esp_iot_sdk_v1.5.2/include/upgrade.h
@@ -85,9 +87,9 @@ set(SOURCE_FILES
esp_iot_sdk_v1.5.2/include/json/jsontree.h
esp_iot_sdk_v1.5.2/include/json/jsonparse.h
esp_iot_sdk_v1.5.2/include/json/json.h
include/user_config.h
include/ets_sys_extra.h
include/helpers.h
user/io.c
user/io.h
user/cgi_wifi.c
@@ -110,8 +112,6 @@ set(SOURCE_FILES
user/serial.h
user/routes.c
user/routes.h
user/uart_asyncdrv.c-
user/uart_asyncdrv.h-
user/cgi_main.c
user/cgi_main.h
user/cgi_sockets.c
@@ -123,7 +123,6 @@ set(SOURCE_FILES
user/wifimgr.h
user/persist.c
user/persist.h
include/helpers.h
user/syscfg.c
user/syscfg.h
user/ascii.h
@@ -141,9 +140,21 @@ set(SOURCE_FILES
user/apars_osc.c
user/apars_osc.h
user/apars_dcs.c
user/apars_dcs.h user/uart_buffer.c user/uart_buffer.h user/jstring.c user/jstring.h)
user/apars_dcs.h
user/uart_buffer.c
user/uart_buffer.h
user/jstring.c
user/jstring.h
user/character_sets.h
user/ini_parser.h
user/ini_parser.c
user/ini_parser.rl
user/utf8.h
user/utf8.c
user/cgi_logging.h user/config_xmacros.h user/config_xmacros.c)
include_directories(include)
include_directories(libesphttpd/esphttpclient)
include_directories(user)
include_directories(libesphttpd/include)
include_directories(libesphttpd/espfs)
@@ -155,15 +166,41 @@ include_directories(esp_iot_sdk_v1.5.2/include)
add_definitions(
-D__ets__
-DICACHE_FLASH
-DDEBUG_LOGBUF_SIZE=2048
-DUSE_OPTIMIZE_PRINTF=1
-DHTTPD_MAX_CONNECTIONS=5
-DHTTPD_STACKSIZE=1000
-DICACHE_FLASH_ATTR=
-DICACHE_RODATA_ATTR=
-DFLAG_GZIP=2
-DADMIN_PASSWORD="asdf"
-DESP_LANG="en"
-DGIT_HASH_BACKEND="asdf"
-DGIT_HASH_FRONTEND="asdf"
-DGIT_HASH="blabla"
-D__TIMEZONE__="UTC"
-DESPFS_HEATSHRINK
-DDEBUG_ANSI=1
-DDEBUG_ANSI_NOIMPL=1
-DDEBUG_CAPTDNS=1
-DDEBUG_CGI=0
-DDEBUG_ESPFS=1
-DDEBUG_HEAP=1
-DDEBUG_HTTP=1
-DDEBUG_HTTPC=1
-DDEBUG_INPUT=1
-DDEBUG_MALLOC=1
-DESPFS_HEATSHRINK)
-DDEBUG_PERSIST=1
-DDEBUG_ROUTER=1
-DDEBUG_UTFCACHE=1
-DDEBUG_WIFI=1
-DDEBUG_WS=1
-DDEBUG_ROUTER=1
)
# all the debug keys should be listed here ^ so clion thinks they are used
# and doesn't mess up the formatting and inspections
add_executable(ESPTerm ${SOURCE_FILES})
+373
View File
@@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
+33 -10
View File
@@ -62,13 +62,18 @@ LIBS = c gcc hal phy pp net80211 wpa main lwip crypto
#Add in esphttpd lib
LIBS += esphttpd
# compiler flags using during compilation of source files
CFLAGS = -Os -ggdb -std=gnu99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \
ifndef ESP_LANG
ESP_LANG = en
endif
# compiler flags using during compilation of source files -ggdb
CFLAGS = -Os -std=gnu99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno-inline-functions \
-nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH \
-Wno-address -Wno-unused
CFLAGS += -DGIT_HASH='"$(shell git rev-parse --short HEAD)"'
CFLAGS += -DADMIN_PASSWORD=$(ADMIN_PASSWORD)
CFLAGS += -DGIT_HASH_BACKEND='"$(shell git rev-parse --short HEAD)"'
CFLAGS += -DGIT_HASH_FRONTEND='"$(shell cd front-end && git rev-parse --short HEAD)"'
CFLAGS += -D__TIMEZONE__='"$(shell date +%Z)"' -DESP_LANG='"$(ESP_LANG)"'
ifdef GLOBAL_CFLAGS
CFLAGS += $(GLOBAL_CFLAGS)
@@ -152,7 +157,6 @@ define maplookup
$(patsubst $(strip $(1)):%,%,$(filter $(strip $(1)):%,$(2)))
endef
#Include options and target specific to the OUTPUT_TYPE
include Makefile.$(OUTPUT_TYPE)
@@ -171,7 +175,7 @@ MODULE_INCDIR := $(addsuffix /include,$(INCDIR))
ESP_FLASH_SIZE_IX=$(call maplookup,$(ESP_SPI_FLASH_SIZE_K),512:0 1024:2 2048:5 4096:6)
ESPTOOL_FREQ=$(call maplookup,$(ESP_FLASH_FREQ_DIV),0:40m 1:26m 2:20m 0xf:80m 15:80m)
ESPTOOL_MODE=$(call maplookup,$(ESP_FLASH_MODE),0:qio 1:qout 2:dio 3:dout)
ESPTOOL_SIZE=$(call maplookup,$(ESP_SPI_FLASH_SIZE_K),512:4m 256:2m 1024:8m 2048:16m 4096:32m)
ESPTOOL_SIZE=$(call maplookup,$(ESP_SPI_FLASH_SIZE_K),512:512KB 256:256KB 1024:1MB 2048:2MB 4096:4MB)
ESPTOOL_OPTS=--port $(ESPPORT) --baud $(ESPBAUD)
ESPTOOL_FLASHDEF=--flash_freq $(ESPTOOL_FREQ) --flash_mode $(ESPTOOL_MODE) --flash_size $(ESPTOOL_SIZE)
@@ -189,27 +193,46 @@ $1/%.o: %.S
$(Q) $(CC) $(INCDIR) $(MODULE_INCDIR) $(EXTRA_INCDIR) $(SDK_INCDIR) $(CFLAGS) -c $$< -o $$@
endef
.PHONY: all web parser checkdirs clean libesphttpd default-tgt
.PHONY: all web parser checkdirs clean libesphttpd default-tgt espsize espmac
web:
$(Q) ./build_web.sh
updweb:
$(Q) cd front-end && git pull
$(Q) ./build_web.sh
parser:
$(Q) ./build_parser.sh
all: checkdirs web parser $(TARGET_OUT) $(FW_BASE)
espsize:
$(Q) esptool --port /dev/ttyUSB0 flash_id
espmac:
$(Q) esptool --port /dev/ttyUSB0 read_mac
all: checkdirs
$(Q) make actual_all -j4 -B
release:
$(Q) ./release.sh
actual_all: parser $(TARGET_OUT) $(FW_BASE)
libesphttpd/Makefile:
$(Q) [[ -e "libesphttpd/Makefile" ]] || echo -e "\e[31mlibesphttpd submodule missing.\nIf build fails, run \"git submodule init\" and \"git submodule update\".\e[0m"
libesphttpd: libesphttpd/Makefile
$(Q) make -C libesphttpd USE_OPENSDK=$(USE_OPENSDK) SERVERNAME_PREFIX="ESPTerm "
$(Q) make -C libesphttpd USE_OPENSDK=$(USE_OPENSDK) -j4
$(APP_AR): libesphttpd $(OBJ)
$(vecho) "AR $@"
$(Q) $(AR) cru $@ $(OBJ)
checkdirs: $(BUILD_DIR)
checkdirs: $(BUILD_DIR) html/favicon.ico
html/favicon.ico:
$(Q) [[ -e "html/favicon.ico" ]] || ./build_web.sh
$(BUILD_DIR):
$(Q) mkdir -p $@
+152 -69
View File
@@ -1,100 +1,171 @@
# ESPTerm
ESPTerm is a terminal emulator running on the ESP8266 WiFi chip.
The firmware emulates VT102 with some additional features based on `xterm` and later VT models, and the terminal screen can be accessed using any web browser, even on a phone or tablet. It works with ESP-01, ESP-01S, ESP-12 and likely many other modules (I use ESP-12 on a NodeMCU board for development).
**ESPTerm is a VT100-like terminal emulator running on the ESP8266 WiFi chip.**
ESPTerm can add remote access via WiFi to any embeded project, all you need is a serial port.
![Photo][photo-hw]<br>
*Fig 1: Breadboard adapter developed for ESPTerm*
## Screenshots, photos
ESPTerm **passes most of VTTEST test cases**, making it functionally comparable to eg.
gnome-terminal, terminator, konsole, GtkTerm or PuTTY.
ESPTerm is **capable of running Midnight Commander** through agetty, **including full
mouse support**, provided agetty is made to believe it's Xterm, which shows ESPTerm
is compatible with ncurses.
- Look at the [GitHub releases page][releases], there are some pics.
In addition to control sequences that manipulate the terminal or user input, there
is a set of simple **networking commands** for device-to-device message exchange and
interacting with remote servers.
## ESPTerm features
To see what escape sequences are supported, check out this [annotated Xterm manual page][xterm-compare]
which was used for reference, or the built-in help page ([online demo][demo-help])
- **Robust WiFi configuration interface**
- static IP
- DHCP
- AP channel and strength setting
- AP password
- SSID search for finding your existing network
- **Almost complete VT102 emulation** with some extras
- *Sufficient to run most Linux console applications, including ncurses-based ones\**
- All standard SGR (text style attributes) supported
- Full UTF-8 support
- Alternate character sets support
- 16 colors
- Scrolling Region, Origin Mode
- Tab Stops
- Cursor save/restore
- Character/Line insert, delete, clear operations
- Audible BEL (ASCII 7 beep)
- Most standard queries (get cursor position, get SGR, get screen size...)
- All DEC private options either implemented or safely consumed by the parser
- **Other features:**
- Screen size up to 80x25
The terminal screen can be accessed using any web browser, even on a phone or tablet.
It works with ESP-01, ESP-01S, ESP-12 and likely many other modules.
With ESPTerm, you can add remote access via WiFi to any embeded project, all you need is
UART and some imagination!
## Try it online
You can try the web user interface here: [espterm.github.io][demo-term]
The demo is almost identical to the real thing, except, of course, it doesn't do much without the
emulator backend that runs on the ESP8266. The web version will be updated to match this repository
after each minor release (and sometimes in between for testing; the version currently being show-cased
can be read on the About page of the demo).
### Browser support
Use the above linked online demo to verify compatibility with your browser.
To our knowledge, ESPTerm **works with**:
- Google Chrome (desktop, mobile)
- Firefox (desktop, mobile not tested)
- Safari (desktop, mobile)
- Chromium
- Opera
- Brave
- Konqueror
It **does not work with**:
- Microsoft Edge - runs, but is extremely laggy
- Internet Explorer (any version) - crashes, missing JS features
- Opera Mini - crashes, missing JS and CSS features
- Blackberry browser - not tested, but unlikely
- Safari on old iOS versions - missing features
- WebOS browser
- Old Android Browser (before 4.4?) - not tested, likely missing JS features
## Main features
- **Almost complete VT102 emulation** with some extras from Xterm, eg.
- Screen size up to 80x25 (the limit can be modified when compiling from source)
- All standard text styles and 16 colors supported
- Full UTF-8 support, alternate character sets
- Standard mouse tracking modes
- You can dynamically set screen title, button labels...
- **Networking commands**
- Command for sending a message to another ESPTerm
- API endpoint (`/api/v1/msg`) for receiving messages sent e.g. from a script on PC, web browser or CURL
- Command for requesting remote servers and getting back response headers and/or body
- **Web Terminal Interface**
- Real-time screen update via WebSocket
- Button to open Android software keyboard
- 5 buttons under the screen for quick commands
- Button labels can be changed (by OSC commands or via settings)
- Screen title can be changed (by OSC commands or via settings)
- Built-in help page with basic troubleshooting and command reference
- Mouse and keyboard input, works also on mobile
- 5 optional buttons for quick commands
- Text file upload tool with adjustable delays and line endings
- *Built-in help page* ([demo][demo-help]) with basic troubleshooting and command reference
- **User-friendly comprehensive WiFi configuration** (Demo: [WiFi][demo-wifi], [network][demo-network] config)
- Static IP, DHCP, channel selection, power
- SSID search utility for finding your existing network
- **Basic security features**
- Possibility to password-lock parts of the web interface
- Admin password for some sensitive operations (can be changed!)
- Configurable AP password & Hidden attribute
- **Advanced settings storage**
- Stored in Flash
- Seamlesly updated and usually backwards compatible in minor releases
- Settings can be saved as defaults and later easily restored (ideal e.g. for classroom use, saving
good tested settings before giving the module to students)
## Bugs? Ideas?
\*) Linux console applications run via agetty at ttyUSB0 were used for testing, obviously you'll be better off using SSH for remote shell
To ask any questions or discuss new features you'd like to see added, it's best to use
the **[ESPTerm Dev mailing list](https://groups.google.com/forum/#!forum/espterm-dev)**
ESPTerm firmware is written in C and is based on SpriteTM's `libesphttpd` http server library, forked by MightyPork to
[MightyPork/libesphttpd](https://github.com/MightyPork/libesphttpd) respectively. This fork includes various improvements and changes required by the project.
Subscribe to the mailing list to also receive new release announcements.
If you found a bug (that happens alot!), please submit it to our [bug tracker](https://github.com/espterm/espterm-firmware/issues).
We also use it to track planned ideas. If you don't want to create a GitHub account for that, just send it to the mailing list.
## Running ESPTerm
To run ESPTerm on your ESP8266, either build it yourself from source using `xtensa-lx106-elf-gcc` (and the included Makefile), or download pre-built binaries from the [GitHub releases section][releases]. Flash the binaries using [esptool](https://github.com/espressif/esptool).
To run ESPTerm on your ESP8266, either build it yourself from source using `xtensa-lx106-elf-gcc`
(and the included Makefile), or download pre-built binaries from the [GitHub releases section][releases].
Flash the binaries using [esptool][esptool].
### Pins
- Pin GPIO2 is used for debug messages at 115200 baud, 8 bit, no parity.
- Pins Rx and Tx are used for the main communication UART. Connect your USB-serial dongle or application microcontroller here.
### Console commands - escape sequences
- A list of most supported commands can be found here: http://bits.ondrovo.com/espterm-xterm.html
- To set the screen title, use `OSC 0 ; title ST` (`OSC` = `ESC ]`, `ST` = `ESC \` or ASCII 7)
- To set buttons text, use `OSC 8 1 ; text ST` with 81 through 85.
- Mouse clicks (as of v0.6.9) generate `CSI row ; col M` at the Tx pin (`CSI` = `ESC [`)
- For more info, look eg. on Wikipedia or here: http://ascii-table.com/ansi-escape-sequences-vt-100.php
- Pins Rx and Tx are used for the main communication UART, parameters of which can be set on the system config page.
Connect your USB-serial dongle or application microcontroller here.
### Setup
- When flashed for the first time, ESPTerm wipes any possible previous WiFi configuration, because it implements its own WiFi config manager with many additional features.
- It should start in AP mode, the default SSID being `TERM-MACADR` with `MACADR` being three unique bytes from the MAC address / Device ID.
- Connect to it via a smartphone or laptop and configure WiFi as desired.
- To re-enable the built-in AP, hold the BOOT (GPIO0) button for about 1 s, until the blue LED starts slowly flashing. Then release the button!
- To reset all settings to defaults, hold the BOOT (GPIO0) button until the blue LED flashes rapidly, then release the button.
- When you accidentally make the blue LED flash, you can cancel the operation by pressing Reset or disconnecting its power supply. The wipe is done on the button's release.
- When flashed for the first time, ESPTerm wipes any possible previous WiFi configuration, because it implements its own WiFi
config manager with many additional features.
- It should start in AP mode, the default SSID being `TERM-MACADR` with `MACADR` being three unique bytes from the MAC
address / Device ID as ASCII hex.
- Connect to the AP via a smartphone or laptop and configure WiFi as desired. If a captive portal page does not open,
try 192.168.4.1 in your web browser.
### Config files
### Rescue from messed up config
ESPTerm has two config "files", one for defaults and one for the currently used settings. In the case of the terminal config, there is also a third, temporary file for changes done via ESC commands.
It can happen that some changes to the WiFi or network config make the module inaccessible.
- To re-enable the built-in AP, hold the BOOT (GPIO0 -> GND) button for about 1 s, until the blue LED starts flashing.
Then quickly release the button.
- To reset all settings to defaults, hold the button a couple seconds until the LED flashes rapidly, then release it.
- You can cancel this wipe/reset operation (when triggered by accident) by pressing Reset or disconnecting the power supply.
When you get your settings *just right*, you can store them as defaults, which can then be at any time restored by holding the BOOT (GPIO0) button. You can do this on the System Settings page. This asks for an "admin password", which you can define when building the firmware in the `esphttpdconfig.mk` file. The default password is `19738426`. This password can't presently be changed without re-flashing the firmware.
### Config banks
You can also restore everything (except the saved defaults) to "factory defaults", there is a button for this on the System Settings page. Those are the initial values in the config files.
ESPTerm has two config "files", one for defaults and one for the currently used settings. In the case of the terminal
config, there is also a third, temporary file for changes done via ESC commands.
When you get your settings *just right*, you can store them as defaults, which can then be at any time restored
by holding the BOOT (GPIO0) button. You can do this on the System Settings page. This asks for an "admin password",
which can (and should!) be changed. This password can't be easily recovered when forgotten.
You can also restore everything (except the saved defaults) to "factory defaults", there is a button for this
on the System Settings page. Those are the initial values you would get after a clean install.
## Research resources
Developing the terminal emulator was complicated by the information being scattered across many places and the existing
implementations I used for reference often got some details wrong or didn't implement certain features at all. Xterm proved to be by far the most complete implementation.
A great tool for checking my code has proven to be [VTTTEST][vttest]. ESPTerm passes most of the tests on the main page and some additional Xterm specific ones, like Mouse Tracking.
Here is a list of useful [VT100 emulation resources][resources] I've collected for reference.
## Development
ESPTerm's firmware is written in C and is based on SpriteTM's `libesphttpd` http server library forked to
[MightyPork/libesphttpd][httpdlib]. This fork includes various improvements and changes required by the project.
### Installation for development
- Clone this project with `--recursive`, or afterwards run `git submodule init` and `git submodule update`.
- Install [esp-open-sdk](https://github.com/pfalcon/esp-open-sdk/) and build it with
- Install [esp-open-sdk][opensdk] and build it with
`make toolchain esptool libhal STANDALONE=n`.
Make sure the `xtensa-lx106-elf/bin` folder is on $PATH.
- Install [esptool](https://github.com/espressif/esptool) (it's in the Arch community repo and on AUR, too)
- Install [esptool][esptool] (it's in the Arch community repo and on AUR, too)
- Set up udev rules so you have access to ttyUSB0 without root, eg:
```
KERNEL=="tty[A-Z]*[0-9]*", GROUP="uucp", MODE="0666"
```
- Install Ragel if you wish to make modifications to the ANSI sequence parser.
If not, comment out its call in `build_parser.sh`. The `.rl` file is the actual source, the `.c` is generated.
- Install Ruby and then the `sass` package with `gem install sass` (or try some other implementation, such as
`sassc`)
- Install dependencies of the front-end submodule (`yarn install` in the front-end folder, installed PHP)
- Make sure your `esphttpdconfig.mk` is set up properly - link to the SDK etc.
The IoT SDK is now included in the project due to problems with obtaining the correct version and patching it.
@@ -103,23 +174,35 @@ than welcome!
### Web resources
The web resources are in `html_orig`. To prepare for a build, run `build_web.sh`, which packs them and
copies over to `html`. The compression and minification is handled by scripts in libesphttpd, specifically,
it runs yuicompressor on js and css and gzip or heatshrink on the other files. The `html` folder is
then embedded in the firmware image.
The web resources are in the `front-end` git submodule. To prepare the web resources for a build,
run `make web`. The resulting files are copied to `html/`. The `html/` folder is then embedded in the firmware image.
It's kind of tricky to develop the web resources locally; you might want to try the "split image"
Makefile option, then you can flash just the html portion with `make htmlflash`. I haven't tried this.
For local development, use the `server.sh` script in `html_orig`. It's possible to talk to API of a running
ESP8266 from here, if you configure `_env.php` with its IP.
For local development, use the `server.sh` script in `html_orig` (more details in the front-end repo's readme).
It's possible to talk to the API endpoints of a running ESP8266 from a page served by your local server
if you configure `_env.php` with its IP.
### Flashing
The Makefile should automatically build the parser and web resources for you when you run `make`.
Sometimes it does not, particularly with `make -B`. Try just plain `make`. You can always run those
build scripts manually, too.
The Makefile should automatically build the parser and web resources for you when you run `make`.
The web resources are normally no re-built, because the build process is quite slow. To manually rebuild them,
run `make web` before `make`.
To flash, just run `make flash`.
To flash, just run `make flash`. It will use parameters you setup in the `esphttpdconfig.mk` file.
[releases]: https://github.com/MightyPork/esp-vt100-firmware/releases
[httpdlib]: https://github.com/MightyPork/libesphttpd
[esptool]: https://github.com/espressif/esptool
[opensdk]: https://github.com/pfalcon/esp-open-sdk/
[demo-help]: https://espterm.github.io/help.html
[demo-wifi]: https://espterm.github.io/cfg_wifi.html
[demo-network]: https://espterm.github.io/cfg_network.html
[demo-term]: https://espterm.github.io/term.html
[xterm-compare]: https://espterm.github.io/docs/espterm-xterm.html
[photo-hw]: https://espterm.github.io/docs/espterm-hw-small.jpg
[resources]: https://espterm.github.io/docs/index.html
[vttest]: http://invisible-island.net/vttest/
+7
View File
@@ -3,3 +3,10 @@
echo "-- Building parser from Ragel source --"
ragel -L -G0 user/ansi_parser.rl -o user/ansi_parser.c
ragel -L -G0 user/ini_parser.rl -o user/ini_parser.c
sed -i "s/static const char _ansi_actions\[\]/static const char _ansi_actions\[\] ESP_CONST_DATA/" user/ansi_parser.c
sed -i "s/static const char _ansi_eof_actions\[\]/static const char _ansi_eof_actions\[\] ESP_CONST_DATA/" user/ansi_parser.c
sed -i "s/static const char _ini_actions\[\]/static const char _ini_actions\[\] ESP_CONST_DATA/" user/ini_parser.c
sed -i "s/static const char _ini_eof_actions\[\]/static const char _ini_eof_actions\[\] ESP_CONST_DATA/" user/ini_parser.c
+3 -13
View File
@@ -3,19 +3,9 @@
echo "-- Preparing WWW files --"
[[ -e html ]] && rm -r html
mkdir -p html/img
mkdir -p html/js
mkdir -p html/css
cd html_orig
sh ./packjs.sh
php ./build_html.php
cd ..
ESP_PROD=1 front-end/build.sh
cp html_orig/js/app.js html/js/
echo "Copying from submodule..."
sass html_orig/sass/app.scss html/css/app.css
rm html/css/app.css.map
cp html_orig/img/* html/img/
cp html_orig/favicon.ico html/favicon.ico
cp -r front-end/out html
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -35,9 +35,9 @@
#define os_snprintf ets_snprintf
#ifdef USE_OPTIMIZE_PRINTF
#define os_printf(fmt, ...) do { \
static const char flash_str[] ICACHE_RODATA_ATTR STORE_ATTR = fmt; \
os_printf_plus(flash_str, ##__VA_ARGS__); \
#define os_printf(fmt, ...) do { \
static const char flash_str[] ICACHE_RODATA_ATTR STORE_ATTR = fmt; \
os_printf_plus(flash_str, ##__VA_ARGS__); \
} while(0)
#else
#define os_printf os_printf_plus
@@ -18,7 +18,7 @@ GZIP_COMPRESSION = yes
# This option works only when GZIP_COMPRESSION is set to "yes"
# http://yui.github.io/yuicompressor/
#Disabled by default.
COMPRESS_W_YUI = yes
COMPRESS_W_YUI = no
YUI-COMPRESSOR = /usr/bin/yui-compressor
@@ -38,20 +38,28 @@ OUTPUT_TYPE = combined
# SPI flash size, in K
ESP_SPI_FLASH_SIZE_K = 1024
# Admin password, used to store settings to flash as defaults
ADMIN_PASSWORD = "19738426"
GLOBAL_CFLAGS = \
-DASYNC_LOG=1 \
-DDEBUG_INI=1 \
-DDEBUG_D2D=0 \
-DDEBUG_ROUTER=0 \
-DDEBUG_CAPTDNS=0 \
-DDEBUG_HTTP=1 \
-DDEBUG_HTTP=0 \
-DDEBUG_HTTPC=0 \
-DDEBUG_ESPFS=0 \
-DDEBUG_PERSIST=1 \
-DDEBUG_UTFCACHE=0 \
-DDEBUG_CGI=0 \
-DDEBUG_WIFI=0 \
-DDEBUG_WS=0 \
-DDEBUG_ANSI=1 \
-DDEBUG_ANSI_NOIMPL=1 \
-DDEBUG_INPUT=0 \
-DDEBUG_HEAP=1 \
-DDEBUG_MALLOC=0 \
-DHTTPD_MAX_BACKLOG_SIZE=8192 \
-DHTTPD_MAX_HEAD_LEN=1024 \
-DHTTPD_MAX_POST_LEN=512 \
-DDEBUG_INPUT=0 \
-DDEBUG_HEAP=1 \
-DDEBUG_MALLOC=0
-DDEBUG_LOGBUF_SIZE=1024 \
-mforce-l32 \
-DUSE_OPTIMIZE_PRINTF=1
Submodule
+1
Submodule front-end added at 9aae724df2
+14
View File
@@ -0,0 +1,14 @@
//
// Created by MightyPork on 2017/10/03.
//
// helper for building release packages
//
// Run with `tcc -run`
//
#include "user/version.h"
#include <stdio.h>
void main() {
printf(FW_VERSION);
}
-1
View File
@@ -1 +0,0 @@
pages/_test_env.php
-70
View File
@@ -1,70 +0,0 @@
<?php
/**
* Those replacements are done by the development server to test it locally
* without esphttpd. This is needed mainly for places where the replacements
* are given to JavaScript, to avoid syntax errors with
*/
return [
'term_title' => 'ESPTerm local debug',
'btn1' => '1',
'btn2' => '2',
'btn3' => '3',
'btn4' => '',
'btn5' => '5',
'labels_seq' => 'TESPTerm local debug1235',
'screenData' => '  HELLOx NRE3',//'\u000b\u0001\u001b\u0001\u0001\u0001\u0001\u0001\f\u0005\u0001\u0010\u0003HELLOx\u0002\u000b\u0001\u0001N\u0001RE\u00023\u0001', //,
'parser_tout_ms' => 10,
'display_tout_ms' => 20,
'fn_alt_mode' => '1',
'opmode' => '2',
'sta_enable' => '0',
'ap_enable' => '1',
'tpw' => '60',
'ap_channel' => '7',
'ap_ssid' => 'ESP-123456',
'ap_password' => 'Passw0rd!',
'ap_hidden' => '0',
'sta_ssid' => 'Chlivek',
'sta_password' => 'windows XP is The Best',
'sta_active_ip' => '1.2.3.4',
'sta_active_ssid' => 'Chlivek',
'vers_fw' => '1.2.3',
'date' => date('Y-m-d'),
'time' => date('G:i'),
'vers_httpd' => '4.5.6',
'vers_sdk' => '1.52',
'githubrepo' => 'https://github.com/MightyPork/esp-vt100-firmware',
'ap_dhcp_time' => '120',
'ap_dhcp_start' => '192.168.4.100',
'ap_dhcp_end' => '192.168.4.200',
'ap_addr_ip' => '192.168.4.1',
'ap_addr_mask' => '255.255.255.0',
'sta_dhcp_enable' => '1',
'sta_addr_ip' => '192.168.0.33',
'sta_addr_mask' => '255.255.255.0',
'sta_addr_gw' => '192.168.0.1',
'sta_mac' => 'ab:cd:ef:01:23:45',
'ap_mac' => '01:23:45:ab:cd:ef',
'term_width' => '26',
'term_height' => '10',
'default_bg' => '0',
'default_fg' => '7',
'uart_baud' => 115200,
'uart_stopbits' => 1,
'uart_parity' => 2,
'theme' => 5,
];
-3
View File
@@ -1,3 +0,0 @@
<?php
define("ESP_IP", "192.168.0.19");
-3
View File
@@ -1,3 +0,0 @@
<?php
define("ESP_IP", "192.168.0.19");
-46
View File
@@ -1,46 +0,0 @@
<?php
$pages = [];
if (! function_exists('pg')) {
/** Add a page */
function pg($key, $bc, $icon, $path, $titleKey = null)
{
global $pages;
$pages[$key] = (object) [
'key' => $key,
'bodyclass' => $bc,
'path' => $path,
'icon' => $icon ? "icn-$icon" : '',
'label' => tr("menu.$key"),
'title' => $titleKey ? tr($titleKey) : tr("menu.$key"),
];
}
}
pg('cfg_term', 'cfg', 'terminal', '/cfg/term');
pg('term_set', 'api', '', '/cfg/term/set');
pg('cfg_wifi', 'cfg', 'wifi', '/cfg/wifi');
pg('cfg_wifi_conn', '', '', '/cfg/wifi/connecting');
pg('wifi_connstatus', 'api', '', '/cfg/wifi/connstatus');
pg('wifi_set', 'api', '', '/cfg/wifi/set');
pg('wifi_scan', 'api', '', '/cfg/wifi/scan');
pg('cfg_network', 'cfg', 'network', '/cfg/network');
pg('network_set', 'api', '', '/cfg/network/set');
pg('cfg_system', 'cfg', 'configure', '/cfg/system');
pg('system_set', 'api', '', '/cfg/system/set');
pg('write_defaults', 'api', '', '/cfg/system/write_defaults');
pg('restore_defaults', 'api', '', '/cfg/system/restore_defaults');
pg('restore_hard', 'api', '', '/cfg/system/restore_hard');
pg('help', 'cfg page-help', 'help', '/help');
pg('about', 'cfg page-about', 'about', '/about');
pg('term', 'term', '', '/', 'title.term');
// ajax API
return $pages;
-71
View File
@@ -1,71 +0,0 @@
<?php
/**
* The common stuff required by both index.php and build_html.php
* this must be required_once
*/
if (defined('BASE_INITED')) return;
define('BASE_INITED', true);
if (!empty($argv[1])) {
parse_str($argv[1], $_GET);
}
if (!file_exists(__DIR__ . '/_env.php')) {
die("Copy <b>_env.php.example</b> to <b>_env.php</b> and check the settings inside!");
}
require_once __DIR__ . '/_env.php';
$prod = defined('STDIN');
define('DEBUG', !$prod);
$root = DEBUG ? json_encode(ESP_IP) : 'location.host';
define('JS_WEB_ROOT', $root);
define('LOCALE', isset($_GET['locale']) ? $_GET['locale'] : 'en');
$_messages = require(__DIR__ . '/lang/' . LOCALE . '.php');
$_pages = require(__DIR__ . '/_pages.php');
define('APP_NAME', 'ESPTerm');
/** URL (dev or production) */
function url($name, $relative = false)
{
global $_pages;
if ($relative) return $_pages[$name]->path;
if (DEBUG) return "/index.php?page=$name";
else return $_pages[$name]->path;
}
/** URL label for a button */
function label($name)
{
global $_pages;
return $_pages[$name]->label;
}
function e($s)
{
return htmlspecialchars($s, ENT_HTML5 | ENT_QUOTES);
}
function tr($key)
{
global $_messages;
return isset($_messages[$key]) ? $_messages[$key] : ('??' . $key . '??');
}
/** Like eval, but allows <?php and ?> */
function include_str($code)
{
$tmp = tmpfile();
$tmpf = stream_get_meta_data($tmp);
$tmpf = $tmpf ['uri'];
fwrite($tmp, $code);
$ret = include($tmpf);
fclose($tmp);
return $ret;
}
-39
View File
@@ -1,39 +0,0 @@
<?php
require_once __DIR__ . '/base.php';
function process_html($s) {
$pattern = '/<!--(.*)-->/Uis';
$s = preg_replace($pattern, '', $s);
$pattern = '/(?:(?:\/\*(?:[^*]|(?:\*+[^*\/]))*\*+\/)|(?:(?<!\:|\\\|\'|\")\/\/.*))/';
$s = preg_replace($pattern, '', $s);
$pattern = '/\s+/s';
$s = preg_replace($pattern, ' ', $s);
return $s;
}
$no_tpl_files = ['help', 'cfg_wifi_conn'];
ob_start();
foreach($_pages as $_k => $p) {
if ($p->bodyclass == 'api') continue;
echo "Generating: $_k ($p->title)\n";
$_GET['page'] = $_k;
ob_flush(); // print the message
ob_clean(); // clean up
include(__DIR__ . '/index.php');
$s = ob_get_contents(); // grab the output
// remove newlines and comments
// as tests have shown, it saves just a couple kilobytes,
// making it not a very big improvement at the expense of ugly html.
// $s = process_html($s);
ob_clean(); // clean up
$of = __DIR__ . '/../html/' . $_k . (in_array($_k, $no_tpl_files) ? '.html' : '.tpl');
file_put_contents($of, $s); // write to a file
}
ob_flush();
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-22
View File
@@ -1,22 +0,0 @@
<?php
/* This script is run on demand to generate JS version of tr() */
require_once __DIR__ . '/base.php';
$selected = [
'wifi.connected_ip_is',
'wifi.not_conn',
'wifi.enter_passwd',
];
$out = [];
foreach ($selected as $key) {
$out[$key] = $_messages[$key];
}
file_put_contents(__DIR__. '/jssrc/lang.js',
"// Generated from PHP locale file\n" .
'var _tr = ' . json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE) . ";\n\n" .
"function tr(key) { return _tr[key] || '?'+key+'?'; }\n"
);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 B

-1
View File
@@ -1 +0,0 @@
/out/
Binary file not shown.
-90
View File
@@ -1,90 +0,0 @@
#!/bin/bash
# resolve current file's directory
DIR=$(dirname $(realpath $0))
OUTPUT_DIR="$DIR/out"
SASS_DIR="$DIR/../sass"
ICON_PREFIX='icn'
# list with full paths, sort from newest
NEWEST=$(ls -dt1 "$DIR"/*.zip | head -1)
if [[ -z "$NEWEST" ]]; then
echo "Fontello zip not found."
exit 1
fi
# Clean the output folder
rm -rf "$OUTPUT_DIR"
mkdir -p "$OUTPUT_DIR"
echo "Unpacking fontello..."
unzip -ju "$NEWEST" -d "$OUTPUT_DIR"
echo "Patching paths in the fontello CSS..."
# Fix bad relative paths in the CSS
sed -i "s|\.\./font/|/fonts/|g" "$OUTPUT_DIR/"*.css
echo "Generating SASS file with icon codes..."
SASSFILE="$SASS_DIR/_fontello.scss"
echo -e "@charset \"UTF-8\";\n\n/* Fontello data, processed by the unpack script. */\n" > "$SASSFILE"
# Extract the base font-face style
#grep -Pazo "(?s)@font-face.*?normal;\n\}" "$OUTPUT_DIR/fontello.css" \
# | sed 's/\x0//g' >> "$SASSFILE"
grep -Pazo "(?s)@font-face \{\n\s*font-family: 'fontello';\n\s*src: url\('data.*?truetype'\);\n\}" "$OUTPUT_DIR/fontello-embedded.css" \
| sed 's/\x0//g' >> "$SASSFILE"
grep -Pazo "(?s)$ICON_PREFIX-\"\]:before .*?\}" "$OUTPUT_DIR/fontello.css" \
| sed 's/\x0//g' \
| sed "s/$ICON_PREFIX-\"\]:before/\n\n%fontello-icon-base \{\n\&::before /g" \
>> "$SASSFILE"
echo -e "\n}" >> "$SASSFILE"
echo -e "\n\n/* Fontello icon codes */" >> "$SASSFILE"
echo -n "\$icon-codes: (" >> "$SASSFILE"
sed -r "s|\.$ICON_PREFIX-([a-z0-9-]+):before \{ content: ('.*?');.*?$|\t\1: \2,|g" "$OUTPUT_DIR/fontello-codes.css" \
| sed -r "s|@.*||g" >> "$SASSFILE"
echo -ne "\n);\n" >> "$SASSFILE"
echo -ne "\n/* Fontello classes */" >> "$SASSFILE"
cat "$OUTPUT_DIR/fontello-codes.css" \
| sed -r 's/\/\*.+\*\///g' \
| sed -r "s|@.*||g" \
| sed 's/:before/::before/g' >> "$SASSFILE"
TAIL=$(cat <<ASDF
[class^="$ICON_PREFIX-"], [class*=" $ICON_PREFIX-"] {
@extend %fontello-icon-base;
}
@mixin icon-base() {
@extend %fontello-icon-base;
}
@mixin icon-content(\$icon-name) {
&::before {
content: map-get(\$icon-codes, \$icon-name);
}
}
@mixin icon(\$icon-name) {
@include icon-base();
@include icon-content(\icon-name);
}
ASDF
)
echo "$TAIL" >> "$SASSFILE"
echo -e "\e[32mFontello ready\e[0m"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

-53
View File
@@ -1,53 +0,0 @@
<?php
require_once __DIR__ . '/base.php';
if (!isset($_GET['page'])) $_GET['page'] = 'term';
$_GET['PAGE_TITLE'] = $_pages[$_GET['page']]->title . ' :: ' . APP_NAME;
$_GET['BODYCLASS'] = $_pages[$_GET['page']]->bodyclass;
if (!function_exists('tplSubs')) {
function tplSubs($str, $reps)
{
return preg_replace_callback('/%(j:|js:|h:|html:)?([a-z0-9-_.]+)%/i', function ($m) use ($reps) {
$key = $m[2];
if (array_key_exists($key, $reps)) {
$val = $reps[$key];
} else {
$val = '';
}
switch ($m[1]) {
case 'j:':
case 'js:':
$v = json_encode($val);
return substr($v, 1, strlen($v) - 2);
case 'h:':
case 'html:':
return htmlspecialchars($val);
default:
return $val;
}
}, $str);
}
}
require __DIR__ . '/pages/_head.php';
$_pf = __DIR__ . '/pages/'.$_GET['page'].'.php';
if (file_exists($_pf)) {
$f = file_get_contents($_pf);
if (DEBUG)
$str = tplSubs($f, require(__DIR__ . '/_debug_replacements.php'));
else $str = $f;
// special symbols
$str = str_replace('\,', '&#8239;', $str);
$str = preg_replace('/(?<=\w)~(?=\w)/', '&nbsp;', $str);
include_str($str);
} else {
echo "404";
}
require __DIR__ . '/pages/_tail.php';
-2281
View File
File diff suppressed because it is too large Load Diff
-189
View File
@@ -1,189 +0,0 @@
/** Global generic init */
$.ready(function () {
// Checkbox UI (checkbox CSS and hidden input with int value)
$('.Row.checkbox').forEach(function(x) {
var inp = x.querySelector('input');
var box = x.querySelector('.box');
$(box).toggleClass('checked', inp.value);
var hdl = function() {
inp.value = 1 - inp.value;
$(box).toggleClass('checked', inp.value)
};
$(x).on('click', hdl).on('keypress', cr(hdl));
});
// Expanding boxes on mobile
$('.Box.mobcol,.Box.fold').forEach(function(x) {
var h = x.querySelector('h2');
var hdl = function() {
$(x).toggleClass('expanded');
};
$(h).on('click', hdl).on('keypress', cr(hdl));
});
qsa('form').forEach(function(x) {
$(x).on('keypress', function(e) {
if ((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey) {
x.submit();
}
})
});
// loader dots...
setInterval(function () {
$('.anim-dots').each(function (x) {
var $x = $(x);
var dots = $x.html() + '.';
if (dots.length == 5) dots = '.';
$x.html(dots);
});
}, 1000);
// flipping number boxes with the mouse wheel
$('input[type=number]').on('mousewheel', function(e) {
var $this = $(this);
var val = +$this.val();
if (isNaN(val)) val = 1;
var step = +($this.attr('step') || 1);
var min = +$this.attr('min');
var max = +$this.attr('max');
if(e.wheelDelta > 0) {
val += step;
} else {
val -= step;
}
if (typeof min != 'undefined') val = Math.max(val, +min);
if (typeof max != 'undefined') val = Math.min(val, +max);
$this.val(val);
if ("createEvent" in document) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true);
$this[0].dispatchEvent(evt);
} else {
$this[0].fireEvent("onchange");
}
e.preventDefault();
});
var errAt = location.search.indexOf('err=');
if (errAt !== -1 && qs('.Box.errors')) {
var errs = location.search.substr(errAt+4).split(',');
var hres = [];
errs.forEach(function(er) {
var lbl = qs('label[for="'+er+'"]');
if (lbl) {
lbl.classList.add('error');
hres.push(lbl.childNodes[0].textContent.trim().replace(/: ?$/, ''));
} else {
hres.push(er);
}
});
qs('.Box.errors .list').innerHTML = hres.join(', ');
qs('.Box.errors').classList.remove('hidden');
}
Modal.init();
Notify.init();
// remove tabindixes from h2 if wide
if (window.innerWidth > 550) {
qsa('.Box h2').forEach(function (x) {
x.removeAttribute('tabindex');
});
// brand works as a link back to term in widescreen mode
var br = qs('#brand');
br && br.addEventListener('click', function() {
location.href='/'; // go to terminal
});
}
});
$._loader = function(vis) {
$('#loader').toggleClass('show', vis);
};
function showPage() {
$('#content').addClass('load');
}
$.ready(function() {
if (window.noAutoShow !== true) {
setTimeout(function () {
showPage();
}, 1);
}
});
/*! http://mths.be/fromcodepoint v0.1.0 by @mathias */
if (!String.fromCodePoint) {
(function() {
var defineProperty = (function() {
// IE 8 only supports `Object.defineProperty` on DOM elements
try {
var object = {};
var $defineProperty = Object.defineProperty;
var result = $defineProperty(object, object, object) && $defineProperty;
} catch(error) {}
return result;
}());
var stringFromCharCode = String.fromCharCode;
var floor = Math.floor;
var fromCodePoint = function() {
var MAX_SIZE = 0x4000;
var codeUnits = [];
var highSurrogate;
var lowSurrogate;
var index = -1;
var length = arguments.length;
if (!length) {
return '';
}
var result = '';
while (++index < length) {
var codePoint = Number(arguments[index]);
if (
!isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
codePoint < 0 || // not a valid Unicode code point
codePoint > 0x10FFFF || // not a valid Unicode code point
floor(codePoint) != codePoint // not an integer
) {
throw RangeError('Invalid code point: ' + codePoint);
}
if (codePoint <= 0xFFFF) { // BMP code point
codeUnits.push(codePoint);
} else { // Astral code point; split in surrogate halves
// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
codePoint -= 0x10000;
highSurrogate = (codePoint >> 10) + 0xD800;
lowSurrogate = (codePoint % 0x400) + 0xDC00;
codeUnits.push(highSurrogate, lowSurrogate);
}
if (index + 1 == length || codeUnits.length > MAX_SIZE) {
result += stringFromCharCode.apply(null, codeUnits);
codeUnits.length = 0;
}
}
return result;
};
if (defineProperty) {
defineProperty(String, 'fromCodePoint', {
'value': fromCodePoint,
'configurable': true,
'writable': true
});
} else {
String.fromCodePoint = fromCodePoint;
}
}());
}
-703
View File
@@ -1,703 +0,0 @@
/*!chibi 3.0.7, Copyright 2012-2016 Kyle Barrow, released under MIT license */
// MODIFIED VERSION.
(function () {
'use strict';
var readyfn = [],
loadedfn = [],
domready = false,
pageloaded = false,
d = document,
w = window;
// Fire any function calls on ready event
function fireReady() {
var i;
domready = true;
for (i = 0; i < readyfn.length; i += 1) {
readyfn[i]();
}
readyfn = [];
}
// Fire any function calls on loaded event
function fireLoaded() {
var i;
pageloaded = true;
// For browsers with no DOM loaded support
if (!domready) {
fireReady();
}
for (i = 0; i < loadedfn.length; i += 1) {
loadedfn[i]();
}
loadedfn = [];
}
// Check DOM ready, page loaded
if (d.addEventListener) {
// Standards
d.addEventListener('DOMContentLoaded', fireReady, false);
w.addEventListener('load', fireLoaded, false);
} else if (d.attachEvent) {
// IE
d.attachEvent('onreadystatechange', fireReady);
// IE < 9
w.attachEvent('onload', fireLoaded);
} else {
// Anything else
w.onload = fireLoaded;
}
// Utility functions
// Loop through node array
function nodeLoop(fn, nodes) {
var i;
// Good idea to walk up the DOM
for (i = nodes.length - 1; i >= 0; i -= 1) {
fn(nodes[i]);
}
}
// Convert to camel case
function cssCamel(property) {
return property.replace(/-\w/g, function (result) {
return result.charAt(1).toUpperCase();
});
}
// Get computed style
function computeStyle(elm, property) {
// IE, everything else or null
return (elm.currentStyle) ? elm.currentStyle[cssCamel(property)] : (w.getComputedStyle) ? w.getComputedStyle(elm, null).getPropertyValue(property) : null;
}
// Returns URI encoded query string pair
function queryPair(name, value) {
return encodeURIComponent(name).replace(/%20/g, '+') + '=' + encodeURIComponent(value).replace(/%20/g, '+');
}
// Set CSS, important to wrap in try to prevent error thown on unsupported property
function setCss(elm, property, value) {
try {
elm.style[cssCamel(property)] = value;
} catch (e) {
}
}
// Show CSS
function showCss(elm) {
elm.style.display = '';
// For elements still hidden by style block
if (computeStyle(elm, 'display') === 'none') {
elm.style.display = 'block';
}
}
// Serialize form & JSON values
function serializeData(nodes) {
var querystring = '', subelm, i, j;
if (nodes.constructor === Object) { // Serialize JSON data
for (subelm in nodes) {
if (nodes.hasOwnProperty(subelm)) {
if (nodes[subelm].constructor === Array) {
for (i = 0; i < nodes[subelm].length; i += 1) {
querystring += '&' + queryPair(subelm, nodes[subelm][i]);
}
} else {
querystring += '&' + queryPair(subelm, nodes[subelm]);
}
}
}
} else { // Serialize node data
nodeLoop(function (elm) {
if (elm.nodeName === 'FORM') {
for (i = 0; i < elm.elements.length; i += 1) {
subelm = elm.elements[i];
if (!subelm.disabled) {
switch (subelm.type) {
// Ignore buttons, unsupported XHR 1 form fields
case 'button':
case 'image':
case 'file':
case 'submit':
case 'reset':
break;
case 'select-one':
if (subelm.length > 0) {
querystring += '&' + queryPair(subelm.name, subelm.value);
}
break;
case 'select-multiple':
for (j = 0; j < subelm.length; j += 1) {
if (subelm[j].selected) {
querystring += '&' + queryPair(subelm.name, subelm[j].value);
}
}
break;
case 'checkbox':
case 'radio':
if (subelm.checked) {
querystring += '&' + queryPair(subelm.name, subelm.value);
}
break;
// Everything else including shinny new HTML5 input types
default:
querystring += '&' + queryPair(subelm.name, subelm.value);
}
}
}
}
}, nodes);
}
// Tidy up first &
return (querystring.length > 0) ? querystring.substring(1) : '';
}
// Class helper
function classHelper(classes, action, nodes) {
var classarray, search, i, replace, has = false;
if (classes) {
// Trim any whitespace
classarray = classes.split(/\s+/);
nodeLoop(function (elm) {
for (i = 0; i < classarray.length; i += 1) {
var clz = classarray[i];
if (action === 'remove') {
elm.classList.remove(clz);
}
else if (action === 'add') {
elm.classList.add(clz);
}
else if (action === 'toggle') {
elm.classList.toggle(clz);
}
else if (action === 'has') {
if (elm.classList.contains(clz)) {
has = true;
break;
}
}
// search = new RegExp('\\b' + classarray[i] + '\\b', 'g');
// replace = new RegExp(' *' + classarray[i] + '\\b', 'g');
// if (action === 'remove') {
// elm.className = elm.className.replace(search, '');
// } else if (action === 'toggle') {
// elm.className = (elm.className.match(search)) ? elm.className.replace(replace, '') : elm.className + ' ' + classarray[i];
// } else if (action === 'has') {
// if (elm.className.match(search)) {
// has = true;
// break;
// }
// }
}
}, nodes);
}
return has;
}
// HTML insertion helper
function insertHtml(value, position, nodes) {
var tmpnodes, tmpnode;
if (value) {
nodeLoop(function (elm) {
// No insertAdjacentHTML support for FF < 8 and IE doesn't allow insertAdjacentHTML table manipulation, so use this instead
// Convert string to node. We can't innerHTML on a document fragment
tmpnodes = d.createElement('div');
tmpnodes.innerHTML = value;
while ((tmpnode = tmpnodes.lastChild) !== null) {
// Catch error in unlikely case elm has been removed
try {
if (position === 'before') {
elm.parentNode.insertBefore(tmpnode, elm);
} else if (position === 'after') {
elm.parentNode.insertBefore(tmpnode, elm.nextSibling);
} else if (position === 'append') {
elm.appendChild(tmpnode);
} else if (position === 'prepend') {
elm.insertBefore(tmpnode, elm.firstChild);
}
} catch (e) {
break;
}
}
}, nodes);
}
}
// Get nodes and return chibi
function chibi(selector) {
var cb, nodes = [], json = false, nodelist, i;
if (selector) {
// Element node, would prefer to use (selector instanceof HTMLElement) but no IE support
if (selector.nodeType && selector.nodeType === 1) {
nodes = [selector]; // return element as node list
} else if (typeof selector === 'object') {
// JSON, document object or node list, would prefer to use (selector instanceof NodeList) but no IE support
json = (typeof selector.length !== 'number');
nodes = selector;
} else if (typeof selector === 'string') {
nodelist = d.querySelectorAll(selector);
// Convert node list to array so results have full access to array methods
// Array.prototype.slice.call not supported in IE < 9 and often slower than loop anyway
for (i = 0; i < nodelist.length; i += 1) {
nodes[i] = nodelist[i];
}
}
}
// Only attach nodes if not JSON
cb = json ? {} : nodes;
// Public functions
// Executes a function on nodes
cb.each = function (fn) {
if (typeof fn === 'function') {
nodeLoop(function (elm) {
// <= IE 8 loses scope so need to apply
return fn.apply(elm, arguments);
}, nodes);
}
return cb;
};
// Find first
cb.first = function () {
return chibi(nodes.shift());
};
// Find last
cb.last = function () {
return chibi(nodes.pop());
};
// Find odd
cb.odd = function () {
var odds = [], i;
for (i = 0; i < nodes.length; i += 2) {
odds.push(nodes[i]);
}
return chibi(odds);
};
// Find even
cb.even = function () {
var evens = [], i;
for (i = 1; i < nodes.length; i += 2) {
evens.push(nodes[i]);
}
return chibi(evens);
};
// Hide node
cb.hide = function () {
nodeLoop(function (elm) {
elm.style.display = 'none';
}, nodes);
return cb;
};
// Show node
cb.show = function () {
nodeLoop(function (elm) {
showCss(elm);
}, nodes);
return cb;
};
// Toggle node display
cb.toggle = function (state) {
if (typeof state != 'undefined') { // ADDED
if (state)
cb.show();
else
cb.hide();
} else {
nodeLoop(function (elm) {
// computeStyle instead of style.display == 'none' catches elements that are hidden via style block
if (computeStyle(elm, 'display') === 'none') {
showCss(elm);
} else {
elm.style.display = 'none';
}
}, nodes);
}
return cb;
};
// Remove node
cb.remove = function () {
nodeLoop(function (elm) {
// Catch error in unlikely case elm has been removed
try {
elm.parentNode.removeChild(elm);
} catch (e) {
}
}, nodes);
return chibi();
};
// Get/Set CSS
cb.css = function (property, value) {
if (property) {
if (value || value === '') {
nodeLoop(function (elm) {
setCss(elm, property, value);
}, nodes);
return cb;
}
if (nodes[0]) {
if (nodes[0].style[cssCamel(property)]) {
return nodes[0].style[cssCamel(property)];
}
if (computeStyle(nodes[0], property)) {
return computeStyle(nodes[0], property);
}
}
}
};
// Get class(es)
cb.getClass = function () {
if (nodes[0] && nodes[0].className.length > 0) {
// Weak IE trim support
return nodes[0].className.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '').replace(/\s+/, ' ');
}
};
// Set (replaces) classes
cb.setClass = function (classes) {
if (classes || classes === '') {
nodeLoop(function (elm) {
elm.className = classes;
}, nodes);
}
return cb;
};
// Add class
cb.addClass = function (classes) {
classHelper(classes, 'add', nodes);
// if (classes) {
// nodeLoop(function (elm) {
// elm.className += ' ' + classes;
// }, nodes);
// }
return cb;
};
// Remove class
cb.removeClass = function (classes) {
classHelper(classes, 'remove', nodes);
return cb;
};
// Toggle class
cb.toggleClass = function (classes, set) {
var method = ((typeof set === 'undefined') ? 'toggle' : (+set ? 'add' : 'remove'));
classHelper(classes, method, nodes);
return cb;
};
// Has class
cb.hasClass = function (classes) {
return classHelper(classes, 'has', nodes);
};
// Get/set HTML
cb.html = function (value) {
if (value || value === '') {
nodeLoop(function (elm) {
elm.innerHTML = value;
}, nodes);
return cb;
}
if (nodes[0]) {
return nodes[0].innerHTML;
}
};
// Insert HTML before selector
cb.htmlBefore = function (value) {
insertHtml(value, 'before', nodes);
return cb;
};
// Insert HTML after selector
cb.htmlAfter = function (value) {
insertHtml(value, 'after', nodes);
return cb;
};
// Insert HTML after selector innerHTML
cb.htmlAppend = function (value) {
insertHtml(value, 'append', nodes);
return cb;
};
// Insert HTML before selector innerHTML
cb.htmlPrepend = function (value) {
insertHtml(value, 'prepend', nodes);
return cb;
};
// Get/Set HTML attributes
cb.attr = function (property, value) {
if (property) {
property = property.toLowerCase();
// IE < 9 doesn't allow style or class via get/setAttribute so switch. cssText returns prettier CSS anyway
if (typeof value !== 'undefined') {//FIXED BUG HERE
nodeLoop(function (elm) {
if (property === 'style') {
elm.style.cssText = value;
} else if (property === 'class') {
elm.className = value;
} else {
elm.setAttribute(property, value);
}
}, nodes);
return cb;
}
if (nodes[0]) {
if (property === 'style') {
if (nodes[0].style.cssText) {
return nodes[0].style.cssText;
}
} else if (property === 'class') {
if (nodes[0].className) {
return nodes[0].className;
}
} else {
if (nodes[0].getAttribute(property)) {
return nodes[0].getAttribute(property);
}
}
}
}
};
// Get/Set HTML data property
cb.data = function (key, value) {
if (key) {
return cb.attr('data-' + key, value);
}
};
// Get/Set form element values
cb.val = function (value) {
var values, i, j;
if (typeof value != 'undefined') { // FIXED A BUG HERE
nodeLoop(function (elm) {
switch (elm.nodeName) {
case 'SELECT':
if (typeof value === 'string' || typeof value === 'number') {
value = [value];
}
for (i = 0; i < elm.length; i += 1) {
// Multiple select
for (j = 0; j < value.length; j += 1) {
elm[i].selected = '';
if (elm[i].value === ""+value[j]) {
elm[i].selected = 'selected';
break;
}
}
}
break;
case 'INPUT':
case 'TEXTAREA':
case 'BUTTON':
elm.value = value;
break;
}
}, nodes);
return cb;
}
if (nodes[0]) {
switch (nodes[0].nodeName) {
case 'SELECT':
values = [];
for (i = 0; i < nodes[0].length; i += 1) {
if (nodes[0][i].selected) {
values.push(nodes[0][i].value);
}
}
return (values.length > 1) ? values : values[0];
case 'INPUT':
case 'TEXTAREA':
case 'BUTTON':
return nodes[0].value;
}
}
};
// Return matching checked checkbox or radios
cb.checked = function (check) {
if (typeof check === 'boolean') {
nodeLoop(function (elm) {
if (elm.nodeName === 'INPUT' && (elm.type === 'checkbox' || elm.type === 'radio')) {
elm.checked = check;
}
}, nodes);
return cb;
}
if (nodes[0] && nodes[0].nodeName === 'INPUT' && (nodes[0].type === 'checkbox' || nodes[0].type === 'radio')) {
return (!!nodes[0].checked);
}
};
// Add event handler
cb.on = function (event, fn) {
if (selector === w || selector === d) {
nodes = [selector];
}
nodeLoop(function (elm) {
if (d.addEventListener) {
elm.addEventListener(event, fn, false);
} else if (d.attachEvent) {
// <= IE 8 loses scope so need to apply, we add this to object so we can detach later (can't detach anonymous functions)
elm[event + fn] = function () {
return fn.apply(elm, arguments);
};
elm.attachEvent('on' + event, elm[event + fn]);
}
}, nodes);
return cb;
};
// Remove event handler
cb.off = function (event, fn) {
if (selector === w || selector === d) {
nodes = [selector];
}
nodeLoop(function (elm) {
if (d.addEventListener) {
elm.removeEventListener(event, fn, false);
} else if (d.attachEvent) {
elm.detachEvent('on' + event, elm[event + fn]);
// Tidy up
elm[event + fn] = null;
}
}, nodes);
return cb;
};
return cb;
}
// Basic XHR
chibi.ajax = function (options) { // if options is a number, it's timeout in ms
var opts = extend({
method: 'GET',
nocache: true,
timeout: 5000,
loader: true,
callback: null
}, options);
opts.method = opts.method.toUpperCase();
var loaderFn = opts.loader ? chibi._loader : function(){};
var url = opts.url;
var method = opts.method;
var query = null;
if (opts.data) {
query = serializeData(opts.data);
}
if (query && (method === 'GET')) {
url += (url.indexOf('?') === -1) ? '?' + query : '&' + query;
query = null;
}
var xhr = new XMLHttpRequest();
// prevent caching
if (opts.nocache) {
var ts = (+(new Date())).toString(36);
url += ((url.indexOf('?') === -1) ? '?' : '&') + '_=' + ts;
}
loaderFn(true);
xhr.open(method, url, true);
xhr.timeout = opts.timeout;
// Abort after given timeout
var abortTmeo = setTimeout(function () {
console.error("XHR timed out.");
xhr.abort();
loaderFn(false);
}, opts.timeout + 10);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
loaderFn(false);
opts.callback && opts.callback(xhr.responseText, xhr.status);
clearTimeout(abortTmeo);
}
};
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
if (method === 'POST') {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
}
xhr.send(query);
return xhr;
};
chibi._loader = function(){};
// Alias to cb.ajax(url, 'get', callback)
chibi.get = function (url, callback, opts) {
opts = opts || {};
opts.url = url;
opts.callback = callback;
opts.method = 'GET';
return chibi.ajax(opts);
};
// Alias to cb.ajax(url, 'post', callback)
chibi.post = function (url, callback, opts) {
opts = opts || {};
opts.url = url;
opts.callback = callback;
opts.method = 'POST';
return chibi.ajax(opts);
};
// Fire on DOM ready
chibi.ready = function (fn) {
if (fn) {
if (domready) {
fn();
return chibi;
} else {
readyfn.push(fn);
}
}
};
// Fire on page loaded
chibi.loaded = function (fn) {
if (fn) {
if (pageloaded) {
fn();
return chibi;
} else {
loadedfn.push(fn);
}
}
};
var entityMap = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'/': '&#x2F;',
'`': '&#x60;',
'=': '&#x3D;'
};
chibi.htmlEscape = function(string) {
return String(string).replace(/[&<>"'`=\/]/g, function (s) {
return entityMap[s];
});
};
// Set Chibi's global namespace here ($)
w.$ = chibi;
}());
-310
View File
@@ -1,310 +0,0 @@
// keymaster.js
// (c) 2011-2013 Thomas Fuchs
// keymaster.js may be freely distributed under the MIT license.
;(function(global){
var k,
_handlers = {},
_mods = { 16: false, 18: false, 17: false, 91: false },
_scope = 'all',
// modifier keys
_MODIFIERS = {
'⇧': 16, shift: 16,
'⌥': 18, alt: 18, option: 18,
'⌃': 17, ctrl: 17, control: 17,
'⌘': 91, command: 91
},
// special keys
_MAP = {
backspace: 8, tab: 9, clear: 12,
enter: 13, 'return': 13,
esc: 27, escape: 27, space: 32,
left: 37, up: 38,
right: 39, down: 40,
del: 46, 'delete': 46,
home: 36, end: 35,
pageup: 33, pagedown: 34,
',': 188, '.': 190, '/': 191,
'`': 192, '-': 189, '=': 187,
';': 186, '\'': 222,
'[': 219, ']': 221, '\\': 220,
// added:
insert: 45,
np_0: 96, np_1: 97, np_2: 98, np_3: 99, np_4: 100, np_5: 101,
np_6: 102, np_7: 103, np_8: 104, np_9: 105, np_mul: 106,
np_add: 107, np_sub: 109, np_point: 110, np_div: 111, numlock: 144,
},
code = function(x){
return _MAP[x] || x.toUpperCase().charCodeAt(0);
},
_downKeys = [];
for(k=1;k<20;k++) _MAP['f'+k] = 111+k;
// IE doesn't support Array#indexOf, so have a simple replacement
function index(array, item){
var i = array.length;
while(i--) if(array[i]===item) return i;
return -1;
}
// for comparing mods before unassignment
function compareArray(a1, a2) {
if (a1.length != a2.length) return false;
for (var i = 0; i < a1.length; i++) {
if (a1[i] !== a2[i]) return false;
}
return true;
}
var modifierMap = {
16:'shiftKey',
18:'altKey',
17:'ctrlKey',
91:'metaKey'
};
function updateModifierKey(event) {
for(k in _mods) _mods[k] = event[modifierMap[k]];
};
function isModifierPressed(mod) {
if (mod=='control'||mod=='ctrl') return _mods[17];
if (mod=='shift') return _mods[16];
if (mod=='meta') return _mods[91];
if (mod=='alt') return _mods[18];
return false;
}
// handle keydown event
function dispatch(event) {
var key, handler, k, i, modifiersMatch, scope;
key = event.keyCode;
if (index(_downKeys, key) == -1) {
_downKeys.push(key);
}
// if a modifier key, set the key.<modifierkeyname> property to true and return
if(key == 93 || key == 224) key = 91; // right command on webkit, command on Gecko
if(key in _mods) {
_mods[key] = true;
// 'assignKey' from inside this closure is exported to window.key
for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = true;
return;
}
updateModifierKey(event);
// see if we need to ignore the keypress (filter() can can be overridden)
// by default ignore key presses if a select, textarea, or input is focused
if(!assignKey.filter.call(this, event)) return;
// abort if no potentially matching shortcuts found
if (!(key in _handlers)) return;
scope = getScope();
// for each potential shortcut
for (i = 0; i < _handlers[key].length; i++) {
handler = _handlers[key][i];
// see if it's in the current scope
if(handler.scope == scope || handler.scope == 'all'){
// check if modifiers match if any
modifiersMatch = handler.mods.length > 0;
for(k in _mods)
if((!_mods[k] && index(handler.mods, +k) > -1) ||
(_mods[k] && index(handler.mods, +k) == -1)) modifiersMatch = false;
// call the handler and stop the event if neccessary
if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch){
if(handler.method(event, handler)===false){
if(event.preventDefault) event.preventDefault();
else event.returnValue = false;
if(event.stopPropagation) event.stopPropagation();
if(event.cancelBubble) event.cancelBubble = true;
}
}
}
}
};
// unset modifier keys on keyup
function clearModifier(event){
var key = event.keyCode, k,
i = index(_downKeys, key);
// remove key from _downKeys
if (i >= 0) {
_downKeys.splice(i, 1);
}
if(key == 93 || key == 224) key = 91;
if(key in _mods) {
_mods[key] = false;
for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = false;
}
};
function resetModifiers() {
for(k in _mods) _mods[k] = false;
for(k in _MODIFIERS) assignKey[k] = false;
};
// parse and assign shortcut
function assignKey(key, scope, method){
var keys, mods;
keys = getKeys(key);
if (method === undefined) {
method = scope;
scope = 'all';
}
// for each shortcut
for (var i = 0; i < keys.length; i++) {
// set modifier keys if any
mods = [];
key = keys[i].split('+');
if (key.length > 1){
mods = getMods(key);
key = [key[key.length-1]];
}
// convert to keycode and...
key = key[0]
key = code(key);
// ...store handler
if (!(key in _handlers)) _handlers[key] = [];
_handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods });
}
};
// unbind all handlers for given key in current scope
function unbindKey(key, scope) {
var multipleKeys, keys,
mods = [],
i, j, obj;
multipleKeys = getKeys(key);
for (j = 0; j < multipleKeys.length; j++) {
keys = multipleKeys[j].split('+');
if (keys.length > 1) {
mods = getMods(keys);
}
key = keys[keys.length - 1];
key = code(key);
if (scope === undefined) {
scope = getScope();
}
if (!_handlers[key]) {
return;
}
for (i = 0; i < _handlers[key].length; i++) {
obj = _handlers[key][i];
// only clear handlers if correct scope and mods match
if (obj.scope === scope && compareArray(obj.mods, mods)) {
_handlers[key][i] = {};
}
}
}
};
// Returns true if the key with code 'keyCode' is currently down
// Converts strings into key codes.
function isPressed(keyCode) {
if (typeof(keyCode)=='string') {
keyCode = code(keyCode);
}
return index(_downKeys, keyCode) != -1;
}
function getPressedKeyCodes() {
return _downKeys.slice(0);
}
function filter(event){
var tagName = (event.target || event.srcElement).tagName;
// ignore keypressed in any elements that support keyboard data input
return !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA');
}
// initialize key.<modifier> to false
for(k in _MODIFIERS) assignKey[k] = false;
// set current scope (default 'all')
function setScope(scope){ _scope = scope || 'all' };
function getScope(){ return _scope || 'all' };
// delete all handlers for a given scope
function deleteScope(scope){
var key, handlers, i;
for (key in _handlers) {
handlers = _handlers[key];
for (i = 0; i < handlers.length; ) {
if (handlers[i].scope === scope) handlers.splice(i, 1);
else i++;
}
}
};
// abstract key logic for assign and unassign
function getKeys(key) {
var keys;
key = key.replace(/\s/g, '');
keys = key.split(',');
if ((keys[keys.length - 1]) == '') {
keys[keys.length - 2] += ',';
}
return keys;
}
// abstract mods logic for assign and unassign
function getMods(key) {
var mods = key.slice(0, key.length - 1);
for (var mi = 0; mi < mods.length; mi++)
mods[mi] = _MODIFIERS[mods[mi]];
return mods;
}
// cross-browser events
function addEvent(object, event, method) {
if (object.addEventListener)
object.addEventListener(event, method, false);
else if(object.attachEvent)
object.attachEvent('on'+event, function(){ method(window.event) });
};
// set the handlers globally on document
addEvent(document, 'keydown', function(event) { dispatch(event) }); // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48
addEvent(document, 'keyup', clearModifier);
// reset modifiers to false whenever the window is (re)focused.
addEvent(window, 'focus', resetModifiers);
// store previously defined key
var previousKey = global.key;
// restore previously defined key and return reference to our key object
function noConflict() {
var k = global.key;
global.key = previousKey;
return k;
}
// set window.key and window.key.set/get/deleteScope, and the default filter
global.key = assignKey;
global.key.setScope = setScope;
global.key.getScope = getScope;
global.key.deleteScope = deleteScope;
global.key.filter = filter;
global.key.isPressed = isPressed;
global.key.isModifier = isModifierPressed;
global.key.getPressedKeyCodes = getPressedKeyCodes;
global.key.noConflict = noConflict;
global.key.unbind = unbindKey;
if(typeof module !== 'undefined') module.exports = assignKey;
})(this);
-8
View File
@@ -1,8 +0,0 @@
// Generated from PHP locale file
var _tr = {
"wifi.connected_ip_is": "Connected, IP is ",
"wifi.not_conn": "Not connected.",
"wifi.enter_passwd": "Enter password for \":ssid:\""
};
function tr(key) { return _tr[key] || '?'+key+'?'; }
-41
View File
@@ -1,41 +0,0 @@
/** Module for toggling a modal overlay */
(function () {
var modal = {};
modal.show = function (sel) {
var $m = $(sel);
$m.removeClass('hidden visible');
setTimeout(function () {
$m.addClass('visible');
}, 1);
};
modal.hide = function (sel) {
var $m = $(sel);
$m.removeClass('visible');
setTimeout(function () {
$m.addClass('hidden');
}, 500); // transition time
};
modal.init = function () {
// close modal by click outside the dialog
$('.Modal').on('click', function () {
if ($(this).hasClass('no-close')) return; // this is a no-close modal
modal.hide(this);
});
$('.Dialog').on('click', function (e) {
e.stopImmediatePropagation();
});
// Hide all modals on esc
$(window).on('keydown', function (e) {
if (e.which == 27) {
modal.hide('.Modal');
}
});
};
window.Modal = modal;
})();
-32
View File
@@ -1,32 +0,0 @@
(function (nt) {
var sel = '#notif';
var hideTmeo1; // timeout to start hiding (transition)
var hideTmeo2; // timeout to add the hidden class
nt.show = function (message, timeout) {
$(sel).html(message);
Modal.show(sel);
clearTimeout(hideTmeo1);
clearTimeout(hideTmeo2);
if (undef(timeout)) timeout = 2500;
hideTmeo1 = setTimeout(nt.hide, timeout);
};
nt.hide = function () {
var $m = $(sel);
$m.removeClass('visible');
hideTmeo2 = setTimeout(function () {
$m.addClass('hidden');
}, 250); // transition time
};
nt.init = function() {
$(sel).on('click', function() {
nt.hide(this);
});
};
})(window.Notify = {});
-715
View File
@@ -1,715 +0,0 @@
/** Decode two-byte number */
function parse2B(s, i) {
return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127;
}
/** Decode three-byte number */
function parse3B(s, i) {
return (s.charCodeAt(i) - 1) + (s.charCodeAt(i+1) - 1) * 127 + (s.charCodeAt(i+2) - 1) * 127 * 127;
}
function Chr(n) {
return String.fromCharCode(n);
}
function encode2B(n) {
var lsb, msb;
lsb = (n % 127);
n = ((n - lsb) / 127);
lsb += 1;
msb = (n + 1);
return Chr(lsb) + Chr(msb);
}
function encode3B(n) {
var lsb, msb, xsb;
lsb = (n % 127);
n = (n - lsb) / 127;
lsb += 1;
msb = (n % 127);
n = (n - msb) / 127;
msb += 1;
xsb = (n + 1);
return Chr(lsb) + Chr(msb) + Chr(xsb);
}
var Screen = (function () {
var W = 0, H = 0; // dimensions
var inited = false;
var cursor = {
a: false, // active (blink state)
x: 0, // 0-based coordinates
y: 0,
fg: 7, // colors 0-15
bg: 0,
attrs: 0,
suppress: false, // do not turn on in blink interval (for safe moving)
forceOn: false,
hidden: false, // do not show
hanging: false, // xenl
};
var screen = [];
var blinkIval;
var cursorFlashStartIval;
var frakturExceptions = {
'C': '\u212d',
'H': '\u210c',
'I': '\u2111',
'R': '\u211c',
'Z': '\u2128',
};
// for BEL
var audioCtx = null;
try {
audioCtx = new (window.AudioContext || window.audioContext || window.webkitAudioContext)();
} catch (er) {
console.error("Browser does not support AudioContext, can't beep.", er);
}
/** Get cell under cursor */
function _curCell() {
return screen[cursor.y*W + cursor.x];
}
/** Safely move cursor */
function cursorSet(y, x) {
// Hide and prevent from showing up during the move
cursor.suppress = true;
_draw(_curCell(), false);
cursor.x = x;
cursor.y = y;
// Show again
cursor.suppress = false;
_draw(_curCell());
}
/** Update cell on display. inv = invert (for cursor) */
function _draw(cell, inv) {
if (!cell) return;
if (typeof inv == 'undefined') {
inv = cursor.a && cursor.x == cell.x && cursor.y == cell.y;
}
var elem = cell.e, fg, bg, cn, t;
// Colors
fg = inv ? cell.bg : cell.fg;
bg = inv ? cell.fg : cell.bg;
// Update
elem.textContent = t = (cell.t + ' ')[0];
cn = 'fg' + fg + ' bg' + bg;
if (cell.attrs & (1<<0)) cn += ' bold';
if (cell.attrs & (1<<2)) cn += ' italic';
if (cell.attrs & (1<<3)) cn += ' under';
if (cell.attrs & (1<<4)) cn += ' blink';
if (cell.attrs & (1<<5)) {
cn += ' fraktur';
// perform substitution
if (t >= 'a' && t <= 'z') {
t = String.fromCodePoint(0x1d51e - 97 + t.charCodeAt(0));
}
else if (t >= 'A' && t <= 'Z') {
// this set is incomplete, some exceptions are needed
if (frakturExceptions.hasOwnProperty(t)) {
t = frakturExceptions[t];
} else {
t = String.fromCodePoint(0x1d504 - 65 + t.charCodeAt(0));
}
}
elem.textContent = t;
}
if (cell.attrs & (1<<6)) cn += ' strike';
if (cell.attrs & (1<<1)) {
cn += ' faint';
// faint requires special html - otherwise it would also dim the background.
// we use opacity on the text...
elem.innerHTML = '<span>' + e(elem.textContent) + '</span>';
}
elem.className = cn;
}
/** Show entire screen */
function _drawAll() {
for (var i = W*H-1; i>=0; i--) {
_draw(screen[i]);
}
}
function _rebuild(rows, cols) {
W = cols;
H = rows;
/* Build screen & show */
var e, cell, scr = qs('#screen');
// Empty the screen node
while (scr.firstChild) scr.removeChild(scr.firstChild);
screen = [];
for(var i = 0; i < W*H; i++) {
e = mk('span');
(function() {
var x = i % W;
var y = Math.floor(i / W);
e.addEventListener('mouseenter', function (evt) {
Input.onMouseMove(x, y);
});
e.addEventListener('mousedown', function (evt) {
Input.onMouseDown(x, y, evt.button+1);
});
e.addEventListener('mouseup', function (evt) {
Input.onMouseUp(x, y, evt.button+1);
});
e.addEventListener('contextmenu', function (evt) {
evt.preventDefault();
});
e.addEventListener('mousewheel', function (evt) {
Input.onMouseWheel(x, y, evt.deltaY>0?1:-1);
return false;
});
})();
/* End of line */
if ((i > 0) && (i % W == 0)) {
scr.appendChild(mk('br'));
}
/* The cell */
scr.appendChild(e);
cell = {
t: ' ',
fg: 7,
bg: 0, // the colors will be replaced immediately as we receive data (user won't see this)
attrs: 0,
e: e,
x: i % W,
y: Math.floor(i / W),
};
screen.push(cell);
_draw(cell);
}
}
/** Init the terminal */
function _init() {
/* Cursor blinking */
clearInterval(blinkIval);
blinkIval = setInterval(function () {
cursor.a = !cursor.a;
// TODO try to invent a new way to indicate "hanging" - this is copied from gtkterm
if (cursor.hidden || cursor.hanging) {
cursor.a = false;
}
if (!cursor.suppress) {
_draw(_curCell(), cursor.forceOn || cursor.a);
}
}, 500);
// blink attribute
setInterval(function () {
$('#screen').removeClass('blink-hide');
setTimeout(function () {
$('#screen').addClass('blink-hide');
}, 800); // 200 ms ON
}, 1000);
inited = true;
}
var SEQ_SET_COLOR_ATTR = 1;
var SEQ_REPEAT = 2;
var SEQ_SET_COLOR = 3;
var SEQ_SET_ATTR = 4;
function _load_content(str) {
var i = 0, ci = 0, j, jc, num, num2, t = ' ', fg, bg, attrs, cell;
if (!inited) _init();
var cursorMoved;
// Set size
num = parse2B(str, i); i += 2; // height
num2 = parse2B(str, i); i += 2; // width
if (num != H || num2 != W) {
_rebuild(num, num2);
}
// console.log("Size ",num, num2);
// Cursor position
num = parse2B(str, i); i += 2; // row
num2 = parse2B(str, i); i += 2; // col
cursorMoved = (cursor.x != num2 || cursor.y != num);
cursorSet(num, num2);
// console.log("Cursor at ",num, num2);
// Attributes
num = parse2B(str, i); i += 2; // fg bg bold hidden
cursor.hidden = !(num & 0x0001);
cursor.hanging = !!(num & 0x0002);
// console.log("Attributes word ",num.toString(16)+'h');
Input.setAlts(
!!(num & 0x0004), // cu
!!(num & 0x0008), // np
!!(num & 0x0010) // fn
);
fg = 7;
bg = 0;
attrs = 0;
// Here come the content
while(i < str.length && ci<W*H) {
j = str[i++];
jc = j.charCodeAt(0);
if (jc == SEQ_SET_COLOR_ATTR) {
num = parse3B(str, i); i += 3;
fg = num & 0x0F;
bg = (num & 0xF0) >> 4;
attrs = (num & 0xFF00)>>8;
}
else if (jc == SEQ_SET_COLOR) {
num = parse2B(str, i); i += 2;
fg = num & 0x0F;
bg = (num & 0xF0) >> 4;
}
else if (jc == SEQ_SET_ATTR) {
num = parse2B(str, i); i += 2;
attrs = num & 0xFF;
}
else if (jc == SEQ_REPEAT) {
num = parse2B(str, i); i += 2;
// console.log("Repeat x ",num);
for (; num>0 && ci<W*H; num--) {
cell = screen[ci++];
cell.fg = fg;
cell.bg = bg;
cell.t = t;
cell.attrs = attrs;
}
}
else {
cell = screen[ci++];
// Unique cell character
t = cell.t = j;
cell.fg = fg;
cell.bg = bg;
cell.attrs = attrs;
// console.log("Symbol ", j);
}
}
_drawAll();
// if (!cursor.hidden || cursor.hanging || !cursor.suppress) {
// // hide cursor asap
// _draw(_curCell(), false);
// }
if (cursorMoved) {
cursor.forceOn = true;
cursorFlashStartIval = setTimeout(function() {
cursor.forceOn = false;
}, 1200);
_draw(_curCell(), true);
}
}
function _load_labels(str) {
var pieces = str.split('\x01');
qs('h1').textContent = pieces[0];
qsa('#buttons button').forEach(function(x, i) {
var s = pieces[i+1].trim();
// if empty string, use the "dim" effect and put nbsp instead to stretch the btn vertically
x.innerHTML = s.length > 0 ? e(s) : "&nbsp;";
x.style.opacity = s.length > 0 ? 1 : 0.2;
});
}
function _beep()
{
var osc, gain;
if (!audioCtx) return;
// Main beep
osc = audioCtx.createOscillator();
gain = audioCtx.createGain();
osc.connect(gain);
gain.connect(audioCtx.destination);
gain.gain.value = 0.5;
osc.frequency.value = 750;
osc.type = 'sine';
osc.start();
osc.stop(audioCtx.currentTime+0.05);
// Surrogate beep (making it sound like 'oops')
osc = audioCtx.createOscillator();
gain = audioCtx.createGain();
osc.connect(gain);
gain.connect(audioCtx.destination);
gain.gain.value = 0.2;
osc.frequency.value = 400;
osc.type = 'sine';
osc.start(audioCtx.currentTime+0.05);
osc.stop(audioCtx.currentTime+0.08);
}
/** Load screen content from a binary sequence (new) */
function load(str) {
var content = str.substr(1);
switch(str.charAt(0)) {
case 'S':
_load_content(content);
break;
case 'T':
_load_labels(content);
break;
case 'B':
_beep();
break;
default:
console.warn("Bad data message type, ignoring.");
}
}
return {
load: load, // full load (string)
};
})();
/** Handle connections */
var Conn = (function() {
var ws;
var heartbeatTout;
var pingIv;
function onOpen(evt) {
console.log("CONNECTED");
}
function onClose(evt) {
console.warn("SOCKET CLOSED, code "+evt.code+". Reconnecting...");
setTimeout(function() {
init();
}, 200);
// this happens when the buffer gets fucked up via invalid unicode.
// we basically use polling instead of socket then
}
function onMessage(evt) {
try {
// . = heartbeat
if (evt.data != '.') {
//console.log("RX: ", evt.data);
// Assume all our messages are screen updates
Screen.load(evt.data);
}
heartbeat();
} catch(e) {
console.error(e);
}
}
function doSend(message) {
console.log("TX: ", message);
if (!ws) return; // for dry testing
if (ws.readyState != 1) {
console.error("Socket not ready");
return;
}
if (typeof message != "string") {
message = JSON.stringify(message);
}
ws.send(message);
}
function init() {
heartbeat();
ws = new WebSocket("ws://"+_root+"/term/update.ws");
ws.onopen = onOpen;
ws.onclose = onClose;
ws.onmessage = onMessage;
console.log("Opening socket.");
// Ask for initial data
$.get('http://'+_root+'/term/init', function(resp, status) {
if (status !== 200) location.reload(true);
console.log("Data received!");
Screen.load(resp);
heartbeat();
showPage();
});
}
function heartbeat() {
clearTimeout(heartbeatTout);
heartbeatTout = setTimeout(heartbeatFail, 2000);
}
function heartbeatFail() {
console.error("Heartbeat lost, probing server...");
pingIv = setInterval(function() {
console.log("> ping");
$.get('http://'+_root+'/system/ping', function(resp, status) {
if (status == 200) {
clearInterval(pingIv);
console.info("Server ready, reloading page...");
location.reload();
}
}, {
timeout: 100,
});
}, 500);
}
return {
ws: null,
init: init,
send: doSend
};
})();
/**
* User input
*
* --- Rx messages: ---
* S - screen content (binary encoding of the entire screen with simple compression)
* T - text labels - Title and buttons, \0x01-separated
* B - beep
* . - heartbeat
*
* --- Tx messages ---
* s - string
* b - action button
* p - mb press
* r - mb release
* m - mouse move
*/
var Input = (function() {
var opts = {
np_alt: false,
cu_alt: false,
fn_alt: false,
};
function sendStrMsg(str) {
Conn.send("s"+str);
}
function sendBtnMsg(n) {
Conn.send("b"+Chr(n));
}
function fa(alt, normal) {
return opts.fn_alt ? alt : normal;
}
function ca(alt, normal) {
return opts.cu_alt ? alt : normal;
}
function na(alt, normal) {
return opts.np_alt ? alt : normal;
}
function _bindFnKeys() {
var keymap = {
'tab': '\x09',
'backspace': '\x08',
'enter': '\x0d',
'ctrl+enter': '\x0a',
'esc': '\x1b',
'up': ca('\x1bOA', '\x1b[A'),
'down': ca('\x1bOB', '\x1b[B'),
'right': ca('\x1bOC', '\x1b[C'),
'left': ca('\x1bOD', '\x1b[D'),
'home': ca('\x1bOH', fa('\x1b[H', '\x1b[1~')),
'insert': '\x1b[2~',
'delete': '\x1b[3~',
'end': ca('\x1bOF', fa('\x1b[F', '\x1b[4~')),
'pageup': '\x1b[5~',
'pagedown': '\x1b[6~',
'f1': fa('\x1bOP', '\x1b[11~'),
'f2': fa('\x1bOQ', '\x1b[12~'),
'f3': fa('\x1bOR', '\x1b[13~'),
'f4': fa('\x1bOS', '\x1b[14~'),
'f5': '\x1b[15~', // note the disconnect
'f6': '\x1b[17~',
'f7': '\x1b[18~',
'f8': '\x1b[19~',
'f9': '\x1b[20~',
'f10': '\x1b[21~', // note the disconnect
'f11': '\x1b[23~',
'f12': '\x1b[24~',
'shift+f1': fa('\x1bO1;2P', '\x1b[25~'),
'shift+f2': fa('\x1bO1;2Q', '\x1b[26~'), // note the disconnect
'shift+f3': fa('\x1bO1;2R', '\x1b[28~'),
'shift+f4': fa('\x1bO1;2S', '\x1b[29~'), // note the disconnect
'shift+f5': fa('\x1b[15;2~', '\x1b[31~'),
'shift+f6': fa('\x1b[17;2~', '\x1b[32~'),
'shift+f7': fa('\x1b[18;2~', '\x1b[33~'),
'shift+f8': fa('\x1b[19;2~', '\x1b[34~'),
'shift+f9': fa('\x1b[20;2~', '\x1b[35~'), // 35-38 are not standard - but what is?
'shift+f10': fa('\x1b[21;2~', '\x1b[36~'),
'shift+f11': fa('\x1b[22;2~', '\x1b[37~'),
'shift+f12': fa('\x1b[23;2~', '\x1b[38~'),
'np_0': na('\x1bOp', '0'),
'np_1': na('\x1bOq', '1'),
'np_2': na('\x1bOr', '2'),
'np_3': na('\x1bOs', '3'),
'np_4': na('\x1bOt', '4'),
'np_5': na('\x1bOu', '5'),
'np_6': na('\x1bOv', '6'),
'np_7': na('\x1bOw', '7'),
'np_8': na('\x1bOx', '8'),
'np_9': na('\x1bOy', '9'),
'np_mul': na('\x1bOR', '*'),
'np_add': na('\x1bOl', '+'),
'np_sub': na('\x1bOS', '-'),
'np_point': na('\x1bOn', '.'),
'np_div': na('\x1bOQ', '/'),
// we don't implement numlock key (should change in numpad_alt mode, but it's even more useless than the rest)
};
for (var k in keymap) {
if (keymap.hasOwnProperty(k)) {
bind(k, keymap[k]);
}
}
}
function bind(combo, str) {
// mac fix - allow also cmd
if (combo.indexOf('ctrl+') !== -1) {
combo += ',' + combo.replace('ctrl', 'command');
}
// unbind possible old binding
key.unbind(combo);
key(combo, function (e) {
e.preventDefault();
sendStrMsg(str)
});
}
function _initKeys() {
// This takes care of text characters typed
window.addEventListener('keypress', function(evt) {
var str = '';
if (evt.key) str = evt.key;
else if (evt.which) str = String.fromCodePoint(evt.which);
if (str.length>0 && str.charCodeAt(0) >= 32) {
// console.log("Typed ", str);
sendStrMsg(str);
}
});
// ctrl-letter codes are sent as simple low ASCII codes
for (var i = 1; i<=26;i++) {
bind('ctrl+' + String.fromCharCode(96+i), String.fromCharCode(i));
}
bind('ctrl+]', '\x1b'); // alternate way to enter ESC
bind('ctrl+\\', '\x1c');
bind('ctrl+[', '\x1d');
bind('ctrl+^', '\x1e');
bind('ctrl+_', '\x1f');
_bindFnKeys();
}
var mb1 = 0;
var mb2 = 0;
var mb3 = 0;
function init() {
_initKeys();
// Button presses
qsa('#buttons button').forEach(function(s) {
s.addEventListener('click', function() {
sendBtnMsg(+this.dataset['n']);
});
});
window.addEventListener('mousedown', function(evt) {
if (evt.button == 0) mb1 = 1;
if (evt.button == 1) mb2 = 1;
if (evt.button == 2) mb3 = 1;
});
window.addEventListener('mouseup', function(evt) {
if (evt.button == 0) mb1 = 0;
if (evt.button == 1) mb2 = 0;
if (evt.button == 2) mb3 = 0;
});
}
function packModifiersForMouse() {
return (key.isModifier('ctrl')?1:0) |
(key.isModifier('shift')?2:0) |
(key.isModifier('alt')?4:0) |
(key.isModifier('meta')?8:0);
}
return {
init: init,
// onTap: sendPosMsg,
sendString: sendStrMsg,
setAlts: function(cu, np, fn) {
if (opts.cu_alt != cu || opts.np_alt != np || opts.fn_alt != fn) {
opts.cu_alt = cu;
opts.np_alt = np;
opts.fn_alt = fn;
// rebind keys - codes have changed
_bindFnKeys();
}
},
onMouseMove: function (x, y) {
var b = mb1 ? 1 : mb2 ? 2 : mb3 ? 3 : 0;
var m = packModifiersForMouse();
Conn.send("m" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m));
},
onMouseDown: function (x, y, b) {
if (b > 3 || b < 1) return;
var m = packModifiersForMouse();
Conn.send("p" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m));
console.log("B ",b," M ",m);
},
onMouseUp: function (x, y, b) {
if (b > 3 || b < 1) return;
var m = packModifiersForMouse();
Conn.send("r" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m));
console.log("B ",b," M ",m);
},
onMouseWheel: function (x, y, dir) {
// -1 ... btn 4 (away from user)
// +1 ... btn 5 (towards user)
var m = packModifiersForMouse();
var b = (dir < 0 ? 4 : 5);
Conn.send("p" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m));
console.log("B ",b," M ",m);
},
};
})();
window.termInit = function () {
Conn.init();
Input.init();
};
-123
View File
@@ -1,123 +0,0 @@
/** Make a node */
function mk(e) {return document.createElement(e)}
/** Find one by query */
function qs(s) {return document.querySelector(s)}
/** Find all by query */
function qsa(s) {return document.querySelectorAll(s)}
/** Convert any to bool safely */
function bool(x) {
return (x === 1 || x === '1' || x === true || x === 'true');
}
/**
* Filter 'spacebar' and 'return' from keypress handler,
* and when they're pressed, fire the callback.
* use $(...).on('keypress', cr(handler))
*/
function cr(hdl) {
return function(e) {
if (e.which == 10 || e.which == 13 || e.which == 32) {
hdl();
}
};
}
/** Extend an objects with options */
function extend(defaults, options) {
var target = {};
Object.keys(defaults).forEach(function(k){
target[k] = defaults[k];
});
Object.keys(options).forEach(function(k){
target[k] = options[k];
});
return target;
}
/** Escape string for use as literal in RegExp */
function rgxe(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
/** Format number to N decimal places, output as string */
function numfmt(x, places) {
var pow = Math.pow(10, places);
return Math.round(x*pow) / pow;
}
/** Get millisecond timestamp */
function msNow() {
return +(new Date);
}
/** Get ms elapsed since msNow() */
function msElapsed(start) {
return msNow() - start;
}
/** Shim for log base 10 */
Math.log10 = Math.log10 || function(x) {
return Math.log(x) / Math.LN10;
};
/**
* Perform a substitution in the given string.
*
* Arguments - array or list of replacements.
* Arguments numeric keys will replace {0}, {1} etc.
* Named keys also work, ie. {foo: "bar"} -> replaces {foo} with bar.
*
* Braces are added to keys if missing.
*
* @returns {String} result
*/
String.prototype.format = function () {
var out = this;
var repl = arguments;
if (arguments.length == 1 && (Array.isArray(arguments[0]) || typeof arguments[0] == 'object')) {
repl = arguments[0];
}
for (var ph in repl) {
if (repl.hasOwnProperty(ph)) {
var ph_orig = ph;
if (!ph.match(/^\{.*\}$/)) {
ph = '{' + ph + '}';
}
// replace all occurrences
var pattern = new RegExp(rgxe(ph), "g");
out = out.replace(pattern, repl[ph_orig]);
}
}
return out;
};
/** HTML escape */
function e(str) {
return $.htmlEscape(str);
}
/** Check for undefined */
function undef(x) {
return typeof x == 'undefined';
}
/** Safe json parse */
function jsp() {
try {
return JSON.parse(e);
} catch(e) {
console.error(e);
return null;
}
}
-160
View File
@@ -1,160 +0,0 @@
(function(w) {
var authStr = ['Open', 'WEP', 'WPA', 'WPA2', 'WPA/WPA2'];
var curSSID;
// Get XX % for a slider input
function rangePt(inp) {
return Math.round(((inp.value / inp.max)*100)) + '%';
}
// Display selected STA SSID etc
function selectSta(name, password, ip) {
$('#sta_ssid').val(name);
$('#sta_password').val(password);
$('#sta-nw').toggleClass('hidden', name.length == 0);
$('#sta-nw-nil').toggleClass('hidden', name.length > 0);
$('#sta-nw .essid').html(e(name));
var nopw = undef(password) || password.length == 0;
$('#sta-nw .x-passwd').html(e(password));
$('#sta-nw .passwd').toggleClass('hidden', nopw);
$('#sta-nw .nopasswd').toggleClass('hidden', !nopw);
$('#sta-nw .ip').html(ip.length>0 ? tr('wifi.connected_ip_is')+ip : tr('wifi.not_conn'));
}
/** Update display for received response */
function onScan(resp, status) {
//var ap_json = {
// "result": {
// "inProgress": "0",
// "APs": [
// {"essid": "Chlivek", "bssid": "88:f7:c7:52:b3:99", "rssi": "204", "enc": "4", "channel": "1"},
// {"essid": "TyNikdy", "bssid": "5c:f4:ab:0d:f1:1b", "rssi": "164", "enc": "3", "channel": "1"},
// ]
// }
//};
if (status != 200) {
// bad response
rescan(5000); // wait 5sm then retry
return;
}
try {
resp = JSON.parse(resp);
} catch (e) {
console.log(e);
rescan(5000);
return;
}
var done = !bool(resp.result.inProgress) && (resp.result.APs.length > 0);
rescan(done ? 15000 : 1000);
if (!done) return; // no redraw yet
// clear the AP list
var $list = $('#ap-list');
// remove old APs
$('#ap-list .AP').remove();
$list.toggleClass('hidden', !done);
$('#ap-loader').toggleClass('hidden', done);
// scan done
resp.result.APs.sort(function (a, b) {
return b.rssi - a.rssi;
}).forEach(function (ap) {
ap.enc = parseInt(ap.enc);
if (ap.enc > 4) return; // hide unsupported auths
var item = mk('div');
var $item = $(item)
.data('ssid', ap.essid)
.data('pwd', ap.enc)
.attr('tabindex', 0)
.addClass('AP');
// mark current SSID
if (ap.essid == curSSID) {
$item.addClass('selected');
}
var inner = mk('div');
$(inner).addClass('inner')
.htmlAppend('<div class="rssi">{0}</div>'.format(ap.rssi_perc))
.htmlAppend('<div class="essid" title="{0}">{0}</div>'.format($.htmlEscape(ap.essid)))
.htmlAppend('<div class="auth">{0}</div>'.format(authStr[ap.enc]));
$item.on('click', function () {
var $th = $(this);
var conn_ssid = $th.data('ssid');
var conn_pass = '';
if (+$th.data('pwd')) {
// this AP needs a password
conn_pass = prompt(tr("wifi.enter_passwd").replace(":ssid:", conn_ssid));
if (!conn_pass) return;
}
$('#sta_password').val(conn_pass);
$('#sta_ssid').val(conn_ssid);
selectSta(conn_ssid, conn_pass, '');
});
item.appendChild(inner);
$list[0].appendChild(item);
});
}
function startScanning() {
$('#ap-loader').removeClass('hidden');
$('#ap-scan').addClass('hidden');
$('#ap-loader .anim-dots').html('.');
scanAPs();
}
/** Ask the CGI what APs are visible (async) */
function scanAPs() {
$.get('http://'+_root+'/cfg/wifi/scan', onScan);
}
function rescan(time) {
setTimeout(scanAPs, time);
}
/** Set up the WiFi page */
function wifiInit(cfg) {
// Update slider value displays
$('.Row.range').forEach(function(x) {
var inp = x.querySelector('input');
var disp1 = x.querySelector('.x-disp1');
var disp2 = x.querySelector('.x-disp2');
var t = rangePt(inp);
$(disp1).html(t);
$(disp2).html(t);
$(inp).on('input', function() {
t = rangePt(inp);
$(disp1).html(t);
$(disp2).html(t);
});
});
// Forget STA credentials
$('#forget-sta').on('click', function() {
selectSta('', '', '');
return false;
});
selectSta(cfg.sta_ssid, cfg.sta_password, cfg.sta_active_ip);
curSSID = cfg.sta_active_ssid;
}
w.init = wifiInit;
w.startScanning = startScanning;
})(window.WiFi = {});
-166
View File
@@ -1,166 +0,0 @@
<?php
return [
'appname' => 'ESPTerm',
'menu.cfg_wifi' => 'WiFi Settings',
'menu.cfg_network' => 'Network Settings',
'menu.cfg_term' => 'Terminal Settings',
'menu.about' => 'About ESPTerm',
'menu.help' => 'Quick Reference',
'menu.term' => 'Back to Terminal',
'menu.cfg_system' => 'System Settings',
'menu.cfg_wifi_conn' => 'Connecting to Network',
'menu.settings' => 'Settings',
'title.term' => 'Terminal',
'term_nav.config' => 'Config',
'term_nav.wifi' => 'WiFi',
'term_nav.help' => 'Help',
'term_nav.about' => 'About',
'net.ap' => 'DHCP Server (AP)',
'net.sta' => 'DHCP Client (Station)',
'net.sta_mac' => 'Station MAC',
'net.ap_mac' => 'AP MAC',
'net.details' => 'MAC addresses',
'term.defaults' => 'Initial Settings',
'term.explain_initials' => '
Those are the initial settings used after ESPTerm powers on.
The selected colors will also be used after receiving a
screen- or attributes-reset command. Those settings will be
applied immediately after saving to preview the changes live.',
'term.example' => 'Default colors preview',
'term.term_title' => 'Header text',
'term.term_width' => 'Width / height',
'term.default_fg_bg' => 'Text / background',
'term.buttons' => 'Button labels',
'term.theme' => 'Color scheme',
'term.parser_tout_ms' => 'Parser timeout',
'term.display_tout_ms' => 'Redraw delay',
'term.display_cooldown_ms' => 'Redraw cooldown',
'term.fn_alt_mode' => 'SS3 Fn keys',
// terminal color labels
'color.0' => 'Black',
'color.1' => 'Red',
'color.2' => 'Green',
'color.3' => 'Yellow',
'color.4' => 'Blue',
'color.5' => 'Purple',
'color.6' => 'Cyan',
'color.7' => 'Silver',
'color.8' => 'Gray',
'color.9' => 'Light Red',
'color.10' => 'Light Green',
'color.11' => 'Light Yellow',
'color.12' => 'Light Blue',
'color.13' => 'Light Purple',
'color.14' => 'Light Cyan',
'color.15' => 'White',
'net.explain_sta' => '
Switch off Dynamic IP to configure the static IP address.',
'net.explain_ap' => '
Those settings affect the built-in DHCP server in AP mode.',
'net.ap_dhcp_time' => 'Lease time',
'net.ap_dhcp_start' => 'Pool start IP',
'net.ap_dhcp_end' => 'Pool end IP',
'net.ap_addr_ip' => 'Own IP address',
'net.ap_addr_mask' => 'Subnet mask',
'net.sta_dhcp_enable' => 'Use dynamic IP',
'net.sta_addr_ip' => 'ESPTerm static IP',
'net.sta_addr_mask' => 'Subnet mask',
'net.sta_addr_gw' => 'Gateway IP',
'wifi.ap' => 'Built-in Access Point',
'wifi.sta' => 'Join Existing Network',
'wifi.enable' => 'Enabled',
'wifi.tpw' => 'Transmit power',
'wifi.ap_channel' => 'Channel',
'wifi.ap_ssid' => 'AP SSID',
'wifi.ap_password' => 'Password',
'wifi.ap_hidden' => 'Hide SSID',
'wifi.sta_info' => 'Selected',
'wifi.not_conn' => 'Not connected.',
'wifi.sta_none' => 'None',
'wifi.sta_active_pw' => '🔒',
'wifi.sta_active_nopw' => '🔓 Open access',
'wifi.connected_ip_is' => 'Connected, IP is ',
'wifi.sta_password' => 'Password:',
'wifi.scanning' => 'Scanning',
'wifi.scan_now' => 'Click here to start scanning!',
'wifi.cant_scan_no_sta' => 'Click here to enable client mode and start scanning!',
'wifi.select_ssid' => 'Available networks:',
'wifi.enter_passwd' => 'Enter password for ":ssid:"',
'wifi.sta_explain' => 'After selecting a network, press Apply to connect.',
'wifi.conn.status' => 'Status:',
'wifi.conn.back_to_config' => 'Back to WiFi config',
'wifi.conn.telemetry_lost' => 'Telemetry lost; something went wrong, or your device disconnected.',
'wifi.conn.explain_android_sucks' => '
If you\'re configuring ESPTerm via a smartphone, or were connected
from another external network, your device may lose connection and this
progress indicator won\'t work. Please wait a while (~ 15 seconds),
then check if the connection succeeded.',
'wifi.conn.explain_reset' => '
To force enable the built-in AP, hold the BOOT
button until the blue LED starts flashing. Hold the button longer (until the LED
flashes rapidly) for a "factory reset".',
'wifi.conn.disabled' =>"Station mode is disabled.",
'wifi.conn.idle' =>"Idle, not connected and has no IP.",
'wifi.conn.success' => "Connected! Received IP ",
'wifi.conn.working' => "Connecting to selected AP",
'wifi.conn.fail' => "Connection failed, check settings & try again. Cause: ",
'system.save_restore' => 'Save & Restore',
'system.confirm_restore' => 'Restore all settings to their default values?',
'system.confirm_restore_hard' =>
'Restore to firmware default settings? This will reset ' .
'all active settings and switch to AP mode with the default SSID.',
'system.confirm_store_defaults' =>
'Enter admin password to confirm you want to store the current settings as defaults.',
'system.password' => 'Admin password:',
'system.restore_defaults' => 'Reset active settings to defaults',
'system.write_defaults' => 'Save active settings as defaults',
'system.restore_hard' => 'Reset active settings to firmware defaults',
'system.explain_persist' => '
ESPTerm contains two persistent memory banks, one for default and
one for active settings. Active settings can be stored as defaults
by the administrator (password required).
',
'system.uart' => 'Serial Port',
'system.explain_uart' => '
This form controls the primary, communication UART. The debug UART is fixed at 115.200 baud, one stop-bit and no parity.
',
'uart.baud' => 'Baud rate',
'uart.parity' => 'Parity',
'uart.parity.none' => 'None',
'uart.parity.odd' => 'Odd',
'uart.parity.even' => 'Even',
'uart.stop_bits' => 'Stop-bits',
'uart.stop_bits.one' => 'One',
'uart.stop_bits.one_and_half' => 'One and half',
'uart.stop_bits.two' => 'Two',
'apply' => 'Apply!',
'enabled' => 'Enabled',
'disabled' => 'Disabled',
'yes' => 'Yes',
'no' => 'No',
'confirm' => 'OK',
'form_errors' => 'Validation errors for:',
];
-13
View File
@@ -1,13 +0,0 @@
#!/bin/bash
echo "Packing js..."
cat jssrc/chibi.js \
jssrc/keymaster.js \
jssrc/utils.js \
jssrc/modal.js \
jssrc/notif.js \
jssrc/appcommon.js \
jssrc/lang.js \
jssrc/wifi.js \
jssrc/term.js > js/app.js
-20
View File
@@ -1,20 +0,0 @@
<nav id="menu">
<div id="brand" tabindex=0><?= tr('appname') ?></div>
<a href="<?= e(url('term')) ?>" class="icn-back"><?= tr('menu.term') ?></a>
<?php
// generate the menu
foreach ($_pages as $k => $page) {
if (strpos($page->bodyclass, 'cfg') === false) continue;
$sel = ($_GET['page'] == $k) ? 'selected' : '';
$text = $page->label;
$url = e(url($k));
echo "<a href=\"$url\" class=\"$page->icon $sel\">$text</a>";
}
?>
</nav>
<script>
function menuOpen() { $('#menu').toggleClass('expanded') }
$('#brand').on('click', menuOpen).on('keypress', cr(menuOpen));
</script>
-31
View File
@@ -1,31 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title><?= $_GET['PAGE_TITLE'] ?></title>
<link href="/css/app.css" rel="stylesheet">
<script src="/js/app.js"></script>
<script>var _root = <?= JS_WEB_ROOT ?>;</script>
</head>
<body class="<?= $_GET['BODYCLASS'] ?>">
<div id="outer">
<?php
$cfg = false;
if (strpos($_GET['BODYCLASS'], 'cfg') !== false) {
$cfg = true;
require __DIR__ . '/_cfg_menu.php';
}
?>
<div id="content">
<img src="/img/loader.gif" alt="Loading…" id="loader">
<?php if ($cfg): ?>
<h1><?= tr('menu.' . $_GET['page']) ?></h1>
<div class="Box errors hidden">
<span class="lead"><?= tr('form_errors') ?></span>&nbsp;<span class="list"></span>
</div>
<?php endif; ?>
-9
View File
@@ -1,9 +0,0 @@
<div class="NotifyMsg hidden" id="notif"></div>
</div>
</div>
</body>
</html>
-69
View File
@@ -1,69 +0,0 @@
<div class="Box">
<img src="/img/cvut.svg" id="logo" class="mq-tablet-min">
<h2>ESP8266 Remote Terminal</h2>
<img src="/img/cvut.svg" id="logo2" class="mq-phone">
<p>
&copy; Ondřej Hruška, 2016-2017
&lt;<a href="mailto:ondra@ondrovo.com">ondra@ondrovo.com</a>&gt;
</p>
<p>
<a href="http://measure.feld.cvut.cz/" target="blank">Katedra měření, FEL ČVUT</a><br>
Department of Measurement, FEE CTU
</p>
</div>
<div class="Box">
<h2>Version</h2>
<table>
<tr>
<th>ESPTerm</th>
<td>v%vers_fw%, built %date% at %time%</td>
</tr>
<tr>
<th>libesphttpd</th>
<td>v%vers_httpd%</td>
</tr>
<tr>
<th>ESP&nbsp;IoT&nbsp;SDK</th>
<td>v%vers_sdk%</td>
</tr>
</table>
</div>
<div class="Box">
<h2>Issues</h2>
<p>
Please report any issues to the <a href="%githubrepo%/issues">bugtracker</a> or send them by e-mail (see above).
</p>
<p>
Firmware updates can be downloaded from the <a href="%githubrepo%/releases">releases page</a> and flashed
with <a href="https://github.com/espressif/esptool">esptool.py</a>.
</p>
</div>
<div class="Box">
<h2>Contributing</h2>
<p>
<i class="icn-github"></i> You're welcome to submit your improvements and ideas to our <a href="%githubrepo%">GitHub repository</a>!
</p>
<p>
<i class="icn-donate"></i> If you'd like to donate, please try <a href="https://paypal.me/mightypork">PayPal</a> or
<a href="https://liberapay.com/MightyPork/">LiberaPay</a>.
</p>
</div>
<div class="Box">
<h2>Thanks</h2>
<p>
The webserver is based on a <a href="https://github.com/MightyPork/libesphttpd">fork</a> of the
<a href="https://github.com/Spritetm/esphttpd">esphttpd</a> library by Jeroen Domburg (Sprite_tm).
</p>
<p>
Using (modified) JS library <a href="https://github.com/kylebarrow/chibi">chibi.js</a> by
Kyle Barrow as a lightweight jQuery alternative.
</p>
</div>
-98
View File
@@ -1,98 +0,0 @@
<?php
$ipmask='pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}$"';
?>
<form class="Box str mobcol" action="<?= e(url('network_set')) ?>" method="GET" id="form-2">
<h2 tabindex=0><?= tr('net.sta') ?></h2>
<div class="Row explain">
<?= tr('net.explain_sta') ?>
</div>
<div class="Row checkbox x-static-toggle" >
<label><?= tr('net.sta_dhcp_enable') ?></label><!--
--><span class="box" tabindex=0 role=checkbox></span>
<input type="hidden" id="sta_dhcp_enable" name="sta_dhcp_enable" value="%sta_dhcp_enable%">
</div>
<div class="Row x-static">
<label for="sta_addr_ip"><?= tr('net.sta_addr_ip') ?></label>
<input type="text" name="sta_addr_ip" id="sta_addr_ip" value="%h:sta_addr_ip%" <?=$ipmask?> required>
</div>
<div class="Row x-static">
<label for="sta_addr_mask"><?= tr('net.sta_addr_mask') ?></label>
<input type="text" name="sta_addr_mask" id="sta_addr_mask" value="%h:sta_addr_mask%" <?=$ipmask?> required>
</div>
<div class="Row x-static">
<label for="sta_addr_gw"><?= tr('net.sta_addr_gw') ?></label>
<input type="text" name="sta_addr_gw" id="sta_addr_gw" value="%h:sta_addr_gw%" <?=$ipmask?> required>
</div>
<div class="Row buttons">
<a class="button icn-ok" href="#" onclick="qs('#form-2').submit()"><?= tr('apply') ?></a>
</div>
</form>
<form class="Box str mobcol" action="<?= e(url('network_set')) ?>" method="GET" id="form-1">
<h2 tabindex=0><?= tr('net.ap') ?></h2>
<div class="Row explain">
<?= tr('net.explain_ap') ?>
</div>
<div class="Row">
<label for="ap_addr_mask"><?= tr('net.ap_addr_mask') ?></label>
<input type="text" name="ap_addr_mask" id="ap_addr_mask" value="%h:ap_addr_mask%" <?=$ipmask?> required>
</div>
<div class="Row">
<label for="ap_addr_ip"><?= tr('net.ap_addr_ip') ?></label>
<input type="text" name="ap_addr_ip" id="ap_addr_ip" value="%h:ap_addr_ip%" <?=$ipmask?> required>
</div>
<div class="Row">
<label for="ap_dhcp_start"><?= tr('net.ap_dhcp_start') ?></label>
<input type="text" name="ap_dhcp_start" id="ap_dhcp_start" value="%h:ap_dhcp_start%" <?=$ipmask?> required>
</div>
<div class="Row">
<label for="ap_dhcp_end"><?= tr('net.ap_dhcp_end') ?></label>
<input type="text" name="ap_dhcp_end" id="ap_dhcp_end" value="%h:ap_dhcp_end%" <?=$ipmask?> required>
</div>
<div class="Row">
<label for="ap_dhcp_time"><?= tr('net.ap_dhcp_time') ?><span class="mq-phone">&nbsp;(min)</span></label>
<input type="number" step=1 min=1 max=2880 name="ap_dhcp_time" id="ap_dhcp_time" value="%ap_dhcp_time%" required>
<span class="mq-no-phone">&nbsp;min</span>
</div>
<div class="Row buttons">
<a class="button icn-ok" href="#" onclick="qs('#form-1').submit()"><?= tr('apply') ?></a>
</div>
</form>
<div class="Box mobcol">
<h2><?= tr('net.details') ?></h2>
<div class="Row">
<label><?= tr('net.sta_mac') ?></label><input type="text" readonly value="%sta_mac%">
</div>
<div class="Row">
<label><?= tr('net.ap_mac') ?></label><input type="text" readonly value="%ap_mac%">
</div>
</div>
<script>
function updateStaticDisp() {
var sttc = !parseInt($('#sta_dhcp_enable').val());
$('.x-static').toggleClass('hidden', !sttc);
}
$('.x-static-toggle').on('click', function() {
setTimeout(function() {
updateStaticDisp();
}, 0)
});
updateStaticDisp();
</script>
-90
View File
@@ -1,90 +0,0 @@
<div class="Box str mobcol">
<h2 tabindex=0><?= tr('system.save_restore') ?></h2>
<div class="Row explain nomargintop">
<?= tr('system.explain_persist') ?>
</div>
<div class="Row buttons2">
<a class="button icn-restore"
onclick="return confirm('<?= tr('system.confirm_restore') ?>');"
href="<?= e(url('restore_defaults')) ?>">
<?= tr('system.restore_defaults') ?>
</a>
</div>
<div class="Row buttons2">
<a onclick="writeDefaults(); return false;" href="#"><?= tr('system.write_defaults') ?></a>
</div>
<div class="Row buttons2">
<a onclick="return confirm('<?= tr('system.confirm_restore_hard') ?>');"
href="<?= e(url('restore_hard')) ?>">
<?= tr('system.restore_hard') ?>
</a>
</div>
</div>
<form class="Box str mobcol" action="<?= e(url('system_set')) ?>" method="GET" id="form-1">
<h2 tabindex=0><?= tr('system.uart') ?></h2>
<div class="Row explain">
<?= tr('system.explain_uart') ?>
</div>
<div class="Row">
<label for="uart_baud"><?= tr('uart.baud') ?><span class="mq-phone">&nbsp;(bps)</span></label>
<select name="uart_baud" id="uart_baud" class="short">
<?php foreach([
300, 600, 1200, 2400, 4800, 9600, 19200, 38400,
57600, 74880, 115200, 230400, 460800, 921600, 1843200, 3686400,
] as $b):
?><option value="<?=$b?>"><?= number_format($b, 0, ',', '.') ?></option>
<?php endforeach; ?>
</select>
<span class="mq-no-phone">&nbsp;bps</span>
</div>
<div class="Row">
<label for="uart_parity"><?= tr('uart.parity') ?></label>
<select name="uart_parity" id="uart_parity" class="short">
<?php foreach([
2 => tr('uart.parity.none'),
1 => tr('uart.parity.odd'),
0 => tr('uart.parity.even'),
] as $k => $label):
?><option value="<?=$k?>"><?=$label?></option>
<?php endforeach; ?>
</select>
</div>
<div class="Row">
<label for="uart_stopbits"><?= tr('uart.stop_bits') ?></label>
<select name="uart_stopbits" id="uart_stopbits" class="short">
<?php foreach([
1 => tr('uart.stop_bits.one'),
2 => tr('uart.stop_bits.one_and_half'),
3 => tr('uart.stop_bits.two'),
] as $k => $label):
?><option value="<?=$k?>"><?=$label?></option>
<?php endforeach; ?>
</select>
</div>
<div class="Row buttons">
<a class="button icn-ok" href="#" onclick="qs('#form-1').submit()"><?= tr('apply') ?></a>
</div>
</form>
<script>
function writeDefaults() {
var pw = prompt('<?= tr('system.confirm_store_defaults') ?>');
if (!pw) return;
location.href = <?=json_encode(url('write_defaults')) ?> + '?pw=' + pw;
}
$('#uart_baud').val(%uart_baud%);
$('#uart_parity').val(%uart_parity%);
$('#uart_stopbits').val(%uart_stopbits%);
</script>
-160
View File
@@ -1,160 +0,0 @@
<form class="Box mobopen str" action="<?= e(url('term_set')) ?>" method="GET" id='form-1'>
<h2><?= tr('term.defaults') ?></h2>
<div class="Row explain">
<?= tr('term.explain_initials') ?>
</div>
<div class="Row">
<label for="theme"><?= tr("term.theme") ?></label>
<select name="theme" id="theme" class="short" onchange="showColor()">
<option value="0">Tango</option>
<option value="1">Linux</option>
<option value="2">XTerm</option>
<option value="3">Rxvt</option>
<option value="4">Ambience</option>
<option value="5">Solarized</option>
</select>
</div>
<div class="Row color-preview">
<div class="colorprev">
<span data-fg=0 class="bg0 fg0">30</span><!--
--><span data-fg=1 class="bg0 fg1">31</span><!--
--><span data-fg=2 class="bg0 fg2">32</span><!--
--><span data-fg=3 class="bg0 fg3">33</span><!--
--><span data-fg=4 class="bg0 fg4">34</span><!--
--><span data-fg=5 class="bg0 fg5">35</span><!--
--><span data-fg=6 class="bg0 fg6">36</span><!--
--><span data-fg=7 class="bg0 fg7">37</span>
</div>
<div class="colorprev">
<span data-fg=8 class="bg0 fg8">90</span><!--
--><span data-fg=9 class="bg0 fg9">91</span><!--
--><span data-fg=10 class="bg0 fg10">92</span><!--
--><span data-fg=11 class="bg0 fg11">93</span><!--
--><span data-fg=12 class="bg0 fg12">94</span><!--
--><span data-fg=13 class="bg0 fg13">95</span><!--
--><span data-fg=14 class="bg0 fg14">96</span><!--
--><span data-fg=15 class="bg0 fg15">97</span>
</div>
<div class="colorprev">
<span data-bg=0 class="bg0 fg15">40</span><!--
--><span data-bg=1 class="bg1 fg15">41</span><!--
--><span data-bg=2 class="bg2 fg15">42</span><!--
--><span data-bg=3 class="bg3 fg0">43</span><!--
--><span data-bg=4 class="bg4 fg15">44</span><!--
--><span data-bg=5 class="bg5 fg15">45</span><!--
--><span data-bg=6 class="bg6 fg15">46</span><!--
--><span data-bg=7 class="bg7 fg0">47</span>
</div>
<div class="colorprev">
<span data-bg=8 class="bg8 fg15">100</span><!--
--><span data-bg=9 class="bg9 fg0">101</span><!--
--><span data-bg=10 class="bg10 fg0">102</span><!--
--><span data-bg=11 class="bg11 fg0">103</span><!--
--><span data-bg=12 class="bg12 fg0">104</span><!--
--><span data-bg=13 class="bg13 fg0">105</span><!--
--><span data-bg=14 class="bg14 fg0">106</span><!--
--><span data-bg=15 class="bg15 fg0">107</span>
</div>
</div>
<div class="Row color-preview">
<div style="
" id="color-example">
<?= tr("term.example") ?>
</div>
</div>
<div class="Row">
<label><?= tr("term.default_fg_bg") ?></label>
<select name="default_fg" id="default_fg" class="short" onchange="showColor()">
<?php for($i=0; $i<16; $i++): ?>
<option value="<?=$i?>"><?= tr("color.$i") ?></option>
<?php endfor; ?>
</select>&nbsp;<!--
--><select name="default_bg" id="default_bg" class="short" onchange="showColor()">
<?php for($i=0; $i<16; $i++): ?>
<option value="<?=$i?>"><?= tr("color.$i") ?></option>
<?php endfor; ?>
</select>
</div>
<div class="Row">
<label for="term_width"><?= tr('term.term_width') ?></label>
<input type="number" step=1 min=1 max=255 name="term_width" id="term_width" value="%term_width%" required>&nbsp;<!--
--><input type="number" step=1 min=1 max=255 name="term_height" id="term_height" value="%term_height%" required>
</div>
<div class="Row">
<label for="term_title"><?= tr('term.term_title') ?></label>
<input type="text" name="term_title" id="term_title" value="%h:term_title%" required>
</div>
<div class="Row">
<label><?= tr("term.buttons") ?></label>
<input class="short" type="text" name="btn1" id="btn1" value="%h:btn1%">&nbsp;
<input class="short" type="text" name="btn2" id="btn2" value="%h:btn2%">&nbsp;
<input class="short" type="text" name="btn3" id="btn3" value="%h:btn3%">&nbsp;
<input class="short" type="text" name="btn4" id="btn4" value="%h:btn4%">&nbsp;
<input class="short" type="text" name="btn5" id="btn5" value="%h:btn5%">
</div>
<div class="Row">
<label for="parser_tout_ms"><?= tr('term.parser_tout_ms') ?><span class="mq-phone">&nbsp;(ms)</span></label>
<input type="number" step=1 min=0 name="parser_tout_ms" id="parser_tout_ms" value="%parser_tout_ms%" required>
<span class="mq-no-phone">&nbsp;ms</span>
</div>
<div class="Row">
<label for="display_tout_ms"><?= tr('term.display_tout_ms') ?><span class="mq-phone">&nbsp;(ms)</span></label>
<input type="number" step=1 min=0 name="display_tout_ms" id="display_tout_ms" value="%display_tout_ms%" required>
<span class="mq-no-phone">&nbsp;ms</span>
</div>
<div class="Row">
<label for="display_cooldown_ms"><?= tr('term.display_cooldown_ms') ?><span class="mq-phone">&nbsp;(ms)</span></label>
<input type="number" step=1 min=0 name="display_cooldown_ms" id="display_cooldown_ms" value="%display_cooldown_ms%" required>
<span class="mq-no-phone">&nbsp;ms</span>
</div>
<div class="Row checkbox" >
<label><?= tr('term.fn_alt_mode') ?></label><!--
--><span class="box" tabindex=0 role=checkbox></span>
<input type="hidden" id="fn_alt_mode" name="fn_alt_mode" value="%fn_alt_mode%">
</div>
<div class="Row buttons">
<a class="button icn-ok" href="#" onclick="qs('#form-1').submit()"><?= tr('apply') ?></a>
</div>
</form>
<script>
$('#default_fg').val(%default_fg%);
$('#default_bg').val(%default_bg%);
$('#theme').val(%theme%);
function showColor() {
var ex = qs('#color-example');
ex.className = '';
ex.classList.add('fg'+$('#default_fg').val());
ex.classList.add('bg'+$('#default_bg').val());
var th = $('#theme').val();
$('.color-preview').forEach(function(e) {
e.className = 'Row color-preview theme-'+th;
});
}
showColor();
$('.colorprev span').on('click', function() {
var fg = this.dataset.fg;
var bg = this.dataset.bg;
if (typeof fg != 'undefined') $('#default_fg').val(fg);
if (typeof bg != 'undefined') $('#default_bg').val(bg);
showColor()
});
</script>
-123
View File
@@ -1,123 +0,0 @@
<form class="Box str mobcol" action="<?= e(url('wifi_set')) ?>" method="GET" id="form-1">
<h2 tabindex=0><?= tr('wifi.ap') ?></h2>
<div class="Row checkbox x-ap-toggle">
<label><?= tr('wifi.enable') ?></label><!--
--><span class="box" tabindex=0></span>
<input type="hidden" id="ap_enabled" name="ap_enable" value="%ap_enable%">
</div>
<div class="Row x-ap-on">
<label for="ap_ssid"><?= tr('wifi.ap_ssid') ?></label>
<input type="text" name="ap_ssid" id="ap_ssid" value="%h:ap_ssid%" required>
</div>
<div class="Row x-ap-on">
<label for="ap_password"><?= tr('wifi.ap_password') ?></label>
<input type="text" name="ap_password" id="ap_password" value="%h:ap_password%">
</div>
<div class="Row x-ap-on">
<label for="ap_channel"><?= tr('wifi.ap_channel') ?></label>
<input type="number" name="ap_channel" id="ap_channel" min=1 max=14 value="%ap_channel%" required>
</div>
<div class="Row range x-ap-on">
<label for="tpw">
<?= tr('wifi.tpw') ?>
<span class="display x-disp1 mq-phone"></span>
</label>
<input type="range" name="tpw" id="tpw" step=1 min=0 max=82 value="%tpw%">
<span class="display x-disp2 mq-no-phone"></span>
</div>
<div class="Row checkbox x-ap-on">
<label><?= tr('wifi.ap_hidden') ?></label><!--
--><span class="box" tabindex=0></span>
<input type="hidden" name="ap_hidden" value="%ap_hidden%">
</div>
<div class="Row buttons">
<a class="button icn-ok" href="#" onclick="qs('#form-1').submit()"><?= tr('apply') ?></a>
</div>
</form>
<form class="Box str mobcol expanded" action="<?= e(url('wifi_set')) ?>" method="GET" id="form-2">
<h2 tabindex=0><?= tr('wifi.sta') ?></h2>
<div class="Row checkbox x-sta-toggle">
<label><?= tr('wifi.enable') ?></label><!--
--><span class="box" tabindex=0></span>
<input type="hidden" id="sta_enabled" name="sta_enable" value="%sta_enable%">
</div>
<div class="Row explain nomargintop x-sta-on">
<span class="spacer"></span>
<?= tr("wifi.sta_explain") ?>
</div>
<input type="hidden" name="sta_ssid" id="sta_ssid" value="">
<input type="hidden" name="sta_password" id="sta_password" value="">
<div class="Row sta-info x-sta-on">
<label><?= tr('wifi.sta_info') ?></label>
<div class="AP-preview hidden" id="sta-nw">
<div class="wrap">
<div class="inner">
<div class="essid"></div>
<div class="passwd"><?= tr('wifi.sta_active_pw') ?>&nbsp;<span class="x-passwd"></span></div>
<div class="nopasswd"><?= tr('wifi.sta_active_nopw') ?></div>
<div class="ip"></div>
</div>
<a class="forget" href="#" id="forget-sta">×</a>
</div>
</div>
<div class="AP-preview-nil" id="sta-nw-nil">
<?= tr('wifi.sta_none') ?>
</div>
</div>
<div id="ap-box" class="x-sta-on">
<label><?= tr('wifi.select_ssid') ?></label>
<div id="ap-scan"><a href="#" onclick="WiFi.startScanning(); return false"><?= tr('wifi.scan_now') ?></a></div>
<div id="ap-loader" class="hidden"><?= tr('wifi.scanning') ?><span class="anim-dots">.</span></div>
<div id="ap-list" class="hidden"></div>
</div>
<div class="Row buttons">
<a class="button icn-ok" href="#" onclick="qs('#form-2').submit()"><?= tr('apply') ?></a>
</div>
</form>
<script>
WiFi.scan_url = '<?= url('wifi_scan', true) ?>';
WiFi.init({
sta_ssid: '%j:sta_ssid%',
sta_password: '%j:sta_password%',
sta_active_ip: '%j:sta_active_ip%',
sta_active_ssid: '%j:sta_active_ssid%',
});
function updateApDisp() {
var a = !!parseInt($('#ap_enabled').val());
$('.x-ap-on').toggleClass('hidden', !a);
}
$('.x-ap-toggle').on('click', function() {
setTimeout(function() {
updateApDisp();
}, 0)
});
function updateStaDisp() {
var a = !!parseInt($('#sta_enabled').val());
$('.x-sta-on').toggleClass('hidden', !a);
}
$('.x-sta-toggle').on('click', function() {
setTimeout(function() {
updateStaDisp();
}, 0)
});
updateApDisp();
updateStaDisp();
</script>
-89
View File
@@ -1,89 +0,0 @@
<h1><?= tr('menu.cfg_wifi_conn') ?></h1>
<div class="Box">
<p><b><?= tr('wifi.conn.status') ?></b> <span id="status"></span><span class="anim-dots">.</span></p>
<a href="<?= e(url('cfg_wifi')) ?>" id="backbtn" class="button"><?= tr('wifi.conn.back_to_config') ?></a>
</div>
<div class="Box">
<p><?= tr('wifi.conn.explain_android_sucks') ?></p>
<p><?= tr('wifi.conn.explain_reset') ?></p>
</div>
<script>
var xhr = new XMLHttpRequest();
var abortTmeo;
var failCounter = 0;
var messages = <?= json_encode([
'disabled' => tr('wifi.conn.disabled'),
'idle' => tr('wifi.conn.idle'),
'success' => tr('wifi.conn.success'),
'working' => tr('wifi.conn.working'),
'fail' => tr('wifi.conn.fail'),
]) ?>;
function onFail() {
$("#status").html(<?= json_encode(tr('wifi.conn.telemetry_lost')) ?>);
$('.anim-dots').addClass('hidden');
}
function getStatus() {
xhr.open("GET", 'http://'+_root+'<?= url('wifi_connstatus', true) ?>');
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
clearTimeout(abortTmeo);
try {
var data = JSON.parse(xhr.responseText);
var done = false;
var msg = messages[data.status] || '...';
if (data.status == 'success') {
msg += data.ip;
done = true;
}
if (data.status == 'fail') {
msg += data.cause;
done = true;
}
$("#status").html(msg);
if (done) {
// $('#backbtn').removeClass('hidden');
$('.anim-dots').addClass('hidden');
} else {
// ask again after a short delay
window.setTimeout(getStatus, 1000);
}
} catch(e) {
failCounter++;
console.log(e);
// repeat
if (failCounter > 5) {
onFail();
}
else {
window.setTimeout(getStatus, 1000);
}
}
} else {
onFail();
}
}
};
// XHR timeout
abortTmeo = setTimeout(function () {
xhr.abort();
onFail();
}, 4000);
xhr.send();
}
getStatus();
</script>
-600
View File
@@ -1,600 +0,0 @@
<div class="Box fold">
<h2>Tips & Troubleshooting</h2>
<div class="Row v">
<img src="/img/adapter.jpg" class="aside" alt="ESPTerm v2">
<ul>
<li><b>Communication UART (Rx, Tx)</b> can be configured in the <a href="<?= url('cfg_system') ?>">System Settings</a>.
<li><b>Boot log and debug messages</b> are available on pin <b>GPIO2</b> (P2) at 115200\,baud, 1 stop bit, no parity.
Those messages may be disabled through compile flags.
<li><b>Loopback test</b>: Connect the Rx and Tx pins with a piece of wire. Anything you type in the browser should
appear on the screen. Set Parser Timeout = 0 in <a href="<?= url('cfg_term') ?>">Terminal Settings</a>
to be able to manually enter escape sequences.
<li><b>For best performance</b>, use the module in Client mode (connected to external network) and minimize the number
of simultaneous connections. Enabling AP consumes extra RAM and strains the processor.
<li>In AP mode, <b>check that the WiFi channel used is clear</b>; interference may cause flaky connection.
A good mobile app to use for this is
<a href="https://play.google.com/store/apps/details?id=com.farproc.wifi.analyzer">WiFi Analyzer (Google Play)</a>.
Adjust the hotspot strength and range using the Tx Power setting.
<li>Hold the BOOT button (GPIO0 to GND) for ~1 second to force enable AP. Hold it for ~6 seconds to restore default settings.
(This is indicated by the blue LED rapidly flashing). Default settings can be overwritten in the
<a href="<?= url('cfg_system') ?>">System Settings</a>.
</ul>
</div>
</div>
<div class="Box fold">
<h2>Escape Sequences Intro</h2>
<div class="Row v">
<img src="/img/vt100.jpg" class="aside" alt="VT102">
<p>ESPTerm emulates VT102 (pictured) with some additions from later VT models and <code>xterm</code>.
All commonly used attributes and commands are supported.
ESPTerm is capable of displaying ncurses applications such as <i>Midnight Commander</i> using <i>agetty</i>.</p>
<p>
ESPTerm accepts UTF-8 characters received on the communication UART and displays them on the screen,
interpreting some codes as Control Characters. Those are e.g. Carriage Return (13), Line Feed (10), Tab (9), Backspace (8) and Bell (7).
Escape sequences start with the control character ESC (27), followed by any number of ASCII characters
forming the body of the command. On this page, we'll use <code>\e</code> to indicate ESC.
</p>
<p>
Escape sequences can be divided based on their first character and structure. Most common types are:
</p>
<ul>
<li>CSI commands: <code>\e[</code> followed by 1 optional leading character, multiple numbers divided by
semicolons, and one or two trailing characters. Those control the cursor, set attributes and manipulate screen content. E.g. <code>\e[?7;10h</code>, <code>\e[2J</code></li>
<li>SGR commands: CSI terminated by <code>m</code>. Those set text attributes and colors. E.g. <code>\e[</code></li>
<li>OSC commands: <code>\e]</code> followed by any number of UTF-8 characters (ESPTerm supports up to 64)
terminated by <code>\e\\</code> (ESC and backslash) or Bell (7). Those are used to exchange text strings.</li>
<li>There are a few other commands that don't follow this pattern, such as <code>\e7</code> or
<code>\e#8</code>, mostly for historical reasons.</li>
</ul>
<p>A list of the most important escape sequences is presented in the following sections.</p>
</div>
</div>
<div class="Box fold">
<h2>Screen Behavior</h2>
<div class="Row v">
<p>The initial screen size, title text and button labels can be configured in <a href="<?= url('cfg_term') ?>">Terminal Settings</a>.</p>
<p>
Screen updates are sent to the browser through a WebSocket after some time of inactivity on the communication UART
(called "Redraw Delay"). After an update is sent, at least a time of "Redraw Cooldown" must elapse before the next
update can be sent. Those delays are used is to avoid burdening the server with tiny updates during a large screen
repaint. If you experience issues (broken image due to dropped bytes), try adjusting those config options. It may also
be useful to try different baud rates.
</p>
</div>
</div>
<div class="Box fold">
<h2>Text Attributes</h2>
<div class="Row v">
<p>
All text attributes are set using SGR commands like <code>\e[10;20;30m</code>, with up to 10 numbers separated by semicolons.
To restore all attributes to their default states, use SGR 0: <code>\e[0m</code> or <code>\e[m</code>.
</p>
<p>Those are the supported text attributes SGR codes:</p>
<table>
<thead><tr><th>Style</th><th>Enable</th><th>Disable</th></tr></thead>
<tbody>
<tr><td><b>Bold</b></td><td>1</td><td>21, 22</td></tr>
<tr><td style="opacity:.6">Faint</td><td>2</td><td>22</td></tr>
<tr><td><i>Italic</i></td><td>3</td><td>23</td></tr>
<tr><td><u>Underlined</u></td><td>4</td><td>24</td></tr>
<tr><td>Blink</td><td>5</td><td>25</td></tr>
<tr><td><span style="color:black;background:#ccc;">Inverse</span></td><td>7</td><td>27</td></tr>
<tr><td><s>Striked</s></td><td>9</td><td>29</td></tr>
<tr><td>𝔉𝔯𝔞𝔨𝔱𝔲𝔯</td><td>20</td><td>23</td></tr>
</tbody>
</table>
</div>
</div>
<div class="Box fold theme-0">
<h2>Colors</h2>
<div class="Row v">
<p>Colors are set using SGR commands (like <code>\e[10;20;30m</code>). The following tables list the SGR codes to use.
Selected colors are used for any new text entered, as well as for empty space when using line and screen clearing commands.
The configured default colors can be restored using SGR 39 for foreground and SGR 49 for background.</p>
<p>The actual color representation depends on a color theme which
can be selected in <a href="<?= url('cfg_term') ?>">Terminal Settings</a>.</p>
<h3>Foreground colors</h3>
<div class="colorprev">
<span class="bg7 fg0">30</span>
<span class="bg0 fg1">31</span>
<span class="bg0 fg2">32</span>
<span class="bg0 fg3">33</span>
<span class="bg0 fg4">34</span>
<span class="bg0 fg5">35</span>
<span class="bg0 fg6">36</span>
<span class="bg0 fg7">37</span>
</div>
<div class="colorprev">
<span class="bg0 fg8">90</span>
<span class="bg0 fg9">91</span>
<span class="bg0 fg10">92</span>
<span class="bg0 fg11">93</span>
<span class="bg0 fg12">94</span>
<span class="bg0 fg13">95</span>
<span class="bg0 fg14">96</span>
<span class="bg0 fg15">97</span>
</div>
<h3>Background colors</h3>
<div class="colorprev">
<span class="bg0 fg15">40</span>
<span class="bg1 fg15">41</span>
<span class="bg2 fg15">42</span>
<span class="bg3 fg0">43</span>
<span class="bg4 fg15">44</span>
<span class="bg5 fg15">45</span>
<span class="bg6 fg15">46</span>
<span class="bg7 fg0">47</span>
</div>
<div class="colorprev">
<span class="bg8 fg15">100</span>
<span class="bg9 fg0">101</span>
<span class="bg10 fg0">102</span>
<span class="bg11 fg0">103</span>
<span class="bg12 fg0">104</span>
<span class="bg13 fg0">105</span>
<span class="bg14 fg0">106</span>
<span class="bg15 fg0">107</span>
</div>
</div>
</div>
<div class="Box fold">
<h2>User Input</h2>
<div class="Row v">
<h3>Keyboard</h3>
<p>
The user can input text using their keyboard, or on Android, using the on-screen keyboard which is open using
a button beneath the screen. Supported are all printable characters, as well as many control keys, such as arrows, Ctrl+letters
and function keys. Sequences sent by function keys are based on VT102 and xterm.
</p>
<p>
The codes sent by Home, End, F1-F4 and cursor keys are affected by various keyboard modes (Application Cursor Keys,
Application Numpad Mode, SS3 Fn Keys Mode).
Some can be set in the <a href="<?= url('cfg_term') ?>">Terminal Settings</a>, others via commands.
</p>
<p>
Here are some examples of control key codes:
</p>
<table>
<thead><tr><th>Key</th><th>Code</th><th>Key</th><th>Code</th></tr></thead>
<tr>
<td>🡑</td>
<td><code>\e[A</code></td>
<td>F1</td>
<td><code>\eOP</code></td>
</tr>
<tr>
<td>🡓</td>
<td><code>\e[B</code></td>
<td>F2</td>
<td><code>\eOQ</code></td>
</tr>
<tr>
<td>🡒</td>
<td><code>\e[C</code></td>
<td>F3</td>
<td><code>\eOR</code></td>
</tr>
<tr>
<td>🡐</td>
<td><code>\e[D</code></td>
<td>F4</td>
<td><code>\eOS</code></td>
</tr>
<tr>
<td>Home</td>
<td><code>\eOH</code></td>
<td>F5</td>
<td><code>\e[15~</code></td>
</tr>
<tr>
<td>End</td>
<td><code>\eOF</code></td>
<td>F6</td>
<td><code>\e[17~</code></td>
</tr>
<tr>
<td>Insert</td>
<td><code>\e[2~</code></td>
<td>F7</td>
<td><code>\e[18~</code></td>
</tr>
<tr>
<td>Delete</td>
<td><code>\e[3~</code></td>
<td>F8</td>
<td><code>\e[19~</code></td>
</tr>
<tr>
<td>Page Up</td>
<td><code>\e[5~</code></td>
<td>F9</td>
<td><code>\e[20~</code></td>
</tr>
<tr>
<td>Page Down</td>
<td><code>\e[6~</code></td>
<td>F10</td>
<td><code>\e[21~</code></td>
</tr>
<tr>
<td>Enter</td>
<td><code>CR (13)</code></td>
<td>F11</td>
<td><code>\e[23~</code></td>
</tr>
<tr>
<td>Ctrl+Enter</td>
<td><code>LF (10)</code></td>
<td>F12</td>
<td><code>\e[24~</code></td>
</tr>
<tr>
<td>Tab</td>
<td><code>TAB (9)</code></td>
<td>ESC</td>
<td><code>ESC (27)</code></td>
</tr>
<tr>
<td>Backspace</td>
<td><code>BS (8)</code></td>
<td>Ctrl+A..Z</td>
<td><code>ASCII 1-26</code></td>
</tr>
</table>
<h3>Action buttons</h3>
<p>
The blue buttons under the screen send ASCII codes 1, 2, 3, 4, 5, which incidentally correspond to Ctrl+A,B,C,D,E.
This choice was made to make button press parsing as simple as possible.
</p>
<h3>Mouse</h3>
<p>
ESPTerm in the current version does not implement standard mouse input. Instead, clicks/taps produce CSI sequences
<code>\e[<i>r</i>;<i>c</i>M</code> (row, column). You can use this for virtual on-screen buttons.
</p>
</div>
</div>
<div class="Box fold">
<h2>Cursor Commands</h2>
<div class="Row v">
<p>
The coordinates are 1-based, origin is top left. The cursor can move within the entire screen,
or in the active Scrolling Region if Origin Mode is enabled.
</p>
<p>After writing a character, the cursor advances to the right. If it has reached the end of the row,
it stays on the same line, but writing the next character makes it jump to the start of the next
line first, scrolling up if needed. If Auto-wrap mode is disabled, the cursor never wraps or scrolls
the screen.
</p>
<p>
<b>Legend:</b>
Italic letters such as <i>n</i> are ASCII numbers that serve as arguments, separated with a semicolon.
If an argument is left out, it's treated as 0 or 1, depending on what makes sense for the command.
</p>
<h3>Movement</h3>
<table class="ansiref w100">
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
<tbody>
<tr>
<td>
\e[<i>n</i>A<br>
\e[<i>n</i>B<br>
\e[<i>n</i>C<br>
\e[<i>n</i>D
</td>
<td>Move cursor up (A), down (B), right (C), left (D)</td>
</tr>
<tr>
<td>
\e[<i>n</i>F<br>
\e[<i>n</i>E
</td>
<td>Go <i>n</i> lines up (F) or down (E), start of line</td>
</tr>
<tr>
<td>
\e[<i>r</i>d<br>
\e[<i>c</i>G<br>
\e[<i>r</i>;<i>c</i>H
</td>
<td>
Go to absolute position - row (d), column (G), or both (H). Use <code>\e[H</code> to go to 1,1.
</td>
</tr>
<tr>
<td>\e[6n</td>
<td>Query cursor position. Sent back as <code>\e[<i>r</i>;<i>c</i>R</code>.</td>
</tr>
</tbody>
</table>
<h3>Save / restore</h3>
<table class="ansiref w100">
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
<tbody>
<tr>
<td>\e[s<br>\e[u</td>
<td>Save (s) or restore (u) cursor position</td>
</tr>
<tr>
<td>\e7<br>\e8</td>
<td>Save (7) or restore (8) cursor position and attributes</td>
</tr>
</tbody>
</table>
<h3>Scrolling Region</h3>
<table class="ansiref w100">
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
<tbody>
<tr>
<td>\e[<i>a</i>;<i>b</i>r</td>
<td>Set scrolling region to rows <i>a</i> through <i>b</i> and go to 1,1. By default, the
scrolling region spans the entire screen height. The cursor can leave the region using
absolute position commands, unless Origin Mode (see below) is active.</td>
</tr>
<tr>
<td>\e[?6h<br>\e[?6l</td>
<td>Enable (h) or disable (l) Origin Mode and go to 1,1. In Origin Mode, all coordinates
are relative to the Scrolling Region and the cursor can't leave the region.</td>
</tr>
<tr>
<td>
\e[<i>n</i>S<br>
\e[<i>n</i>T
</td>
<td>Move contents of the Scrolling Region up (S) or down (T), pad with empty lines of the current background color.</td>
</tr>
</tbody>
</table>
<h3>Tab stops</h3>
<table class="ansiref w100">
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
<tbody>
<tr>
<td>\eH</td>
<td>Set tab stop at the current column. There are, by default, tabs every 8 columns.</td>
</tr>
<tr>
<td>
\e[<i>n</i>I<br>
\e[<i>n</i>Z
</td>
<td>Advance (I) or go back (Z) <i>n</i> tab stops or end/start of line. ASCII TAB (9) is equivalent to <code>\e[1I</code></td>
</tr>
<tr>
<td>
\e[0g<br>
\e[3g<br>
</td>
<td>Clear tab stop at the current column (0), or all columns (3).</td>
</tr>
</tbody>
</table>
<h3>Other options</h3>
<table class="ansiref w100">
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
<tbody>
<tr>
<td>\e[?7h<br>\e[?7l</td>
<td>Enable (h) or disable (l) cursor auto-wrap and screen auto-scroll</td>
</tr>
<tr>
<td>\e[?25h<br>\e[?25l</td>
<td>Show (h) or hide (l) the cursor</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="Box fold">
<h2>Screen Content Manipulation</h2>
<div class="Row v">
<p>
<b>Legend:</b>
Italic letters such as <i>n</i> are ASCII numbers that serve as arguments, separated with a semicolon.
If an argument is left out, it's treated as 0 or 1, depending on what makes sense for the command.
</p>
<table class="ansiref w100">
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
<tbody>
<tr>
<td>\e[<i>m</i>J</td>
<td>Clear part of screen. <i>m</i>: 0 - from cursor, 1 - to cursor, 2 - all</td>
</tr>
<tr>
<td>\e[<i>m</i>K</td>
<td>Erase part of line. <i>m</i>: 0 - from cursor, 1 - to cursor, 2 - all</td>
</tr>
<tr>
<td>
\e[<i>n</i>X
</td>
<td>Erase <i>n</i> characters in line.</td>
</tr>
<tr>
<td>
\e[<i>n</i>L<br>
\e[<i>n</i>M
</td>
<td>Insert (L) or delete (M) <i>n</i> lines. Following lines are pulled up or pushed down.</td>
</tr>
<tr>
<td>
\e[<i>n</i>@<br>
\e[<i>nP
</td>
<td>Insert (@) or delete (P) <i>n</i> characters. The rest of the line is pulled left or pushed right.
Characters going past the end of line are lost.</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="Box fold">
<h2>Alternate Character Sets</h2>
<div class="Row v">
<p>
ESPTerm implements Alternate Character Sets as a way to print box drawing characters
and special symbols. A character set can change what each received ASCII character
is printed as on the screen (eg. "{" is "π" in codepage 0). The implementation is based
on the original VT devices.
Since ESPTerm also fully supports UTF-8, you can probably ignore this feature and use
Unicode directly. It's added for compatibility with some programs that use this.
</p>
<p>The following codepages are implemented:</p>
<ul>
<li>B - US ASCII (default)</li>
<li>A - UK ASCII: # replaced with £</li>
<li>0 - Symbols and basic line drawing (standard DEC alternate character set)</li>
<li>1 - Symbols and advanced line drawing (based on DOS codepage 437)</li>
</ul>
<p>To see what character maps to which symbol, look in the source code or try it. All codepages use codes 32-127, 32 being space.</p>
<p>
There are two character set slots, G0 and G1.
Those slots are selected as active using ASCII codes Shift In and Shift Out (those originally served for shifting
a red-black typewriter tape). Each slot (G0 and G1) can have a different codepage assigned. G0 and G1 and the active slot number are
saved and restored with the cursor and cleared with a screen reset (<code>\ec</code>).
</p>
<p>The following commands are used:</p>
<table class="ansiref w100">
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
<tbody>
<tr>
<td>\e(<i>x</i></td>
<td>Set G0 = codepage <i>x</i></td>
</tr>
<tr>
<td>\e)<i>x</i></td>
<td>Set G1 = codepage <i>x</i></td>
</tr>
<tr>
<td>SO (14)</td>
<td>Activate G0</td>
</tr>
<tr>
<td>SI (15)</td>
<td>Activate G1</td>
</tr>
</table>
</div>
</div>
<div class="Box fold">
<h2>System Commands</h2>
<div class="Row v">
<p>
It's possible to dynamically change the screen title text and action button labels.
Setting an empty label to a button makes it look disabled. The buttons send ASCII 1-5 when clicked.
Those changes are not retained after restart.
</p>
<table class="ansiref w100">
<thead><tr><th>Code</th><th>Meaning</th></tr></thead>
<tbody>
<tr>
<td>\ec</td>
<td>
Clear screen, reset attributes and cursor.
The screen size, title and button labels remain unchanged.
</td>
</tr>
<tr>
<td>\e[5n</td>
<td>
Query device status, ESPTerm replies with <code>\e[0n</code> "device is OK".
Can be used to check if the terminal has booted up and is ready to receive commands.</td>
</tr>
<tr>
<td>CAN (24)</td>
<td>
This ASCII code is not a command, but is sent by ESPTerm when it becomes ready to receive commands.
When this code is received on the UART, it means ESPTerm has restarted and is ready. Use this to detect
spontaneous restarts which require a full screen repaint.
</td>
</tr>
<tr>
<td>\e]0;<i>title</i>\a</td>
<td>Set screen title (this is a standard OSC command)</td>
</tr>
<tr>
<td>
\e]<i>81</i>;<i>btn1</i>\a<br>
\e]<i>82</i>;<i>btn2</i>\a<br>
\e]<i>83</i>;<i>btn3</i>\a<br>
\e]<i>84</i>;<i>btn4</i>\a<br>
\e]<i>85</i>;<i>btn5</i>\a<br>
</td>
<td>Set button 1-5 label - eg. <code>\e]81;Yes\a</code>
sets the first button text to "Yes".</td>
</tr>
<tr>
<td>\e[8;<i>r</i>;<i>c</i>t</td>
<td>Set screen size (this is a command borrowed from xterm)</td>
</tr>
</tbody>
</table>
</div>
</div>
-62
View File
@@ -1,62 +0,0 @@
<script>
// Workaround for badly loaded page
setTimeout(function() {
if (typeof termInit == 'undefined' || typeof $ == 'undefined') {
console.error("Page load failed, refreshing…");
location.reload(true);
}
}, 2000);
</script>
<h1></h1>
<div id="termwrap">
<div id="screen" class="theme-%theme%"></div>
<div id="buttons">
<button data-n="1" class="btn-blue"></button><!--
--><button data-n="2" class="btn-blue"></button><!--
--><button data-n="3" class="btn-blue"></button><!--
--><button data-n="4" class="btn-blue"></button><!--
--><button data-n="5" class="btn-blue"></button>
</div>
</div>
<textarea id="softkb-input" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
<nav id="botnav">
<a href="#" onclick="toggleSoftKb(true); return false" class="icn-keyboard mq-tablet-max"></a><!--
--><a href="<?= url('cfg_term') ?>"><?= tr('term_nav.config') ?></a><!--
--><a href="<?= url('cfg_wifi') ?>"><?= tr('term_nav.wifi') ?></a><!--
--><a href="<?= url('help') ?>"><?= tr('term_nav.help') ?></a><!--
--><a href="<?= url('about') ?>"><?= tr('term_nav.about') ?></a>
</nav>
<script>
try {
window.noAutoShow = true;
termInit(); // the screen will be loaded via ajax
Screen.load('%j:labels_seq%');
// auto-clear the input box
$('#softkb-input').on('input', function(e) {
setTimeout(function(){
var str = $('#softkb-input').val();
$('#softkb-input').val('');
Input.sendString(str);
}, 1);
});
} catch(e) {
console.error(e);
console.error("Fail, reloading in 1s…");
setTimeout(function() {
location.reload(true);
}, 1000);
}
function toggleSoftKb(yes) {
var i = qs('#softkb-input');
if (yes) i.focus();
else i.blur();
}
</script>
File diff suppressed because one or more lines are too long
-17
View File
@@ -1,17 +0,0 @@
// Customize Neat grid
@import "lib/neat/neat-helpers";
// optionally change gri settings
// Define breakpoints
$phone: new-breakpoint(max-width 544px);
$tablet: new-breakpoint(min-width 545px max-width 1000px);
$normal: new-breakpoint(min-width 1001px max-width 1376px);
$large: new-breakpoint(min-width 1377px);
$tablet-max: new-breakpoint(max-width 1000px);
$normal-max: new-breakpoint(max-width 1376px);
$tablet-min: new-breakpoint(min-width 545px);
$normal-min: new-breakpoint(min-width 1001px);
-439
View File
@@ -1,439 +0,0 @@
/* normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS and IE text size adjust after device orientation change,
* without disabling user zoom.
*/
*, *:before, *:after {
box-sizing: border-box;
}
html {
font-family: sans-serif; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined for any HTML5 element in IE 8/9.
* Correct `block` display not defined for `details` or `summary` in IE 10/11
* and Firefox.
* Correct `block` display not defined for `main` in IE 11.
*/
//article,
//aside,
//details,
//figcaption,
figure,
//footer,
//header,
//hgroup,
//main,
//menu,
nav
//section,
//summary
{
display: block;
}
/**
* 1. Correct `inline-block` display not defined in IE 8/9.
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
*/
//audio,
canvas,
progress
//video
{
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
//
//audio:not([controls]) {
// display: none;
// height: 0;
//}
/**
* Address `[hidden]` styling not present in IE 8/9/10.
* Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.
*/
[hidden]
//template
{
display: none;
}
/* Links
========================================================================== */
/**
* Remove the gray background color from active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* Improve readability of focused elements when they are also in an
* active/hover state.
*/
a:active,
a:hover {
outline: 0;
}
/* Text-level semantics
========================================================================== */
/**
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
*/
//abbr[title] {
// border-bottom: 1px dotted;
//}
/**
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
*/
b
//strong
{
font-weight: bold;
}
/**
* Address styling not present in Safari and Chrome.
*/
//
//dfn {
// font-style: italic;
//}
/**
* Address variable `h1` font-size and margin within `section` and `article`
* contexts in Firefox 4+, Safari, and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
h2 {
font-size: 2em;
margin: 0.67em 0;
}
/**
* Address styling not present in IE 8/9.
*/
//
//mark {
// background: #ff0;
// color: #000;
//}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* Embedded content
========================================================================== */
/**
* Remove border when inside `a` element in IE 8/9/10.
*/
img {
border: 0;
}
/**
* Correct overflow not hidden in IE 9/10/11.
*/
svg:not(:root) {
overflow: hidden;
}
/* Grouping content
========================================================================== */
/**
* Address margin not present in IE 8/9 and Safari.
*/
//figure {
// margin: 1em 40px;
//}
/**
* Address differences between Firefox and other browsers.
*/
hr {
box-sizing: content-box;
height: 0;
}
/**
* Contain overflow in all browsers.
*/
pre {
overflow: auto;
}
/**
* Address odd `em`-unit font size rendering in all browsers.
*/
code,
//kbd,
pre
//samp
{
font-family: monospace;
font-size: 1em;
}
/* Forms
========================================================================== */
/**
* Known limitation: by default, Chrome and Safari on OS X allow very limited
* styling of `select`, unless a `border` property is set.
*/
/**
* 1. Correct color not being inherited.
* Known issue: affects color of disabled elements.
* 2. Correct font properties not being inherited.
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
*/
button,
input,
//optgroup,
select,
textarea {
color: inherit; /* 1 */
font: inherit; /* 2 */
margin: 0; /* 3 */
}
/**
* Address `overflow` set to `hidden` in IE 8/9/10/11.
*/
button {
overflow: visible;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
* Correct `select` style inheritance in Firefox.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
//html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* Remove inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
input {
line-height: normal;
}
/**
* It's recommended that you don't attempt to style these elements.
* Firefox's implementation doesn't respect box-sizing, padding, or width.
*
* 1. Address box sizing set to `content-box` in IE 8/9/10.
* 2. Remove excess padding in IE 8/9/10.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
* `font-size` values of the `input`, it causes the cursor style of the
* decrement button to change from `default` to `text`.
*/
//input[type="number"]::-webkit-inner-spin-button,
//input[type="number"]::-webkit-outer-spin-button {
// height: auto;
//}
/**
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome.
*/
//
//input[type="search"] {
// -webkit-appearance: textfield; /* 1 */
// box-sizing: content-box; /* 2 */
//}
/**
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
* Safari (but not Chrome) clips the cancel button when the search input has
* padding (and `textfield` appearance).
*/
//
//input[type="search"]::-webkit-search-cancel-button,
//input[type="search"]::-webkit-search-decoration {
// -webkit-appearance: none;
//}
/**
* Define consistent border, margin, and padding.
*/
//
//fieldset {
// border: 1px solid #c0c0c0;
// margin: 0 2px;
// padding: 0.35em 0.625em 0.75em;
//}
/**
* 1. Correct `color` not being inherited in IE 8/9/10/11.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* Remove default vertical scrollbar in IE 8/9/10/11.
*/
textarea {
overflow: auto;
}
/**
* Don't inherit the `font-weight` (applied by a rule above).
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
*/
//
//optgroup {
// font-weight: bold;
//}
/* Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}
-55
View File
@@ -1,55 +0,0 @@
// Add a highlight for debugging
@mixin highlight($color) {
outline: 1px solid $color;
background: rgba($color, .05);
box-shadow: 0 0 2px 2px rgba($color, .2), inset 0 0 2px 2px rgba($color, .2);
}
// Ellipsis, but for block elements
@mixin block-ellipsis($width: 100%) {
display: block;
max-width: $width;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: normal;
}
// No margins, padding, borders
@mixin naked() {
border: 0 none;
margin: 0;
padding: 0;
text-decoration: none;
}
@mixin translate($x, $y) {
@include transform(translate($x, $y));
}
// Disallow wrapping
@mixin nowrap() {
white-space: nowrap;
word-wrap: normal;
}
@mixin click-through() {
pointer-events: none;
}
// Disallow text selection
@mixin noselect() {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
// Allow text selection
@mixin can-select() {
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
cursor: text;
}
-53
View File
@@ -1,53 +0,0 @@
@import "normalize";
@import "fontello";
@import "lib/bourbon/bourbon";
@import "grid-settings";
@import "lib/neat/neat";
@import "utils";
$form-label-w: 160px;
$form-label-gap: 8px;
$form-field-w: 250px;
$c-red-outline: #ff0099;
$c-form-label-fg: white;
$c-form-field-bg: #3c3c3c;
$c-form-field-fg: white;
$c-form-highlight: #2972ba;
$c-form-highlight-a: #2ea1f9;
@function dist($x) {
@return modular-scale($x, 1rem, $golden);
}
@function fsize($x) {
@return modular-scale($x, 1em, $major-second);
}
@import "layout/index";
@import "form/index";
// import all our pages
@import "pages/wifi";
@import "pages/term";
@import "pages/about";
// media queries
@include media($tablet-min) {
.mq-phone { display: none!important; }
}
@include media($phone) {
.mq-tablet-min, .mq-no-phone { display: none !important; }
}
@include media($normal-min) {
.mq-tablet-max { display: none !important; }
}
@include media($tablet-max) {
.mq-normal-min { display: none !important; }
}
-58
View File
@@ -1,58 +0,0 @@
@import "fancy_button_mixins";
$btn-gray-f: #DDDDDD;
$btn-gray-b: #505050;
$btn-gray-l: #343434; // line
$btn-green-f: #FEFEFE;
$btn-green-b: #2ca94b;
$btn-green-fa: #FEFEFE;
$btn-green-ba: #28ba5c;
$btn-red-f: #FEFEFE;
$btn-red-b: #D04E51;
$btn-red-fa: #FEFEFE;
$btn-red-ba: #d4403f;
$btn-blue-f: #FEFEFE;
$btn-blue-b: #3983cd;
$btn-blue-fa: #FEFEFE;
$btn-blue-ba: #2076C6;
$btn-orange-f: #FEFEFE;
$btn-orange-b: #dd8751;
$btn-orange-fa: #FEFEFE;
$btn-orange-ba: #C6733F;
button, input[type=submit], .button {
@include fancy-btn-base();
&.narrow {
min-width: initial;
}
&::before {
vertical-align: -1px;
margin-left: 0;
}
text-shadow: 1.5px 1.5px 2px rgba(black, 0.4);
@include fancy-btn-colors($btn-blue-f, $btn-blue-b, $btn-blue-fa, $btn-blue-ba);
&:focus {
outline-color: $c-red-outline;
}
}
//input[type="submit"], .btn-green {
// @include fancy-btn-colors($btn-green-f, $btn-green-b, $btn-green-fa, $btn-green-ba);
//}
//.btn-red {
// @include fancy-btn-colors($btn-red-f, $btn-red-b, $btn-red-fa, $btn-red-ba);
//}
//.btn-blue {
// @include fancy-btn-colors($btn-blue-f, $btn-blue-b, $btn-blue-fa, $btn-blue-ba);
//}
@@ -1,58 +0,0 @@
// Button styling
@mixin fancy-btn-base() {
text-align: center;
cursor: pointer;
display: inline-block;
border-radius: 2px;
padding: 0 0.6em;
border: 0 none;
//outline: 0 none !important;
line-height: 1.8em;
font-size: 1.1em;
margin-bottom: 3px;
min-width: 5em;
@include noselect();
//&[class^="icon-"]::before, &[class*=" icon-"]::before {
// margin-left:0;
//}
&:active {
position: relative;
top: 2px;
}
//&, &:active, &:hover, &:visited {
// text-decoration: none;
//}
}
@mixin fancy-btn-colors-full($text_p, $back_p, $side_p, $text_a, $back_a, $side_a) {
background-color: $back_p;
box-shadow: 0 3px 0 $side_p;
text-decoration: none !important;
&, &:link, &:visited {
color: $text_p;
}
&:hover, &:active, &.active, &.selected {
background-color: $back_a;
color: $text_a;
}
&:hover, &.selected, &.active {
box-shadow: 0 3px 0 $side_a;
}
// thinner shadow
&:active {
box-shadow: 0 1px 0 $side_a;
}
}
@mixin fancy-btn-colors($text_p, $back_p, $text_a, $back_a) {
@include fancy-btn-colors-full($text_p, $back_p, darken($back_p, 14), $text_a, $back_a, darken($back_a, 16));
}
-82
View File
@@ -1,82 +0,0 @@
@import "buttons";
#{$all-text-inputs}, select, label.select-wrap {
width: $form-field-w;
}
input[type="number"], input.short, select.short {
width: $form-field-w/2;
}
#{$all-text-inputs}, select {
border: 0 none;
border-bottom: 2px solid $c-form-highlight;
background-color: $c-form-field-bg;
color: $c-form-field-fg;
padding: 6px;
line-height: 1em;
//outline: 0 none !important;
//-moz-outline: 0 none !important;
font-weight: normal;
&:focus, &:hover {
border-bottom-color: $c-form-highlight-a;
}
}
.Row.checkbox {
$h: 27px;
line-height: $h;
.box {
overflow: hidden;
width: $h;
height: $h;
border: 1px solid #808080;
border-radius: 3px;
background: $c-form-field-bg;
display: inline-block;
position: relative;
cursor: pointer;
color: $c-form-highlight-a;
&::before {
font-family: "fontello";
position: absolute;
//content: '×';
//content: '';
left: 0; top: 0; right: 0; bottom: 0;
line-height: 25px;
text-align: center;
font-size: 20px;
vertical-align: middle;
display: none;
}
@include icon-content('ok');
&.checked::before {
display: block;
}
}
}
.Row.range {
.display {
margin-left: 1ex;
}
label .display { font-weight: normal; }
}
//#{$all-text-inputs} {
// @include can-select();
//}
//textarea {
// font-family: monospace;
// line-height: 1.2em;
// display: block; // fixes weird bottom margin
//}
//@import "select";
-203
View File
@@ -1,203 +0,0 @@
// Unified Form wrapper
form { @include naked(); }
.Box.errors {
.list {
color: crimson;
font-weight: bold;
}
.lead {
color: white;
}
}
.Row {
vertical-align: middle;
margin: 12px auto;
text-align: left;
display: flex;
flex-direction: row;
align-items: center;
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
&.v {
display: block;
}
.aside {
float: right;
margin-left: 5px;
margin-bottom: 5px;
@include media($phone) {
margin: 0; float: none;
}
}
.spacer {
width: $form-label-w;
@include media($phone) {
display: none;
}
}
// buttons2 is the same style, but different selector for use in the admin page
&.buttons, &.buttons2 {
margin: 16px auto;
input, .button {
margin-right: dist(-1);
}
}
&.centered {
justify-content: center;
}
&.message {
font-size: 1em;
//margin-left: $label-gap + $w-labels;
text-shadow: 1px 1px 3px black;
text-align: center;
&.error {
color: crimson;
}
&.ok {
color: #0fe851;
}
}
&.separator {
padding-top: 14px;
border-top: 2px solid rgba(255, 255, 255, 0.1);
}
textarea {
display: inline-block;
vertical-align: top;
min-height: 10rem;
flex-grow: 1;
resize: vertical;
}
label {
font-weight: bold;
color: $c-form-label-fg;
display: inline-block;
width: $form-label-w;
text-align: right;
text-shadow: 1px 1px 3px black;
padding: $form-label-gap;
align-self: flex-start;
@include noselect;
@include nowrap;
}
label.error {
color: crimson;
}
//.checkbox-wrap {
// display: inline-block;
// width: $form-label-w;
// padding: $form-label-gap;
// text-align: right;
// align-self: flex-start;
//
// input[type=checkbox] {
// margin: auto;
// width: auto;
// height: auto;
// }
//
// & + label {
// width: $form-field-w;
// padding-left: 0;
// text-align: left;
// cursor: pointer;
// }
//}
input[type="range"] {
width: 200px;
}
// special phone style
@include media($phone) {
flex-direction: column;
margin: 6px auto;
&.buttons, &.centered, &.checkbox {
flex-direction: row;
}
&.buttons {
justify-content: center;
// remove margin on lats button
:last-child {
margin-right:0;
}
}
label {
padding-left: 0;
text-align: left;
width: auto;
}
.checkbox-wrap {
order: 1;
text-align: left;
padding-bottom: 0;
border-radius: .4px;
width: auto;
& + label {
width: auto;
}
}
#{$all-text-inputs}, input[type="range"], textarea, select {
width: 100%;
}
}
}
// red asterisk
form span.required {
color: red;
}
.RadioGroup {
display: inline-block;
line-height: 1.5em;
vertical-align: middle;
label {
width: auto;
text-align: left;
cursor: pointer;
font-weight: normal;
}
input[type="radio"] {
vertical-align: middle;
margin: 0 0 0 5px;
}
}
-13
View File
@@ -1,13 +0,0 @@
@import 'fancy_button_mixins';
@import 'buttons';
@import 'form_elements';
%form-row-spacing {
& > * {
margin-right: dist(-2);
&:last-child { margin-right: 0 }
}
}
@import 'form_layout';
//@import 'select';
-52
View File
@@ -1,52 +0,0 @@
// target chrome only
@media screen and (-webkit-min-device-pixel-ratio: 0) {
select { padding-right: 18px }
}
select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
cursor: pointer;
line-height: 1.2em;
//padding: 3.5px;
padding-right: 1em;
// hack for firefox to disable dotted outline
&:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 $c-form-field-fg;
}
option {
background: $c-form-field-bg;
}
}
label.select-wrap {
position: relative;
display: inline !important;
margin: 0 !important;
padding: 0 !important;
width: auto !important;
&:after {
content: '<>'; /* will be rotated */
font-family: "Consolas", monospace;
font-weight: bold;
color: $c-form-highlight-a;
top: 50%;
@include transform(translate(0, -50%) rotate(90deg));
right: 2px;
position:absolute;
z-index: 100;
pointer-events: none;
}
}
-41
View File
@@ -1,41 +0,0 @@
html {
font-family: Arial, sans-serif;
color: #D0D0D0;
background: #131315;
}
html, body {
@include naked();
width: 100%;
height: 100%;
overflow: hidden;
}
a, a:visited, a:link {
cursor: pointer;
color: #5abfff;
text-decoration: none;
}
a:hover {
color: #5abfff;
text-decoration: underline;
}
.hidden {
display: none !important;
}
[onclick] {
cursor: pointer;
}
ul > * {
padding-top: .3em;
padding-bottom: .3em;
}
ul {
margin-top: 0;
margin-bottom: 0;
}
-174
View File
@@ -1,174 +0,0 @@
.Box {
display: block;
max-width: 900px;
margin-top: dist(0);
padding: dist(-1) dist(0);
// clear floats
&::after {
content: '';
display: block;
clear: both
}
@include media($phone) {
margin-top: dist(-1);
}
h1, h2 {
overflow: hidden;
}
h1 + & {
margin-top: 0;
}
h2 {
margin-top: 0;
margin-bottom: 0 !important;
}
p:last-child {
margin-bottom: 0.5em;
}
border-radius: 3px;
background-color: rgba(white, .07);
box-shadow: 0 0 4px black;
border: 1px solid #4f4f4f;
&.wide {
width: initial;
max-width: initial;
}
&.medium {
max-width: 1200px;
}
//.Valfield {
// display: inline-block;
// min-width: 10em;
//}
// Submit Top Right
&.str {
position: relative;
.Row.buttons {
position: absolute;
@include media($phone) {
right: dist(0);
top: 1.8em;
margin: 1rem auto;
}
@include media($tablet-min) {
right: 0;
top: 0;
margin-top: dist(-1);
}
}
}
&.str.mobopen .Row.buttons {
top: 0;
margin-top: dist(-1);
}
.Row.explain {
display: block; // this stops flex messing up text with inline tags
max-width: 600px; margin-left: 0;
line-height: 1.2;
@include media($phone) {
margin-top: 60px;
}
&.nomargintop {
margin-top: 12px !important;
}
&.padleft {
}
}
&.mobopen .Row.explain {
margin-top: 12px; // default from .Row
@include media($phone) {
margin-top: 18px;
}
}
}
@mixin mobcol-base {
h2 {
position: relative;
cursor: pointer;
padding: 2px 1.3rem 2px 5px;
margin: 0 -5px 0 -5px;
&::after {
position: absolute;
right: 4px;
content: '';
top:50%;
font-size: 120%;
font-weight: bold;
transform: translate(0,-50%) rotate(90deg);
}
}
&.expanded h2::after {
transform: translate(-25%,-50%) rotate(-90deg);
margin-bottom: dist(0);
}
.Row {
display: none;
}
&.expanded {
.Row {
display: flex;
&.v {
display: block;
}
}
}
}
.Box.fold {
@include mobcol-base;
}
@include media($phone) {
.Box.fold, .Box.mobcol {
h2 {
padding: 2px 1.3rem 2px 5px;
margin: 0 -5px 0 -5px;
}
&.expanded h2::after {
margin-bottom: dist(0);
}
}
.Box.mobcol {
@include mobcol-base;
#ap-box {
display: none;
}
&.expanded {
#ap-box {
display: block;
}
}
}
}
-60
View File
@@ -1,60 +0,0 @@
#content {
flex-grow: 1;
position: relative;
padding: dist(0);
@include media($phone) {
padding: dist(-1);
}
overflow-y: auto;
& > * {
margin-left: auto;
margin-right: auto;
}
h1 {
text-align: center;
font-size: fsize(7);
margin-top: 0;
margin-bottom: dist(0);
}
h2 {
font-size: fsize(3);
margin-bottom: dist(-1);
}
@include media($phone) {
h1 {
font-size: fsize(5);
margin-bottom: dist(-1);
}
h2 {
font-size: fsize(2);
margin-bottom: dist(-1);
}
}
td, th {
padding: dist(-2);
}
tbody th {
text-align: right;
width: $form-label-w;
color: $c-form-label-fg;
}
}
#content {
// fade in effect
opacity: 0;
transition: opacity 0.15s ease-in;
}
#content.load {
opacity: 1;
}
-9
View File
@@ -1,9 +0,0 @@
@import "base";
@import "outer-wrap";
@import "menu";
@import "content";
@import "loader";
@import "box";
@import "modal";
-18
View File
@@ -1,18 +0,0 @@
// Loader wheel in top right corner
#loader {
position: absolute;
right: dist(1);
top: dist(1);
transition: opacity .2s;
opacity: 0;
@include media($phone) {
top: dist(0);
right: dist(0);
}
&.show {
opacity:1;
}
}
-111
View File
@@ -1,111 +0,0 @@
#menu {
$menu-bg: #3983CD;
$menu-hl: #5badff; //#1bd886;
$menu-fg: white;
flex: 0 0 15rem;
background: $menu-bg;
& > * {
display: block;
text-decoration: none;
padding: dist(-1) dist(0);
@include nowrap;
@include noselect;
}
#brand {
cursor: pointer;
color: $menu-fg;
background: darken($menu-bg, 10%);
font-size: 120%;
text-align: center;
position:relative;
transition: none;
font-weight: bold;
margin-bottom: dist(0);
@include media($phone) {
background: $menu-bg;
cursor: pointer;
margin-bottom: dist(-2);
&::after {
position: absolute;
color: rgba(black, .4);
right: dist(0);
content: '';
top:50%;
font-size: 120%;
font-weight: bold;
transform: translate(0,-50%) rotate(90deg);
}
}
}
&.expanded #brand {
background: darken($menu-bg, 10%);
@include media($phone) {
&:after { transform: translate(-25%,-50%) rotate(-90deg) }
}
}
a {
font-size: 130%;
color: $menu-fg;
transition: background-color 0.2s;
text-shadow: 0 0 5px rgba(black, .4);
&:hover, &.selected {
background: $menu-hl;
text-shadow: 0 0 5px rgba(black, .6);
}
&.selected {
position: relative;
box-shadow: 0 0 5px rgba(black, .5);
}
&:focus {
outline-color: $c-red-outline;
}
//&::before {
// content: "";
// padding-right: .5rem;
// position: relative;
// top: -0.1rem;
//}
// Fontello
&::before {
vertical-align: -2px;
margin-left: 0;
margin-right: 15px;
}
@include media($phone) {
display: none;
&::before {margin-left: 10px;}
}
}
&.expanded a { display:block }
@include media($tablet) {
#brand {
font-size: 95%;
margin-bottom: dist(-1);
}
a { font-size: 105%; }
flex-basis: 10rem;
& > * { padding: dist(-2) dist(-1); }
}
}
-79
View File
@@ -1,79 +0,0 @@
.Modal {
position: fixed;
width: 100%; height: 100%;
left: 0; top: 0; right: 0; bottom: 0;
display: flex;
justify-content: center;
align-items: center;
transition: opacity .5s;
background: rgba(black, .65);
opacity: 0;
&.visible { opacity: 1 }
&.hidden { display: none }
}
.Dialog {
margin: dist(-1);
padding: dist(0);
overflow: hidden;
max-width: 100%;
max-height: 100%;
flex: 0 1 30rem;
//min-height: 15rem;
background: #1c1c1e;
border-left: 6px solid $c-form-highlight;
border-right: 6px solid $c-form-highlight;
border-top: 1px solid $c-form-highlight;
border-bottom: 1px solid $c-form-highlight;
box-shadow: 0 0 6px 0 black;
border-radius: 6px;
h1,h2 {
margin-top:0;
}
p:last-child {
margin-bottom: 0;
}
}
// "toast"
.NotifyMsg {
position: fixed;
top: dist(1);
right: dist(2);
padding: dist(-1) dist(0);
// center horizontally
//left: 50%;
//@include translate(-50%,0);
// hack to remove blur in chrome
-webkit-font-smoothing: subpixel-antialiased;
-webkit-transform: translateZ(0) scale(1.0, 1.0);
background: #3887d0;
&.error {
background: #d03e42;
}
color: white;
text-shadow: 0 0 2px black;
box-shadow: 0 0 6px 0 rgba(black, .6);
border-radius: 5px;
max-width: 80%;
@include media($phone) {
width: calc(100% - #{dist(0)});
}
transition: opacity .5s;
opacity: 0;
&.visible { opacity: 1 }
&.hidden { display: none }
}
-22
View File
@@ -1,22 +0,0 @@
/* Main outer container */
#outer {
display: flex;
position: absolute;
width: 100%;
height: 100%;
left: 0;
right: 0;
top: 0;
bottom: 0;
overflow: hidden;
flex-direction: row;
}
@include media($phone) {
#outer {
display: block;
overflow-y: scroll;
}
}
@@ -1,411 +0,0 @@
// The following features have been deprecated and will be removed in the next MAJOR version release
@mixin inline-block {
display: inline-block;
@warn "The inline-block mixin is deprecated and will be removed in the next major version release";
}
@mixin button ($style: simple, $base-color: #4294f0, $text-size: inherit, $padding: 7px 18px) {
@if type-of($style) == string and type-of($base-color) == color {
@include buttonstyle($style, $base-color, $text-size, $padding);
}
@if type-of($style) == string and type-of($base-color) == number {
$padding: $text-size;
$text-size: $base-color;
$base-color: #4294f0;
@if $padding == inherit {
$padding: 7px 18px;
}
@include buttonstyle($style, $base-color, $text-size, $padding);
}
@if type-of($style) == color and type-of($base-color) == color {
$base-color: $style;
$style: simple;
@include buttonstyle($style, $base-color, $text-size, $padding);
}
@if type-of($style) == color and type-of($base-color) == number {
$padding: $text-size;
$text-size: $base-color;
$base-color: $style;
$style: simple;
@if $padding == inherit {
$padding: 7px 18px;
}
@include buttonstyle($style, $base-color, $text-size, $padding);
}
@if type-of($style) == number {
$padding: $base-color;
$text-size: $style;
$base-color: #4294f0;
$style: simple;
@if $padding == #4294f0 {
$padding: 7px 18px;
}
@include buttonstyle($style, $base-color, $text-size, $padding);
}
&:disabled {
cursor: not-allowed;
opacity: 0.5;
}
@warn "The button mixin is deprecated and will be removed in the next major version release";
}
// Selector Style Button
@mixin buttonstyle($type, $b-color, $t-size, $pad) {
// Grayscale button
@if $type == simple and $b-color == grayscale($b-color) {
@include simple($b-color, true, $t-size, $pad);
}
@if $type == shiny and $b-color == grayscale($b-color) {
@include shiny($b-color, true, $t-size, $pad);
}
@if $type == pill and $b-color == grayscale($b-color) {
@include pill($b-color, true, $t-size, $pad);
}
@if $type == flat and $b-color == grayscale($b-color) {
@include flat($b-color, true, $t-size, $pad);
}
// Colored button
@if $type == simple {
@include simple($b-color, false, $t-size, $pad);
}
@else if $type == shiny {
@include shiny($b-color, false, $t-size, $pad);
}
@else if $type == pill {
@include pill($b-color, false, $t-size, $pad);
}
@else if $type == flat {
@include flat($b-color, false, $t-size, $pad);
}
}
// Simple Button
@mixin simple($base-color, $grayscale: false, $textsize: inherit, $padding: 7px 18px) {
$color: hsl(0, 0, 100%);
$border: adjust-color($base-color, $saturation: 9%, $lightness: -14%);
$inset-shadow: adjust-color($base-color, $saturation: -8%, $lightness: 15%);
$stop-gradient: adjust-color($base-color, $saturation: 9%, $lightness: -11%);
$text-shadow: adjust-color($base-color, $saturation: 15%, $lightness: -18%);
@if is-light($base-color) {
$color: hsl(0, 0, 20%);
$text-shadow: adjust-color($base-color, $saturation: 10%, $lightness: 4%);
}
@if $grayscale == true {
$border: grayscale($border);
$inset-shadow: grayscale($inset-shadow);
$stop-gradient: grayscale($stop-gradient);
$text-shadow: grayscale($text-shadow);
}
border: 1px solid $border;
border-radius: 3px;
box-shadow: inset 0 1px 0 0 $inset-shadow;
color: $color;
display: inline-block;
font-size: $textsize;
font-weight: bold;
@include linear-gradient ($base-color, $stop-gradient);
padding: $padding;
text-decoration: none;
text-shadow: 0 1px 0 $text-shadow;
background-clip: padding-box;
&:hover:not(:disabled) {
$base-color-hover: adjust-color($base-color, $saturation: -4%, $lightness: -5%);
$inset-shadow-hover: adjust-color($base-color, $saturation: -7%, $lightness: 5%);
$stop-gradient-hover: adjust-color($base-color, $saturation: 8%, $lightness: -14%);
@if $grayscale == true {
$base-color-hover: grayscale($base-color-hover);
$inset-shadow-hover: grayscale($inset-shadow-hover);
$stop-gradient-hover: grayscale($stop-gradient-hover);
}
@include linear-gradient ($base-color-hover, $stop-gradient-hover);
box-shadow: inset 0 1px 0 0 $inset-shadow-hover;
cursor: pointer;
}
&:active:not(:disabled),
&:focus:not(:disabled) {
$border-active: adjust-color($base-color, $saturation: 9%, $lightness: -14%);
$inset-shadow-active: adjust-color($base-color, $saturation: 7%, $lightness: -17%);
@if $grayscale == true {
$border-active: grayscale($border-active);
$inset-shadow-active: grayscale($inset-shadow-active);
}
border: 1px solid $border-active;
box-shadow: inset 0 0 8px 4px $inset-shadow-active, inset 0 0 8px 4px $inset-shadow-active;
}
}
// Shiny Button
@mixin shiny($base-color, $grayscale: false, $textsize: inherit, $padding: 7px 18px) {
$color: hsl(0, 0, 100%);
$border: adjust-color($base-color, $red: -117, $green: -111, $blue: -81);
$border-bottom: adjust-color($base-color, $red: -126, $green: -127, $blue: -122);
$fourth-stop: adjust-color($base-color, $red: -79, $green: -70, $blue: -46);
$inset-shadow: adjust-color($base-color, $red: 37, $green: 29, $blue: 12);
$second-stop: adjust-color($base-color, $red: -56, $green: -50, $blue: -33);
$text-shadow: adjust-color($base-color, $red: -140, $green: -141, $blue: -114);
$third-stop: adjust-color($base-color, $red: -86, $green: -75, $blue: -48);
@if is-light($base-color) {
$color: hsl(0, 0, 20%);
$text-shadow: adjust-color($base-color, $saturation: 10%, $lightness: 4%);
}
@if $grayscale == true {
$border: grayscale($border);
$border-bottom: grayscale($border-bottom);
$fourth-stop: grayscale($fourth-stop);
$inset-shadow: grayscale($inset-shadow);
$second-stop: grayscale($second-stop);
$text-shadow: grayscale($text-shadow);
$third-stop: grayscale($third-stop);
}
@include linear-gradient(top, $base-color 0%, $second-stop 50%, $third-stop 50%, $fourth-stop 100%);
border: 1px solid $border;
border-bottom: 1px solid $border-bottom;
border-radius: 5px;
box-shadow: inset 0 1px 0 0 $inset-shadow;
color: $color;
display: inline-block;
font-size: $textsize;
font-weight: bold;
padding: $padding;
text-align: center;
text-decoration: none;
text-shadow: 0 -1px 1px $text-shadow;
&:hover:not(:disabled) {
$first-stop-hover: adjust-color($base-color, $red: -13, $green: -15, $blue: -18);
$second-stop-hover: adjust-color($base-color, $red: -66, $green: -62, $blue: -51);
$third-stop-hover: adjust-color($base-color, $red: -93, $green: -85, $blue: -66);
$fourth-stop-hover: adjust-color($base-color, $red: -86, $green: -80, $blue: -63);
@if $grayscale == true {
$first-stop-hover: grayscale($first-stop-hover);
$second-stop-hover: grayscale($second-stop-hover);
$third-stop-hover: grayscale($third-stop-hover);
$fourth-stop-hover: grayscale($fourth-stop-hover);
}
@include linear-gradient(top, $first-stop-hover 0%,
$second-stop-hover 50%,
$third-stop-hover 50%,
$fourth-stop-hover 100%);
cursor: pointer;
}
&:active:not(:disabled),
&:focus:not(:disabled) {
$inset-shadow-active: adjust-color($base-color, $red: -111, $green: -116, $blue: -122);
@if $grayscale == true {
$inset-shadow-active: grayscale($inset-shadow-active);
}
box-shadow: inset 0 0 20px 0 $inset-shadow-active;
}
}
// Pill Button
@mixin pill($base-color, $grayscale: false, $textsize: inherit, $padding: 7px 18px) {
$color: hsl(0, 0, 100%);
$border-bottom: adjust-color($base-color, $hue: 8, $saturation: -11%, $lightness: -26%);
$border-sides: adjust-color($base-color, $hue: 4, $saturation: -21%, $lightness: -21%);
$border-top: adjust-color($base-color, $hue: -1, $saturation: -30%, $lightness: -15%);
$inset-shadow: adjust-color($base-color, $hue: -1, $saturation: -1%, $lightness: 7%);
$stop-gradient: adjust-color($base-color, $hue: 8, $saturation: 14%, $lightness: -10%);
$text-shadow: adjust-color($base-color, $hue: 5, $saturation: -19%, $lightness: -15%);
@if is-light($base-color) {
$color: hsl(0, 0, 20%);
$text-shadow: adjust-color($base-color, $saturation: 10%, $lightness: 4%);
}
@if $grayscale == true {
$border-bottom: grayscale($border-bottom);
$border-sides: grayscale($border-sides);
$border-top: grayscale($border-top);
$inset-shadow: grayscale($inset-shadow);
$stop-gradient: grayscale($stop-gradient);
$text-shadow: grayscale($text-shadow);
}
border: 1px solid $border-top;
border-color: $border-top $border-sides $border-bottom;
border-radius: 16px;
box-shadow: inset 0 1px 0 0 $inset-shadow;
color: $color;
display: inline-block;
font-size: $textsize;
font-weight: normal;
line-height: 1;
@include linear-gradient ($base-color, $stop-gradient);
padding: $padding;
text-align: center;
text-decoration: none;
text-shadow: 0 -1px 1px $text-shadow;
background-clip: padding-box;
&:hover:not(:disabled) {
$base-color-hover: adjust-color($base-color, $lightness: -4.5%);
$border-bottom: adjust-color($base-color, $hue: 8, $saturation: 13.5%, $lightness: -32%);
$border-sides: adjust-color($base-color, $hue: 4, $saturation: -2%, $lightness: -27%);
$border-top: adjust-color($base-color, $hue: -1, $saturation: -17%, $lightness: -21%);
$inset-shadow-hover: adjust-color($base-color, $saturation: -1%, $lightness: 3%);
$stop-gradient-hover: adjust-color($base-color, $hue: 8, $saturation: -4%, $lightness: -15.5%);
$text-shadow-hover: adjust-color($base-color, $hue: 5, $saturation: -5%, $lightness: -22%);
@if $grayscale == true {
$base-color-hover: grayscale($base-color-hover);
$border-bottom: grayscale($border-bottom);
$border-sides: grayscale($border-sides);
$border-top: grayscale($border-top);
$inset-shadow-hover: grayscale($inset-shadow-hover);
$stop-gradient-hover: grayscale($stop-gradient-hover);
$text-shadow-hover: grayscale($text-shadow-hover);
}
@include linear-gradient ($base-color-hover, $stop-gradient-hover);
background-clip: padding-box;
border: 1px solid $border-top;
border-color: $border-top $border-sides $border-bottom;
box-shadow: inset 0 1px 0 0 $inset-shadow-hover;
cursor: pointer;
text-shadow: 0 -1px 1px $text-shadow-hover;
}
&:active:not(:disabled),
&:focus:not(:disabled) {
$active-color: adjust-color($base-color, $hue: 4, $saturation: -12%, $lightness: -10%);
$border-active: adjust-color($base-color, $hue: 6, $saturation: -2.5%, $lightness: -30%);
$border-bottom-active: adjust-color($base-color, $hue: 11, $saturation: 6%, $lightness: -31%);
$inset-shadow-active: adjust-color($base-color, $hue: 9, $saturation: 2%, $lightness: -21.5%);
$text-shadow-active: adjust-color($base-color, $hue: 5, $saturation: -12%, $lightness: -21.5%);
@if $grayscale == true {
$active-color: grayscale($active-color);
$border-active: grayscale($border-active);
$border-bottom-active: grayscale($border-bottom-active);
$inset-shadow-active: grayscale($inset-shadow-active);
$text-shadow-active: grayscale($text-shadow-active);
}
background: $active-color;
border: 1px solid $border-active;
border-bottom: 1px solid $border-bottom-active;
box-shadow: inset 0 0 6px 3px $inset-shadow-active;
text-shadow: 0 -1px 1px $text-shadow-active;
}
}
// Flat Button
@mixin flat($base-color, $grayscale: false, $textsize: inherit, $padding: 7px 18px) {
$color: hsl(0, 0, 100%);
@if is-light($base-color) {
$color: hsl(0, 0, 20%);
}
background-color: $base-color;
border-radius: 3px;
border: 0;
color: $color;
display: inline-block;
font-size: $textsize;
font-weight: bold;
padding: $padding;
text-decoration: none;
background-clip: padding-box;
&:hover:not(:disabled){
$base-color-hover: adjust-color($base-color, $saturation: 4%, $lightness: 5%);
@if $grayscale == true {
$base-color-hover: grayscale($base-color-hover);
}
background-color: $base-color-hover;
cursor: pointer;
}
&:active:not(:disabled),
&:focus:not(:disabled) {
$base-color-active: adjust-color($base-color, $saturation: -4%, $lightness: -5%);
@if $grayscale == true {
$base-color-active: grayscale($base-color-active);
}
background-color: $base-color-active;
cursor: pointer;
}
}
// Flexible grid
@function flex-grid($columns, $container-columns: $fg-max-columns) {
$width: $columns * $fg-column + ($columns - 1) * $fg-gutter;
$container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter;
@return percentage($width / $container-width);
@warn "The flex-grid function is deprecated and will be removed in the next major version release";
}
// Flexible gutter
@function flex-gutter($container-columns: $fg-max-columns, $gutter: $fg-gutter) {
$container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter;
@return percentage($gutter / $container-width);
@warn "The flex-gutter function is deprecated and will be removed in the next major version release";
}
@function grid-width($n) {
@return $n * $gw-column + ($n - 1) * $gw-gutter;
@warn "The grid-width function is deprecated and will be removed in the next major version release";
}
@function golden-ratio($value, $increment) {
@return modular-scale($increment, $value, $ratio: $golden);
@warn "The golden-ratio function is deprecated and will be removed in the next major version release. Please use the modular-scale function, instead.";
}
@mixin box-sizing($box) {
@include prefixer(box-sizing, $box, webkit moz spec);
@warn "The box-sizing mixin is deprecated and will be removed in the next major version release. This property can now be used un-prefixed.";
}
-87
View File
@@ -1,87 +0,0 @@
// Bourbon 4.2.6
// http://bourbon.io
// Copyright 2011-2015 thoughtbot, inc.
// MIT License
@import "settings/prefixer";
@import "settings/px-to-em";
@import "settings/asset-pipeline";
@import "functions/assign-inputs";
@import "functions/contains";
@import "functions/contains-falsy";
@import "functions/is-length";
@import "functions/is-light";
@import "functions/is-number";
@import "functions/is-size";
@import "functions/px-to-em";
@import "functions/px-to-rem";
@import "functions/shade";
@import "functions/strip-units";
@import "functions/tint";
@import "functions/transition-property-name";
@import "functions/unpack";
@import "functions/modular-scale";
@import "helpers/convert-units";
@import "helpers/directional-values";
@import "helpers/font-source-declaration";
@import "helpers/gradient-positions-parser";
@import "helpers/linear-angle-parser";
@import "helpers/linear-gradient-parser";
@import "helpers/linear-positions-parser";
@import "helpers/linear-side-corner-parser";
@import "helpers/radial-arg-parser";
@import "helpers/radial-positions-parser";
@import "helpers/radial-gradient-parser";
@import "helpers/render-gradients";
@import "helpers/shape-size-stripper";
@import "helpers/str-to-num";
@import "css3/animation";
@import "css3/appearance";
@import "css3/backface-visibility";
@import "css3/background";
@import "css3/background-image";
@import "css3/border-image";
@import "css3/calc";
@import "css3/columns";
@import "css3/filter";
@import "css3/flex-box";
@import "css3/font-face";
@import "css3/font-feature-settings";
@import "css3/hidpi-media-query";
@import "css3/hyphens";
@import "css3/image-rendering";
@import "css3/keyframes";
@import "css3/linear-gradient";
@import "css3/perspective";
@import "css3/placeholder";
@import "css3/radial-gradient";
@import "css3/selection";
@import "css3/text-decoration";
@import "css3/transform";
@import "css3/transition";
@import "css3/user-select";
@import "addons/border-color";
@import "addons/border-radius";
@import "addons/border-style";
@import "addons/border-width";
@import "addons/buttons";
@import "addons/clearfix";
@import "addons/ellipsis";
@import "addons/font-stacks";
@import "addons/hide-text";
@import "addons/margin";
@import "addons/padding";
@import "addons/position";
@import "addons/prefixer";
@import "addons/retina-image";
@import "addons/size";
@import "addons/text-inputs";
@import "addons/timing-functions";
@import "addons/triangle";
@import "addons/word-wrap";
@import "bourbon-deprecated-upcoming";
-26
View File
@@ -1,26 +0,0 @@
@charset "UTF-8";
/// Provides a quick method for targeting `border-color` on specific sides of a box. Use a `null` value to skip a side.
///
/// @param {Arglist} $vals
/// List of arguments
///
/// @example scss - Usage
/// .element {
/// @include border-color(#a60b55 #76cd9c null #e8ae1a);
/// }
///
/// @example css - CSS Output
/// .element {
/// border-left-color: #e8ae1a;
/// border-right-color: #76cd9c;
/// border-top-color: #a60b55;
/// }
///
/// @require {mixin} directional-property
///
/// @output `border-color`
@mixin border-color($vals...) {
@include directional-property(border, color, $vals...);
}
-48
View File
@@ -1,48 +0,0 @@
@charset "UTF-8";
/// Provides a quick method for targeting `border-radius` on both corners on the side of a box.
///
/// @param {Number} $radii
/// List of arguments
///
/// @example scss - Usage
/// .element-one {
/// @include border-top-radius(5px);
/// }
///
/// .element-two {
/// @include border-left-radius(3px);
/// }
///
/// @example css - CSS Output
/// .element-one {
/// border-top-left-radius: 5px;
/// border-top-right-radius: 5px;
/// }
///
/// .element-two {
/// border-bottom-left-radius: 3px;
/// border-top-left-radius: 3px;
/// }
///
/// @output `border-radius`
@mixin border-top-radius($radii) {
border-top-left-radius: $radii;
border-top-right-radius: $radii;
}
@mixin border-right-radius($radii) {
border-bottom-right-radius: $radii;
border-top-right-radius: $radii;
}
@mixin border-bottom-radius($radii) {
border-bottom-left-radius: $radii;
border-bottom-right-radius: $radii;
}
@mixin border-left-radius($radii) {
border-bottom-left-radius: $radii;
border-top-left-radius: $radii;
}
-25
View File
@@ -1,25 +0,0 @@
@charset "UTF-8";
/// Provides a quick method for targeting `border-style` on specific sides of a box. Use a `null` value to skip a side.
///
/// @param {Arglist} $vals
/// List of arguments
///
/// @example scss - Usage
/// .element {
/// @include border-style(dashed null solid);
/// }
///
/// @example css - CSS Output
/// .element {
/// border-bottom-style: solid;
/// border-top-style: dashed;
/// }
///
/// @require {mixin} directional-property
///
/// @output `border-style`
@mixin border-style($vals...) {
@include directional-property(border, style, $vals...);
}
-25
View File
@@ -1,25 +0,0 @@
@charset "UTF-8";
/// Provides a quick method for targeting `border-width` on specific sides of a box. Use a `null` value to skip a side.
///
/// @param {Arglist} $vals
/// List of arguments
///
/// @example scss - Usage
/// .element {
/// @include border-width(1em null 20px);
/// }
///
/// @example css - CSS Output
/// .element {
/// border-bottom-width: 20px;
/// border-top-width: 1em;
/// }
///
/// @require {mixin} directional-property
///
/// @output `border-width`
@mixin border-width($vals...) {
@include directional-property(border, width, $vals...);
}
-64
View File
@@ -1,64 +0,0 @@
@charset "UTF-8";
/// Generates variables for all buttons. Please note that you must use interpolation on the variable: `#{$all-buttons}`.
///
/// @example scss - Usage
/// #{$all-buttons} {
/// background-color: #f00;
/// }
///
/// #{$all-buttons-focus},
/// #{$all-buttons-hover} {
/// background-color: #0f0;
/// }
///
/// #{$all-buttons-active} {
/// background-color: #00f;
/// }
///
/// @example css - CSS Output
/// button,
/// input[type="button"],
/// input[type="reset"],
/// input[type="submit"] {
/// background-color: #f00;
/// }
///
/// button:focus,
/// input[type="button"]:focus,
/// input[type="reset"]:focus,
/// input[type="submit"]:focus,
/// button:hover,
/// input[type="button"]:hover,
/// input[type="reset"]:hover,
/// input[type="submit"]:hover {
/// background-color: #0f0;
/// }
///
/// button:active,
/// input[type="button"]:active,
/// input[type="reset"]:active,
/// input[type="submit"]:active {
/// background-color: #00f;
/// }
///
/// @require assign-inputs
///
/// @type List
///
/// @todo Remove double assigned variables (Lines 5962) in v5.0.0
$buttons-list: 'button',
'input[type="button"]',
'input[type="reset"]',
'input[type="submit"]';
$all-buttons: assign-inputs($buttons-list);
$all-buttons-active: assign-inputs($buttons-list, active);
$all-buttons-focus: assign-inputs($buttons-list, focus);
$all-buttons-hover: assign-inputs($buttons-list, hover);
$all-button-inputs: $all-buttons;
$all-button-inputs-active: $all-buttons-active;
$all-button-inputs-focus: $all-buttons-focus;
$all-button-inputs-hover: $all-buttons-hover;
-25
View File
@@ -1,25 +0,0 @@
@charset "UTF-8";
/// Provides an easy way to include a clearfix for containing floats.
///
/// @link http://cssmojo.com/latest_new_clearfix_so_far/
///
/// @example scss - Usage
/// .element {
/// @include clearfix;
/// }
///
/// @example css - CSS Output
/// .element::after {
/// clear: both;
/// content: "";
/// display: table;
/// }
@mixin clearfix {
&::after {
clear: both;
content: "";
display: table;
}
}
-30
View File
@@ -1,30 +0,0 @@
@charset "UTF-8";
/// Truncates text and adds an ellipsis to represent overflow.
///
/// @param {Number} $width [100%]
/// Max-width for the string to respect before being truncated
///
/// @example scss - Usage
/// .element {
/// @include ellipsis;
/// }
///
/// @example css - CSS Output
/// .element {
/// display: inline-block;
/// max-width: 100%;
/// overflow: hidden;
/// text-overflow: ellipsis;
/// white-space: nowrap;
/// word-wrap: normal;
/// }
@mixin ellipsis($width: 100%) {
display: inline-block;
max-width: $width;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: normal;
}
-31
View File
@@ -1,31 +0,0 @@
@charset "UTF-8";
/// Georgia font stack.
///
/// @type List
$georgia: "Georgia", "Cambria", "Times New Roman", "Times", serif;
/// Helvetica font stack.
///
/// @type List
$helvetica: "Helvetica Neue", "Helvetica", "Roboto", "Arial", sans-serif;
/// Lucida Grande font stack.
///
/// @type List
$lucida-grande: "Lucida Grande", "Tahoma", "Verdana", "Arial", sans-serif;
/// Monospace font stack.
///
/// @type List
$monospace: "Bitstream Vera Sans Mono", "Consolas", "Courier", monospace;
/// Verdana font stack.
///
/// @type List
$verdana: "Verdana", "Geneva", sans-serif;
-27
View File
@@ -1,27 +0,0 @@
/// Hides the text in an element, commonly used to show an image. Some elements will need block-level styles applied.
///
/// @link http://zeldman.com/2012/03/01/replacing-the-9999px-hack-new-image-replacement
///
/// @example scss - Usage
/// .element {
/// @include hide-text;
/// }
///
/// @example css - CSS Output
/// .element {
/// overflow: hidden;
/// text-indent: 101%;
/// white-space: nowrap;
/// }
///
/// @todo Remove height argument in v5.0.0
@mixin hide-text($height: null) {
overflow: hidden;
text-indent: 101%;
white-space: nowrap;
@if $height {
@warn "The `hide-text` mixin has changed and no longer requires a height. The height argument will no longer be accepted in v5.0.0";
}
}
-26
View File
@@ -1,26 +0,0 @@
@charset "UTF-8";
/// Provides a quick method for targeting `margin` on specific sides of a box. Use a `null` value to skip a side.
///
/// @param {Arglist} $vals
/// List of arguments
///
/// @example scss - Usage
/// .element {
/// @include margin(null 10px 3em 20vh);
/// }
///
/// @example css - CSS Output
/// .element {
/// margin-bottom: 3em;
/// margin-left: 20vh;
/// margin-right: 10px;
/// }
///
/// @require {mixin} directional-property
///
/// @output `margin`
@mixin margin($vals...) {
@include directional-property(margin, false, $vals...);
}
-26
View File
@@ -1,26 +0,0 @@
@charset "UTF-8";
/// Provides a quick method for targeting `padding` on specific sides of a box. Use a `null` value to skip a side.
///
/// @param {Arglist} $vals
/// List of arguments
///
/// @example scss - Usage
/// .element {
/// @include padding(12vh null 10px 5%);
/// }
///
/// @example css - CSS Output
/// .element {
/// padding-bottom: 10px;
/// padding-left: 5%;
/// padding-top: 12vh;
/// }
///
/// @require {mixin} directional-property
///
/// @output `padding`
@mixin padding($vals...) {
@include directional-property(padding, false, $vals...);
}
-48
View File
@@ -1,48 +0,0 @@
@charset "UTF-8";
/// Provides a quick method for setting an elements position. Use a `null` value to skip a side.
///
/// @param {Position} $position [relative]
/// A CSS position value
///
/// @param {Arglist} $coordinates [null null null null]
/// List of values that correspond to the 4-value syntax for the edges of a box
///
/// @example scss - Usage
/// .element {
/// @include position(absolute, 0 null null 10em);
/// }
///
/// @example css - CSS Output
/// .element {
/// left: 10em;
/// position: absolute;
/// top: 0;
/// }
///
/// @require {function} is-length
/// @require {function} unpack
@mixin position($position: relative, $coordinates: null null null null) {
@if type-of($position) == list {
$coordinates: $position;
$position: relative;
}
$coordinates: unpack($coordinates);
$offsets: (
top: nth($coordinates, 1),
right: nth($coordinates, 2),
bottom: nth($coordinates, 3),
left: nth($coordinates, 4)
);
position: $position;
@each $offset, $value in $offsets {
@if is-length($value) {
#{$offset}: $value;
}
}
}
-66
View File
@@ -1,66 +0,0 @@
@charset "UTF-8";
/// A mixin for generating vendor prefixes on non-standardized properties.
///
/// @param {String} $property
/// Property to prefix
///
/// @param {*} $value
/// Value to use
///
/// @param {List} $prefixes
/// Prefixes to define
///
/// @example scss - Usage
/// .element {
/// @include prefixer(border-radius, 10px, webkit ms spec);
/// }
///
/// @example css - CSS Output
/// .element {
/// -webkit-border-radius: 10px;
/// -moz-border-radius: 10px;
/// border-radius: 10px;
/// }
///
/// @require {variable} $prefix-for-webkit
/// @require {variable} $prefix-for-mozilla
/// @require {variable} $prefix-for-microsoft
/// @require {variable} $prefix-for-opera
/// @require {variable} $prefix-for-spec
@mixin prefixer($property, $value, $prefixes) {
@each $prefix in $prefixes {
@if $prefix == webkit {
@if $prefix-for-webkit {
-webkit-#{$property}: $value;
}
} @else if $prefix == moz {
@if $prefix-for-mozilla {
-moz-#{$property}: $value;
}
} @else if $prefix == ms {
@if $prefix-for-microsoft {
-ms-#{$property}: $value;
}
} @else if $prefix == o {
@if $prefix-for-opera {
-o-#{$property}: $value;
}
} @else if $prefix == spec {
@if $prefix-for-spec {
#{$property}: $value;
}
} @else {
@warn "Unrecognized prefix: #{$prefix}";
}
}
}
@mixin disable-prefix-for-all() {
$prefix-for-webkit: false !global;
$prefix-for-mozilla: false !global;
$prefix-for-microsoft: false !global;
$prefix-for-opera: false !global;
$prefix-for-spec: false !global;
}
-25
View File
@@ -1,25 +0,0 @@
@mixin retina-image($filename, $background-size, $extension: png, $retina-filename: null, $retina-suffix: _2x, $asset-pipeline: $asset-pipeline) {
@if $asset-pipeline {
background-image: image-url("#{$filename}.#{$extension}");
} @else {
background-image: url("#{$filename}.#{$extension}");
}
@include hidpi {
@if $asset-pipeline {
@if $retina-filename {
background-image: image-url("#{$retina-filename}.#{$extension}");
} @else {
background-image: image-url("#{$filename}#{$retina-suffix}.#{$extension}");
}
} @else {
@if $retina-filename {
background-image: url("#{$retina-filename}.#{$extension}");
} @else {
background-image: url("#{$filename}#{$retina-suffix}.#{$extension}");
}
}
background-size: $background-size;
}
}
-51
View File
@@ -1,51 +0,0 @@
@charset "UTF-8";
/// Sets the `width` and `height` of the element.
///
/// @param {List} $size
/// A list of at most 2 size values.
///
/// If there is only a single value in `$size` it is used for both width and height. All units are supported.
///
/// @example scss - Usage
/// .first-element {
/// @include size(2em);
/// }
///
/// .second-element {
/// @include size(auto 10em);
/// }
///
/// @example css - CSS Output
/// .first-element {
/// width: 2em;
/// height: 2em;
/// }
///
/// .second-element {
/// width: auto;
/// height: 10em;
/// }
///
/// @todo Refactor in 5.0.0 to use a comma-separated argument
@mixin size($value) {
$width: nth($value, 1);
$height: $width;
@if length($value) > 1 {
$height: nth($value, 2);
}
@if is-size($height) {
height: $height;
} @else {
@warn "`#{$height}` is not a valid length for the `$height` parameter in the `size` mixin.";
}
@if is-size($width) {
width: $width;
} @else {
@warn "`#{$width}` is not a valid length for the `$width` parameter in the `size` mixin.";
}
}
-113
View File
@@ -1,113 +0,0 @@
@charset "UTF-8";
/// Generates variables for all text-based inputs. Please note that you must use interpolation on the variable: `#{$all-text-inputs}`.
///
/// @example scss - Usage
/// #{$all-text-inputs} {
/// border: 1px solid #f00;
/// }
///
/// #{$all-text-inputs-focus},
/// #{$all-text-inputs-hover} {
/// border: 1px solid #0f0;
/// }
///
/// #{$all-text-inputs-active} {
/// border: 1px solid #00f;
/// }
///
/// @example css - CSS Output
/// input[type="color"],
/// input[type="date"],
/// input[type="datetime"],
/// input[type="datetime-local"],
/// input[type="email"],
/// input[type="month"],
/// input[type="number"],
/// input[type="password"],
/// input[type="search"],
/// input[type="tel"],
/// input[type="text"],
/// input[type="time"],
/// input[type="url"],
/// input[type="week"],
/// textarea {
/// border: 1px solid #f00;
/// }
///
/// input[type="color"]:focus,
/// input[type="date"]:focus,
/// input[type="datetime"]:focus,
/// input[type="datetime-local"]:focus,
/// input[type="email"]:focus,
/// input[type="month"]:focus,
/// input[type="number"]:focus,
/// input[type="password"]:focus,
/// input[type="search"]:focus,
/// input[type="tel"]:focus,
/// input[type="text"]:focus,
/// input[type="time"]:focus,
/// input[type="url"]:focus,
/// input[type="week"]:focus,
/// textarea:focus,
/// input[type="color"]:hover,
/// input[type="date"]:hover,
/// input[type="datetime"]:hover,
/// input[type="datetime-local"]:hover,
/// input[type="email"]:hover,
/// input[type="month"]:hover,
/// input[type="number"]:hover,
/// input[type="password"]:hover,
/// input[type="search"]:hover,
/// input[type="tel"]:hover,
/// input[type="text"]:hover,
/// input[type="time"]:hover,
/// input[type="url"]:hover,
/// input[type="week"]:hover,
/// textarea:hover {
/// border: 1px solid #0f0;
/// }
///
/// input[type="color"]:active,
/// input[type="date"]:active,
/// input[type="datetime"]:active,
/// input[type="datetime-local"]:active,
/// input[type="email"]:active,
/// input[type="month"]:active,
/// input[type="number"]:active,
/// input[type="password"]:active,
/// input[type="search"]:active,
/// input[type="tel"]:active,
/// input[type="text"]:active,
/// input[type="time"]:active,
/// input[type="url"]:active,
/// input[type="week"]:active,
/// textarea:active {
/// border: 1px solid #00f;
/// }
///
/// @require assign-inputs
///
/// @type List
$text-inputs-list: //'input[type="color"]',
//'input[type="date"]',
//'input[type="datetime"]',
//'input[type="datetime-local"]',
//'input[type="email"]',
//'input[type="month"]',
'input[type="number"]',
'input[type="password"]',
//'input[type="search"]',
//'input[type="tel"]',
'input[type="text"]',
//'input[type="time"]',
//'input[type="url"]',
//'input[type="week"]',
//'input:not([type])',
'textarea';
$all-text-inputs: assign-inputs($text-inputs-list);
$all-text-inputs-active: assign-inputs($text-inputs-list, active);
$all-text-inputs-focus: assign-inputs($text-inputs-list, focus);
$all-text-inputs-hover: assign-inputs($text-inputs-list, hover);
@@ -1,34 +0,0 @@
@charset "UTF-8";
/// CSS cubic-bezier timing functions. Timing functions courtesy of jquery.easie (github.com/jaukia/easie)
///
/// Timing functions are the same as demoed here: http://jqueryui.com/resources/demos/effect/easing.html
///
/// @type cubic-bezier
$ease-in-quad: cubic-bezier(0.550, 0.085, 0.680, 0.530);
$ease-in-cubic: cubic-bezier(0.550, 0.055, 0.675, 0.190);
$ease-in-quart: cubic-bezier(0.895, 0.030, 0.685, 0.220);
$ease-in-quint: cubic-bezier(0.755, 0.050, 0.855, 0.060);
$ease-in-sine: cubic-bezier(0.470, 0.000, 0.745, 0.715);
$ease-in-expo: cubic-bezier(0.950, 0.050, 0.795, 0.035);
$ease-in-circ: cubic-bezier(0.600, 0.040, 0.980, 0.335);
$ease-in-back: cubic-bezier(0.600, -0.280, 0.735, 0.045);
$ease-out-quad: cubic-bezier(0.250, 0.460, 0.450, 0.940);
$ease-out-cubic: cubic-bezier(0.215, 0.610, 0.355, 1.000);
$ease-out-quart: cubic-bezier(0.165, 0.840, 0.440, 1.000);
$ease-out-quint: cubic-bezier(0.230, 1.000, 0.320, 1.000);
$ease-out-sine: cubic-bezier(0.390, 0.575, 0.565, 1.000);
$ease-out-expo: cubic-bezier(0.190, 1.000, 0.220, 1.000);
$ease-out-circ: cubic-bezier(0.075, 0.820, 0.165, 1.000);
$ease-out-back: cubic-bezier(0.175, 0.885, 0.320, 1.275);
$ease-in-out-quad: cubic-bezier(0.455, 0.030, 0.515, 0.955);
$ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1.000);
$ease-in-out-quart: cubic-bezier(0.770, 0.000, 0.175, 1.000);
$ease-in-out-quint: cubic-bezier(0.860, 0.000, 0.070, 1.000);
$ease-in-out-sine: cubic-bezier(0.445, 0.050, 0.550, 0.950);
$ease-in-out-expo: cubic-bezier(1.000, 0.000, 0.000, 1.000);
$ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.150, 0.860);
$ease-in-out-back: cubic-bezier(0.680, -0.550, 0.265, 1.550);
-63
View File
@@ -1,63 +0,0 @@
@mixin triangle($size, $color, $direction) {
$width: nth($size, 1);
$height: nth($size, length($size));
$foreground-color: nth($color, 1);
$background-color: if(length($color) == 2, nth($color, 2), transparent);
height: 0;
width: 0;
@if ($direction == up) or ($direction == down) or ($direction == right) or ($direction == left) {
$width: $width / 2;
$height: if(length($size) > 1, $height, $height/2);
@if $direction == up {
border-bottom: $height solid $foreground-color;
border-left: $width solid $background-color;
border-right: $width solid $background-color;
} @else if $direction == right {
border-bottom: $width solid $background-color;
border-left: $height solid $foreground-color;
border-top: $width solid $background-color;
} @else if $direction == down {
border-left: $width solid $background-color;
border-right: $width solid $background-color;
border-top: $height solid $foreground-color;
} @else if $direction == left {
border-bottom: $width solid $background-color;
border-right: $height solid $foreground-color;
border-top: $width solid $background-color;
}
} @else if ($direction == up-right) or ($direction == up-left) {
border-top: $height solid $foreground-color;
@if $direction == up-right {
border-left: $width solid $background-color;
} @else if $direction == up-left {
border-right: $width solid $background-color;
}
} @else if ($direction == down-right) or ($direction == down-left) {
border-bottom: $height solid $foreground-color;
@if $direction == down-right {
border-left: $width solid $background-color;
} @else if $direction == down-left {
border-right: $width solid $background-color;
}
} @else if ($direction == inset-up) {
border-color: $background-color $background-color $foreground-color;
border-style: solid;
border-width: $height $width;
} @else if ($direction == inset-down) {
border-color: $foreground-color $background-color $background-color;
border-style: solid;
border-width: $height $width;
} @else if ($direction == inset-right) {
border-color: $background-color $background-color $background-color $foreground-color;
border-style: solid;
border-width: $width $height;
} @else if ($direction == inset-left) {
border-color: $background-color $foreground-color $background-color $background-color;
border-style: solid;
border-width: $width $height;
}
}
-29
View File
@@ -1,29 +0,0 @@
@charset "UTF-8";
/// Provides an easy way to change the `word-wrap` property.
///
/// @param {String} $wrap [break-word]
/// Value for the `word-break` property.
///
/// @example scss - Usage
/// .wrapper {
/// @include word-wrap(break-word);
/// }
///
/// @example css - CSS Output
/// .wrapper {
/// overflow-wrap: break-word;
/// word-break: break-all;
/// word-wrap: break-word;
/// }
@mixin word-wrap($wrap: break-word) {
overflow-wrap: $wrap;
word-wrap: $wrap;
@if $wrap == break-word {
word-break: break-all;
} @else {
word-break: $wrap;
}
}
-43
View File
@@ -1,43 +0,0 @@
// http://www.w3.org/TR/css3-animations/#the-animation-name-property-
// Each of these mixins support comma separated lists of values, which allows different transitions for individual properties to be described in a single style rule. Each value in the list corresponds to the value at that same position in the other properties.
@mixin animation($animations...) {
@include prefixer(animation, $animations, webkit moz spec);
}
@mixin animation-name($names...) {
@include prefixer(animation-name, $names, webkit moz spec);
}
@mixin animation-duration($times...) {
@include prefixer(animation-duration, $times, webkit moz spec);
}
@mixin animation-timing-function($motions...) {
// ease | linear | ease-in | ease-out | ease-in-out
@include prefixer(animation-timing-function, $motions, webkit moz spec);
}
@mixin animation-iteration-count($values...) {
// infinite | <number>
@include prefixer(animation-iteration-count, $values, webkit moz spec);
}
@mixin animation-direction($directions...) {
// normal | alternate
@include prefixer(animation-direction, $directions, webkit moz spec);
}
@mixin animation-play-state($states...) {
// running | paused
@include prefixer(animation-play-state, $states, webkit moz spec);
}
@mixin animation-delay($times...) {
@include prefixer(animation-delay, $times, webkit moz spec);
}
@mixin animation-fill-mode($modes...) {
// none | forwards | backwards | both
@include prefixer(animation-fill-mode, $modes, webkit moz spec);
}
-3
View File
@@ -1,3 +0,0 @@
@mixin appearance($value) {
@include prefixer(appearance, $value, webkit moz ms o spec);
}
@@ -1,3 +0,0 @@
@mixin backface-visibility($visibility) {
@include prefixer(backface-visibility, $visibility, webkit spec);
}
-42
View File
@@ -1,42 +0,0 @@
//************************************************************************//
// Background-image property for adding multiple background images with
// gradients, or for stringing multiple gradients together.
//************************************************************************//
@mixin background-image($images...) {
$webkit-images: ();
$spec-images: ();
@each $image in $images {
$webkit-image: ();
$spec-image: ();
@if (type-of($image) == string) {
$url-str: str-slice($image, 1, 3);
$gradient-type: str-slice($image, 1, 6);
@if $url-str == "url" {
$webkit-image: $image;
$spec-image: $image;
}
@else if $gradient-type == "linear" {
$gradients: _linear-gradient-parser($image);
$webkit-image: map-get($gradients, webkit-image);
$spec-image: map-get($gradients, spec-image);
}
@else if $gradient-type == "radial" {
$gradients: _radial-gradient-parser($image);
$webkit-image: map-get($gradients, webkit-image);
$spec-image: map-get($gradients, spec-image);
}
}
$webkit-images: append($webkit-images, $webkit-image, comma);
$spec-images: append($spec-images, $spec-image, comma);
}
background-image: $webkit-images;
background-image: $spec-images;
}
-55
View File
@@ -1,55 +0,0 @@
//************************************************************************//
// Background property for adding multiple backgrounds using shorthand
// notation.
//************************************************************************//
@mixin background($backgrounds...) {
$webkit-backgrounds: ();
$spec-backgrounds: ();
@each $background in $backgrounds {
$webkit-background: ();
$spec-background: ();
$background-type: type-of($background);
@if $background-type == string or $background-type == list {
$background-str: if($background-type == list, nth($background, 1), $background);
$url-str: str-slice($background-str, 1, 3);
$gradient-type: str-slice($background-str, 1, 6);
@if $url-str == "url" {
$webkit-background: $background;
$spec-background: $background;
}
@else if $gradient-type == "linear" {
$gradients: _linear-gradient-parser("#{$background}");
$webkit-background: map-get($gradients, webkit-image);
$spec-background: map-get($gradients, spec-image);
}
@else if $gradient-type == "radial" {
$gradients: _radial-gradient-parser("#{$background}");
$webkit-background: map-get($gradients, webkit-image);
$spec-background: map-get($gradients, spec-image);
}
@else {
$webkit-background: $background;
$spec-background: $background;
}
}
@else {
$webkit-background: $background;
$spec-background: $background;
}
$webkit-backgrounds: append($webkit-backgrounds, $webkit-background, comma);
$spec-backgrounds: append($spec-backgrounds, $spec-background, comma);
}
background: $webkit-backgrounds;
background: $spec-backgrounds;
}

Some files were not shown because too many files have changed in this diff Show More