commit
4a032ee3b5
@ -1,3 +1,7 @@ |
|||||||
#!/bin/bash |
#!/bin/bash |
||||||
|
|
||||||
export FRONT_END_HASH=$(git rev-parse --short HEAD) |
export FRONT_END_HASH=$(git rev-parse --short HEAD) |
||||||
|
|
||||||
|
if [ -z "$ESP_LANG" ]; then |
||||||
|
export ESP_LANG=en |
||||||
|
fi |
||||||
|
@ -1,22 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
/* This script is run on demand to generate JS version of tr() */ |
|
||||||
|
|
||||||
require_once __DIR__ . '/base.php'; |
|
||||||
|
|
||||||
$selected = [ |
|
||||||
'wifi.connected_ip_is', |
|
||||||
'wifi.not_conn', |
|
||||||
'wifi.enter_passwd', |
|
||||||
]; |
|
||||||
|
|
||||||
$out = []; |
|
||||||
foreach ($selected as $key) { |
|
||||||
$out[$key] = $_messages[$key]; |
|
||||||
} |
|
||||||
|
|
||||||
file_put_contents(__DIR__. '/js/lang.js', |
|
||||||
"// Generated from PHP locale file\n" . |
|
||||||
'let _tr = ' . json_encode($out, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE) . ";\n\n" . |
|
||||||
"module.exports = function tr (key) { return _tr[key] || '?' + key + '?' }\n" |
|
||||||
); |
|
@ -1,8 +1,5 @@ |
|||||||
// Generated from PHP locale file
|
let data = require('locale-data') |
||||||
let _tr = { |
|
||||||
"wifi.connected_ip_is": "Connected, IP is ", |
|
||||||
"wifi.not_conn": "Not connected.", |
|
||||||
"wifi.enter_passwd": "Enter password for \":ssid:\"" |
|
||||||
}; |
|
||||||
|
|
||||||
module.exports = function tr (key) { return _tr[key] || '?' + key + '?' } |
module.exports = function localize (key) { |
||||||
|
return data[key] || `?${key}?` |
||||||
|
} |
||||||
|
@ -0,0 +1,118 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2010 Tim Baumann |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in |
||||||
|
* all copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
* THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// Extracted from ColorTriangle and
|
||||||
|
// Converted to ES6 by MightyPork (2017)
|
||||||
|
|
||||||
|
/******************* |
||||||
|
* Color conversion * |
||||||
|
*******************/ |
||||||
|
|
||||||
|
const M = Math |
||||||
|
const TAU = 2 * M.PI |
||||||
|
|
||||||
|
exports.hue2rgb = function (v1, v2, h) { |
||||||
|
if (h < 0) h += 1 |
||||||
|
if (h > 1) h -= 1 |
||||||
|
|
||||||
|
if ((6 * h) < 1) return v1 + (v2 - v1) * 6 * h |
||||||
|
if ((2 * h) < 1) return v2 |
||||||
|
if ((3 * h) < 2) return v1 + (v2 - v1) * ((2 / 3) - h) * 6 |
||||||
|
return v1 |
||||||
|
} |
||||||
|
|
||||||
|
exports.hsl2rgb = function (h, s, l) { |
||||||
|
h /= TAU |
||||||
|
let r, g, b |
||||||
|
|
||||||
|
if (s === 0) { |
||||||
|
r = g = b = l |
||||||
|
} else { |
||||||
|
let var_1, var_2 |
||||||
|
|
||||||
|
if (l < 0.5) var_2 = l * (1 + s) |
||||||
|
else var_2 = (l + s) - (s * l) |
||||||
|
|
||||||
|
var_1 = 2 * l - var_2 |
||||||
|
|
||||||
|
r = exports.hue2rgb(var_1, var_2, h + (1 / 3)) |
||||||
|
g = exports.hue2rgb(var_1, var_2, h) |
||||||
|
b = exports.hue2rgb(var_1, var_2, h - (1 / 3)) |
||||||
|
} |
||||||
|
return [r, g, b] |
||||||
|
} |
||||||
|
|
||||||
|
exports.rgb2hsl = function (r, g, b) { |
||||||
|
const min = M.min(r, g, b) |
||||||
|
const max = M.max(r, g, b) |
||||||
|
const d = max - min // delta
|
||||||
|
|
||||||
|
let h, s, l |
||||||
|
|
||||||
|
l = (max + min) / 2 |
||||||
|
|
||||||
|
if (d === 0) { |
||||||
|
// gray
|
||||||
|
h = s = 0 // HSL results from 0 to 1
|
||||||
|
} else { |
||||||
|
// chroma
|
||||||
|
if (l < 0.5) s = d / (max + min) |
||||||
|
else s = d / (2 - max - min) |
||||||
|
|
||||||
|
const d_r = (((max - r) / 6) + (d / 2)) / d |
||||||
|
const d_g = (((max - g) / 6) + (d / 2)) / d |
||||||
|
const d_b = (((max - b) / 6) + (d / 2)) / d // deltas
|
||||||
|
|
||||||
|
if (r === max) h = d_b - d_g |
||||||
|
else if (g === max) h = (1 / 3) + d_r - d_b |
||||||
|
else if (b === max) h = (2 / 3) + d_g - d_r |
||||||
|
|
||||||
|
if (h < 0) h += 1 |
||||||
|
else if (h > 1) h -= 1 |
||||||
|
} |
||||||
|
h *= TAU |
||||||
|
return [h, s, l] |
||||||
|
} |
||||||
|
|
||||||
|
exports.hex2rgb = function (hex) { |
||||||
|
const groups = hex.match(/^#([\da-f]{3,6})$/i) |
||||||
|
if (groups) { |
||||||
|
hex = groups[1] |
||||||
|
const bytes = hex.length / 3 |
||||||
|
const max = (16 ** bytes) - 1 |
||||||
|
return [0, 1, 2].map(x => parseInt(hex.slice(x * bytes, (x + 1) * bytes), 16) / max) |
||||||
|
} |
||||||
|
return [0, 0, 0] |
||||||
|
} |
||||||
|
|
||||||
|
function pad (n) { |
||||||
|
return `00${n}`.substr(-2) |
||||||
|
} |
||||||
|
|
||||||
|
exports.rgb255ToHex = function (r, g, b) { |
||||||
|
return '#' + [r, g, b].map(x => pad(x.toString(16))).join('') |
||||||
|
} |
||||||
|
|
||||||
|
exports.rgb2hex = function (r, g, b) { |
||||||
|
return '#' + [r, g, b].map(x => pad(Math.round(x * 255).toString(16))).join('') |
||||||
|
} |
@ -0,0 +1,572 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2010 Tim Baumann |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in |
||||||
|
* all copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
* THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
// NOTE: Converted to ES6 by MightyPork (2017)
|
||||||
|
// Modified for ESPTerm
|
||||||
|
|
||||||
|
const EventEmitter = require('events') |
||||||
|
const { |
||||||
|
rgb2hex, |
||||||
|
hex2rgb, |
||||||
|
hsl2rgb, |
||||||
|
rgb2hsl |
||||||
|
} = require('./color_utils') |
||||||
|
|
||||||
|
const win = window |
||||||
|
const doc = document |
||||||
|
const M = Math |
||||||
|
const TAU = 2 * M.PI |
||||||
|
|
||||||
|
function times (i, fn) { |
||||||
|
for (let j = 0; j < i; j++) { |
||||||
|
fn(j) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function each (obj, fn) { |
||||||
|
if (obj.length) { |
||||||
|
times(obj.length, function (i) { |
||||||
|
fn(obj[i], i) |
||||||
|
}) |
||||||
|
} else { |
||||||
|
for (let key in obj) { |
||||||
|
if (obj.hasOwnProperty(key)) { |
||||||
|
fn(obj[key], key) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = class ColorTriangle extends EventEmitter { |
||||||
|
/**************** |
||||||
|
* ColorTriangle * |
||||||
|
****************/ |
||||||
|
|
||||||
|
// Constructor function:
|
||||||
|
constructor (color, options) { |
||||||
|
super() |
||||||
|
|
||||||
|
this.options = { |
||||||
|
size: 150, |
||||||
|
padding: 8, |
||||||
|
triangleSize: 0.8, |
||||||
|
wheelPointerColor1: '#444', |
||||||
|
wheelPointerColor2: '#eee', |
||||||
|
trianglePointerSize: 16, |
||||||
|
// wheelPointerSize: 16,
|
||||||
|
trianglePointerColor1: '#eee', |
||||||
|
trianglePointerColor2: '#444', |
||||||
|
background: 'transparent' |
||||||
|
} |
||||||
|
|
||||||
|
this.pixelRatio = window.devicePixelRatio |
||||||
|
|
||||||
|
this.setOptions(options) |
||||||
|
this.calculateProperties() |
||||||
|
|
||||||
|
this.createContainer() |
||||||
|
this.createTriangle() |
||||||
|
this.createWheel() |
||||||
|
this.createWheelPointer() |
||||||
|
this.createTrianglePointer() |
||||||
|
this.attachEvents() |
||||||
|
|
||||||
|
color = color || '#f00' |
||||||
|
if (typeof color == 'string') { |
||||||
|
this.setHEX(color) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
calculateProperties () { |
||||||
|
let opts = this.options |
||||||
|
|
||||||
|
this.padding = opts.padding |
||||||
|
this.innerSize = opts.size - opts.padding * 2 |
||||||
|
this.triangleSize = opts.triangleSize * this.innerSize |
||||||
|
this.wheelThickness = (this.innerSize - this.triangleSize) / 2 |
||||||
|
this.wheelPointerSize = opts.wheelPointerSize || this.wheelThickness |
||||||
|
|
||||||
|
this.wheelRadius = (this.innerSize) / 2 |
||||||
|
this.triangleRadius = (this.triangleSize) / 2 |
||||||
|
this.triangleSideLength = M.sqrt(3) * this.triangleRadius |
||||||
|
} |
||||||
|
|
||||||
|
calculatePositions () { |
||||||
|
const r = this.triangleRadius |
||||||
|
const hue = this.hue |
||||||
|
const third = TAU / 3 |
||||||
|
const s = this.saturation |
||||||
|
const l = this.lightness |
||||||
|
|
||||||
|
// Colored point
|
||||||
|
const hx = this.hx = M.cos(hue) * r |
||||||
|
const hy = this.hy = -M.sin(hue) * r |
||||||
|
// Black point
|
||||||
|
const sx = this.sx = M.cos(hue - third) * r |
||||||
|
const sy = this.sy = -M.sin(hue - third) * r |
||||||
|
// White point
|
||||||
|
const vx = this.vx = M.cos(hue + third) * r |
||||||
|
const vy = this.vy = -M.sin(hue + third) * r |
||||||
|
// Current point
|
||||||
|
const mx = (sx + vx) / 2 |
||||||
|
const my = (sy + vy) / 2 |
||||||
|
const a = (1 - 2 * M.abs(l - 0.5)) * s |
||||||
|
this.x = sx + (vx - sx) * l + (hx - mx) * a |
||||||
|
this.y = sy + (vy - sy) * l + (hy - my) * a |
||||||
|
} |
||||||
|
|
||||||
|
createContainer () { |
||||||
|
let c = this.container = doc.createElement('div') |
||||||
|
c.className = 'color-triangle' |
||||||
|
|
||||||
|
c.style.display = 'block' |
||||||
|
c.style.padding = `${this.padding}px` |
||||||
|
c.style.position = 'relative' |
||||||
|
c.style.boxShadow = '0 1px 10px black' |
||||||
|
c.style.borderRadius = '5px' |
||||||
|
c.style.width = c.style.height = `${this.innerSize + 2 * this.padding}px` |
||||||
|
c.style.background = this.options.background |
||||||
|
} |
||||||
|
|
||||||
|
createWheel () { |
||||||
|
let c = this.wheel = doc.createElement('canvas') |
||||||
|
c.width = c.height = this.innerSize * this.pixelRatio |
||||||
|
c.style.width = c.style.height = `${this.innerSize}px` |
||||||
|
c.style.position = 'absolute' |
||||||
|
c.style.margin = c.style.padding = '0' |
||||||
|
c.style.left = c.style.top = `${this.padding}px` |
||||||
|
|
||||||
|
this.drawWheel(c.getContext('2d')) |
||||||
|
this.container.appendChild(c) |
||||||
|
} |
||||||
|
|
||||||
|
drawWheel (ctx) { |
||||||
|
let s, i |
||||||
|
|
||||||
|
ctx.save() |
||||||
|
ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0) |
||||||
|
ctx.translate(this.wheelRadius, this.wheelRadius) |
||||||
|
s = this.wheelRadius - this.triangleRadius |
||||||
|
// Draw a circle for every color
|
||||||
|
for (i = 0; i < 360; i++) { |
||||||
|
ctx.rotate(TAU / -360) // rotate one degree
|
||||||
|
ctx.beginPath() |
||||||
|
ctx.fillStyle = 'hsl(' + i + ', 100%, 50%)' |
||||||
|
ctx.arc(this.wheelRadius - (s / 2), 0, s / 2, 0, TAU, true) |
||||||
|
ctx.fill() |
||||||
|
} |
||||||
|
ctx.restore() |
||||||
|
} |
||||||
|
|
||||||
|
createTriangle () { |
||||||
|
let c = this.triangle = doc.createElement('canvas') |
||||||
|
|
||||||
|
c.width = c.height = this.innerSize * this.pixelRatio |
||||||
|
c.style.width = c.style.height = `${this.innerSize}px` |
||||||
|
c.style.position = 'absolute' |
||||||
|
c.style.margin = c.style.padding = '0' |
||||||
|
c.style.left = c.style.top = this.padding + 'px' |
||||||
|
|
||||||
|
this.triangleCtx = c.getContext('2d') |
||||||
|
|
||||||
|
this.container.appendChild(c) |
||||||
|
} |
||||||
|
|
||||||
|
drawTriangle () { |
||||||
|
const hx = this.hx |
||||||
|
const hy = this.hy |
||||||
|
const sx = this.sx |
||||||
|
const sy = this.sy |
||||||
|
const vx = this.vx |
||||||
|
const vy = this.vy |
||||||
|
const size = this.innerSize |
||||||
|
|
||||||
|
let ctx = this.triangleCtx |
||||||
|
|
||||||
|
// clear
|
||||||
|
ctx.clearRect(0, 0, size * this.pixelRatio, size * this.pixelRatio) |
||||||
|
|
||||||
|
ctx.save() |
||||||
|
ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0) |
||||||
|
ctx.translate(this.wheelRadius, this.wheelRadius) |
||||||
|
|
||||||
|
// make a triangle
|
||||||
|
ctx.beginPath() |
||||||
|
ctx.moveTo(hx, hy) |
||||||
|
ctx.lineTo(sx, sy) |
||||||
|
ctx.lineTo(vx, vy) |
||||||
|
ctx.closePath() |
||||||
|
ctx.clip() |
||||||
|
|
||||||
|
ctx.fillStyle = '#000' |
||||||
|
ctx.fillRect(-this.wheelRadius, -this.wheelRadius, size, size) |
||||||
|
// => black triangle
|
||||||
|
|
||||||
|
// create gradient from hsl(hue, 1, 1) to transparent
|
||||||
|
let grad0 = ctx.createLinearGradient(hx, hy, (sx + vx) / 2, (sy + vy) / 2) |
||||||
|
const hsla = 'hsla(' + M.round(this.hue * (360 / TAU)) + ', 100%, 50%, ' |
||||||
|
grad0.addColorStop(0, hsla + '1)') |
||||||
|
grad0.addColorStop(1, hsla + '0)') |
||||||
|
ctx.fillStyle = grad0 |
||||||
|
ctx.fillRect(-this.wheelRadius, -this.wheelRadius, size, size) |
||||||
|
// => gradient: one side of the triangle is black, the opponent angle is $color
|
||||||
|
|
||||||
|
// create color gradient from white to transparent
|
||||||
|
let grad1 = ctx.createLinearGradient(vx, vy, (hx + sx) / 2, (hy + sy) / 2) |
||||||
|
grad1.addColorStop(0, '#fff') |
||||||
|
grad1.addColorStop(1, 'rgba(255, 255, 255, 0)') |
||||||
|
ctx.globalCompositeOperation = 'lighter' |
||||||
|
ctx.fillStyle = grad1 |
||||||
|
ctx.fillRect(-this.wheelRadius, -this.wheelRadius, size, size) |
||||||
|
// => white angle
|
||||||
|
|
||||||
|
ctx.restore() |
||||||
|
} |
||||||
|
|
||||||
|
// The two pointers
|
||||||
|
createWheelPointer () { |
||||||
|
let c = this.wheelPointer = doc.createElement('canvas') |
||||||
|
const size = this.wheelPointerSize |
||||||
|
c.width = c.height = size * this.pixelRatio |
||||||
|
c.style.width = c.style.height = `${size}px` |
||||||
|
c.style.position = 'absolute' |
||||||
|
c.style.margin = c.style.padding = '0' |
||||||
|
this.drawPointer(c.getContext('2d'), size / 2, this.options.wheelPointerColor1, this.options.wheelPointerColor2) |
||||||
|
this.container.appendChild(c) |
||||||
|
} |
||||||
|
|
||||||
|
moveWheelPointer () { |
||||||
|
const r = this.wheelPointerSize / 2 |
||||||
|
const s = this.wheelPointer.style |
||||||
|
s.top = this.padding + this.wheelRadius - M.sin(this.hue) * (this.triangleRadius + this.wheelThickness / 2) - r + 'px' |
||||||
|
s.left = this.padding + this.wheelRadius + M.cos(this.hue) * (this.triangleRadius + this.wheelThickness / 2) - r + 'px' |
||||||
|
} |
||||||
|
|
||||||
|
createTrianglePointer () { // create pointer in the triangle
|
||||||
|
let c = this.trianglePointer = doc.createElement('canvas') |
||||||
|
const size = this.options.trianglePointerSize |
||||||
|
|
||||||
|
c.width = c.height = size * this.pixelRatio |
||||||
|
c.style.width = c.style.height = `${size}px` |
||||||
|
c.style.position = 'absolute' |
||||||
|
c.style.margin = c.style.padding = '0' |
||||||
|
this.drawPointer(c.getContext('2d'), size / 2, this.options.trianglePointerColor1, this.options.trianglePointerColor2) |
||||||
|
this.container.appendChild(c) |
||||||
|
} |
||||||
|
|
||||||
|
moveTrianglePointer (x, y) { |
||||||
|
const s = this.trianglePointer.style |
||||||
|
const r = this.options.trianglePointerSize / 2 |
||||||
|
s.top = (this.y + this.wheelRadius + this.padding - r) + 'px' |
||||||
|
s.left = (this.x + this.wheelRadius + this.padding - r) + 'px' |
||||||
|
} |
||||||
|
|
||||||
|
drawPointer (ctx, r, color1, color2) { |
||||||
|
ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0) |
||||||
|
ctx.fillStyle = color2 |
||||||
|
ctx.beginPath() |
||||||
|
ctx.arc(r, r, r, 0, TAU, true) |
||||||
|
ctx.fill() // => black circle
|
||||||
|
ctx.fillStyle = color1 |
||||||
|
ctx.beginPath() |
||||||
|
ctx.arc(r, r, r - 2, 0, TAU, true) |
||||||
|
ctx.fill() // => white circle with 1px black border
|
||||||
|
ctx.fillStyle = color2 |
||||||
|
ctx.beginPath() |
||||||
|
ctx.arc(r, r, r / 4 + 2, 0, TAU, true) |
||||||
|
ctx.fill() // => black circle with big white border and a small black border
|
||||||
|
ctx.globalCompositeOperation = 'destination-out' |
||||||
|
ctx.beginPath() |
||||||
|
ctx.arc(r, r, r / 4, 0, TAU, true) |
||||||
|
ctx.fill() // => transparent center
|
||||||
|
} |
||||||
|
|
||||||
|
// The Element and the DOM
|
||||||
|
inject (parent) { |
||||||
|
parent.appendChild(this.container) |
||||||
|
} |
||||||
|
|
||||||
|
getRelativeCoordinates (evt) { |
||||||
|
let elem = this.triangle |
||||||
|
let rect = elem.getBoundingClientRect() |
||||||
|
|
||||||
|
return { |
||||||
|
x: evt.clientX - rect.x, |
||||||
|
y: evt.clientY - rect.y |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dispose () { |
||||||
|
let parent = this.container.parentNode |
||||||
|
if (parent) { |
||||||
|
parent.removeChild(this.container) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
getElement () { |
||||||
|
return this.container |
||||||
|
} |
||||||
|
|
||||||
|
// Color accessors
|
||||||
|
getCSS () { |
||||||
|
const h = Math.round(this.hue * (360 / TAU)) |
||||||
|
const s = Math.round(this.saturation * 100) |
||||||
|
const l = Math.round(this.lightness * 100) |
||||||
|
|
||||||
|
return `hsl(${h}, ${s}%, ${l}%)` |
||||||
|
} |
||||||
|
|
||||||
|
getHEX () { |
||||||
|
return rgb2hex(...this.getRGB()) |
||||||
|
} |
||||||
|
|
||||||
|
setHEX (hex) { |
||||||
|
this.setRGB(...hex2rgb(hex)) |
||||||
|
} |
||||||
|
|
||||||
|
getRGB () { |
||||||
|
return hsl2rgb(...this.getHSL()) |
||||||
|
} |
||||||
|
|
||||||
|
setRGB (r, g, b) { |
||||||
|
this.setHSL(...rgb2hsl(r, g, b)) |
||||||
|
} |
||||||
|
|
||||||
|
getHSL () { |
||||||
|
return [this.hue, this.saturation, this.lightness] |
||||||
|
} |
||||||
|
|
||||||
|
setHSL (h, s, l) { |
||||||
|
this.hue = h |
||||||
|
this.saturation = s |
||||||
|
this.lightness = l |
||||||
|
|
||||||
|
this.initColor() |
||||||
|
} |
||||||
|
|
||||||
|
initColor () { |
||||||
|
this.calculatePositions() |
||||||
|
this.moveWheelPointer() |
||||||
|
this.drawTriangle() |
||||||
|
this.moveTrianglePointer() |
||||||
|
} |
||||||
|
|
||||||
|
// Mouse event handling
|
||||||
|
attachEvents () { |
||||||
|
this.down = null |
||||||
|
|
||||||
|
let mousedown = (evt) => { |
||||||
|
evt.stopPropagation() |
||||||
|
evt.preventDefault() |
||||||
|
|
||||||
|
doc.body.addEventListener('mousemove', mousemove, false) |
||||||
|
doc.body.addEventListener('mouseup', mouseup, false) |
||||||
|
|
||||||
|
let xy = this.getRelativeCoordinates(evt) |
||||||
|
this.map(xy.x, xy.y) |
||||||
|
} |
||||||
|
|
||||||
|
let mousemove = (evt) => { |
||||||
|
let xy = this.getRelativeCoordinates(evt) |
||||||
|
this.move(xy.x, xy.y) |
||||||
|
} |
||||||
|
|
||||||
|
let mouseup = (evt) => { |
||||||
|
if (this.down) { |
||||||
|
this.down = null |
||||||
|
this.emit('dragend') |
||||||
|
} |
||||||
|
doc.body.removeEventListener('mousemove', mousemove, false) |
||||||
|
doc.body.removeEventListener('mouseup', mouseup, false) |
||||||
|
} |
||||||
|
|
||||||
|
this.container.addEventListener('mousedown', mousedown, false) |
||||||
|
this.container.addEventListener('mousemove', mousemove, false) |
||||||
|
} |
||||||
|
|
||||||
|
map (x, y) { |
||||||
|
let x0 = x |
||||||
|
let y0 = y |
||||||
|
x -= this.wheelRadius |
||||||
|
y -= this.wheelRadius |
||||||
|
|
||||||
|
const r = M.sqrt(x * x + y * y) // Pythagoras
|
||||||
|
if (r > this.triangleRadius && r < this.wheelRadius) { |
||||||
|
// Wheel
|
||||||
|
this.down = 'wheel' |
||||||
|
this.emit('dragstart') |
||||||
|
this.move(x0, y0) |
||||||
|
} else if (r < this.triangleRadius) { |
||||||
|
// Inner circle
|
||||||
|
this.down = 'triangle' |
||||||
|
this.emit('dragstart') |
||||||
|
this.move(x0, y0) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
move (x, y) { |
||||||
|
if (!this.down) { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
x -= this.wheelRadius |
||||||
|
y -= this.wheelRadius |
||||||
|
|
||||||
|
let rad = M.atan2(-y, x) |
||||||
|
if (rad < 0) { |
||||||
|
rad += TAU |
||||||
|
} |
||||||
|
|
||||||
|
if (this.down === 'wheel') { |
||||||
|
this.hue = rad |
||||||
|
this.initColor() |
||||||
|
this.emit('drag') |
||||||
|
} else if (this.down === 'triangle') { |
||||||
|
// get radius and max radius
|
||||||
|
let rad0 = (rad + TAU - this.hue) % TAU |
||||||
|
let rad1 = rad0 % (TAU / 3) - (TAU / 6) |
||||||
|
let a = 0.5 * this.triangleRadius |
||||||
|
let b = M.tan(rad1) * a |
||||||
|
let r = M.sqrt(x * x + y * y) // Pythagoras
|
||||||
|
let maxR = M.sqrt(a * a + b * b) // Pythagoras
|
||||||
|
|
||||||
|
if (r > maxR) { |
||||||
|
const dx = M.tan(rad1) * r |
||||||
|
let rad2 = M.atan(dx / maxR) |
||||||
|
if (rad2 > TAU / 6) { |
||||||
|
rad2 = TAU / 6 |
||||||
|
} else if (rad2 < -TAU / 6) { |
||||||
|
rad2 = -TAU / 6 |
||||||
|
} |
||||||
|
rad += rad2 - rad1 |
||||||
|
|
||||||
|
rad0 = (rad + TAU - this.hue) % TAU |
||||||
|
rad1 = rad0 % (TAU / 3) - (TAU / 6) |
||||||
|
b = M.tan(rad1) * a |
||||||
|
r = maxR = M.sqrt(a * a + b * b) // Pythagoras
|
||||||
|
} |
||||||
|
|
||||||
|
x = M.round(M.cos(rad) * r) |
||||||
|
y = M.round(-M.sin(rad) * r) |
||||||
|
|
||||||
|
const l = this.lightness = ((M.sin(rad0) * r) / this.triangleSideLength) + 0.5 |
||||||
|
|
||||||
|
const widthShare = 1 - (M.abs(l - 0.5) * 2) |
||||||
|
let s = this.saturation = (((M.cos(rad0) * r) + (this.triangleRadius / 2)) / (1.5 * this.triangleRadius)) / widthShare |
||||||
|
s = M.max(0, s) // cannot be lower than 0
|
||||||
|
s = M.min(1, s) // cannot be greater than 1
|
||||||
|
|
||||||
|
this.lightness = l |
||||||
|
this.saturation = s |
||||||
|
|
||||||
|
this.x = x |
||||||
|
this.y = y |
||||||
|
this.moveTrianglePointer() |
||||||
|
|
||||||
|
this.emit('drag') |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*************** |
||||||
|
* Init helpers * |
||||||
|
***************/ |
||||||
|
|
||||||
|
static initInput (input, options) { |
||||||
|
options = options || {} |
||||||
|
|
||||||
|
let ct |
||||||
|
let openColorTriangle = function () { |
||||||
|
let hex = input.value |
||||||
|
if (options.parseColor) hex = options.parseColor(hex) |
||||||
|
if (!ct) { |
||||||
|
options.size = options.size || input.offsetWidth |
||||||
|
options.background = win.getComputedStyle(input, null).backgroundColor |
||||||
|
options.margin = options.margin || 10 |
||||||
|
options.event = options.event || 'dragend' |
||||||
|
|
||||||
|
ct = new ColorTriangle(hex, options) |
||||||
|
ct.on(options.event, () => { |
||||||
|
const hex = ct.getHEX() |
||||||
|
input.value = options.uppercase ? hex.toUpperCase() : hex |
||||||
|
fireChangeEvent() |
||||||
|
}) |
||||||
|
} else { |
||||||
|
ct.setHEX(hex) |
||||||
|
} |
||||||
|
|
||||||
|
let top = input.offsetTop |
||||||
|
if (win.innerHeight - input.getBoundingClientRect().top > input.offsetHeight + options.margin + options.size) { |
||||||
|
top += input.offsetHeight + options.margin // below
|
||||||
|
} else { |
||||||
|
top -= options.margin + options.size // above
|
||||||
|
} |
||||||
|
|
||||||
|
let el = ct.getElement() |
||||||
|
el.style.position = 'absolute' |
||||||
|
el.style.left = input.offsetLeft + 'px' |
||||||
|
el.style.top = top + 'px' |
||||||
|
el.style.zIndex = '1338' // above everything
|
||||||
|
|
||||||
|
ct.inject(input.parentNode) |
||||||
|
} |
||||||
|
|
||||||
|
let closeColorTriangle = () => { |
||||||
|
if (ct) { |
||||||
|
ct.dispose() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
let fireChangeEvent = () => { |
||||||
|
let evt = doc.createEvent('HTMLEvents') |
||||||
|
evt.initEvent('input', true, false) // bubbles = true, cancable = false
|
||||||
|
input.dispatchEvent(evt) // fire event
|
||||||
|
} |
||||||
|
|
||||||
|
input.addEventListener('focus', openColorTriangle, false) |
||||||
|
input.addEventListener('blur', closeColorTriangle, false) |
||||||
|
input.addEventListener('keyup', () => { |
||||||
|
const val = input.value |
||||||
|
if (val.match(/^#((?:[0-9A-Fa-f]{3})|(?:[0-9A-Fa-f]{6}))$/)) { |
||||||
|
openColorTriangle() |
||||||
|
fireChangeEvent() |
||||||
|
} else { |
||||||
|
closeColorTriangle() |
||||||
|
} |
||||||
|
}, false) |
||||||
|
} |
||||||
|
|
||||||
|
/******************* |
||||||
|
* Helper functions * |
||||||
|
*******************/ |
||||||
|
|
||||||
|
setOptions (opts) { |
||||||
|
opts = opts || {} |
||||||
|
let dflt = this.options |
||||||
|
let options = this.options = {} |
||||||
|
|
||||||
|
each(dflt, function (val, key) { |
||||||
|
options[key] = (opts.hasOwnProperty(key)) |
||||||
|
? opts[key] |
||||||
|
: val |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
const { qs } = require('../utils') |
||||||
|
|
||||||
|
module.exports = function initButtons (input) { |
||||||
|
let container = qs('#action-buttons') |
||||||
|
|
||||||
|
// button labels
|
||||||
|
let labels = [] |
||||||
|
|
||||||
|
// button elements
|
||||||
|
let buttons = [] |
||||||
|
|
||||||
|
// add a button element
|
||||||
|
let pushButton = function pushButton () { |
||||||
|
let button = document.createElement('button') |
||||||
|
button.classList.add('action-button') |
||||||
|
button.setAttribute('data-n', buttons.length) |
||||||
|
buttons.push(button) |
||||||
|
container.appendChild(button) |
||||||
|
|
||||||
|
button.addEventListener('click', e => { |
||||||
|
// might as well use the attribute ¯\_(ツ)_/¯
|
||||||
|
let index = +button.getAttribute('data-n') |
||||||
|
input.sendButton(index) |
||||||
|
}) |
||||||
|
|
||||||
|
return button |
||||||
|
} |
||||||
|
|
||||||
|
// remove a button element
|
||||||
|
let popButton = function popButton () { |
||||||
|
let button = buttons.pop() |
||||||
|
button.parentNode.removeChild(button) |
||||||
|
} |
||||||
|
|
||||||
|
// sync with DOM
|
||||||
|
let update = function updateButtons () { |
||||||
|
if (labels.length > buttons.length) { |
||||||
|
for (let i = buttons.length; i < labels.length; i++) { |
||||||
|
pushButton() |
||||||
|
} |
||||||
|
} else if (buttons.length > labels.length) { |
||||||
|
for (let i = labels.length; i <= buttons.length; i++) { |
||||||
|
popButton() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (let i = 0; i < labels.length; i++) { |
||||||
|
let label = labels[i].trim() |
||||||
|
let button = buttons[i] |
||||||
|
button.textContent = label || '\u00a0' // label or nbsp
|
||||||
|
if (!label) button.classList.add('inactive') |
||||||
|
else button.classList.remove('inactive') |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return { update, labels } |
||||||
|
} |
@ -0,0 +1,105 @@ |
|||||||
|
const ColorTriangle = require('./lib/colortriangle') |
||||||
|
const $ = require('./lib/chibi') |
||||||
|
const themes = require('./term/themes') |
||||||
|
const { qs } = require('./utils') |
||||||
|
|
||||||
|
function selectedTheme () { |
||||||
|
return +$('#theme').val() |
||||||
|
} |
||||||
|
|
||||||
|
exports.init = function () { |
||||||
|
$('#theme').on('change', showColor) |
||||||
|
|
||||||
|
$('#default_fg').on('input', showColor) |
||||||
|
$('#default_bg').on('input', showColor) |
||||||
|
|
||||||
|
let opts = { |
||||||
|
padding: 10, |
||||||
|
event: 'drag', |
||||||
|
uppercase: true, |
||||||
|
trianglePointerSize: 20, |
||||||
|
// wheelPointerSize: 12,
|
||||||
|
size: 200, |
||||||
|
parseColor: (color) => { |
||||||
|
return themes.toHex(color, selectedTheme()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ColorTriangle.initInput(qs('#default_fg'), opts) |
||||||
|
ColorTriangle.initInput(qs('#default_bg'), opts) |
||||||
|
|
||||||
|
$('.colorprev.bg span').on('click', function () { |
||||||
|
const bg = this.dataset.bg |
||||||
|
if (typeof bg != 'undefined') $('#default_bg').val(bg) |
||||||
|
showColor() |
||||||
|
}) |
||||||
|
|
||||||
|
$('.colorprev.fg span').on('click', function () { |
||||||
|
const fg = this.dataset.fg |
||||||
|
if (typeof fg != 'undefined') $('#default_fg').val(fg) |
||||||
|
showColor() |
||||||
|
}) |
||||||
|
|
||||||
|
let $presets = $('#fgbg_presets') |
||||||
|
for (let i = 0; i < themes.fgbgThemes.length; i++) { |
||||||
|
const thm = themes.fgbgThemes[i] |
||||||
|
const fg = thm[0] |
||||||
|
const bg = thm[1] |
||||||
|
const lbl = thm[2] |
||||||
|
const tit = thm[3] |
||||||
|
$presets.htmlAppend( |
||||||
|
'<span class="preset" ' + |
||||||
|
'data-xfg="' + fg + '" data-xbg="' + bg + '" ' + |
||||||
|
'style="color:' + fg + ';background:' + bg + '" title="' + tit + '"> ' + lbl + ' </span>') |
||||||
|
|
||||||
|
if ((i + 1) % 5 === 0) $presets.htmlAppend('<br>') |
||||||
|
} |
||||||
|
|
||||||
|
$('.preset').on('click', function () { |
||||||
|
$('#default_fg').val(this.dataset.xfg) |
||||||
|
$('#default_bg').val(this.dataset.xbg) |
||||||
|
showColor() |
||||||
|
}) |
||||||
|
|
||||||
|
showColor() |
||||||
|
} |
||||||
|
|
||||||
|
function showColor () { |
||||||
|
let ex = qs('.color-example') |
||||||
|
let fg = $('#default_fg').val() |
||||||
|
let bg = $('#default_bg').val() |
||||||
|
|
||||||
|
if (/^\d+$/.test(fg)) { |
||||||
|
fg = +fg |
||||||
|
} else if (!/^#[\da-f]{6}$/i.test(fg)) { |
||||||
|
fg = 'black' |
||||||
|
} |
||||||
|
|
||||||
|
if (/^\d+$/.test(bg)) { |
||||||
|
bg = +bg |
||||||
|
} else if (!/^#[\da-f]{6}$/i.test(bg)) { |
||||||
|
bg = 'black' |
||||||
|
} |
||||||
|
|
||||||
|
const themeN = selectedTheme() |
||||||
|
ex.dataset.fg = fg |
||||||
|
ex.dataset.bg = bg |
||||||
|
|
||||||
|
themes.themePreview(themeN) |
||||||
|
|
||||||
|
$('.colorprev.fg span').css('background', themes.toHex(bg, themeN)) |
||||||
|
} |
||||||
|
|
||||||
|
exports.nextTheme = () => { |
||||||
|
let sel = qs('#theme') |
||||||
|
let i = sel.selectedIndex |
||||||
|
sel.options[++i % sel.options.length].selected = true |
||||||
|
showColor() |
||||||
|
} |
||||||
|
|
||||||
|
exports.prevTheme = () => { |
||||||
|
let sel = qs('#theme') |
||||||
|
let i = sel.selectedIndex |
||||||
|
sel.options[(sel.options.length + (--i)) % sel.options.length].selected = true |
||||||
|
showColor() |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
#! /usr/bin/env php |
||||||
|
<?php |
||||||
|
|
||||||
|
require_once __DIR__ . '/../base.php'; |
||||||
|
|
||||||
|
$selected = array_slice($argv, 1); |
||||||
|
|
||||||
|
$output = []; |
||||||
|
|
||||||
|
foreach ($selected as $key) { |
||||||
|
$output[$key] = tr($key); |
||||||
|
} |
||||||
|
|
||||||
|
fwrite(STDOUT, json_encode($output, JSON_UNESCAPED_UNICODE)); |
@ -0,0 +1,54 @@ |
|||||||
|
/* |
||||||
|
* This is a Webpack loader that loads the language data by running |
||||||
|
* dump_selected.php. |
||||||
|
*/ |
||||||
|
|
||||||
|
const { spawnSync } = require('child_process') |
||||||
|
const path = require('path') |
||||||
|
const selectedKeys = require('./js-keys') |
||||||
|
|
||||||
|
module.exports = function (source) { |
||||||
|
let child = spawnSync(path.resolve(__dirname, '_js-dump.php'), selectedKeys, { |
||||||
|
timeout: 1000 |
||||||
|
}) |
||||||
|
|
||||||
|
let data |
||||||
|
try { |
||||||
|
data = JSON.parse(child.stdout.toString().trim()) |
||||||
|
} catch (err) { |
||||||
|
console.error(`\x1b[31;1m[lang-loader] Failed to parse JSON:`) |
||||||
|
console.error(child.stdout.toString().trim()) |
||||||
|
console.error(`\x1b[m`) |
||||||
|
|
||||||
|
if (err) throw err |
||||||
|
} |
||||||
|
|
||||||
|
// adapted from webpack/loader-utils
|
||||||
|
let remainingRequest = this.remainingRequest |
||||||
|
if (!remainingRequest) { |
||||||
|
remainingRequest = this.loaders.slice(this.loaderIndex + 1) |
||||||
|
.map(obj => obj.request) |
||||||
|
.concat([this.resource]).join('!') |
||||||
|
} |
||||||
|
|
||||||
|
let currentRequest = this.currentRequest |
||||||
|
if (!currentRequest) { |
||||||
|
remainingRequest = this.loaders.slice(this.loaderIndex) |
||||||
|
.map(obj => obj.request) |
||||||
|
.concat([this.resource]).join('!') |
||||||
|
} |
||||||
|
|
||||||
|
let map = { |
||||||
|
version: 3, |
||||||
|
file: currentRequest, |
||||||
|
sourceRoot: '', |
||||||
|
sources: [remainingRequest], |
||||||
|
sourcesContent: [source], |
||||||
|
names: [], |
||||||
|
mappings: 'AAAA;AAAA' |
||||||
|
} |
||||||
|
|
||||||
|
this.callback(null, |
||||||
|
`/* Generated language file */\n` + |
||||||
|
`module.exports=${JSON.stringify(data)}\n`, map) |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
return [ |
||||||
|
'appname' => 'ESPTerm', |
||||||
|
'appname_demo' => 'ESPTerm<sup> DEMO</sup>', |
||||||
|
|
||||||
|
// not used - api etc. Added to suppress warnings |
||||||
|
'menu.term_set' => '', |
||||||
|
'menu.wifi_connstatus' => '', |
||||||
|
'menu.wifi_set' => '', |
||||||
|
'menu.wifi_scan' => '', |
||||||
|
'menu.network_set' => '', |
||||||
|
'menu.system_set' => '', |
||||||
|
'menu.write_defaults' => '', |
||||||
|
'menu.restore_defaults' => '', |
||||||
|
'menu.restore_hard' => '', |
||||||
|
'menu.reset_screen' => '', |
||||||
|
'menu.index' => '', |
||||||
|
]; |
@ -0,0 +1,271 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
return [ |
||||||
|
'menu.cfg_wifi' => 'Nastavení WiFi', |
||||||
|
'menu.cfg_network' => 'Nastavení sítě', |
||||||
|
'menu.cfg_term' => 'Nastavení terminalu', |
||||||
|
'menu.about' => 'About', |
||||||
|
'menu.help' => 'Nápověda', |
||||||
|
'menu.term' => 'Zpět k terminálu', |
||||||
|
'menu.cfg_system' => 'Nastavení systému', |
||||||
|
'menu.cfg_wifi_conn' => 'Připojování', |
||||||
|
'menu.settings' => 'Nastavení', |
||||||
|
|
||||||
|
// Terminal page |
||||||
|
|
||||||
|
'title.term' => 'Terminál', // page title of the terminal page |
||||||
|
|
||||||
|
'term_nav.fullscreen' => 'Celá obr.', |
||||||
|
'term_nav.config' => 'Nastavení', |
||||||
|
'term_nav.wifi' => 'WiFi', |
||||||
|
'term_nav.help' => 'Nápověda', |
||||||
|
'term_nav.about' => 'About', |
||||||
|
'term_nav.paste' => 'Vložit', |
||||||
|
'term_nav.upload' => 'Nahrát', |
||||||
|
'term_nav.keybd' => 'Klávesnice', |
||||||
|
'term_nav.paste_prompt' => 'Vložte text k~odeslání:', |
||||||
|
|
||||||
|
'term_conn.connecting' => 'Připojuji se', |
||||||
|
'term_conn.waiting_content' => 'Čekám na data', |
||||||
|
'term_conn.disconnected' => 'Odpojen', |
||||||
|
'term_conn.waiting_server' => 'Čekám na server', |
||||||
|
'term_conn.reconnecting' => 'Obnova spojení', |
||||||
|
|
||||||
|
// Terminal settings page |
||||||
|
|
||||||
|
'term.defaults' => 'Výchozí nastavení', |
||||||
|
'term.expert' => 'Pokročilé volby', |
||||||
|
'term.explain_initials' => ' |
||||||
|
Tato nastavení jsou použita po spuštění a při resetu obrazovky |
||||||
|
(příkaz RIS, <code>\ec</code>). Tyto volby lze měnit za běhu |
||||||
|
pomocí řídicích sekvencí. |
||||||
|
', |
||||||
|
'term.explain_expert' => ' |
||||||
|
Interní parametry terminálu. Změnou časování lze dosáhnout kratší |
||||||
|
latence a~rychlejšího překreslování, hodnoty záleží na konkrétní |
||||||
|
aplikaci. Timeout parseru je čas do automatického zrušení započaté |
||||||
|
řídicí sekvence.', |
||||||
|
|
||||||
|
'term.example' => 'Náhled výchozích barev', |
||||||
|
|
||||||
|
'term.explain_scheme' => ' |
||||||
|
Výchozí barvu textu a pozadí vyberete kliknutím na barvy v~paletě. |
||||||
|
Dále lze použít ANSI barvy 0-255 a hex ve formátu #FFFFFF. |
||||||
|
', |
||||||
|
|
||||||
|
'term.fgbg_presets' => 'Předvolby výchozích<br>barev textu a pozadí', |
||||||
|
'term.color_scheme' => 'Barevné schéma', |
||||||
|
'term.reset_screen' => 'Resetovat obrazovku a parser', |
||||||
|
'term.term_title' => 'Nadpis', |
||||||
|
'term.term_width' => 'Šířka', |
||||||
|
'term.term_height' => 'Výška', |
||||||
|
'term.buttons' => 'Text tlačítke', |
||||||
|
'term.theme' => 'Barevná paleta', |
||||||
|
'term.cursor_shape' => 'Styl kurzoru', |
||||||
|
'term.parser_tout_ms' => 'Timeout parseru', |
||||||
|
'term.display_tout_ms' => 'Prodleva překreslení', |
||||||
|
'term.display_cooldown_ms' => 'Min. čas překreslení', |
||||||
|
'term.allow_decopt_12' => 'Povolit \e?12h/l', |
||||||
|
'term.fn_alt_mode' => 'SS3 Fx klávesy', |
||||||
|
'term.show_config_links' => 'Menu pod obrazovkou', |
||||||
|
'term.show_buttons' => 'Zobrazit tlačítka', |
||||||
|
'term.loopback' => 'Loopback (<span style="text-decoration:overline">SRM</span>)', |
||||||
|
'term.crlf_mode' => 'Enter = CR+LF (LNM)', |
||||||
|
'term.want_all_fn' => 'Zachytávat F5, F11, F12', |
||||||
|
'term.button_msgs' => 'Reporty tlačítek<br>(dek. ASCII CSV)', |
||||||
|
'term.color_fg' => 'Výchozí text', |
||||||
|
'term.color_bg' => 'Výchozí pozadí', |
||||||
|
'term.color_fg_prev' => 'Barva textu', |
||||||
|
'term.color_bg_prev' => 'Barva pozadí', |
||||||
|
'term.colors_preview' => '', |
||||||
|
// 'term.debugbar' => 'Ladění ', |
||||||
|
// 'term.ascii_debug' => 'Použít debug parser', |
||||||
|
|
||||||
|
'cursor.block_blink' => 'Blok, blikající', |
||||||
|
'cursor.block_steady' => 'Blok, stálý', |
||||||
|
'cursor.underline_blink' => 'Podtržítko, blikající', |
||||||
|
'cursor.underline_steady' => 'Podtržítko, stálé', |
||||||
|
'cursor.bar_blink' => 'Svislice, blikající', |
||||||
|
'cursor.bar_steady' => 'Svislice, stálá', |
||||||
|
|
||||||
|
// Text upload dialog |
||||||
|
|
||||||
|
'upload.title' => 'Upload textu', |
||||||
|
'upload.prompt' => 'Načíst ze souboru:', |
||||||
|
'upload.endings' => 'Konce řádku:', |
||||||
|
'upload.endings.cr' => 'CR (klávesa Enter)', |
||||||
|
'upload.endings.crlf' => 'CR LF (Windows)', |
||||||
|
'upload.endings.lf' => 'LF (Linux)', |
||||||
|
'upload.chunk_delay' => 'Prodleva (ms):', |
||||||
|
'upload.chunk_size' => 'Délka úseku (0=řádek):', |
||||||
|
'upload.progress' => 'Proběh:', |
||||||
|
|
||||||
|
// Network config page |
||||||
|
|
||||||
|
'net.explain_sta' => ' |
||||||
|
Odškrtněte "Použít dynamickou IP" pro nastavení statické IP adresy.', |
||||||
|
|
||||||
|
'net.explain_ap' => ' |
||||||
|
Tato nastavení ovlivňují interní DHCP server v AP režimu (hotspot).', |
||||||
|
|
||||||
|
'net.ap_dhcp_time' => 'Doba zapůjčení adresy', |
||||||
|
'net.ap_dhcp_start' => 'Začátek IP poolu', |
||||||
|
'net.ap_dhcp_end' => 'Konec IP poolu', |
||||||
|
'net.ap_addr_ip' => 'Vlastní IP adresa', |
||||||
|
'net.ap_addr_mask' => 'Maska podsítě', |
||||||
|
|
||||||
|
'net.sta_dhcp_enable' => 'Použít dynamickou IP', |
||||||
|
'net.sta_addr_ip' => 'Statická IP modulu', |
||||||
|
'net.sta_addr_mask' => 'Maska podsítě', |
||||||
|
'net.sta_addr_gw' => 'Gateway', |
||||||
|
|
||||||
|
'net.ap' => 'DHCP server (AP)', |
||||||
|
'net.sta' => 'DHCP klient', |
||||||
|
'net.sta_mac' => 'MAC adresa klienta', |
||||||
|
'net.ap_mac' => 'MAC adresa AP', |
||||||
|
'net.details' => 'MAC adresy', |
||||||
|
|
||||||
|
// Wifi config page |
||||||
|
|
||||||
|
'wifi.ap' => 'WiFi hotspot', |
||||||
|
'wifi.sta' => 'Připojení k~externí síti', |
||||||
|
|
||||||
|
'wifi.enable' => 'Zapnuto', |
||||||
|
'wifi.tpw' => 'Vysílací výkon', |
||||||
|
'wifi.ap_channel' => 'WiFi kanál', |
||||||
|
'wifi.ap_ssid' => 'Jméno hotspotu', |
||||||
|
'wifi.ap_password' => 'Přístupové heslo', |
||||||
|
'wifi.ap_hidden' => 'Skrýt síť', |
||||||
|
'wifi.sta_info' => 'Zvolená síť', |
||||||
|
|
||||||
|
'wifi.not_conn' => 'Nepřipojen.', |
||||||
|
'wifi.sta_none' => 'Žádná', |
||||||
|
'wifi.sta_active_pw' => '🔒 Uložené heslo', |
||||||
|
'wifi.sta_active_nopw' => '🔓 Bez hesla', |
||||||
|
'wifi.connected_ip_is' => 'Připojen, IP: ', |
||||||
|
'wifi.sta_password' => 'Heslo:', |
||||||
|
|
||||||
|
'wifi.scanning' => 'Hledám sítě', |
||||||
|
'wifi.scan_now' => 'Klikněte pro vyhledání sítí!', |
||||||
|
'wifi.cant_scan_no_sta' => 'Klikněte pro zapnutí režimu klienta a vyhledání sítí!', |
||||||
|
'wifi.select_ssid' => 'Dostupné sítě:', |
||||||
|
'wifi.enter_passwd' => 'Zadejte heslo pro ":ssid:"', |
||||||
|
'wifi.sta_explain' => 'Vyberte síť a připojte se tlačítkem vpravo nahoře.', |
||||||
|
|
||||||
|
// Wifi connecting status page |
||||||
|
|
||||||
|
'wificonn.status' => 'Stav:', |
||||||
|
'wificonn.back_to_config' => 'Zpět k~nastavení WiFi', |
||||||
|
'wificonn.telemetry_lost' => 'Spojení bylo přerušeno; připojování selhalo, nebo jste byli odpojeni od sítě.', |
||||||
|
'wificonn.explain_android_sucks' => ' |
||||||
|
Pokud ESPTerm konfigurujete pomocí mobilu nebo z~externí sítě, může se stát |
||||||
|
že některé ze zařízení změní síť a~ukazatel průběhu přestane fungovat. |
||||||
|
Počkejte ~15s a pak zkontrolujte, zda se připojení zdařilo. |
||||||
|
', |
||||||
|
|
||||||
|
'wificonn.explain_reset' => ' |
||||||
|
Interní hotspot lze kdykoliv vynutit podržením tlačítka BOOT, až modrá LED začne blikat. |
||||||
|
Podržíte-li tlačítko déle (LED začne blikat rychleji), dojde k~obnovení do výchozích anstavení.', |
||||||
|
|
||||||
|
'wificonn.disabled' => "Režim klienta není povolen.", |
||||||
|
'wificonn.idle' => "Žádná IP adresa, připojování neprobíhá.", |
||||||
|
'wificonn.success' => "Připijen! IP adresa je ", |
||||||
|
'wificonn.working' => "Připojuji k zvolené síti", |
||||||
|
'wificonn.fail' => "Připojení selhalo, zkontrolujte nastavení a~pokus opakujte. Důvod: ", |
||||||
|
|
||||||
|
// Access restrictions form |
||||||
|
|
||||||
|
'pwlock.title' => 'Omezení přístupu', |
||||||
|
'pwlock.explain' => ' |
||||||
|
Části webového rozhraní lze chránit heslem. Nemáte-li v úmyslu heslo měnit, |
||||||
|
do jeho políčka nic nevyplňujte.<br> |
||||||
|
Výchozí přístupové heslo je "%def_access_pw%". |
||||||
|
', |
||||||
|
'pwlock.region' => 'Chránit heslem', |
||||||
|
'pwlock.region.none' => 'Nic, vše volně přístupné', |
||||||
|
'pwlock.region.settings_noterm' => 'Nastavení, mimo terminál', |
||||||
|
'pwlock.region.settings' => 'Všechna nastavení', |
||||||
|
'pwlock.region.menus' => 'Celá admin. sekce', |
||||||
|
'pwlock.region.all' => 'Vše, včetně terminálu', |
||||||
|
'pwlock.new_access_pw' => 'Nové přístupové heslo', |
||||||
|
'pwlock.new_access_pw2' => 'Zopakujte nové heslo', |
||||||
|
'pwlock.admin_pw' => 'Systémové heslo', |
||||||
|
'pwlock.access_name' => 'Uživatelské jméno', |
||||||
|
|
||||||
|
// Setting admin password |
||||||
|
|
||||||
|
'adminpw.title' => 'Změna systémového hesla', |
||||||
|
'adminpw.explain' => |
||||||
|
' |
||||||
|
Systémové heslo slouží k úpravám uložených výchozích nastavení |
||||||
|
a ke změně přístupových oprávnění. |
||||||
|
Toto heslo je uloženo mimo ostatní data, obnovení do výchozách nastavení |
||||||
|
na něj nemá vliv. |
||||||
|
Toto heslo nelze jednoduše obnovit, v případě zapomenutí vymažte flash paměť a obnovte firmware.<br> |
||||||
|
Vychozí systémové heslo je "%def_admin_pw%". |
||||||
|
', |
||||||
|
'adminpw.new_admin_pw' => 'Nové systémové heslo', |
||||||
|
'adminpw.new_admin_pw2' => 'Zopakujte nové heslo', |
||||||
|
'adminpw.old_admin_pw' => 'Původní systémové heslo', |
||||||
|
|
||||||
|
// Persist form |
||||||
|
|
||||||
|
'persist.title' => 'Záloha a~obnovení konfigurace', |
||||||
|
'persist.explain' => ' |
||||||
|
Všechna nastavení jsou ukládána do flash paměti. V~paměti jsou |
||||||
|
vyhrazené dva oddíly, aktivní nastavení a záloha. Zálohu lze přepsat |
||||||
|
za použití systémového hesla, původní nastavení z ní pak můžete kdykoliv obnovit. |
||||||
|
Pro obnovení ze zálohy stačí podržet tlačítko BOOT, až modrá LED začne rychle blikat. |
||||||
|
', |
||||||
|
'persist.confirm_restore' => 'Chcete obnovit všechna nastavení?', |
||||||
|
'persist.confirm_restore_hard' => |
||||||
|
'Opravdu chcete načíst tovární nastavení? Všechna nastavení kromě zálohy a systémového hesla |
||||||
|
budou přepsána, včetně nastavení WiFi!', |
||||||
|
'persist.confirm_store_defaults' => |
||||||
|
'Zadejte systémové heslo pro přepsání zálohy aktuálními parametry.', |
||||||
|
'persist.password' => 'Systémové heslo:', |
||||||
|
'persist.restore_defaults' => 'Obnovit ze zálohy', |
||||||
|
'persist.write_defaults' => 'Zálohovat aktuální nastavení', |
||||||
|
'persist.restore_hard' => 'Načíst tovární nastavení', |
||||||
|
'persist.restore_hard_explain' => |
||||||
|
'(Tímto vymažete nastavení WiFi! Záloha a systémové heslo zůstanou beze změny.)', |
||||||
|
|
||||||
|
// UART settings form |
||||||
|
|
||||||
|
'uart.title' => 'Sériový port', |
||||||
|
'uart.explain' => ' |
||||||
|
Tímto formulářem můžete upravit nastavení komunikačního UARTu. |
||||||
|
Ladicí výpisy jsou na pinu P2 s~pevnými parametry: 115200 baud, 1 stop bit, žádná parita. |
||||||
|
', |
||||||
|
'uart.baud' => 'Rychlost', |
||||||
|
'uart.parity' => 'Parita', |
||||||
|
'uart.parity.none' => 'Źádná', |
||||||
|
'uart.parity.odd' => 'Lichá', |
||||||
|
'uart.parity.even' => 'Sudá', |
||||||
|
'uart.stop_bits' => 'Stop-bity', |
||||||
|
'uart.stop_bits.one' => '1', |
||||||
|
'uart.stop_bits.one_and_half' => '1.5', |
||||||
|
'uart.stop_bits.two' => '2', |
||||||
|
|
||||||
|
// HW tuning form |
||||||
|
|
||||||
|
'hwtuning.title' => 'Tuning hardwaru', |
||||||
|
'hwtuning.explain' => ' |
||||||
|
ESP8266 lze přetaktovat z~80~MHz na 160~MHz. Vyšší rychlost umožní rychlejší překreslování |
||||||
|
obrazovky a stránky se budou načítat rychleji. Nevýhodou je vyšší spotřeba a citlivost k~rušení. |
||||||
|
', |
||||||
|
'hwtuning.overclock' => 'Přetaktovat na 160~MHz', |
||||||
|
|
||||||
|
// Generic button / dialog labels |
||||||
|
|
||||||
|
'apply' => 'Uložit!', |
||||||
|
'start' => 'Start', |
||||||
|
'cancel' => 'Zrušit', |
||||||
|
'enabled' => 'Zapnuto', |
||||||
|
'disabled' => 'Vypnuto', |
||||||
|
'yes' => 'Ano', |
||||||
|
'no' => 'Ne', |
||||||
|
'confirm' => 'OK', |
||||||
|
'copy' => 'Kopírovat', |
||||||
|
'form_errors' => 'Neplatné hodnoty:', |
||||||
|
]; |
@ -0,0 +1,270 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
return [ |
||||||
|
'menu.cfg_wifi' => 'WLAN-Einstellungen', |
||||||
|
'menu.cfg_network' => 'Netzwerkeinstellungen', |
||||||
|
'menu.cfg_term' => 'Terminaleinstellungen', |
||||||
|
'menu.about' => 'Über ESPTerm', |
||||||
|
'menu.help' => 'Schnellreferenz', |
||||||
|
'menu.term' => 'Zurück zum Terminal', |
||||||
|
'menu.cfg_system' => 'Systemeinstellungen', |
||||||
|
'menu.cfg_wifi_conn' => 'Verbinden mit dem Netzwerk', |
||||||
|
'menu.settings' => 'Einstellungen', |
||||||
|
|
||||||
|
// Terminal page |
||||||
|
|
||||||
|
'title.term' => 'Terminal', // page title of the terminal page |
||||||
|
|
||||||
|
'term_nav.fullscreen' => 'Vollbild', |
||||||
|
'term_nav.config' => 'Konfiguration', |
||||||
|
'term_nav.wifi' => 'WLAN', |
||||||
|
'term_nav.help' => 'Hilfe', |
||||||
|
'term_nav.about' => 'Info', |
||||||
|
'term_nav.paste' => 'Einfügen', |
||||||
|
'term_nav.upload' => 'Hochladen', |
||||||
|
'term_nav.keybd' => 'Tastatur', |
||||||
|
'term_nav.paste_prompt' => 'Text einfügen zum Versenden:', |
||||||
|
|
||||||
|
'term_conn.connecting' => 'Verbinden', |
||||||
|
'term_conn.waiting_content' => 'Warten auf Inhalt', |
||||||
|
'term_conn.disconnected' => 'Nicht verbunden', |
||||||
|
'term_conn.waiting_server' => 'Warten auf Server', |
||||||
|
'term_conn.reconnecting' => 'Verbinden', |
||||||
|
|
||||||
|
// Terminal settings page |
||||||
|
|
||||||
|
'term.defaults' => 'Anfangseinstellungen', |
||||||
|
'term.expert' => 'Expertenoptionen', |
||||||
|
'term.explain_initials' => ' |
||||||
|
Dies sind die Anfangseinstellungen, die benutzt werden, nachdem ESPTerm startet, |
||||||
|
oder wenn der Bildschirm mit dem <code>\ec</code>-Kommando zurückgesetzt wird. |
||||||
|
Sie können durch Escape-Sequenzen verändert werden. |
||||||
|
', |
||||||
|
'term.explain_expert' => ' |
||||||
|
Dies sind erweiterte Konfigurationsoptionen, die meistens nicht verändert |
||||||
|
werden müssen. Bearbeite sie nur, wenn du weißt, was du tust.', |
||||||
|
|
||||||
|
'term.example' => 'Standardfarbenvorschau', |
||||||
|
|
||||||
|
'term.explain_scheme' => ' |
||||||
|
Um die Standardtextfarbe und Standardhintergrundfarbe auszuwählen, klicke auf |
||||||
|
die Vorschaupalette, oder benutze die Zahlen 0-15 für die Themafarben, 16-255 |
||||||
|
für Standardfarben, oder Hexadezimal (#FFFFFF) für True Color (24-bit). |
||||||
|
', |
||||||
|
|
||||||
|
'term.fgbg_presets' => 'Voreinstellungen', |
||||||
|
'term.color_scheme' => 'Farbschema', |
||||||
|
'term.reset_screen' => 'Bildschirm & Parser zurücksetzen', |
||||||
|
'term.term_title' => 'Titeltext', |
||||||
|
'term.term_width' => 'Breite', |
||||||
|
'term.term_height' => 'Höhe', |
||||||
|
'term.buttons' => 'Tastentext', |
||||||
|
'term.theme' => 'Farbthema', |
||||||
|
'term.cursor_shape' => 'Cursorstil', |
||||||
|
'term.parser_tout_ms' => 'Parser-Auszeit', |
||||||
|
'term.display_tout_ms' => 'Zeichenverzögerung', |
||||||
|
'term.display_cooldown_ms' => 'Zeichenabkühlzeit', |
||||||
|
'term.allow_decopt_12' => '\e?12h/l erlauben', |
||||||
|
'term.fn_alt_mode' => 'SS3 Fn-Tasten', |
||||||
|
'term.show_config_links' => 'Links anzeigen', |
||||||
|
'term.show_buttons' => 'Tasten anzeigen', |
||||||
|
'term.loopback' => 'Lokales Echo (<span style="text-decoration:overline">SRM</span>)', |
||||||
|
'term.crlf_mode' => 'Enter = CR+LF (LNM)', |
||||||
|
'term.want_all_fn' => 'F5, F11, F12 erfassen', |
||||||
|
'term.button_msgs' => 'Tastencodes<br>(ASCII, dec, CSV)', |
||||||
|
'term.color_fg' => 'Standardvordergr.', |
||||||
|
'term.color_bg' => 'Standardhintergr.', |
||||||
|
'term.color_fg_prev' => 'Vordergrund', |
||||||
|
'term.color_bg_prev' => 'Hintergrund', |
||||||
|
'term.colors_preview' => '', |
||||||
|
'term.debugbar' => 'Debug-Leiste anzeigen', |
||||||
|
'term.ascii_debug' => 'Kontrollcodes anzeigen', |
||||||
|
|
||||||
|
'cursor.block_blink' => 'Block, blinkend', |
||||||
|
'cursor.block_steady' => 'Block, ruhig', |
||||||
|
'cursor.underline_blink' => 'Unterstrich, blinkend', |
||||||
|
'cursor.underline_steady' => 'Unterstrich, ruhig', |
||||||
|
'cursor.bar_blink' => 'Balken, blinkend', |
||||||
|
'cursor.bar_steady' => 'Balken, ruhig', |
||||||
|
|
||||||
|
// Text upload dialog |
||||||
|
|
||||||
|
'upload.title' => 'Text Hochladen', |
||||||
|
'upload.prompt' => 'Eine Textdatei laden:', |
||||||
|
'upload.endings' => 'Zeilenumbruch:', |
||||||
|
'upload.endings.cr' => 'CR (Enter-Taste)', |
||||||
|
'upload.endings.crlf' => 'CR LF (Windows)', |
||||||
|
'upload.endings.lf' => 'LF (Linux)', |
||||||
|
'upload.chunk_delay' => 'Datenblockverzögerung (ms):', |
||||||
|
'upload.chunk_size' => 'Datenblockgröße (0=Linie):', |
||||||
|
'upload.progress' => 'Hochladen:', |
||||||
|
|
||||||
|
// Network config page |
||||||
|
|
||||||
|
'net.explain_sta' => ' |
||||||
|
Schalte Dynamische IP aus um die statische IP-Addresse zu konfigurieren.', |
||||||
|
|
||||||
|
'net.explain_ap' => ' |
||||||
|
Diese Einstellungen beeinflussen den eingebauten DHCP-Server im AP-Modus.', |
||||||
|
|
||||||
|
'net.ap_dhcp_time' => 'Leasezeit', |
||||||
|
'net.ap_dhcp_start' => 'Pool Start-IP', |
||||||
|
'net.ap_dhcp_end' => 'Pool End-IP', |
||||||
|
'net.ap_addr_ip' => 'Eigene IP-Addresse', |
||||||
|
'net.ap_addr_mask' => 'Subnet-Maske', |
||||||
|
|
||||||
|
'net.sta_dhcp_enable' => 'Dynamische IP', |
||||||
|
'net.sta_addr_ip' => 'ESPTerm statische IP', |
||||||
|
'net.sta_addr_mask' => 'Subnet-Maske', |
||||||
|
'net.sta_addr_gw' => 'Gateway-IP', |
||||||
|
|
||||||
|
'net.ap' => 'DHCP Server (AP)', |
||||||
|
'net.sta' => 'DHCP Client (Station)', |
||||||
|
'net.sta_mac' => 'Station MAC', |
||||||
|
'net.ap_mac' => 'AP MAC', |
||||||
|
'net.details' => 'MAC-Addressen', |
||||||
|
|
||||||
|
// Wifi config page |
||||||
|
|
||||||
|
'wifi.ap' => 'Eingebauter Access Point', |
||||||
|
'wifi.sta' => 'Bestehendes Netzwerk beitreten', |
||||||
|
|
||||||
|
'wifi.enable' => 'Aktiviert', |
||||||
|
'wifi.tpw' => 'Sendeleistung', |
||||||
|
'wifi.ap_channel' => 'Kanal', |
||||||
|
'wifi.ap_ssid' => 'AP SSID', |
||||||
|
'wifi.ap_password' => 'Passwort', |
||||||
|
'wifi.ap_hidden' => 'SSID verbergen', |
||||||
|
'wifi.sta_info' => 'Ausgewählt', |
||||||
|
|
||||||
|
'wifi.not_conn' => 'Nicht verbunden.', |
||||||
|
'wifi.sta_none' => 'Keine', |
||||||
|
'wifi.sta_active_pw' => '🔒 Passwort gespeichert', |
||||||
|
'wifi.sta_active_nopw' => '🔓 Offen', |
||||||
|
'wifi.connected_ip_is' => 'Verbunden, IP ist ', |
||||||
|
'wifi.sta_password' => 'Passwort:', |
||||||
|
|
||||||
|
'wifi.scanning' => 'Scannen', |
||||||
|
'wifi.scan_now' => 'Klicke hier um zu scannen!', |
||||||
|
'wifi.cant_scan_no_sta' => 'Klicke hier um Client-Modus zu aktivieren und zu scannen!', |
||||||
|
'wifi.select_ssid' => 'Verfügbare Netzwerke:', |
||||||
|
'wifi.enter_passwd' => 'Passwort für ":ssid:"', |
||||||
|
'wifi.sta_explain' => |
||||||
|
'Nach dem Auswählen eines Netzwerks, drücke Bestätigen, um dich zu verbinden.', |
||||||
|
|
||||||
|
// Wifi connecting status page |
||||||
|
|
||||||
|
'wificonn.status' => 'Status:', |
||||||
|
'wificonn.back_to_config' => 'Zurück zur WLAN-Konfiguration', |
||||||
|
'wificonn.telemetry_lost' => 'Telemetrie verloren; etwas lief schief, oder dein Gerät wurde getrennt.', |
||||||
|
'wificonn.explain_android_sucks' => ' |
||||||
|
Wenn du gerade ESPTerm mit einem Handy oder über ein anderes externes Netzwerk |
||||||
|
konfigurierst, kann dein Gerät die Verbindung verlieren und diese Fortschrittsanzeige |
||||||
|
wird nicht funktionieren. Bitte warte eine Weile (etwa 15 Sekunden) und prüfe dann, |
||||||
|
ob die Verbindung gelangen ist.', |
||||||
|
|
||||||
|
'wificonn.explain_reset' => ' |
||||||
|
Um den eingebauten AP zur Aktivierung zu zwingen, halte den BOOT-Knopf gedrückt bis die |
||||||
|
blaue LED beginnt, zu blinken. Halte ihn länger gedrückt (bis die LED schnell blinkt) |
||||||
|
um eine "Werksrückstellung" zu vollziehen.', |
||||||
|
|
||||||
|
'wificonn.disabled' => "Stationsmodus ist deaktiviert.", |
||||||
|
'wificonn.idle' => "Nicht verbunden und ohne IP.", |
||||||
|
'wificonn.success' => "Verbunden! Empfangene IP: ", |
||||||
|
'wificonn.working' => "Verbinden mit dem ausgewählten AP", |
||||||
|
'wificonn.fail' => "Verbindung fehlgeschlagen; prüfe die Einstellungen und versuche es erneut. Grund: ", |
||||||
|
|
||||||
|
// Access restrictions form |
||||||
|
|
||||||
|
'pwlock.title' => 'Zugriffsbeschränkungen', |
||||||
|
'pwlock.explain' => ' |
||||||
|
Manche, oder alle Teile des Web-Interface können mit einem Passwort geschützt werden. |
||||||
|
Lass die Passwortfelder leer wenn du es sie verändern möchtest.<br> |
||||||
|
Das voreingestellte Passwort ist "%def_access_pw%".', |
||||||
|
'pwlock.region' => 'Geschützte Seiten', |
||||||
|
'pwlock.region.none' => 'Keine, alles offen', |
||||||
|
'pwlock.region.settings_noterm' => 'WLAN-, Netzwerk- & Systemeinstellungen', |
||||||
|
'pwlock.region.settings' => 'Alle Einstellungsseiten', |
||||||
|
'pwlock.region.menus' => 'Dieser ganze Menüabschnitt', |
||||||
|
'pwlock.region.all' => 'Alles, sogar das Terminal', |
||||||
|
'pwlock.new_access_pw' => 'Neues Passwort', |
||||||
|
'pwlock.new_access_pw2' => 'Wiederholen', |
||||||
|
'pwlock.admin_pw' => 'Systempasswort', |
||||||
|
'pwlock.access_name' => 'Benutzername', |
||||||
|
|
||||||
|
// Setting admin password |
||||||
|
|
||||||
|
'adminpw.title' => 'Systempasswort ändern', |
||||||
|
'adminpw.explain' =>' |
||||||
|
Das "Systempasswort" wird benutzt, um die gespeicherten Standardeinstellungen |
||||||
|
und die Zugriffsbeschränkungen zu verändern. Dieses Passwort wird nicht als Teil |
||||||
|
der Hauptkonfiguration gespeichert, d.h. Speichern / Wiederherstellen wird das |
||||||
|
Passwort nicht beeinflussen. Wenn das Systempasswort vergessen wird, ist |
||||||
|
die einfachste Weise, wieder Zugriff zu erhalten, ein Re-flash des Chips.<br> |
||||||
|
Das voreingestellte Systempasswort ist "%def_admin_pw%". |
||||||
|
', |
||||||
|
'adminpw.new_admin_pw' => 'Neues Systempasswort', |
||||||
|
'adminpw.new_admin_pw2' => 'Wiederholen', |
||||||
|
'adminpw.old_admin_pw' => 'Altes Systempasswort', |
||||||
|
|
||||||
|
// Persist form |
||||||
|
|
||||||
|
'persist.title' => 'Speichern & Wiederherstellen', |
||||||
|
'persist.explain' => ' |
||||||
|
ESPTerm speichert alle Einstellungen im Flash-Speicher. Die aktiven Einstellungen |
||||||
|
können in den “Voreinstellungsbereich” kopiert werden und später wiederhergestellt |
||||||
|
werden mit der Taste unten.', |
||||||
|
'persist.confirm_restore' => 'Alle Einstellungen zu den Voreinstellungen zurücksetzen?', |
||||||
|
'persist.confirm_restore_hard' => ' |
||||||
|
Zurücksetzen zu den Firmware-Voreinstellungen? Dies wird alle aktiven |
||||||
|
Einstellungen zürucksetzen und den AP-Modus aktivieren mit der Standard-SSID.', |
||||||
|
'persist.confirm_store_defaults' => |
||||||
|
'Systempasswort eingeben um Voreinstellungen zu überschreiben', |
||||||
|
'persist.password' => 'Systempasswort:', |
||||||
|
'persist.restore_defaults' => 'Zu gespeicherten Voreinstellungen zurücksetzen', |
||||||
|
'persist.write_defaults' => 'Aktive Einstellungen als Voreinstellungen speichern', |
||||||
|
'persist.restore_hard' => 'Aktive Einstellungen zu Werkseinstellungen zurücksetzen', |
||||||
|
'persist.restore_hard_explain' => ' |
||||||
|
(Dies löscht die WLAN-Konfiguration! Beeinflusst die gespeicherten Voreinstellungen |
||||||
|
oder das Systempasswort nicht.)', |
||||||
|
|
||||||
|
// UART settings form |
||||||
|
|
||||||
|
'uart.title' => 'Serieller Port Parameter', |
||||||
|
'uart.explain' => ' |
||||||
|
Dies steuert den Kommunikations-UART. Der Debug-UART ist auf 115.200 baud fest |
||||||
|
eingestellt mit einem Stop-Bit und keiner Parität. |
||||||
|
', |
||||||
|
'uart.baud' => 'Baudrate', |
||||||
|
'uart.parity' => 'Parität', |
||||||
|
'uart.parity.none' => 'Keine', |
||||||
|
'uart.parity.odd' => 'Ungerade', |
||||||
|
'uart.parity.even' => 'Gerade', |
||||||
|
'uart.stop_bits' => 'Stop-Bits', |
||||||
|
'uart.stop_bits.one' => 'Eins', |
||||||
|
'uart.stop_bits.one_and_half' => 'Eineinhalb', |
||||||
|
'uart.stop_bits.two' => 'Zwei', |
||||||
|
|
||||||
|
// HW tuning form |
||||||
|
|
||||||
|
'hwtuning.title' => 'Hardware-Tuning', |
||||||
|
'hwtuning.explain' => ' |
||||||
|
ESP8266 kann übertaktet werden von 80 MHz auf 160 MHz. |
||||||
|
Alles wird etwas schneller sein, aber mit höherem Stromverbrauch, |
||||||
|
und eventuell auch mit höherer Interferenz. Mit Sorgfalt benutzen. |
||||||
|
', |
||||||
|
'hwtuning.overclock' => 'Übertakten', |
||||||
|
|
||||||
|
// Generic button / dialog labels |
||||||
|
|
||||||
|
'apply' => 'Bestätigen!', |
||||||
|
'start' => 'Starten', |
||||||
|
'cancel' => 'Abbrechen', |
||||||
|
'enabled' => 'Aktiviert', |
||||||
|
'disabled' => 'Deaktiviert', |
||||||
|
'yes' => 'Ja', |
||||||
|
'no' => 'Nein', |
||||||
|
'confirm' => 'OK', |
||||||
|
'copy' => 'Kopieren', |
||||||
|
'form_errors' => 'Gültigkeitsfehler für:', |
||||||
|
]; |
@ -0,0 +1,12 @@ |
|||||||
|
// define language keys used by JS here
|
||||||
|
module.exports = [ |
||||||
|
'wifi.connected_ip_is', |
||||||
|
'wifi.not_conn', |
||||||
|
'wifi.enter_passwd', |
||||||
|
'term_nav.fullscreen', |
||||||
|
'term_conn.connecting', |
||||||
|
'term_conn.waiting_content', |
||||||
|
'term_conn.disconnected', |
||||||
|
'term_conn.waiting_server', |
||||||
|
'term_conn.reconnecting' |
||||||
|
] |
@ -0,0 +1,92 @@ |
|||||||
|
<div class="Box fold"> |
||||||
|
<h2>Commands: Networking</h2> |
||||||
|
|
||||||
|
<div class="Row v"> |
||||||
|
<p> |
||||||
|
ESPTerm implements commands for device-to-device messaging and for requesting external |
||||||
|
servers. This can be used e.g. for remote control, status reporting or data upload / download. |
||||||
|
</p> |
||||||
|
|
||||||
|
<p> |
||||||
|
Networking commands use the format `\e^...\a`, a Privacy Message (PM). |
||||||
|
PM is similar to OSC, which uses `]` in place of `^`. The PM payload (text between `\e^` and `\a`) |
||||||
|
must be shorter than 256 bytes, and should not contain any control characters (ASCII < 32). |
||||||
|
</p> |
||||||
|
|
||||||
|
<h3>Device-to-device Messaging</h3> |
||||||
|
|
||||||
|
<p> |
||||||
|
To send a message to another ESPTerm module, use: `\e^M;<i>DestIP</i>;<i>message</i>\a`. |
||||||
|
</p> |
||||||
|
|
||||||
|
<p> |
||||||
|
This command sends a POST request to `http://<i><DestIP></i>/api/v1/msg`. |
||||||
|
The IP address may be appended by a port, if needed (eg. :8080). In addition to POST, |
||||||
|
a GET request can also be used. In that case, any GET arguments (`/api/v1/msg?<i>arguments</i>`) |
||||||
|
will be used instead of the request body. This is intended for external access |
||||||
|
when sending POST requests is not convenient. |
||||||
|
</p> |
||||||
|
|
||||||
|
<p> |
||||||
|
Each ESPTerm listens for such requests and relays them to UART: |
||||||
|
`\e^m;<i>SrcIP</i>;L=<i>length</i>;<i>message</i>\a`, with _length_ being the byte length of |
||||||
|
_message_, as ASCII. |
||||||
|
</p> |
||||||
|
|
||||||
|
<p> |
||||||
|
Notice a pattern with the first letter: capital is always a command, lower case a response. |
||||||
|
This is followed with the HTTP commands and any networking commands added in the future. |
||||||
|
</p> |
||||||
|
|
||||||
|
<p> |
||||||
|
*Example:* Node 192.168.0.10 sends a message to 192.168.0.19: `\e^M;192.168.0.19;Hello\a`. |
||||||
|
Node 192.168.0.19 receives `\e^m;192.168.0.10;L=5;Hello\a` on the UART. Note that the IP |
||||||
|
address in the reception message is that of the first node, thus it can be used to send a message back. |
||||||
|
</p> |
||||||
|
|
||||||
|
<h3>External HTTP requests</h3> |
||||||
|
|
||||||
|
<p> |
||||||
|
To request an external server, use `\e^H;<i>method</i>;<i>options</i>;<i>url</i>\n<i>body</i>\a`. |
||||||
|
</p> |
||||||
|
|
||||||
|
<ul> |
||||||
|
<li>`_method_` - can be any usual HTTP verb, such as `GET`, `POST`, `PUT`, `HEAD`. |
||||||
|
<li>`_options_` - is a comma-separated list of flags and parameters: |
||||||
|
<ul> |
||||||
|
<li>`H` - get response headers |
||||||
|
<li>`B` - get response body |
||||||
|
<li>`X` - ignore the response, return nothing |
||||||
|
<li>`N=<i>nonce</i>` - a custom string that will be added in the options field of the response message. |
||||||
|
Use this to keep track of which request a response belongs to. |
||||||
|
<li>`T=<i>ms</i>` - request timeout (default 5000~ms), in milliseconds |
||||||
|
<li>`L=<i>bytes</i>` - limit response length (default 0 = don't limit). Applies to the head, body, or both combined, depending on the `H` and `B` flags |
||||||
|
<li>`l=<i>bytes</i>` - limit the response buffer size (default 5000~B). |
||||||
|
This can reduce RAM usage, however it shouldn't be set too small, as this buffer |
||||||
|
is used for both headers and the response body. |
||||||
|
</ul> |
||||||
|
<li>`_url_` - full request URL, including `http://`. Port may be specified if different from :80, |
||||||
|
and GET arguments may be appended to the URL if needed. |
||||||
|
<li>`_body_` - optional, separated from `_url_` by a single line feed character (`\n`). |
||||||
|
This can be used for POST and PUT requests. Note: the command may be truncated to the |
||||||
|
maximum total length of 256 characters if too long. |
||||||
|
</ul> |
||||||
|
|
||||||
|
<p>The response has the following format: `\e^h;<i>status</i>;<i>options</i>;<i>response</i>\a`</p> |
||||||
|
|
||||||
|
<ul> |
||||||
|
<li>`_status_` - a HTTP status code, eg. 200 is OK, 404 Not found. |
||||||
|
<li>`_options_` - similar to those in the request, here describing the response data. |
||||||
|
This field can contain comma-separated `B`, `H` and `L=<i>bytes</i>` and `N=<i>nonce</i>`. |
||||||
|
<li>`_response_` - the response, as requested. If both headers and body are received, |
||||||
|
they will be separated by an empty line (i.e. `\r\n\r\n`). Response can be up to several |
||||||
|
kilobytes long, depending on the `L=` and `l=` options. |
||||||
|
</ul> |
||||||
|
|
||||||
|
<p> |
||||||
|
*Example:* `\e^H;GET;B;http://wtfismyip.com/text\a` - get the body of a web page |
||||||
|
(wtfismyip.com is a service that sends back your IP address). |
||||||
|
A response could be `\e^h;200;B,L=11;80.70.60.50\a`. |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
</div> |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue