Compare commits

...
109 Commits
Author SHA1 Message Date
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 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
MightyPork 8028f14387 version bump 2017-09-05 02:14:55 +02:00
MightyPork daba5340f7 fixes and tweaks. implemented polling reload on HB loss 2017-09-05 02:13:16 +02:00
MightyPork a82e14961c RAM tuning 2017-09-05 01:48:37 +02:00
MightyPork 6c9ae7e126 remove debug log 2017-09-04 22:54:27 +02:00
MightyPork a3c84f9a8f Merge branch 'better-mouse' 2017-09-04 22:53:38 +02:00
MightyPork 7aa3cd7f75 working mouse 2017-09-04 22:53:29 +02:00
MightyPork 58ed6098c4 untested impl of mouse tracking modes 2017-09-04 22:03:08 +02:00
MightyPork 16a36a3d26 cleaning 2017-09-04 01:49:17 +02:00
MightyPork 9d1cefeaab done client-side mouse reporting 2017-09-04 01:46:00 +02:00
MightyPork 357600de8f expanded Help section and grouped some things; added screen resize command 2017-09-04 01:01:16 +02:00
225 changed files with 3220 additions and 20096 deletions
+2
View File
@@ -1,4 +1,5 @@
html/
html_demo/
build/
firmware/
espfs/mkespfsimage/*.o
@@ -10,6 +11,7 @@ espfs/espfstest/espfstest
html_compressed/
ldscript_memspecific.ld
eagle.app.sym
node_modules
# Garbage added by CLion
.idea/
+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
+3 -3
View File
@@ -110,8 +110,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
@@ -141,7 +139,7 @@ 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/apars_dcs.h user/uart_buffer.c user/uart_buffer.h user/jstring.c user/jstring.h user/character_sets.h user/utf8.h user/utf8.c user/cgi_logging.h)
include_directories(include)
include_directories(user)
@@ -155,6 +153,7 @@ include_directories(esp_iot_sdk_v1.5.2/include)
add_definitions(
-D__ets__
-DICACHE_FLASH
-DUSE_OPTIMIZE_PRINTF=1
-DHTTPD_MAX_CONNECTIONS=5
-DHTTPD_STACKSIZE=1000
-DICACHE_FLASH_ATTR=
@@ -163,6 +162,7 @@ add_definitions(
-DADMIN_PASSWORD="asdf"
-DGIT_HASH="blabla"
-DDEBUG_HEAP=1
-DDEBUG_MALLOC=1
-DESPFS_HEATSHRINK)
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.
+21 -9
View File
@@ -62,12 +62,13 @@ 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 \
# 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 += -DGIT_HASH_BACKEND='"$(shell git rev-parse --short HEAD)"'
CFLAGS += -DGIT_HASH_FRONTEND='"$(shell cd front-end && git rev-parse --short HEAD)"'
CFLAGS += -DADMIN_PASSWORD=$(ADMIN_PASSWORD)
ifdef GLOBAL_CFLAGS
@@ -152,7 +153,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 +171,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,7 +189,7 @@ $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
@@ -197,19 +197,31 @@ web:
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
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) SERVERNAME_PREFIX="ESPTerm " -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 $@
+110 -70
View File
@@ -1,100 +1,128 @@
# 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
As of release 1.0, ESPTerm **passes most of VTTEST test cases** (from the main menu and some Xterm specific), 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 sufficiently well
implemented to work with ncurses.
- Look at the [GitHub releases page][releases], there are some pics.
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])
## ESPTerm features
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 an ESP-12
on a LoLin NodeMCU board from eBay for development).
- **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
With ESPTerm, you can add remote access via WiFi to any embeded project, all you need is
a serial port 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).
## 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...
- **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
\*) Linux console applications run via agetty at ttyUSB0 were used for testing, obviously you'll be better off using SSH for remote shell
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.
- 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
## 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.
### Rescue from messed up config
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.
### 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.
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 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.
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.
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.
The default password is `19738426`. This password can't presently be changed without re-flashing the firmware.
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.
## Research resources
Developing ESPTerm wasn't an easy task, because the information is scattered across many places and the existing
terminal emulators I originally used for reference (terminator, Konsole) are not implemented correctly in some details.
A great tool for checking my implementation has proven to be [VTTTEST][vttest] and Xterm as a reference
implementation that is probably the most complete emulator available, although it's cumbersome to use and its age
really shows in the looks.
I've comnpiled a list of those I found most helpful here: [VT100 emulation resources][resources]
## 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 +131,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/
+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
+13 -4
View File
@@ -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
@@ -44,11 +44,20 @@ ADMIN_PASSWORD = "19738426"
GLOBAL_CFLAGS = \
-DDEBUG_ROUTER=0 \
-DDEBUG_CAPTDNS=0 \
-DDEBUG_HTTP=1 \
-DDEBUG_HTTP=0 \
-DDEBUG_ESPFS=0 \
-DDEBUG_PERSIST=0 \
-DDEBUG_UTFCACHE=0 \
-DDEBUG_CGI=0 \
-DDEBUG_WIFI=0 \
-DDEBUG_WS=0 \
-DDEBUG_ANSI=1 \
-DDEBUG_ANSI_NOIMPL=1 \
-DHTTPD_MAX_BACKLOG_SIZE=8192 \
-DDEBUG_INPUT=0 \
-DDEBUG_HEAP=1
-DDEBUG_HEAP=1 \
-DDEBUG_MALLOC=0 \
-DHTTPD_MAX_BACKLOG_SIZE=8192 \
-DHTTPD_MAX_HEAD_LEN=1024 \
-DHTTPD_MAX_POST_LEN=512 \
-mforce-l32 \
-DUSE_OPTIMIZE_PRINTF=1
Submodule
+1
Submodule front-end added at d7551c3ea9
-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;
}
-37
View File
@@ -1,37 +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;
}
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 . '.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';
-2163
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;
}());
-301
View File
@@ -1,301 +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]];
};
// 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.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 = {});
-606
View File
@@ -1,606 +0,0 @@
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('click', function () {
Input.onTap(y, x);
});
})();
/* 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;
}
/** 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;
}
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;
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, 3000);
}
function heartbeatFail() {
console.error("Heartbeat lost, reloading...");
location.reload();
}
return {
ws: null,
init: init,
send: doSend
};
})();
/** User input */
var Input = (function() {
var opts = {
np_alt: false,
cu_alt: false,
fn_alt: false,
};
function sendStrMsg(str) {
Conn.send("STR:"+str);
}
function sendPosMsg(y, x) {
Conn.send("TAP:"+y+','+x);
}
function sendBtnMsg(n) {
Conn.send("BTN:"+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();
}
function init() {
_initKeys();
// Button presses
qsa('#buttons button').forEach(function(s) {
s.addEventListener('click', function() {
sendBtnMsg(+this.dataset['n']);
});
});
}
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();
}
},
};
})();
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>
-598
View File
@@ -1,598 +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. Here are some examples:
</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>Title and Button Labels</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>\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>
</tbody>
</table>
</div>
</div>
<div class="Box fold">
<h2>System Commands</h2>
<div class="Row v">
<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>
</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;
}
-59
View File
@@ -1,59 +0,0 @@
@mixin border-image($borders...) {
$webkit-borders: ();
$spec-borders: ();
@each $border in $borders {
$webkit-border: ();
$spec-border: ();
$border-type: type-of($border);
@if $border-type == string or list {
$border-str: if($border-type == list, nth($border, 1), $border);
$url-str: str-slice($border-str, 1, 3);
$gradient-type: str-slice($border-str, 1, 6);
@if $url-str == "url" {
$webkit-border: $border;
$spec-border: $border;
}
@else if $gradient-type == "linear" {
$gradients: _linear-gradient-parser("#{$border}");
$webkit-border: map-get($gradients, webkit-image);
$spec-border: map-get($gradients, spec-image);
}
@else if $gradient-type == "radial" {
$gradients: _radial-gradient-parser("#{$border}");
$webkit-border: map-get($gradients, webkit-image);
$spec-border: map-get($gradients, spec-image);
}
@else {
$webkit-border: $border;
$spec-border: $border;
}
}
@else {
$webkit-border: $border;
$spec-border: $border;
}
$webkit-borders: append($webkit-borders, $webkit-border, comma);
$spec-borders: append($spec-borders, $spec-border, comma);
}
-webkit-border-image: $webkit-borders;
border-image: $spec-borders;
border-style: solid;
}
//Examples:
// @include border-image(url("image.png"));
// @include border-image(url("image.png") 20 stretch);
// @include border-image(linear-gradient(45deg, orange, yellow));
// @include border-image(linear-gradient(45deg, orange, yellow) stretch);
// @include border-image(linear-gradient(45deg, orange, yellow) 20 30 40 50 stretch round);
// @include border-image(radial-gradient(top, cover, orange, yellow, orange));
-4
View File
@@ -1,4 +0,0 @@
@mixin calc($property, $value) {
#{$property}: -webkit-calc(#{$value});
#{$property}: calc(#{$value});
}

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