commit
75496b8b00
@ -0,0 +1,10 @@ |
||||
_env.php |
||||
js/* |
||||
css/* |
||||
!.gitkeep |
||||
node_modules/ |
||||
*.~ |
||||
*.bak |
||||
.idea |
||||
.sass-cache |
||||
*.map |
@ -0,0 +1,3 @@ |
||||
<?php |
||||
|
||||
define("ESP_IP", "192.168.0.19"); |
@ -0,0 +1,50 @@ |
||||
<?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'); |
||||
|
||||
pg('reset_screen', 'api', '', '/system/cls', 'title.term'); |
||||
|
||||
pg('index', 'api', '', '/', ''); |
||||
|
||||
// ajax API |
||||
|
||||
return $pages; |
@ -0,0 +1,186 @@ |
||||
<?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('ESP_DEMO', (bool)getenv('ESP_DEMO')); |
||||
if (ESP_DEMO) { |
||||
define('DEMO_SCREEN', '"S\u0019\u0001Q\u0001\u0017\u0001K\u0001\u0015\u0004\u0003\b\u0001 \u0002P\u0001┌ESPTerm─Demo─\u0002\u0002\u0001\u0003\u0002\u000131\u0003\u0003\u000132\u0003\u0004\u00013\u0002\u0002\u0001\u0003\u0005\u000134\u0003\u0006\u000135\u0003\u0007\u000136\u0003\b\u000137\u0003\t\u000190\u0003\n\u000191\u0003\u000b\u000192\u0003\f\u000193\u0003\r\u000194\u0003\u000e\u000195\u0003\u000f\u000196\u0003\u0010\u000197\u0003\b\u0001─\u0002\r\u0001┐ \u0002\u0015\u0001│ \u00029\u0001│ \u0002\u0004\u0001│\u0002\t\u0001 \u0002\b\u0001│\u0004\u0002\u0001Bold \u0004\u0003\u0001F\u0004\u0003\u0001a\u0004\u0003\u0001i\u0004\u0003\u0001n\u0004\u0003\u0001t\u0004\u0003\u0001 \u0004\u0005\u0001I\u0004\u0005\u0001t\u0004\u0005\u0001a\u0004\u0005\u0001l\u0004\u0005\u0001i\u0004\u0005\u0001c\u0004\u0005\u0001 \u0004\t\u0001U\u0004\t\u0001n\u0004\t\u0001d\u0004\t\u0001e\u0004\t\u0001r\u0004\t\u0001l\u0004\t\u0001i\u0004\t\u0001n\u0004\t\u0001e\u0004\u0001\u0001 \u0004\u0011\u0001B\u0004\u0011\u0001l\u0004\u0011\u0001i\u0004\u0011\u0001n\u0004\u0011\u0001k\u0004\u0011\u0001 \u0001q\u0001\u0001Inverse\u0003\b\u0001 \u0004A\u0001S\u0004A\u0001t\u0004A\u0001r\u0004A\u0001i\u0004A\u0001k\u0004A\u0001e\u0004\u0001\u0001 \u0004!\u0001F\u0004!\u0001r\u0004!\u0001a\u0004!\u0001k\u0004!\u0001t\u0004!\u0001u\u0004!\u0001r\u0004\u0001\u0001 │ \u0002\u0002\u0001─\u0002\u0002\u0001\u0003\n\u0002 \u0002\t\u0001\u0003\b\u0001─\u0002\u0002\u0001 \u0002\u0006\u0001│ \u00029\u0001│ \u0002\u0002\u0001─\u0002\u0002\u0001\u0003\n\u0002 \u0003\u0002\u0002ESP826\u0002\u0002\u0001\u0003\n\u0002 \u0003\b\u0001─\u0002\u0002\u0001 \u0002\u0006\u0001└─\u00029\u0001┤ \u0002\u0002\u0001─\u0002\u0002\u0001\u0003\n\u0002 \u0002\t\u0001\u0003\b\u0001─\u0002\u0002\u0001 \u0002@\u0001│ \u0002\u0002\u0001─\u0002\u0002\u0001\u0003\n\u0002 \u0003\u0002\u0002(@)#\u0002\u0004\u0001\u0003\n\u0002 \u0003\b\u0001─\u0002\u0002\u0001 \u0002\u0007\u0001\u0003O\u0001 This is a static demo of the ESPTerm Web Interface \u0002\u0004\u0001\u0003\b\u0001 \u0002\u0002\u0001│ \u0002\u0002\u0001─\u0002\u0002\u0001\u0003\n\u0002 \u0002\t\u0001\u0003\b\u0001─\u0002\u0002\u0001 \u0002\u0007\u0001\u0003O\u0001 \u00027\u0001\u0003\b\u0001 \u0002\u0002\u0001│ \u0002\u0004\u0001│\u0002\t\u0001 \u0002\t\u0001\u0003O\u0001 Try the links beneath this scre\u0002\u0002\u0001n to browse the menu. \u0003\b\u0001 \u0002\u0002\u0001♦ \u0002\u0016\u0001\u0003O\u0001 \u00027\u0001\u0003\b\u0001 \u0002\u0019\u0001\u0003O\u0001 <°)\u0002\u0003\u0001>< ESPTerm ful\u0002\u0002\u0001y sup\u0002\u0002\u0001orts UTF-8 お は よ ー \u0002\u0002\u0001><(\u0002\u0003\u0001°> \u0003\b\u0001 \u0002\u0019\u0001\u0003O\u0001 \u00027\u0001\u0003\b\u0001 \u0002i\u0001\u0003\u000b\u0001Other interesting features:\u0003\b\u0001 \u0002\u0018\u0001↓ \u0002n\u0001\u0003\u0003\u0001- Almost ful\u0002\u0002\u0001 VT10\u0002\u0002\u0001 emulation \u0003\b\u0001 \u0003\u0006\u0001()\u0003\b\u0001 \u0003\u0006\u0001()\u0003\b\u0001 \u0002\b\u0001Funguje tu čeština! \u0002\u0011\u0001\u0003\u0005\u0001- Xterm-like mouse tracking\u0003\b\u0001 \u0002\u0003\u0001=\u0002\u0002\u0001\u0003\t\u0002°.°\u0003\b\u0001=\u0002\u0002\u0001 \u0003\u0006\u0001<-\u0002\u0003\u0001, \u0003\b\u0001 \u0002$\u0001\u0003\u0004\u0001- File upload utility\u0003\b\u0001 \u0002\n\u0001\'\u0002\u0002\u0001 \'\u0002\u0002\u0001 \u0002\u0002\u0001\u0003\u0006\u0001 \u0002\u0004\u0001mouse\u0003\b\u0001 \u0002!\u0001\u0003\u0002\u0001- User-friendly config interface\u0003\b\u0001 \u00020\u0001\u0003\u000e\u0001-\u0003\u0002\u0001 \u0003\u000e\u0001Advanced WiFi & network set\u0002\u0002\u0001ings\u0003\b\u0001 \u0002\u0011\u0001\u0003\f\u0001Try ESPTerm today!\u0003\b\u0001 \u0002\u000b\u0001- Built-in help page \u0002\u001a\u0001\u0003\u0007\u0001-\u0002\u0002\u0001>\u0003\b\u0001 \u0002\u0002\u0001\u0003\f\u0001Pre-built binaries\u0003\b\u0001 \u0003\f\u0001are\u0003\b\u0001 \u0002\"\u0001\u0003\u0007\u0001link on the About page \u0002\u0002\u0001\u0003\f\u0001available on GitHub! \u0003\b\u0001 \u0002U\u0001"'); |
||||
define('DEMO_APS', <<<APS |
||||
{ |
||||
"result": { |
||||
"inProgress": 0, |
||||
"APs": [ |
||||
{"essid": "Cisco", "bssid": "88:f7:c7:52:b3:99", "rssi": 205, "rssi_perc": 100, "enc": 4, "channel": 7}, |
||||
{"essid": "UPC Wi-Free", "bssid": "8a:f7:c7:52:b3:9b", "rssi": 203, "rssi_perc": 100, "enc": 5, "channel": 1}, |
||||
{"essid": "UPC Wi-Free", "bssid": "0a:95:2a:0c:84:31", "rssi": 166, "rssi_perc": 32, "enc": 5, "channel": 1}, |
||||
{"essid": "MujO2Internet_2EEB96", "bssid": "d0:60:8c:2e:eb:96", "rssi": 174, "rssi_perc": 48, "enc": 4, "channel": 4}, |
||||
{"essid": "Internet", "bssid": "38:72:c0:32:bd:0d", "rssi": 164, "rssi_perc": 28, "enc": 2, "channel": 10}, |
||||
{"essid": "MyO2Internet_08C850", "bssid": "78:c1:a7:08:c8:50", "rssi": 186, "rssi_perc": 72, "enc": 4, "channel": 11}, |
||||
{"essid": "UPC Wi-Free", "bssid": "06:7c:34:9a:6f:7c", "rssi": 167, "rssi_perc": 34, "enc": 0, "channel": 11}, |
||||
{"essid": "Internet_B0", "bssid": "5c:f4:ab:11:3b:b3", "rssi": 175, "rssi_perc": 50, "enc": 3, "channel": 13}, |
||||
{"essid": "UPC5716805", "bssid": "08:95:2a:0c:84:3f", "rssi": 165, "rssi_perc": 30, "enc": 4, "channel": 1} |
||||
] |
||||
} |
||||
} |
||||
APS |
||||
); |
||||
} |
||||
|
||||
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"; |
||||
if (ESP_DEMO) return "$name.html"; |
||||
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 je($s) |
||||
{ |
||||
return htmlspecialchars(json_encode($s), ENT_HTML5); |
||||
} |
||||
|
||||
|
||||
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; |
||||
} |
||||
|
||||
if (!function_exists('utf8')) { |
||||
function utf8($num) |
||||
{ |
||||
if($num<=0x7F) return chr($num); |
||||
if($num<=0x7FF) return chr(($num>>6)+192).chr(($num&63)+128); |
||||
if($num<=0xFFFF) return chr(($num>>12)+224).chr((($num>>6)&63)+128).chr(($num&63)+128); |
||||
if($num<=0x1FFFFF) return chr(($num>>18)+240).chr((($num>>12)&63)+128).chr((($num>>6)&63)+128).chr(($num&63)+128); |
||||
return ''; |
||||
} |
||||
} |
||||
|
||||
if (!function_exists('load_esp_charsets')) { |
||||
function load_esp_charsets() { |
||||
$chsf = __DIR__ . '/../user/character_sets.h'; |
||||
|
||||
if (! file_exists($chsf)) { |
||||
return [ |
||||
'!! ERROR: `../user/character_sets.h` not found !!' => [ |
||||
['65', 'A', '&'], |
||||
], |
||||
]; |
||||
} |
||||
|
||||
$re_table = '/\/\/ %%BEGIN:(.)%%\s*(.*?)\s*\/\/ %%END:\1%%/s'; |
||||
preg_match_all($re_table, file_get_contents($chsf), $m_tbl); |
||||
|
||||
$re_bounds = '/#define CODEPAGE_(.)_BEGIN\s+(\d+)\n#define CODEPAGE_\1_END\s+(\d+)/'; |
||||
preg_match_all($re_bounds, file_get_contents($chsf), $m_bounds); |
||||
|
||||
$cps = []; |
||||
|
||||
foreach ($m_tbl[2] as $i => $str) { |
||||
$name = $m_tbl[1][$i]; |
||||
$start = intval($m_bounds[2][$i]); |
||||
$table = []; |
||||
$str = preg_replace('/,\s*\/\/[^\n]*/', '', $str); |
||||
$rows = explode("\n", $str); |
||||
$rows = array_map('trim', $rows); |
||||
|
||||
foreach($rows as $j => $v) { |
||||
if (strpos($v, '0x') === 0) { |
||||
$v = substr($v, 2); |
||||
$v = hexdec($v); |
||||
} else { |
||||
$v = intval($v); |
||||
} |
||||
$ascii = $start+$j; |
||||
$table[] = [ |
||||
$ascii, |
||||
chr($ascii), |
||||
utf8($v==0? $ascii :$v), |
||||
]; |
||||
} |
||||
$cps[$name] = $table; |
||||
} |
||||
return $cps; |
||||
} |
||||
} |
||||
|
||||
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); |
||||
} |
||||
} |
@ -0,0 +1,50 @@ |
||||
<?php |
||||
|
||||
require_once __DIR__ . '/base.php'; |
||||
|
||||
function process_html($s) { |
||||
$pattern = '/<!--(.*)-->/Uis'; |
||||
$s = preg_replace($pattern, '', $s); |
||||
|
||||
$pattern = '/(?:(?:\/\*(?:[^*]|(?:\*+[^*\/]))*\*+\/)|(?:(?<!\:|\\\|\'|\")\/\/.*))/'; |
||||
$s = preg_replace($pattern, '', $s); |
||||
|
||||
$pattern = '/\s+/s'; |
||||
$s = preg_replace($pattern, ' ', $s); |
||||
return $s; |
||||
} |
||||
|
||||
$no_tpl_files = ['help', 'cfg_wifi_conn']; |
||||
|
||||
$dest = ESP_DEMO ? __DIR__ . '/../html_demo/' : __DIR__ . '/../html/'; |
||||
|
||||
ob_start(); |
||||
foreach($_pages as $_k => $p) { |
||||
if ($p->bodyclass == 'api') { |
||||
if (ESP_DEMO) { |
||||
$target = 'term.html'; |
||||
echo "Generating: ~$_k.html -> $target\n"; |
||||
$s = "<!DOCTYPE HTML><meta http-equiv=\"refresh\" content=\"0;url=$target\">"; |
||||
} else { |
||||
continue; |
||||
} |
||||
} else { |
||||
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 = $dest . $_k . ((in_array($_k, $no_tpl_files)||ESP_DEMO) ? '.html' : '.tpl'); |
||||
file_put_contents($of, $s); // write to a file |
||||
} |
||||
|
||||
ob_flush(); |
@ -0,0 +1,23 @@ |
||||
<?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', |
||||
'wifi.passwd_saved', |
||||
]; |
||||
|
||||
$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" |
||||
); |
After Width: | Height: | Size: 318 B |
@ -0,0 +1 @@ |
||||
/out/ |
Binary file not shown.
@ -0,0 +1,93 @@ |
||||
#!/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.*?woff'\)" "$OUTPUT_DIR/fontello-embedded.css" \ |
||||
| sed 's/\x0//g' >> "$SASSFILE" |
||||
|
||||
echo -e ";\n}" >> "$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" |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 18 KiB |
@ -0,0 +1,50 @@ |
||||
<?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; |
||||
|
||||
$_pf = __DIR__ . '/pages/'.$_GET['page'].'.php'; |
||||
|
||||
if (!file_exists($_pf)) { |
||||
header("Location: /", true, 302); |
||||
die(); |
||||
} |
||||
|
||||
require __DIR__ . '/pages/_head.php'; |
||||
|
||||
$include_re = '/<\?php\s*(require|include)\s*\(?\s*?(?:__DIR__\s*\.)?\s*(["\'])(.*?)\2\s*\)?;\s*\?>/'; |
||||
|
||||
if (file_exists($_pf)) { |
||||
$f = file_get_contents($_pf); |
||||
|
||||
// Resolve requires inline - they wont work after dumping the resulting file to /tmp for eval |
||||
$f = preg_replace_callback($include_re, function ($m) use ($_pf) { |
||||
$n = dirname($_pf).'/'.$m[3]; |
||||
if (file_exists($n)) { |
||||
return file_get_contents($n); |
||||
} else { |
||||
return "<p>NOT FOUND: $n</p>"; |
||||
} |
||||
}, $f); |
||||
|
||||
if (DEBUG || ESP_DEMO) |
||||
$str = tplSubs($f, require(__DIR__ . '/_debug_replacements.php')); |
||||
else $str = $f; |
||||
|
||||
// special symbols |
||||
$str = str_replace('\,', ' ', $str); |
||||
$str = preg_replace('/(?<=[^ \\\\])~(?=[^ ])/', ' ', $str); |
||||
$str = str_replace('\~', '~', $str); |
||||
$str = preg_replace('/(?<![\w\\\\])`([^ `][^`]*?[^ `]|[^ `])`(?!\w)/', '<code>$1</code>', $str); |
||||
$str = preg_replace('/(?<![\w\\\\])_([^ _][^_]*?[^ _]|[^ _])_(?!\w)/', '<i>$1</i>', $str); |
||||
$str = preg_replace('/(?<![\w\\\\])\*([^ *][^*]*?[^ *]|[^ *])\*(?!\w)/', '<b>$1</b>', $str); |
||||
|
||||
$str = preg_replace("/\s*(\\\\\\\\)[\n \t]+/", '<br>', $str); |
||||
include_str($str); |
||||
} |
||||
|
||||
require __DIR__ . '/pages/_tail.php'; |
@ -0,0 +1,189 @@ |
||||
/** 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)); |
||||
}); |
||||
|
||||
$('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) { |
||||
$('.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; |
||||
} |
||||
}()); |
||||
} |
@ -0,0 +1,703 @@ |
||||
/*!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 = { |
||||
'&': '&', |
||||
'<': '<', |
||||
'>': '>', |
||||
'"': '"', |
||||
"'": ''', |
||||
'/': '/', |
||||
'`': '`', |
||||
'=': '=' |
||||
}; |
||||
|
||||
chibi.htmlEscape = function(string) { |
||||
return String(string).replace(/[&<>"'`=\/]/g, function (s) { |
||||
return entityMap[s]; |
||||
}); |
||||
}; |
||||
|
||||
// Set Chibi's global namespace here ($)
|
||||
w.$ = chibi; |
||||
}()); |
@ -0,0 +1,310 @@ |
||||
// keymaster.js
|
||||
// (c) 2011-2013 Thomas Fuchs
|
||||
// keymaster.js may be freely distributed under the MIT license.
|
||||
|
||||
;(function(global){ |
||||
var k, |
||||
_handlers = {}, |
||||
_mods = { 16: false, 18: false, 17: false, 91: false }, |
||||
_scope = 'all', |
||||
// modifier keys
|
||||
_MODIFIERS = { |
||||
'⇧': 16, shift: 16, |
||||
'⌥': 18, alt: 18, option: 18, |
||||
'⌃': 17, ctrl: 17, control: 17, |
||||
'⌘': 91, command: 91 |
||||
}, |
||||
// special keys
|
||||
_MAP = { |
||||
backspace: 8, tab: 9, clear: 12, |
||||
enter: 13, 'return': 13, |
||||
esc: 27, escape: 27, space: 32, |
||||
left: 37, up: 38, |
||||
right: 39, down: 40, |
||||
del: 46, 'delete': 46, |
||||
home: 36, end: 35, |
||||
pageup: 33, pagedown: 34, |
||||
',': 188, '.': 190, '/': 191, |
||||
'`': 192, '-': 189, '=': 187, |
||||
';': 186, '\'': 222, |
||||
'[': 219, ']': 221, '\\': 220, |
||||
// added:
|
||||
insert: 45, |
||||
np_0: 96, np_1: 97, np_2: 98, np_3: 99, np_4: 100, np_5: 101, |
||||
np_6: 102, np_7: 103, np_8: 104, np_9: 105, np_mul: 106, |
||||
np_add: 107, np_sub: 109, np_point: 110, np_div: 111, numlock: 144, |
||||
}, |
||||
code = function(x){ |
||||
return _MAP[x] || x.toUpperCase().charCodeAt(0); |
||||
}, |
||||
_downKeys = []; |
||||
|
||||
for(k=1;k<20;k++) _MAP['f'+k] = 111+k; |
||||
|
||||
// IE doesn't support Array#indexOf, so have a simple replacement
|
||||
function index(array, item){ |
||||
var i = array.length; |
||||
while(i--) if(array[i]===item) return i; |
||||
return -1; |
||||
} |
||||
|
||||
// for comparing mods before unassignment
|
||||
function compareArray(a1, a2) { |
||||
if (a1.length != a2.length) return false; |
||||
for (var i = 0; i < a1.length; i++) { |
||||
if (a1[i] !== a2[i]) return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
var modifierMap = { |
||||
16:'shiftKey', |
||||
18:'altKey', |
||||
17:'ctrlKey', |
||||
91:'metaKey' |
||||
}; |
||||
function updateModifierKey(event) { |
||||
for(k in _mods) _mods[k] = event[modifierMap[k]]; |
||||
}; |
||||
|
||||
function isModifierPressed(mod) { |
||||
if (mod=='control'||mod=='ctrl') return _mods[17]; |
||||
if (mod=='shift') return _mods[16]; |
||||
if (mod=='meta') return _mods[91]; |
||||
if (mod=='alt') return _mods[18]; |
||||
return false; |
||||
} |
||||
|
||||
// handle keydown event
|
||||
function dispatch(event) { |
||||
var key, handler, k, i, modifiersMatch, scope; |
||||
key = event.keyCode; |
||||
|
||||
if (index(_downKeys, key) == -1) { |
||||
_downKeys.push(key); |
||||
} |
||||
|
||||
// if a modifier key, set the key.<modifierkeyname> property to true and return
|
||||
if(key == 93 || key == 224) key = 91; // right command on webkit, command on Gecko
|
||||
if(key in _mods) { |
||||
_mods[key] = true; |
||||
// 'assignKey' from inside this closure is exported to window.key
|
||||
for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = true; |
||||
return; |
||||
} |
||||
updateModifierKey(event); |
||||
|
||||
// see if we need to ignore the keypress (filter() can can be overridden)
|
||||
// by default ignore key presses if a select, textarea, or input is focused
|
||||
if(!assignKey.filter.call(this, event)) return; |
||||
|
||||
// abort if no potentially matching shortcuts found
|
||||
if (!(key in _handlers)) return; |
||||
|
||||
scope = getScope(); |
||||
|
||||
// for each potential shortcut
|
||||
for (i = 0; i < _handlers[key].length; i++) { |
||||
handler = _handlers[key][i]; |
||||
|
||||
// see if it's in the current scope
|
||||
if(handler.scope == scope || handler.scope == 'all'){ |
||||
// check if modifiers match if any
|
||||
modifiersMatch = handler.mods.length > 0; |
||||
for(k in _mods) |
||||
if((!_mods[k] && index(handler.mods, +k) > -1) || |
||||
(_mods[k] && index(handler.mods, +k) == -1)) modifiersMatch = false; |
||||
// call the handler and stop the event if neccessary
|
||||
if((handler.mods.length == 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91]) || modifiersMatch){ |
||||
if(handler.method(event, handler)===false){ |
||||
if(event.preventDefault) event.preventDefault(); |
||||
else event.returnValue = false; |
||||
if(event.stopPropagation) event.stopPropagation(); |
||||
if(event.cancelBubble) event.cancelBubble = true; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
|
||||
// unset modifier keys on keyup
|
||||
function clearModifier(event){ |
||||
var key = event.keyCode, k, |
||||
i = index(_downKeys, key); |
||||
|
||||
// remove key from _downKeys
|
||||
if (i >= 0) { |
||||
_downKeys.splice(i, 1); |
||||
} |
||||
|
||||
if(key == 93 || key == 224) key = 91; |
||||
if(key in _mods) { |
||||
_mods[key] = false; |
||||
for(k in _MODIFIERS) if(_MODIFIERS[k] == key) assignKey[k] = false; |
||||
} |
||||
}; |
||||
|
||||
function resetModifiers() { |
||||
for(k in _mods) _mods[k] = false; |
||||
for(k in _MODIFIERS) assignKey[k] = false; |
||||
}; |
||||
|
||||
// parse and assign shortcut
|
||||
function assignKey(key, scope, method){ |
||||
var keys, mods; |
||||
keys = getKeys(key); |
||||
if (method === undefined) { |
||||
method = scope; |
||||
scope = 'all'; |
||||
} |
||||
|
||||
// for each shortcut
|
||||
for (var i = 0; i < keys.length; i++) { |
||||
// set modifier keys if any
|
||||
mods = []; |
||||
key = keys[i].split('+'); |
||||
if (key.length > 1){ |
||||
mods = getMods(key); |
||||
key = [key[key.length-1]]; |
||||
} |
||||
// convert to keycode and...
|
||||
key = key[0] |
||||
key = code(key); |
||||
// ...store handler
|
||||
if (!(key in _handlers)) _handlers[key] = []; |
||||
_handlers[key].push({ shortcut: keys[i], scope: scope, method: method, key: keys[i], mods: mods }); |
||||
} |
||||
}; |
||||
|
||||
// unbind all handlers for given key in current scope
|
||||
function unbindKey(key, scope) { |
||||
var multipleKeys, keys, |
||||
mods = [], |
||||
i, j, obj; |
||||
|
||||
multipleKeys = getKeys(key); |
||||
|
||||
for (j = 0; j < multipleKeys.length; j++) { |
||||
keys = multipleKeys[j].split('+'); |
||||
|
||||
if (keys.length > 1) { |
||||
mods = getMods(keys); |
||||
} |
||||
|
||||
key = keys[keys.length - 1]; |
||||
key = code(key); |
||||
|
||||
if (scope === undefined) { |
||||
scope = getScope(); |
||||
} |
||||
if (!_handlers[key]) { |
||||
return; |
||||
} |
||||
for (i = 0; i < _handlers[key].length; i++) { |
||||
obj = _handlers[key][i]; |
||||
// only clear handlers if correct scope and mods match
|
||||
if (obj.scope === scope && compareArray(obj.mods, mods)) { |
||||
_handlers[key][i] = {}; |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
|
||||
// Returns true if the key with code 'keyCode' is currently down
|
||||
// Converts strings into key codes.
|
||||
function isPressed(keyCode) { |
||||
if (typeof(keyCode)=='string') { |
||||
keyCode = code(keyCode); |
||||
} |
||||
return index(_downKeys, keyCode) != -1; |
||||
} |
||||
|
||||
function getPressedKeyCodes() { |
||||
return _downKeys.slice(0); |
||||
} |
||||
|
||||
function filter(event){ |
||||
var tagName = (event.target || event.srcElement).tagName; |
||||
// ignore keypressed in any elements that support keyboard data input
|
||||
return !(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA'); |
||||
} |
||||
|
||||
// initialize key.<modifier> to false
|
||||
for(k in _MODIFIERS) assignKey[k] = false; |
||||
|
||||
// set current scope (default 'all')
|
||||
function setScope(scope){ _scope = scope || 'all' }; |
||||
function getScope(){ return _scope || 'all' }; |
||||
|
||||
// delete all handlers for a given scope
|
||||
function deleteScope(scope){ |
||||
var key, handlers, i; |
||||
|
||||
for (key in _handlers) { |
||||
handlers = _handlers[key]; |
||||
for (i = 0; i < handlers.length; ) { |
||||
if (handlers[i].scope === scope) handlers.splice(i, 1); |
||||
else i++; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
// abstract key logic for assign and unassign
|
||||
function getKeys(key) { |
||||
var keys; |
||||
key = key.replace(/\s/g, ''); |
||||
keys = key.split(','); |
||||
if ((keys[keys.length - 1]) == '') { |
||||
keys[keys.length - 2] += ','; |
||||
} |
||||
return keys; |
||||
} |
||||
|
||||
// abstract mods logic for assign and unassign
|
||||
function getMods(key) { |
||||
var mods = key.slice(0, key.length - 1); |
||||
for (var mi = 0; mi < mods.length; mi++) |
||||
mods[mi] = _MODIFIERS[mods[mi]]; |
||||
return mods; |
||||
} |
||||
|
||||
// cross-browser events
|
||||
function addEvent(object, event, method) { |
||||
if (object.addEventListener) |
||||
object.addEventListener(event, method, false); |
||||
else if(object.attachEvent) |
||||
object.attachEvent('on'+event, function(){ method(window.event) }); |
||||
}; |
||||
|
||||
// set the handlers globally on document
|
||||
addEvent(document, 'keydown', function(event) { dispatch(event) }); // Passing _scope to a callback to ensure it remains the same by execution. Fixes #48
|
||||
addEvent(document, 'keyup', clearModifier); |
||||
|
||||
// reset modifiers to false whenever the window is (re)focused.
|
||||
addEvent(window, 'focus', resetModifiers); |
||||
|
||||
// store previously defined key
|
||||
var previousKey = global.key; |
||||
|
||||
// restore previously defined key and return reference to our key object
|
||||
function noConflict() { |
||||
var k = global.key; |
||||
global.key = previousKey; |
||||
return k; |
||||
} |
||||
|
||||
// set window.key and window.key.set/get/deleteScope, and the default filter
|
||||
global.key = assignKey; |
||||
global.key.setScope = setScope; |
||||
global.key.getScope = getScope; |
||||
global.key.deleteScope = deleteScope; |
||||
global.key.filter = filter; |
||||
global.key.isPressed = isPressed; |
||||
global.key.isModifier = isModifierPressed; |
||||
global.key.getPressedKeyCodes = getPressedKeyCodes; |
||||
global.key.noConflict = noConflict; |
||||
global.key.unbind = unbindKey; |
||||
|
||||
if(typeof module !== 'undefined') module.exports = assignKey; |
||||
|
||||
})(this); |
@ -0,0 +1,8 @@ |
||||
// 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+'?'; } |
@ -0,0 +1,44 @@ |
||||
/** Module for toggling a modal overlay */ |
||||
(function () { |
||||
var modal = {}; |
||||
var curCloseCb = null; |
||||
|
||||
modal.show = function (sel, closeCb) { |
||||
var $m = $(sel); |
||||
$m.removeClass('hidden visible'); |
||||
setTimeout(function () { |
||||
$m.addClass('visible'); |
||||
}, 1); |
||||
curCloseCb = closeCb; |
||||
}; |
||||
|
||||
modal.hide = function (sel) { |
||||
var $m = $(sel); |
||||
$m.removeClass('visible'); |
||||
setTimeout(function () { |
||||
$m.addClass('hidden'); |
||||
if (curCloseCb) curCloseCb(); |
||||
}, 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; |
||||
})(); |
@ -0,0 +1,32 @@ |
||||
(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 = {}); |
@ -0,0 +1,6 @@ |
||||
/** Init the terminal sub-module - called from HTML */ |
||||
window.termInit = function () { |
||||
Conn.init(); |
||||
Input.init(); |
||||
TermUpl.init(); |
||||
}; |
@ -0,0 +1,134 @@ |
||||
/** Handle connections */ |
||||
var Conn = (function () { |
||||
var ws; |
||||
var heartbeatTout; |
||||
var pingIv; |
||||
var xoff = false; |
||||
var autoXoffTout; |
||||
var reconTout; |
||||
|
||||
var pageShown = false; |
||||
|
||||
function onOpen(evt) { |
||||
console.log("CONNECTED"); |
||||
doSend("i"); |
||||
} |
||||
|
||||
function onClose(evt) { |
||||
console.warn("SOCKET CLOSED, code " + evt.code + ". Reconnecting..."); |
||||
clearTimeout(reconTout); |
||||
reconTout = setTimeout(function () { |
||||
init(); |
||||
}, 2000); |
||||
// this happens when the buffer gets fucked up via invalid unicode.
|
||||
// we basically use polling instead of socket then
|
||||
} |
||||
|
||||
function onMessage(evt) { |
||||
try { |
||||
// . = heartbeat
|
||||
switch (evt.data.charAt(0)) { |
||||
case 'B': |
||||
case 'T': |
||||
case 'S': |
||||
Screen.load(evt.data); |
||||
if(!pageShown) { |
||||
showPage(); |
||||
pageShown = true; |
||||
} |
||||
break; |
||||
|
||||
case '-': |
||||
//console.log('xoff');
|
||||
xoff = true; |
||||
autoXoffTout = setTimeout(function () { |
||||
xoff = false; |
||||
}, 250); |
||||
break; |
||||
|
||||
case '+': |
||||
//console.log('xon');
|
||||
xoff = false; |
||||
clearTimeout(autoXoffTout); |
||||
break; |
||||
} |
||||
heartbeat(); |
||||
} catch (e) { |
||||
console.error(e); |
||||
} |
||||
} |
||||
|
||||
function canSend() { |
||||
return !xoff; |
||||
} |
||||
|
||||
function doSend(message) { |
||||
if (_demo) { |
||||
console.log("TX: ", message); |
||||
return true; // Simulate success
|
||||
} |
||||
if (xoff) { |
||||
// TODO queue
|
||||
console.log("Can't send, flood control."); |
||||
return false; |
||||
} |
||||
|
||||
if (!ws) return false; // for dry testing
|
||||
if (ws.readyState != 1) { |
||||
console.error("Socket not ready"); |
||||
return false; |
||||
} |
||||
if (typeof message != "string") { |
||||
message = JSON.stringify(message); |
||||
} |
||||
ws.send(message); |
||||
return true; |
||||
} |
||||
|
||||
function init() { |
||||
if (_demo) { |
||||
console.log("Demo mode!"); |
||||
Screen.load(_demo_screen); |
||||
showPage(); |
||||
return; |
||||
} |
||||
|
||||
clearTimeout(reconTout); |
||||
clearTimeout(heartbeatTout); |
||||
|
||||
ws = new WebSocket("ws://" + _root + "/term/update.ws"); |
||||
ws.onopen = onOpen; |
||||
ws.onclose = onClose; |
||||
ws.onmessage = onMessage; |
||||
console.log("Opening socket."); |
||||
heartbeat(); |
||||
} |
||||
|
||||
function heartbeat() { |
||||
clearTimeout(heartbeatTout); |
||||
heartbeatTout = setTimeout(heartbeatFail, 2000); |
||||
} |
||||
|
||||
function heartbeatFail() { |
||||
console.error("Heartbeat lost, probing server..."); |
||||
pingIv = setInterval(function () { |
||||
console.log("> ping"); |
||||
$.get('http://' + _root + '/system/ping', function (resp, status) { |
||||
if (status == 200) { |
||||
clearInterval(pingIv); |
||||
console.info("Server ready, reloading page..."); |
||||
location.reload(); |
||||
} |
||||
}, { |
||||
timeout: 100, |
||||
}); |
||||
}, 1000); |
||||
} |
||||
|
||||
return { |
||||
ws: null, |
||||
init: init, |
||||
send: doSend, |
||||
canSend: canSend, // check flood control
|
||||
}; |
||||
})(); |
@ -0,0 +1,262 @@ |
||||
/** |
||||
* User input |
||||
* |
||||
* --- Rx messages: --- |
||||
* S - screen content (binary encoding of the entire screen with simple compression) |
||||
* T - text labels - Title and buttons, \0x01-separated |
||||
* B - beep |
||||
* . - heartbeat |
||||
* |
||||
* --- Tx messages --- |
||||
* s - string |
||||
* b - action button |
||||
* p - mb press |
||||
* r - mb release |
||||
* m - mouse move |
||||
*/ |
||||
var Input = (function() { |
||||
var opts = { |
||||
np_alt: false, |
||||
cu_alt: false, |
||||
fn_alt: false, |
||||
mt_click: false, |
||||
mt_move: false, |
||||
no_keys: false, |
||||
}; |
||||
|
||||
/** Send a literal message */ |
||||
function sendStrMsg(str) { |
||||
return Conn.send("s"+str); |
||||
} |
||||
|
||||
/** Send a button event */ |
||||
function sendBtnMsg(n) { |
||||
Conn.send("b"+Chr(n)); |
||||
} |
||||
|
||||
/** Fn alt choice for key message */ |
||||
function fa(alt, normal) { |
||||
return opts.fn_alt ? alt : normal; |
||||
} |
||||
|
||||
/** Cursor alt choice for key message */ |
||||
function ca(alt, normal) { |
||||
return opts.cu_alt ? alt : normal; |
||||
} |
||||
|
||||
/** Numpad alt choice for key message */ |
||||
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]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** Bind a keystroke to message */ |
||||
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) { |
||||
if (opts.no_keys) return; |
||||
e.preventDefault(); |
||||
sendStrMsg(str) |
||||
}); |
||||
} |
||||
|
||||
/** Bind/rebind key messages */ |
||||
function _initKeys() { |
||||
// This takes care of text characters typed
|
||||
window.addEventListener('keypress', function(evt) { |
||||
if (opts.no_keys) return; |
||||
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(); |
||||
} |
||||
|
||||
// mouse button states
|
||||
var mb1 = 0; |
||||
var mb2 = 0; |
||||
var mb3 = 0; |
||||
|
||||
/** Init the Input module */ |
||||
function init() { |
||||
_initKeys(); |
||||
|
||||
// Button presses
|
||||
$('#action-buttons button').forEach(function(s) { |
||||
s.addEventListener('click', function() { |
||||
sendBtnMsg(+this.dataset['n']); |
||||
}); |
||||
}); |
||||
|
||||
// global mouse state tracking - for motion reporting
|
||||
window.addEventListener('mousedown', function(evt) { |
||||
if (evt.button == 0) mb1 = 1; |
||||
if (evt.button == 1) mb2 = 1; |
||||
if (evt.button == 2) mb3 = 1; |
||||
}); |
||||
|
||||
window.addEventListener('mouseup', function(evt) { |
||||
if (evt.button == 0) mb1 = 0; |
||||
if (evt.button == 1) mb2 = 0; |
||||
if (evt.button == 2) mb3 = 0; |
||||
}); |
||||
} |
||||
|
||||
/** Prepare modifiers byte for mouse message */ |
||||
function packModifiersForMouse() { |
||||
return (key.isModifier('ctrl')?1:0) | |
||||
(key.isModifier('shift')?2:0) | |
||||
(key.isModifier('alt')?4:0) | |
||||
(key.isModifier('meta')?8:0); |
||||
} |
||||
|
||||
return { |
||||
/** Init the Input module */ |
||||
init: init, |
||||
|
||||
/** Send a literal string message */ |
||||
sendString: sendStrMsg, |
||||
|
||||
/** Enable alternate key modes (cursors, numpad, fn) */ |
||||
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(); |
||||
} |
||||
}, |
||||
|
||||
setMouseMode: function(click, move) { |
||||
opts.mt_click = click; |
||||
opts.mt_move = move; |
||||
}, |
||||
|
||||
// Mouse events
|
||||
onMouseMove: function (x, y) { |
||||
if (!opts.mt_move) return; |
||||
var b = mb1 ? 1 : mb2 ? 2 : mb3 ? 3 : 0; |
||||
var m = packModifiersForMouse(); |
||||
Conn.send("m" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); |
||||
}, |
||||
onMouseDown: function (x, y, b) { |
||||
if (!opts.mt_click) return; |
||||
if (b > 3 || b < 1) return; |
||||
var m = packModifiersForMouse(); |
||||
Conn.send("p" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); |
||||
// console.log("B ",b," M ",m);
|
||||
}, |
||||
onMouseUp: function (x, y, b) { |
||||
if (!opts.mt_click) return; |
||||
if (b > 3 || b < 1) return; |
||||
var m = packModifiersForMouse(); |
||||
Conn.send("r" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); |
||||
// console.log("B ",b," M ",m);
|
||||
}, |
||||
onMouseWheel: function (x, y, dir) { |
||||
if (!opts.mt_click) return; |
||||
// -1 ... btn 4 (away from user)
|
||||
// +1 ... btn 5 (towards user)
|
||||
var m = packModifiersForMouse(); |
||||
var b = (dir < 0 ? 4 : 5); |
||||
Conn.send("p" + encode2B(y) + encode2B(x) + encode2B(b) + encode2B(m)); |
||||
// console.log("B ",b," M ",m);
|
||||
}, |
||||
mouseTracksClicks: function() { |
||||
return opts.mt_click; |
||||
}, |
||||
blockKeys: function(yes) { |
||||
opts.no_keys = yes; |
||||
} |
||||
}; |
||||
})(); |
||||
|
@ -0,0 +1,378 @@ |
||||
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, // force on unless hanging: used to keep cursor visible during move
|
||||
hidden: false, // do not show (DEC opt)
|
||||
hanging: false, // cursor at column "W+1" - not visible
|
||||
}; |
||||
|
||||
var screen = []; |
||||
var blinkIval; |
||||
var cursorFlashStartIval; |
||||
|
||||
// Some non-bold Fraktur symbols are outside the contiguous block
|
||||
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("No AudioContext!", 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()); |
||||
} |
||||
|
||||
function alpha2fraktur(t) { |
||||
// 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)); |
||||
} |
||||
} |
||||
return t; |
||||
} |
||||
|
||||
/** 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 fg, bg, cn, t; |
||||
|
||||
fg = inv ? cell.bg : cell.fg; |
||||
bg = inv ? cell.fg : cell.bg; |
||||
|
||||
t = cell.t; |
||||
if (!t.length) t = ' '; |
||||
|
||||
cn = 'fg' + fg + ' bg' + bg; |
||||
if (cell.attrs & (1<<0)) cn += ' bold'; |
||||
if (cell.attrs & (1<<1)) cn += ' faint'; |
||||
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'; |
||||
t = alpha2fraktur(t); |
||||
} |
||||
if (cell.attrs & (1<<6)) cn += ' strike'; |
||||
|
||||
cell.slot.textContent = t; |
||||
cell.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 cOuter, cInner, cell, screenDiv = qs('#screen'); |
||||
|
||||
// Empty the screen node
|
||||
while (screenDiv.firstChild) screenDiv.removeChild(screenDiv.firstChild); |
||||
|
||||
screen = []; |
||||
|
||||
for(var i = 0; i < W*H; i++) { |
||||
cOuter = mk('span'); |
||||
cInner = mk('span'); |
||||
|
||||
/* Mouse tracking */ |
||||
(function() { |
||||
var x = i % W; |
||||
var y = Math.floor(i / W); |
||||
cOuter.addEventListener('mouseenter', function (evt) { |
||||
Input.onMouseMove(x, y); |
||||
}); |
||||
cOuter.addEventListener('mousedown', function (evt) { |
||||
Input.onMouseDown(x, y, evt.button+1); |
||||
}); |
||||
cOuter.addEventListener('mouseup', function (evt) { |
||||
Input.onMouseUp(x, y, evt.button+1); |
||||
}); |
||||
cOuter.addEventListener('contextmenu', function (evt) { |
||||
if (Input.mouseTracksClicks()) { |
||||
evt.preventDefault(); |
||||
} |
||||
}); |
||||
cOuter.addEventListener('mousewheel', function (evt) { |
||||
Input.onMouseWheel(x, y, evt.deltaY>0?1:-1); |
||||
return false; |
||||
}); |
||||
})(); |
||||
|
||||
/* End of line */ |
||||
if ((i > 0) && (i % W == 0)) { |
||||
screenDiv.appendChild(mk('br')); |
||||
} |
||||
/* The cell */ |
||||
cOuter.appendChild(cInner); |
||||
screenDiv.appendChild(cOuter); |
||||
|
||||
cell = { |
||||
t: ' ', |
||||
fg: 7, |
||||
bg: 0, // the colors will be replaced immediately as we receive data (user won't see this)
|
||||
attrs: 0, |
||||
elem: cOuter, |
||||
slot: cInner, |
||||
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; |
||||
if (cursor.hidden || cursor.hanging) { |
||||
cursor.a = false; |
||||
} |
||||
|
||||
if (!cursor.suppress) { |
||||
_draw(_curCell(), cursor.forceOn || cursor.a); |
||||
} |
||||
}, 500); |
||||
|
||||
/* blink attribute animation */ |
||||
setInterval(function () { |
||||
$('#screen').removeClass('blink-hide'); |
||||
setTimeout(function () { |
||||
$('#screen').addClass('blink-hide'); |
||||
}, 800); // 200 ms ON
|
||||
}, 1000); |
||||
|
||||
inited = true; |
||||
} |
||||
|
||||
// constants for decoding the update blob
|
||||
var SEQ_SET_COLOR_ATTR = 1; |
||||
var SEQ_REPEAT = 2; |
||||
var SEQ_SET_COLOR = 3; |
||||
var SEQ_SET_ATTR = 4; |
||||
|
||||
/** Parse received screen update object (leading S removed already) */ |
||||
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 attribs
|
||||
cursor.hidden = !(num & (1<<0)); // DEC opt "visible"
|
||||
cursor.hanging = !!(num & (1<<1)); |
||||
// console.log("Attributes word ",num.toString(16)+'h');
|
||||
|
||||
Input.setAlts( |
||||
!!(num & (1<<2)), // cursors alt
|
||||
!!(num & (1<<3)), // numpad alt
|
||||
!!(num & (1<<4)) // fn keys alt
|
||||
); |
||||
|
||||
var mt_click = !!(num & (1<<5)); |
||||
var mt_move = !!(num & (1<<6)); |
||||
Input.setMouseMode( |
||||
mt_click, |
||||
mt_move |
||||
); |
||||
$('#screen').toggleClass('noselect', mt_move); |
||||
|
||||
var show_buttons = !!(num & (1<<7)); |
||||
var show_config_links = !!(num & (1<<8)); |
||||
$('.x-term-conf-btn').toggleClass('hidden', !show_config_links); |
||||
$('#action-buttons').toggleClass('hidden', !show_buttons); |
||||
|
||||
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); |
||||
} |
||||
} |
||||
|
||||
/** Apply labels to buttons and screen title (leading T removed already) */ |
||||
function _load_labels(str) { |
||||
var pieces = str.split('\x01'); |
||||
qs('h1').textContent = pieces[0]; |
||||
$('#action-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) : " "; |
||||
x.style.opacity = s.length > 0 ? 1 : 0.2; |
||||
}) |
||||
} |
||||
|
||||
/** Audible beep for ASCII 7 */ |
||||
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) { |
||||
//console.log(JSON.stringify(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."); |
||||
console.log(str); |
||||
} |
||||
} |
||||
|
||||
return { |
||||
load: load, // full load (string)
|
||||
}; |
||||
})(); |
@ -0,0 +1,146 @@ |
||||
/** File upload utility */ |
||||
var TermUpl = (function() { |
||||
var lines, // array of lines without newlines
|
||||
line_i, // current line index
|
||||
fuTout, // timeout handle for line sending
|
||||
send_delay_ms, // delay between lines (ms)
|
||||
nl_str, // newline string to use
|
||||
curLine, // current line (when using fuOil)
|
||||
inline_pos; // Offset in line (for long lines)
|
||||
|
||||
// lines longer than this are split to chunks
|
||||
// sending a super-ling string through the socket is not a good idea
|
||||
var MAX_LINE_LEN = 128; |
||||
|
||||
function fuOpen() { |
||||
fuStatus("Ready..."); |
||||
Modal.show('#fu_modal', onClose); |
||||
$('#fu_form').toggleClass('busy', false); |
||||
Input.blockKeys(true); |
||||
} |
||||
|
||||
function onClose() { |
||||
console.log("Upload modal closed."); |
||||
clearTimeout(fuTout); |
||||
line_i = 0; |
||||
Input.blockKeys(false); |
||||
} |
||||
|
||||
function fuStatus(msg) { |
||||
qs('#fu_prog').textContent = msg; |
||||
} |
||||
|
||||
function fuSend() { |
||||
var v = qs('#fu_text').value; |
||||
if (!v.length) { |
||||
fuClose(); |
||||
return; |
||||
} |
||||
|
||||
lines = v.split('\n'); |
||||
line_i = 0; |
||||
inline_pos = 0; // offset in line
|
||||
send_delay_ms = qs('#fu_delay').value; |
||||
|
||||
// sanitize - 0 causes overflows
|
||||
if (send_delay_ms < 0) { |
||||
send_delay_ms = 0; |
||||
qs('#fu_delay').value = send_delay_ms; |
||||
} |
||||
|
||||
nl_str = { |
||||
'CR': '\r', |
||||
'LF': '\n', |
||||
'CRLF': '\r\n', |
||||
}[qs('#fu_crlf').value]; |
||||
|
||||
$('#fu_form').toggleClass('busy', true); |
||||
fuStatus("Starting..."); |
||||
fuSendLine(); |
||||
} |
||||
|
||||
function fuSendLine() { |
||||
if (!$('#fu_modal').hasClass('visible')) { |
||||
// Modal is closed, cancel
|
||||
return; |
||||
} |
||||
|
||||
if (!Conn.canSend()) { |
||||
// postpone
|
||||
fuTout = setTimeout(fuSendLine, 1); |
||||
return; |
||||
} |
||||
|
||||
if (inline_pos == 0) { |
||||
curLine = lines[line_i++] + nl_str; |
||||
} |
||||
|
||||
var chunk; |
||||
if ((curLine.length - inline_pos) <= MAX_LINE_LEN) { |
||||
chunk = curLine.substr(inline_pos, MAX_LINE_LEN); |
||||
inline_pos = 0; |
||||
} else { |
||||
chunk = curLine.substr(inline_pos, MAX_LINE_LEN); |
||||
inline_pos += MAX_LINE_LEN; |
||||
} |
||||
|
||||
if (!Input.sendString(chunk)) { |
||||
fuStatus("FAILED!"); |
||||
return; |
||||
} |
||||
|
||||
var all = lines.length; |
||||
|
||||
fuStatus(line_i+" / "+all+ " ("+(Math.round((line_i/all)*1000)/10)+"%)"); |
||||
|
||||
if (lines.length > line_i || inline_pos > 0) { |
||||
fuTout = setTimeout(fuSendLine, send_delay_ms); |
||||
} else { |
||||
closeWhenReady(); |
||||
} |
||||
} |
||||
|
||||
function closeWhenReady() { |
||||
if (!Conn.canSend()) { |
||||
// stuck in XOFF still, wait to process...
|
||||
fuStatus("Waiting for Tx buffer..."); |
||||
setTimeout(closeWhenReady, 100); |
||||
} else { |
||||
fuStatus("Done."); |
||||
// delay to show it
|
||||
setTimeout(function() { |
||||
fuClose(); |
||||
}, 100); |
||||
} |
||||
} |
||||
|
||||
function fuClose() { |
||||
Modal.hide('#fu_modal'); |
||||
} |
||||
|
||||
return { |
||||
init: function() { |
||||
qs('#fu_file').addEventListener('change', function (evt) { |
||||
var reader = new FileReader(); |
||||
var file = evt.target.files[0]; |
||||
console.log("Selected file type: "+file.type); |
||||
if (!file.type.match(/text\/.*|application\/(json|csv|.*xml.*|.*script.*)/)) { |
||||
// Deny load of blobs like img - can crash browser and will get corrupted anyway
|
||||
if (!confirm("This does not look like a text file: "+file.type+"\nReally load?")) { |
||||
qs('#fu_file').value = ''; |
||||
return; |
||||
} |
||||
} |
||||
reader.onload = function(e) { |
||||
var txt = e.target.result.replace(/[\r\n]+/,'\n'); |
||||
qs('#fu_text').value = txt; |
||||
}; |
||||
console.log("Loading file..."); |
||||
reader.readAsText(file); |
||||
}, false); |
||||
}, |
||||
close: fuClose, |
||||
start: fuSend, |
||||
open: fuOpen, |
||||
} |
||||
})(); |
@ -0,0 +1,161 @@ |
||||
/** 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(str) { |
||||
try { |
||||
return JSON.parse(str); |
||||
} catch(e) { |
||||
console.error(e); |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/** Create a character from ASCII code */ |
||||
function Chr(n) { |
||||
return String.fromCharCode(n); |
||||
} |
||||
|
||||
/** Decode number from 2B encoding */ |
||||
function parse2B(s, i) { |
||||
return (s.charCodeAt(i++) - 1) + (s.charCodeAt(i) - 1) * 127; |
||||
} |
||||
|
||||
/** Decode number from 3B encoding */ |
||||
function parse3B(s, i) { |
||||
return (s.charCodeAt(i) - 1) + (s.charCodeAt(i+1) - 1) * 127 + (s.charCodeAt(i+2) - 1) * 127 * 127; |
||||
} |
||||
|
||||
/** Encode using 2B encoding, returns string. */ |
||||
function encode2B(n) { |
||||
var lsb, msb; |
||||
lsb = (n % 127); |
||||
n = ((n - lsb) / 127); |
||||
lsb += 1; |
||||
msb = (n + 1); |
||||
return Chr(lsb) + Chr(msb); |
||||
} |
||||
|
||||
/** Encode using 3B encoding, returns string. */ |
||||
function encode3B(n) { |
||||
var lsb, msb, xsb; |
||||
lsb = (n % 127); |
||||
n = (n - lsb) / 127; |
||||
lsb += 1; |
||||
msb = (n % 127); |
||||
n = (n - msb) / 127; |
||||
msb += 1; |
||||
xsb = (n + 1); |
||||
return Chr(lsb) + Chr(msb) + Chr(xsb); |
||||
} |
@ -0,0 +1,163 @@ |
||||
(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 .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() { |
||||
if (_demo) { |
||||
onScan(_demo_aps, 200); |
||||
} else { |
||||
$.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 = {}); |
@ -0,0 +1,180 @@ |
||||
<?php |
||||
|
||||
return [ |
||||
'appname' => 'ESPTerm', |
||||
'appname_demo' => 'ESPTerm<sup> DEMO</sup>', |
||||
|
||||
'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', |
||||
'term_nav.paste' => 'Paste', |
||||
'term_nav.upload' => 'Upload', |
||||
'term_nav.keybd' => 'Keyboard', |
||||
'term_nav.paste_prompt' => 'Paste text to send:', |
||||
|
||||
'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.expert' => 'Expert Options', |
||||
'term.explain_initials' => ' |
||||
Those are the initial settings used after ESPTerm powers on or when the screen |
||||
reset command is received. Some options can be changed by the application via escape sequences, |
||||
those changes won\'t be saved in Flash. |
||||
', |
||||
'term.explain_expert' => ' |
||||
Those are advanced config options that usually don\'t need to be changed. |
||||
Edit them only if you know what you\'re doing.', |
||||
|
||||
'term.example' => 'Default colors preview', |
||||
|
||||
'term.reset_screen' => 'Reset screen & parser', |
||||
'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', |
||||
'term.show_config_links' => 'Show nav links', |
||||
'term.show_buttons' => 'Show buttons', |
||||
'term.loopback' => 'Local Echo', |
||||
'term.button_msgs' => 'Button codes<br>(ASCII, dec, CSV)', |
||||
|
||||
// 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' => '🔒 Password saved', |
||||
'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:', |
||||
]; |
@ -0,0 +1,14 @@ |
||||
#!/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_* \ |
||||
jssrc/term.js > js/app.js |
@ -0,0 +1,20 @@ |
||||
<nav id="menu"> |
||||
<div id="brand" tabindex=0><?= tr('appname'.(ESP_DEMO?'_demo':'')) ?></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> |
@ -0,0 +1,36 @@ |
||||
<!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 ?>;
|
||||
var _demo = <?= (int)ESP_DEMO ?>;
|
||||
<?php if($_GET['page']=='term'): ?>var _demo_screen = <?= ESP_DEMO ? DEMO_SCREEN : 0 ?>;<?php endif; ?> |
||||
<?php if($_GET['page']=='cfg_wifi'): ?>var _demo_aps = <?= ESP_DEMO ? json_encode(DEMO_APS) : '' ?>;<?php endif; ?> |
||||
</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> <span class="list"></span>
|
||||
</div> |
||||
|
||||
<?php endif; ?> |
@ -0,0 +1,13 @@ |
||||
|
||||
<?php if ($_GET['BODYCLASS'] !== 'page-term'): ?> |
||||
<div class="botpad"></div> |
||||
<?php endif; ?> |
||||
|
||||
<div class="NotifyMsg hidden" id="notif"></div> |
||||
|
||||
</div> |
||||
|
||||
</div> |
||||
|
||||
</body> |
||||
</html> |
@ -0,0 +1,69 @@ |
||||
<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> |
||||
© Ondřej Hruška, 2016-2017 |
||||
<<a href="mailto:ondra@ondrovo.com">ondra@ondrovo.com</a>> |
||||
</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 IoT 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> |
@ -0,0 +1,98 @@ |
||||
<?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"> (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"> 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> |
@ -0,0 +1,90 @@ |
||||
<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"> (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"> 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> |
@ -0,0 +1,203 @@ |
||||
<div class="Box"> |
||||
<a href="<?= e(url('reset_screen')) ?>"><?= tr('term.reset_screen') ?></a>
|
||||
</div> |
||||
|
||||
<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> <!-- |
||||
--><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> <!-- |
||||
--><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%"> |
||||
<input class="short" type="text" name="btn2" id="btn2" value="%h:btn2%"> |
||||
<input class="short" type="text" name="btn3" id="btn3" value="%h:btn3%"> |
||||
<input class="short" type="text" name="btn4" id="btn4" value="%h:btn4%"> |
||||
<input class="short" type="text" name="btn5" id="btn5" value="%h:btn5%"> |
||||
</div> |
||||
|
||||
<div class="Row buttons"> |
||||
<a class="button icn-ok" href="#" onclick="qs('#form-1').submit()"><?= tr('apply') ?></a>
|
||||
</div> |
||||
</form> |
||||
|
||||
<form class="Box fold str" action="<?= e(url('term_set')) ?>" method="GET" id='form-2'>
|
||||
<h2><?= tr('term.expert') ?></h2>
|
||||
|
||||
<div class="Row explain"> |
||||
<?= tr('term.explain_expert') ?> |
||||
</div> |
||||
|
||||
<div class="Row"> |
||||
<label for="parser_tout_ms"><?= tr('term.parser_tout_ms') ?><span class="mq-phone"> (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"> ms</span> |
||||
</div> |
||||
|
||||
<div class="Row"> |
||||
<label for="display_tout_ms"><?= tr('term.display_tout_ms') ?><span class="mq-phone"> (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"> ms</span> |
||||
</div> |
||||
|
||||
<div class="Row"> |
||||
<label for="display_cooldown_ms"><?= tr('term.display_cooldown_ms') ?><span class="mq-phone"> (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"> ms</span> |
||||
</div> |
||||
|
||||
<div class="Row"> |
||||
<label><?= tr("term.button_msgs") ?></label>
|
||||
<input class="short" type="text" name="bm1" id="bm1" value="%h:bm1%"> |
||||
<input class="short" type="text" name="bm2" id="bm2" value="%h:bm2%"> |
||||
<input class="short" type="text" name="bm3" id="bm3" value="%h:bm3%"> |
||||
<input class="short" type="text" name="bm4" id="bm4" value="%h:bm4%"> |
||||
<input class="short" type="text" name="bm5" id="bm5" value="%h:bm5%"> |
||||
</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 checkbox" > |
||||
<label><?= tr('term.show_buttons') ?></label><!--
|
||||
--><span class="box" tabindex=0 role=checkbox></span> |
||||
<input type="hidden" id="show_buttons" name="show_buttons" value="%show_buttons%"> |
||||
</div> |
||||
|
||||
<div class="Row checkbox" > |
||||
<label><?= tr('term.show_config_links') ?></label><!--
|
||||
--><span class="box" tabindex=0 role=checkbox></span> |
||||
<input type="hidden" id="show_config_links" name="show_config_links" value="%show_config_links%"> |
||||
</div> |
||||
|
||||
<div class="Row checkbox" > |
||||
<label><?= tr('term.loopback') ?></label><!--
|
||||
--><span class="box" tabindex=0 role=checkbox></span> |
||||
<input type="hidden" id="loopback" name="loopback" value="%loopback%"> |
||||
</div> |
||||
|
||||
<div class="Row buttons"> |
||||
<a class="button icn-ok" href="#" onclick="qs('#form-2').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> |
@ -0,0 +1,123 @@ |
||||
<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') ?></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> |
@ -0,0 +1,89 @@ |
||||
<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> |
@ -0,0 +1,20 @@ |
||||
<div class="Box"> |
||||
<a href="#" onclick="hpfold(1);return false">Expand all</a> | <a href="#" onclick="hpfold(0);return false">Collapse all</a> |
||||
</div> |
||||
|
||||
<?php require __DIR__ . "/help/troubleshooting.php"; ?> |
||||
<?php require __DIR__ . "/help/nomenclature.php"; ?> |
||||
<?php require __DIR__ . "/help/screen_behavior.php"; ?> |
||||
<?php require __DIR__ . "/help/input.php"; ?> |
||||
<?php require __DIR__ . "/help/charsets.php"; ?> |
||||
<?php require __DIR__ . "/help/sgr_styles.php"; ?> |
||||
<?php require __DIR__ . "/help/sgr_colors.php"; ?> |
||||
<?php require __DIR__ . "/help/cmd_cursor.php"; ?> |
||||
<?php require __DIR__ . "/help/cmd_screen.php"; ?> |
||||
<?php require __DIR__ . "/help/cmd_system.php"; ?> |
||||
|
||||
<script> |
||||
function hpfold(yes) { |
||||
$('.fold').toggleClass('expanded', !!yes); |
||||
} |
||||
</script> |
@ -0,0 +1,80 @@ |
||||
<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. |
||||
</p> |
||||
|
||||
<p> |
||||
Since ESPTerm also supports UTF-8, this feature is the most useful for applications |
||||
which can't print UTF-8 or already use alternate character sets for historical reasons. |
||||
</p> |
||||
|
||||
<h3>Supported codepages</h3> |
||||
|
||||
<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, ESPTerm specific)</li> |
||||
</ul> |
||||
|
||||
<p> |
||||
All codepages use codes 32-127, 32 being space. A character with no entry in the active codepage |
||||
stays unchanged. |
||||
</p> |
||||
|
||||
<?php |
||||
$codepages = load_esp_charsets(); |
||||
foreach($codepages as $name => $cp) { |
||||
echo "<h4>Codepage `$name`</h4>\n"; |
||||
echo '<div class="charset">'; |
||||
foreach($cp as $point) { |
||||
$dis = $point[1]==$point[2]?' class="none"' : ''; |
||||
echo "<div$dis><span>$point[0]</span><span>$point[1]</span><span>$point[2]</span></div>"; |
||||
} |
||||
echo '</div>'; |
||||
} |
||||
?> |
||||
|
||||
<h3>Switching commands</h3> |
||||
|
||||
<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). Often only G0 is used for simplicity. |
||||
</p> |
||||
|
||||
<p> |
||||
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> |
@ -0,0 +1,199 @@ |
||||
<div class="Box fold"> |
||||
<h2>Commands: Cursor Functions</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> |
||||
*Legend:* |
||||
Italic letters such as _n_ 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> |
||||
<code> |
||||
\e[<i>n</i>A \\ |
||||
\e[<i>n</i>B \\ |
||||
\e[<i>n</i>C \\ |
||||
\e[<i>n</i>D |
||||
</code> |
||||
</td> |
||||
<td>Move cursor up (`A`), down (`B`), right (`C`), left (`D`)</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<code> |
||||
\e[<i>n</i>F \\ |
||||
\e[<i>n</i>E |
||||
</code> |
||||
</td> |
||||
<td>Go _n_ lines up (`F`) or down (`E`), start of line</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<code> |
||||
\e[<i>r</i>d \\ |
||||
\e[<i>c</i>G \\ |
||||
\e[<i>r</i>;<i>c</i>H |
||||
</code> |
||||
</td> |
||||
<td> |
||||
Go to absolute position - row (`d`), column (`G`), or both (`H`). Use `\e[H` to go to 1,1. |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
`\e[6n` |
||||
</td> |
||||
<td> |
||||
Query cursor position. Sent back as `\e[<i>r</i>;<i>c</i>R`. |
||||
</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> |
||||
<code> |
||||
\e[s \\ |
||||
\e[u |
||||
</code> |
||||
</td> |
||||
<td>Save (`s`) or restore (`u`) cursor position</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<code> |
||||
\e7 \\ |
||||
\e8 |
||||
</code> |
||||
</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 _a_ through _b_ 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> |
||||
<code> |
||||
\e[?6h \\ |
||||
\e[?6l |
||||
</code> |
||||
</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> |
||||
<code> |
||||
\e[<i>n</i>S \\ |
||||
\e[<i>n</i>T |
||||
</code> |
||||
</td> |
||||
<td> |
||||
Move contents of the Scrolling Region up (`S`) or down (`T`), pad with empty |
||||
lines of the current background color. This is similar to what happens when AutoWrap |
||||
is enabled and some text is printed at the very end of the screen. |
||||
</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> |
||||
<code> |
||||
\e[<i>n</i>I \\ |
||||
\e[<i>n</i>Z |
||||
</code> |
||||
</td> |
||||
<td>Advance (`I`) or go back (`Z`) _n_ tab stops or end/start of line. ASCII _TAB_ (9) is equivalent to <code>\e[1I</code></td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<code> |
||||
\e[0g \\ |
||||
\e[3g \\ |
||||
</code> |
||||
</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> |
||||
<code> |
||||
\e[?7h \\ |
||||
\e[?7l |
||||
</code> |
||||
</td> |
||||
<td>Enable (`h`) or disable (`l`) cursor auto-wrap and screen auto-scroll</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<code> |
||||
\e[?25h \\ |
||||
\e[?25l |
||||
</code> |
||||
</td> |
||||
<td>Show (`h`) or hide (`l`) the cursor</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
</div> |
@ -0,0 +1,63 @@ |
||||
<div class="Box fold"> |
||||
<h2>Commands: Screen Functions</h2> |
||||
|
||||
<div class="Row v"> |
||||
<p> |
||||
<b>Legend:</b> |
||||
Italic letters such as _n_ 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. _m_: 0 - from cursor, 1 - to cursor, 2 - all |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
`\e[<i>m</i>K` |
||||
</td> |
||||
<td> |
||||
Erase part of line. _m_: 0 - from cursor, 1 - to cursor, 2 - all |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
`\e[<i>n</i>X`</td> |
||||
<td> |
||||
Erase _n_ characters in line. |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<code> |
||||
\e[<i>n</i>L \\ |
||||
\e[<i>n</i>M |
||||
</code> |
||||
</td> |
||||
<td> |
||||
Insert (`L`) or delete (`M`) _n_ lines. Following lines are pulled up or pushed down. |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<code> |
||||
\e[<i>n</i>@ \\ |
||||
\e[<i>n</i>P |
||||
</code> |
||||
</td> |
||||
<td> |
||||
Insert (`@`) or delete (`P`) _n_ 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> |
@ -0,0 +1,103 @@ |
||||
<div class="Box fold"> |
||||
<h2>Commands: System Functions</h2> |
||||
|
||||
<div class="Row v"> |
||||
<p> |
||||
It's possible to dynamically change the screen title text and action button labels. |
||||
Setting an empty label to a button makes it look disabled. The buttons send ASCII 1-5 when clicked. |
||||
Those changes are not retained after restart. |
||||
</p> |
||||
|
||||
<table class="ansiref w100"> |
||||
<thead><tr><th>Code</th><th>Meaning</th></tr></thead> |
||||
<tbody> |
||||
<tr> |
||||
<td>`\ec`</td> |
||||
<td> |
||||
Clear screen, reset attributes and cursor. |
||||
The screen size, title and button labels remain unchanged. |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td>`\e[5n`</td> |
||||
<td> |
||||
Query device status, ESPTerm replies with `\e[0n` "device is OK". |
||||
Can be used to check if the terminal has booted up and is ready to receive commands. |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td>_CAN_ (24)</td> |
||||
<td> |
||||
This ASCII code is not a command, but is sent by ESPTerm when it becomes ready to receive commands. |
||||
When this code is received on the UART, it means ESPTerm has restarted and is ready. Use this to detect |
||||
spontaneous restarts which require a full screen repaint. |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td>`\e]0;<i>t</i>\a`</td> |
||||
<td>Set screen title to _t_ (this is a standard OSC command)</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<code> |
||||
\e]<i>80+n</i>;<i>t</i>\a |
||||
</code> |
||||
</td> |
||||
<td> |
||||
Set label for button _n_ = 1-5 (code 81-85) to _t_ - e.g.`\e]81;Yes\a` |
||||
sets the first button text to "Yes". |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<code> |
||||
\e]<i>90+n</i>;<i>m</i>\a |
||||
</code> |
||||
</td> |
||||
<td> |
||||
Set message for button _n_ = 1-5 (code 81-85) to _m_ - e.g.`\e]94;iv\a` |
||||
sets the 3rd button to send string "iv" when pressed. |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<code> |
||||
\e[?800h \\ |
||||
\e[?800l |
||||
</code> |
||||
</td> |
||||
<td> |
||||
Show (`h`) or hide (`l`) action buttons (the blue buttons under the screen). |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<code> |
||||
\e[?801h \\ |
||||
\e[?801l |
||||
</code> |
||||
</td> |
||||
<td> |
||||
Show (`h`) or hide (`l`) menu/help links under the screen. |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td> |
||||
<code> |
||||
\e[12h \\ |
||||
\e[12l |
||||
</code> |
||||
</td> |
||||
<td> |
||||
Enable (`h`) or disable (`l`) Send-Receive Mode (SRM). |
||||
SRM is the opposite of Local Echo, meaning `\e[12h` disables and `\e[12l` enables Local Echo. |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td>`\e[8;<i>r</i>;<i>c</i>t`</td> |
||||
<td>Set screen size to _r_ rows and _c_ columns (this is a command borrowed from Xterm)</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
</div> |
@ -0,0 +1,254 @@ |
||||
|
||||
<div class="Box fold"> |
||||
<h2>User Input: Keyboard, Mouse</h2> |
||||
|
||||
<div class="Row v"> |
||||
<h3>Keyboard</h3> |
||||
|
||||
<p> |
||||
The user can input text using their keyboard, or on Android, using the on-screen keyboard which is open using |
||||
a button beneath the screen. Supported are all printable characters, as well as many control keys, such as arrows, _Ctrl+letters_ |
||||
and function keys. Sequences sent by function keys are based on VT102 and Xterm. |
||||
</p> |
||||
|
||||
<p> |
||||
The codes sent by _Home_, _End_, _F1-F4_ and cursor keys are affected by various keyboard modes (_Application Cursor Keys_, |
||||
_Application Numpad Mode_, _SS3 Fn Keys Mode_). Some can be set in the <a href="<?= url('cfg_term') ?>">Terminal Settings</a>,
|
||||
others via commands. |
||||
</p> |
||||
|
||||
<p> |
||||
Here are some examples of control key codes: |
||||
</p> |
||||
|
||||
<table> |
||||
<thead><tr><th>Key</th><th>Code</th><th>Key</th><th>Code</th></tr></thead> |
||||
<tr> |
||||
<td>Up</td> |
||||
<td>`\e[A`,~`\eOA`</td> |
||||
<td>F1</td> |
||||
<td>`\eOP`,~`\e[11\~`</td> |
||||
</tr> |
||||
<tr> |
||||
<td>Down</td> |
||||
<td>`\e[B`,~`\eOB`</td> |
||||
<td>F2</td> |
||||
<td>`\eOQ`,~`\e[12\~`</td> |
||||
</tr> |
||||
<tr> |
||||
<td>Right</td> |
||||
<td>`\e[C`,~`\eOC`</td> |
||||
<td>F3</td> |
||||
<td>`\eOR`,~`\e[13\~`</td> |
||||
</tr> |
||||
<tr> |
||||
<td>Left</td> |
||||
<td>`\e[D`,~`\eOD`</td> |
||||
<td>F4</td> |
||||
<td>`\eOS`,~`\e[14\~`</td> |
||||
</tr> |
||||
<tr> |
||||
<td>Home</td> |
||||
<td>`\eOH`,~`\e[H`,~`\e[1\~`</td> |
||||
<td>F5</td> |
||||
<td>`\e[15~`</td> |
||||
</tr> |
||||
<tr> |
||||
<td>End</td> |
||||
<td>`\eOF`,~`\e[F`,~`\e[4\~`</td> |
||||
<td>F6</td> |
||||
<td>`\e[17\~`</td> |
||||
</tr> |
||||
<tr> |
||||
<td>Insert</td> |
||||
<td>`\e[2\~`</td> |
||||
<td>F7</td> |
||||
<td>`\e[18\~`</td> |
||||
</tr> |
||||
<tr> |
||||
<td>Delete</td> |
||||
<td>`\e[3\~`</td> |
||||
<td>F8</td> |
||||
<td>`\e[19\~`</td> |
||||
</tr> |
||||
<tr> |
||||
<td>Page Up</td> |
||||
<td>`\e[5\~`</td> |
||||
<td>F9</td> |
||||
<td>`\e[20\~`</td> |
||||
</tr> |
||||
<tr> |
||||
<td>Page Down</td> |
||||
<td>`\e[6\~`</td> |
||||
<td>F10</td> |
||||
<td>`\e[21\~`</td> |
||||
</tr> |
||||
<tr> |
||||
<td>Enter</td> |
||||
<td>`\r` (13)</td> |
||||
<td>F11</td> |
||||
<td>`\e[23\~`</td> |
||||
</tr> |
||||
<tr> |
||||
<td>Ctrl+Enter</td> |
||||
<td>`\n` (10)</td> |
||||
<td>F12</td> |
||||
<td>`\e[24\~`</td> |
||||
</tr> |
||||
<tr> |
||||
<td>Tab</td> |
||||
<td>`\t` (9)</td> |
||||
<td>ESC</td> |
||||
<td>`\e` (27)</td> |
||||
</tr> |
||||
<tr> |
||||
<td>Backspace</td> |
||||
<td>`\b` (8)</td> |
||||
<td>Ctrl+A..Z</td> |
||||
<td>ASCII 1-26</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 implements standard mouse tracking modes based on Xterm. Mouse tracking can be used to implement |
||||
powerful user interactions such as on-screen buttons, draggable sliders or dials, menus etc. ESPTerm's |
||||
mouse tracking was tested using VTTest and should be compatible with all terminal applications |
||||
that request mouse tracking. |
||||
</p> |
||||
|
||||
<p> |
||||
Mouse can be tracked in different ways; some are easier to parse, others more powerful. The coordinates |
||||
can also be encoded in different ways. All mouse tracking options are set using option commands: |
||||
`CSI ? _n_ h` to enable, `CSI ? _n_ l` to disable option _n_. |
||||
</p> |
||||
|
||||
<h4>Mouse Tracking Modes</h4> |
||||
|
||||
<p> |
||||
All tracking modes produce three numbers which are then encoded and send to the application. |
||||
First is the _event number_ N, then the _X and Y coordinates_, 1-based. |
||||
</p> |
||||
|
||||
<p> |
||||
Mouse buttons are numbered: 1=left, 2=middle, 3=right. |
||||
Wheel works as two buttons (4 and 5) which generate only press events (no release). |
||||
</p> |
||||
|
||||
<div class="tscroll"> |
||||
<table class="nomen"> |
||||
<thead><tr><th>Option</th><th>Name</th><th>Description</th></tr></thead> |
||||
<tr> |
||||
<td>`9`</td> |
||||
<td>*X10~mode*</td> |
||||
<td> |
||||
This is the most basic tracking mode, in which <b>only button presses</b> are reported. |
||||
N = button - 1: (0 left, 1 middle, 2 right, 3, 4 wheel). |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td>`1000`</td> |
||||
<td>*Normal~mode*</td> |
||||
<td> |
||||
In Normal mode, both button presses and releases are reported. |
||||
The lower two bits of N indicate the button pressed: |
||||
`00b` (0) left, `01b` (1) middle, `10b` (2) right, `11b` (3) button release. |
||||
Wheel buttons are reported as 0 and 1 with added 64 (e.g. 64 and 65). |
||||
Normal mode also supports tracking of modifier keys, which are added to N as bit masks: |
||||
4=_Shift_, 8=_Meta/Alt_, 16=_Control/Cmd_. Example: middle button with _Shift_ = 1 + 4 = `101b` (5). |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td>`1002`</td> |
||||
<td>*Button-Event tracking*</td> |
||||
<td> |
||||
This is similar to Normal mode (`1000`), but mouse motion with a button held is also reported. |
||||
A motion event is generated when the mouse cursor moves between screen character cells. |
||||
A motion event has the same N as a press event, but 32 is added. |
||||
For example, drag-drop event with the middle button will produce N = 1 (press), 33 (dragging) and 3 (release). |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td>`1003`</td> |
||||
<td>*Any-Event tracking*</td> |
||||
<td> |
||||
This mode is almost identical to Button Event tracking (1002), but motion events |
||||
are sent even when no mouse buttons are held. This could be used to draw on-screen mouse cursor, for example. |
||||
Motion events with no buttons will use N = 32 + _11b_ (35). |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td>`1004`</td> |
||||
<td>*Focus~tracking*</td> |
||||
<td> |
||||
Focus tracking is a separate function from the other mouse tracking modes, therefore they can be enabled together. |
||||
Focus tracking reports when the terminal window (in Xterm) gets or loses focus, or in ESPTerm's case, when any |
||||
user is connected. This can be used to pause/resume a game or on-screen animations. |
||||
Focus tracking mode sends `CSI I` when the terminal receives, and `CSI O` when it loses focus. |
||||
</td> |
||||
</tr> |
||||
</table> |
||||
</div> |
||||
|
||||
<h4>Mouse Report Encoding</h4> |
||||
|
||||
<p> |
||||
The following encoding schemes can be used with any of the tracking modes (except Focus tracking, which is not affected). |
||||
</p> |
||||
|
||||
<div class="tscroll"> |
||||
<table class="nomen"> |
||||
<thead><tr><th>Option</th><th>Name</th><th>Description</th></tr></thead> |
||||
<tr> |
||||
<td>--</td> |
||||
<td>*Normal~encoding*</td> |
||||
<td> |
||||
This is the default encoding scheme used when no other option is selected. |
||||
In this mode, a mouse report has the format `CSI M _n_ _x_ _y_`, |
||||
where _n_, _x_ and _y_ are characters with ASCII value = 32 (space) + the respective number, e.g. |
||||
0 becomes 32 (space), 1 becomes 33 (!). The reason for adding 32 is to avoid producing control characters. |
||||
Example: `\e[M !!` - left button press at coordinates 1,1 when using X10 mode. |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td>`1005`</td> |
||||
<td>*UTF-8~encoding*</td> |
||||
<td> |
||||
This scheme should encode each of the numbers as a UTF-8 code point, expanding the maximum possible value. |
||||
Since ESPTerm's screen size is limited and this has no practical benefit, this serves simply as an alias |
||||
to the normal scheme. |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td>`1006`</td> |
||||
<td>*SGR~encoding*</td> |
||||
<td> |
||||
In SGR encoding, the response looks like a SGR sequence with the three numbers as semicolon-separated |
||||
ASCII values. In this case 32 is not added like in the Normal and UTF-8 schemes, because |
||||
it would serve nor purpose here. Also, button release is not reported as 11b, |
||||
but using the normal button code while changing the final SGR character: `M` for button press |
||||
and `m` for button release. Example: `\e[2;80;24m` - the right button was released |
||||
at row 80, column 24. |
||||
</td> |
||||
</tr> |
||||
<tr> |
||||
<td>`1015`</td> |
||||
<td>*URXVT~encoding*</td> |
||||
<td> |
||||
This is similar to SGR encoding, but the final character is always `M` and the numbers are |
||||
like in the Normal scheme, with 32 added. This scheme has no real advantage over the previous schemes and |
||||
was added solely for completeness. |
||||
</td> |
||||
</tr> |
||||
</table> |
||||
</div> |
||||
</div> |
||||
</div> |
@ -0,0 +1,84 @@ |
||||
<div class="Box fold"> |
||||
<h2>Basic Intro & Nomenclature</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 Xterm. |
||||
All commonly used attributes and commands are supported. |
||||
ESPTerm is capable of displaying ncurses applications such as _Midnight Commander_ using _agetty_. |
||||
</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). |
||||
</p> |
||||
|
||||
<p> |
||||
Escape sequences start with the control character _ESC_ (27), |
||||
followed by any number of ASCII characters forming the body of the command. |
||||
</p> |
||||
|
||||
<h3>Nomenclature & Command Types</h3> |
||||
|
||||
<p> |
||||
Examples on this help page use the following symbols for special characters and command types:\\ |
||||
(spaces are for clarity only, _DO NOT_ include them in the commands!) |
||||
</p> |
||||
|
||||
<div class="tscroll"> |
||||
<table class="nomen"> |
||||
<thead><tr><th>Name</th><th>Symbol</th><th>ASCII</th><th>C string</th><th>Function</th></tr></thead> |
||||
<tbody> |
||||
<tr> |
||||
<td>*ESC*</td> |
||||
<td>`\e`</td> |
||||
<td>`ESC` (27)</td> |
||||
<td>`"\e"`, `"\x1b"`, `"\033"`</td> |
||||
<td>Introduces an escape sequence. _(Note: `\e` is a GCC extension)_</td> |
||||
</tr> |
||||
<tr> |
||||
<td>*Bell*</td> |
||||
<td>`\a`</td> |
||||
<td>`BEL`~(7)</td> |
||||
<td>`"\a"`, `"\x7"`, `"\07"`</td> |
||||
<td>Audible beep</td> |
||||
</tr> |
||||
<tr> |
||||
<td>*String Terminator*</td> |
||||
<td>`ST`</td> |
||||
<td>`ESC \`~(27~92)<br>_or_~`\a`~(7)</td> |
||||
<td>`"\x1b\\"`, `"\a"`</td> |
||||
<td>Terminates a string command (`\a` can be used as an alternative)</td> |
||||
</tr> |
||||
<tr> |
||||
<td>*Control Sequence Introducer*</td> |
||||
<td>`CSI`</td> |
||||
<td>`ESC [`</td> |
||||
<td>`"\x1b["`</td> |
||||
<td>Starts a CSI command. Examples: `\e[?7;10h`, `\e[2J`</td> |
||||
</tr> |
||||
<tr> |
||||
<td>*Operating System Command*</td> |
||||
<td>`OSC`</td> |
||||
<td>`ESC ]`</td> |
||||
<td>`"\x1b]"`</td> |
||||
<td>Starts an OSC command. Is followed by a command string terminated by `ST`. Example: `\e]0;My Screen Title\a`</td> |
||||
</tr> |
||||
<tr> |
||||
<td>*Select Graphic Rendition*</td> |
||||
<td>`SGR`</td> |
||||
<td>`CSI <i>n</i>;<i>n</i>;<i>n</i>m`</td> |
||||
<td>`"\x1b[1;2;3m"`</td> |
||||
<td>Set text attributes, like color or style. 0 to 10 numbers can be used, `\e[m` is treated as `\e[0m`</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
|
||||
<p>There are also some other commands that don't follow the CSI, SGR or OSC pattern, such as `\e7` or |
||||
`\e#8`. A list of the most important escape sequences is presented in the following sections.</p> |
||||
</div> |
||||
</div> |
@ -0,0 +1,17 @@ |
||||
<div class="Box fold"> |
||||
<h2>Screen Behavior & Refreshing</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> |
@ -0,0 +1,65 @@ |
||||
|
||||
<div class="Box fold theme-0"> |
||||
<h2>Commands: Color SGR</h2> |
||||
|
||||
<div class="Row v"> |
||||
<p> |
||||
Colors are set using SGR commands (like `\e[10;20;30m`). 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> |
@ -0,0 +1,26 @@ |
||||
<div class="Box fold"> |
||||
<h2>Commands: Style SGR</h2> |
||||
|
||||
<div class="Row v"> |
||||
<p> |
||||
All text attributes are set using SGR commands like `\e[10;20;30m`, with up to 10 numbers separated by semicolons. |
||||
To restore all attributes to their default states, use SGR 0: `\e[0m` or `\e[m`. |
||||
</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> |
@ -0,0 +1,33 @@ |
||||
<div class="Box fold"> |
||||
<h2>Tips & Troubleshooting</h2> |
||||
|
||||
<div class="Row v"> |
||||
<ul> |
||||
<li>*Communication UART (Rx, Tx)* can be configured in the <a href="<?= url('cfg_system') ?>">System Settings</a>.
|
||||
|
||||
<li>*Boot log and debug messages* are available on pin *GPIO2* (P2) at 115200\,baud, 1 stop bit, no parity. |
||||
Those messages may be disabled through compile flags. |
||||
|
||||
<li>*Loopback test*: 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>There is very little RAM available to the webserver, and it can support at most 4 connections at the same time. |
||||
Each terminal session (open window with the terminal screen) uses one persistent connection for screen updates. |
||||
*Avoid leaving unused windows open*, or either the RAM or connections may be exhausted. |
||||
|
||||
<li>*For best performance*, use the module in Client mode (connected to external network) and minimize the number |
||||
of simultaneous connections. Enabling AP consumes extra RAM because the DHCP server and Captive Portal |
||||
DNS server are started. |
||||
|
||||
<li>In AP mode, *check that the WiFi channel used is clear*; 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> |
@ -0,0 +1,93 @@ |
||||
<script> |
||||
// Workaround for badly loaded page |
||||
setTimeout(function() { |
||||
if (typeof termInit == 'undefined' || typeof $ == 'undefined') { |
||||
console.error("Page load failed, refreshing…"); |
||||
location.reload(true); |
||||
} |
||||
}, 3000); |
||||
</script> |
||||
|
||||
<div class="Modal light hidden" id="fu_modal"> |
||||
<div id="fu_form" class="Dialog"> |
||||
<div class="fu-content"> |
||||
<h2>Text Upload</h2> |
||||
<p> |
||||
<label for="fu_file">Load a text file:</label> |
||||
<input type="file" id="fu_file" accept="text/*" /><br> |
||||
<textarea id="fu_text"></textarea> |
||||
</p> |
||||
<p> |
||||
<label for="fu_crlf">Line Endings:</label> |
||||
<select id="fu_crlf"> |
||||
<option value="CR">CR (Enter key)</option> |
||||
<option value="CRLF">CR LF (Windows)</option> |
||||
<option value="LF">LF (Linux)</option> |
||||
</select> |
||||
</p> |
||||
<p> |
||||
<label for="fu_delay">Line Delay (ms):</label> |
||||
<input id="fu_delay" type="number" value=1 min=0> |
||||
</p> |
||||
</div> |
||||
<div class="fu-buttons"> |
||||
<button onclick="TermUpl.start()" class="icn-ok x-fu-go">Start</button> |
||||
<button onclick="TermUpl.close()" class="icn-cancel x-fu-cancel">Cancel</button> |
||||
<i class="fu-prog-box">Upload: <span id="fu_prog"></span></i> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
<h1><!-- Screen title gets loaded here by JS --></h1> |
||||
|
||||
<div id="term-wrap"> |
||||
<div id="screen" class="theme-%theme%"></div> |
||||
|
||||
<div id="action-buttons"> |
||||
<button data-n="1"></button><!-- |
||||
--><button data-n="2"></button><!-- |
||||
--><button data-n="3"></button><!-- |
||||
--><button data-n="4"></button><!-- |
||||
--><button data-n="5"></button> |
||||
</div> |
||||
</div> |
||||
|
||||
<textarea id="softkb-input" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea> |
||||
|
||||
<nav id="term-nav"> |
||||
<a href="#" onclick="kbOpen(true);return false" class="mq-tablet-max"><i class="icn-keyboard"></i><span><?= tr('term_nav.keybd') ?></span></a><!--
|
||||
--><a href="#" onclick="TermUpl.open();return false"><i class="icn-download"></i><span><?= tr('term_nav.upload') ?></span></a><!--
|
||||
--><a href="<?= url('cfg_term') ?>" class="x-term-conf-btn"><i class="icn-configure"></i><span><?= tr('term_nav.config') ?></span></a><!--
|
||||
--><a href="<?= url('cfg_wifi') ?>" class="x-term-conf-btn"><i class="icn-wifi"></i><span><?= tr('term_nav.wifi') ?></span></a><!--
|
||||
--><a href="<?= url('help') ?>" class="x-term-conf-btn"><i class="icn-help"></i><span><?= tr('term_nav.help') ?></span></a><!--
|
||||
--><a href="<?= url('about') ?>" class="x-term-conf-btn"><i class="icn-about"></i><span><?= tr('term_nav.about') ?></span></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 3s…"); |
||||
setTimeout(function() { |
||||
location.reload(true); |
||||
}, 3000); |
||||
} |
||||
|
||||
function kbOpen(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
@ -0,0 +1,17 @@ |
||||
|
||||
// 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); |
@ -0,0 +1,439 @@ |
||||
/* 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: "DejaVu Sans Mono", "Inconsolata", 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; |
||||
} |
@ -0,0 +1,55 @@ |
||||
// 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; |
||||
} |
@ -0,0 +1,57 @@ |
||||
@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; |
||||
|
||||
$c-modal-bg: #242426; |
||||
|
||||
$screen-stack: "DejaVu Sans Mono", "Liberation Mono", "Inconsolata", monospace; |
||||
|
||||
@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; } |
||||
} |
@ -0,0 +1,58 @@ |
||||
@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); |
||||
//} |
@ -0,0 +1,58 @@ |
||||
|
||||
// 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)); |
||||
} |
@ -0,0 +1,82 @@ |
||||
@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"; |
@ -0,0 +1,204 @@ |
||||
// 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; |
||||
line-height: 1.35em; |
||||
|
||||
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; |
||||
} |
||||
} |
@ -0,0 +1,13 @@ |
||||
@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'; |
@ -0,0 +1,52 @@ |
||||
|
||||
// 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; |
||||
} |
||||
} |
@ -0,0 +1,41 @@ |
||||
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; |
||||
} |
@ -0,0 +1,179 @@ |
||||
.botpad { |
||||
display:block; |
||||
height: 5em; |
||||
} |
||||
|
||||
.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; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,60 @@ |
||||
#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; |
||||
} |
@ -0,0 +1,9 @@ |
||||
@import "base"; |
||||
|
||||
@import "outer-wrap"; |
||||
@import "menu"; |
||||
@import "content"; |
||||
@import "loader"; |
||||
|
||||
@import "box"; |
||||
@import "modal"; |
@ -0,0 +1,18 @@ |
||||
// 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; |
||||
} |
||||
} |
@ -0,0 +1,111 @@ |
||||
#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); } |
||||
} |
||||
} |
@ -0,0 +1,95 @@ |
||||
.Modal { |
||||
z-index: 100; |
||||
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 } |
||||
|
||||
&.light { |
||||
background: rgba(black, .25); |
||||
} |
||||
|
||||
@include media($phone) { |
||||
flex-direction: column; |
||||
justify-content: flex-start; |
||||
overflow-y: auto; |
||||
.Dialog { |
||||
margin-top: dist(-1) !important; |
||||
flex-basis: unset; |
||||
flex-shrink: 0; |
||||
} |
||||
} |
||||
} |
||||
|
||||
.Dialog { |
||||
margin: dist(-1); |
||||
padding: dist(0); |
||||
overflow: hidden; |
||||
|
||||
//max-width: 100%; |
||||
//max-height: 100%; |
||||
flex-basis: 35rem; |
||||
//min-height: 15rem; |
||||
|
||||
background: $c-modal-bg;//#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 } |
||||
} |
@ -0,0 +1,22 @@ |
||||
/* 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; |
||||
} |
||||
} |
@ -0,0 +1,411 @@ |
||||
// 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."; |
||||
} |
@ -0,0 +1,87 @@ |
||||
// 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"; |
@ -0,0 +1,26 @@ |
||||
@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...); |
||||
} |
@ -0,0 +1,48 @@ |
||||
@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; |
||||
} |
@ -0,0 +1,25 @@ |
||||
@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...); |
||||
} |
@ -0,0 +1,25 @@ |
||||
@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...); |
||||
} |
@ -0,0 +1,64 @@ |
||||
@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 59–62) 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; |
@ -0,0 +1,25 @@ |
||||
@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; |
||||
} |
||||
} |
@ -0,0 +1,30 @@ |
||||
@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; |
||||
} |
@ -0,0 +1,31 @@ |
||||
@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; |
@ -0,0 +1,27 @@ |
||||
/// 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"; |
||||
} |
||||
} |
@ -0,0 +1,26 @@ |
||||
@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...); |
||||
} |
@ -0,0 +1,26 @@ |
||||
@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...); |
||||
} |
@ -0,0 +1,48 @@ |
||||
@charset "UTF-8"; |
||||
|
||||
/// Provides a quick method for setting an element’s 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; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,66 @@ |
||||
@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; |
||||
} |
@ -0,0 +1,25 @@ |
||||
@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; |
||||
} |
||||
} |
@ -0,0 +1,51 @@ |
||||
@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."; |
||||
} |
||||
} |
@ -0,0 +1,113 @@ |
||||
@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); |
@ -0,0 +1,34 @@ |
||||
@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); |
@ -0,0 +1,63 @@ |
||||
@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; |
||||
} |
||||
} |
@ -0,0 +1,29 @@ |
||||
@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; |
||||
} |
||||
} |
@ -0,0 +1,43 @@ |
||||
// 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); |
||||
} |
@ -0,0 +1,3 @@ |
||||
@mixin appearance($value) { |
||||
@include prefixer(appearance, $value, webkit moz ms o spec); |
||||
} |
@ -0,0 +1,3 @@ |
||||
@mixin backface-visibility($visibility) { |
||||
@include prefixer(backface-visibility, $visibility, webkit spec); |
||||
} |
@ -0,0 +1,42 @@ |
||||
//************************************************************************// |
||||
// 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; |
||||
} |
@ -0,0 +1,55 @@ |
||||
//************************************************************************// |
||||
// Background property for adding multiple backgrounds using shorthand |
||||
// notation. |
||||
//************************************************************************// |
||||
|
||||
@mixin background($backgrounds...) { |
||||
$webkit-backgrounds: (); |
||||
$spec-backgrounds: (); |
||||
|
||||
@each $background in $backgrounds { |
||||
$webkit-background: (); |
||||
$spec-background: (); |
||||
$background-type: type-of($background); |
||||
|
||||
@if $background-type == string or $background-type == list { |
||||
$background-str: if($background-type == list, nth($background, 1), $background); |
||||
|
||||
$url-str: str-slice($background-str, 1, 3); |
||||
$gradient-type: str-slice($background-str, 1, 6); |
||||
|
||||
@if $url-str == "url" { |
||||
$webkit-background: $background; |
||||
$spec-background: $background; |
||||
} |
||||
|
||||
@else if $gradient-type == "linear" { |
||||
$gradients: _linear-gradient-parser("#{$background}"); |
||||
$webkit-background: map-get($gradients, webkit-image); |
||||
$spec-background: map-get($gradients, spec-image); |
||||
} |
||||
|
||||
@else if $gradient-type == "radial" { |
||||
$gradients: _radial-gradient-parser("#{$background}"); |
||||
$webkit-background: map-get($gradients, webkit-image); |
||||
$spec-background: map-get($gradients, spec-image); |
||||
} |
||||
|
||||
@else { |
||||
$webkit-background: $background; |
||||
$spec-background: $background; |
||||
} |
||||
} |
||||
|
||||
@else { |
||||
$webkit-background: $background; |
||||
$spec-background: $background; |
||||
} |
||||
|
||||
$webkit-backgrounds: append($webkit-backgrounds, $webkit-background, comma); |
||||
$spec-backgrounds: append($spec-backgrounds, $spec-background, comma); |
||||
} |
||||
|
||||
background: $webkit-backgrounds; |
||||
background: $spec-backgrounds; |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue