mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2025-12-13 11:02:12 +01:00
Expand MQTT hostname to 64 bytes #103
This commit is contained in:
parent
f62dcef161
commit
adb1fd3aaa
9 changed files with 85 additions and 32 deletions
|
|
@ -57,6 +57,7 @@ double BaseSettings::parseValueDouble(JsonObject &obj, const char *prop, double
|
||||||
}
|
}
|
||||||
bool ConfigSettings::begin() {
|
bool ConfigSettings::begin() {
|
||||||
uint32_t chipId = 0;
|
uint32_t chipId = 0;
|
||||||
|
|
||||||
uint64_t mac = ESP.getEfuseMac();
|
uint64_t mac = ESP.getEfuseMac();
|
||||||
for(int i=0; i<17; i=i+8) {
|
for(int i=0; i<17; i=i+8) {
|
||||||
chipId |= ((mac >> (40 - i)) & 0xff) << i;
|
chipId |= ((mac >> (40 - i)) & 0xff) << i;
|
||||||
|
|
@ -80,6 +81,7 @@ bool ConfigSettings::load() {
|
||||||
pref.getString("hostname", this->hostname, sizeof(this->hostname));
|
pref.getString("hostname", this->hostname, sizeof(this->hostname));
|
||||||
this->ssdpBroadcast = pref.getBool("ssdpBroadcast", true);
|
this->ssdpBroadcast = pref.getBool("ssdpBroadcast", true);
|
||||||
this->connType = static_cast<conn_types>(pref.getChar("connType", 0x00));
|
this->connType = static_cast<conn_types>(pref.getChar("connType", 0x00));
|
||||||
|
Serial.printf("Preference GFG Free Entries: %d\n", pref.freeEntries());
|
||||||
pref.end();
|
pref.end();
|
||||||
if(this->connType == conn_types::unset) {
|
if(this->connType == conn_types::unset) {
|
||||||
// We are doing this to convert the data from previous versions.
|
// We are doing this to convert the data from previous versions.
|
||||||
|
|
@ -290,6 +292,7 @@ bool IPSettings::load() {
|
||||||
pref.getString("dns2", buff, sizeof(buff));
|
pref.getString("dns2", buff, sizeof(buff));
|
||||||
this->dns2.fromString(buff);
|
this->dns2.fromString(buff);
|
||||||
}
|
}
|
||||||
|
Serial.printf("Preference IP Free Entries: %d\n", pref.freeEntries());
|
||||||
pref.end();
|
pref.end();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#ifndef configsettings_h
|
#ifndef configsettings_h
|
||||||
#define configsettings_h
|
#define configsettings_h
|
||||||
|
|
||||||
#define FW_VERSION "v2.0.0"
|
#define FW_VERSION "v2.0.1"
|
||||||
enum DeviceStatus {
|
enum DeviceStatus {
|
||||||
DS_OK = 0,
|
DS_OK = 0,
|
||||||
DS_ERROR = 1,
|
DS_ERROR = 1,
|
||||||
|
|
@ -25,7 +25,7 @@ class BaseSettings {
|
||||||
};
|
};
|
||||||
class NTPSettings: BaseSettings {
|
class NTPSettings: BaseSettings {
|
||||||
public:
|
public:
|
||||||
char ntpServer[64] = "pool.ntp.org";
|
char ntpServer[65] = "pool.ntp.org";
|
||||||
char posixZone[64] = "";
|
char posixZone[64] = "";
|
||||||
bool fromJSON(JsonObject &obj);
|
bool fromJSON(JsonObject &obj);
|
||||||
bool toJSON(JsonObject &obj);
|
bool toJSON(JsonObject &obj);
|
||||||
|
|
@ -38,8 +38,8 @@ class NTPSettings: BaseSettings {
|
||||||
class WifiSettings: BaseSettings {
|
class WifiSettings: BaseSettings {
|
||||||
public:
|
public:
|
||||||
WifiSettings();
|
WifiSettings();
|
||||||
char ssid[64] = "";
|
char ssid[65] = "";
|
||||||
char passphrase[64] = "";
|
char passphrase[65] = "";
|
||||||
//bool ssdpBroadcast = true;
|
//bool ssdpBroadcast = true;
|
||||||
bool begin();
|
bool begin();
|
||||||
bool fromJSON(JsonObject &obj);
|
bool fromJSON(JsonObject &obj);
|
||||||
|
|
@ -111,12 +111,12 @@ class SecuritySettings: BaseSettings {
|
||||||
class MQTTSettings: BaseSettings {
|
class MQTTSettings: BaseSettings {
|
||||||
public:
|
public:
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
char hostname[32] = "";
|
char hostname[65] = "";
|
||||||
char protocol[10] = "mqtt://";
|
char protocol[10] = "mqtt://";
|
||||||
uint16_t port = 1883;
|
uint16_t port = 1883;
|
||||||
char username[32] = "";
|
char username[33] = "";
|
||||||
char password[32] = "";
|
char password[33] = "";
|
||||||
char rootTopic[64] = "";
|
char rootTopic[65] = "";
|
||||||
bool begin();
|
bool begin();
|
||||||
bool save();
|
bool save();
|
||||||
bool load();
|
bool load();
|
||||||
|
|
|
||||||
|
|
@ -2817,6 +2817,8 @@ void Transceiver::emitFrame(somfy_frame_t *frame, somfy_rx_t *rx) {
|
||||||
evt.appendMessage(buf);
|
evt.appendMessage(buf);
|
||||||
snprintf(buf, sizeof(buf), "\"bits\":%d,", rx->bit_length);
|
snprintf(buf, sizeof(buf), "\"bits\":%d,", rx->bit_length);
|
||||||
evt.appendMessage(buf);
|
evt.appendMessage(buf);
|
||||||
|
snprintf(buf, sizeof(buf), "\"proto\":%d,", static_cast<uint8_t>(frame->proto));
|
||||||
|
evt.appendMessage(buf);
|
||||||
snprintf(buf, sizeof(buf), "\"valid\":%s,", frame->valid ? "true" : "false");
|
snprintf(buf, sizeof(buf), "\"valid\":%s,", frame->valid ? "true" : "false");
|
||||||
evt.appendMessage(buf);
|
evt.appendMessage(buf);
|
||||||
snprintf(buf, sizeof(buf), "\"sync\":%d,\"pulses\":[", frame->hwsync);
|
snprintf(buf, sizeof(buf), "\"sync\":%d,\"pulses\":[", frame->hwsync);
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
6
Web.cpp
6
Web.cpp
|
|
@ -2131,7 +2131,7 @@ void Web::begin() {
|
||||||
});
|
});
|
||||||
server.on("/connectmqtt", []() {
|
server.on("/connectmqtt", []() {
|
||||||
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
if(server.method() == HTTP_OPTIONS) { server.send(200, "OK"); return; }
|
||||||
DynamicJsonDocument doc(512);
|
DynamicJsonDocument doc(1024);
|
||||||
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
DeserializationError err = deserializeJson(doc, server.arg("plain"));
|
||||||
if (err) {
|
if (err) {
|
||||||
Serial.print("Error parsing JSON ");
|
Serial.print("Error parsing JSON ");
|
||||||
|
|
@ -2150,7 +2150,7 @@ void Web::begin() {
|
||||||
settings.MQTT.fromJSON(obj);
|
settings.MQTT.fromJSON(obj);
|
||||||
settings.MQTT.save();
|
settings.MQTT.save();
|
||||||
|
|
||||||
StaticJsonDocument<512> sdoc;
|
DynamicJsonDocument sdoc(1024);
|
||||||
JsonObject sobj = sdoc.to<JsonObject>();
|
JsonObject sobj = sdoc.to<JsonObject>();
|
||||||
settings.MQTT.toJSON(sobj);
|
settings.MQTT.toJSON(sobj);
|
||||||
serializeJson(sdoc, g_content);
|
serializeJson(sdoc, g_content);
|
||||||
|
|
@ -2163,7 +2163,7 @@ void Web::begin() {
|
||||||
});
|
});
|
||||||
server.on("/mqttsettings", []() {
|
server.on("/mqttsettings", []() {
|
||||||
webServer.sendCORSHeaders();
|
webServer.sendCORSHeaders();
|
||||||
DynamicJsonDocument doc(512);
|
DynamicJsonDocument doc(1024);
|
||||||
JsonObject obj = doc.to<JsonObject>();
|
JsonObject obj = doc.to<JsonObject>();
|
||||||
settings.MQTT.toJSON(obj);
|
settings.MQTT.toJSON(obj);
|
||||||
serializeJson(doc, g_content);
|
serializeJson(doc, g_content);
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="stylesheet" href="main.css?v=2.0.0" type="text/css" />
|
<link rel="stylesheet" href="main.css?v=2.0.1" type="text/css" />
|
||||||
<link rel="stylesheet" href="widgets.css?v=2.0.0" type="text/css" />
|
<link rel="stylesheet" href="widgets.css?v=2.0.1" type="text/css" />
|
||||||
<link rel="stylesheet" href="icons.css?v=2.0.0" type="text/css" />
|
<link rel="stylesheet" href="icons.css?v=2.0.0" type="text/css" />
|
||||||
<link rel="icon" type="image/png" href="favicon.png" />
|
<link rel="icon" type="image/png" href="favicon.png" />
|
||||||
<script type="text/javascript" src="index.js?v=2.0.0"></script>
|
<script type="text/javascript" src="index.js?v=2.0.1"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="divContainer" class="container main" data-auth="false">
|
<div id="divContainer" class="container main" data-auth="false">
|
||||||
|
|
@ -251,7 +251,7 @@
|
||||||
<option>MQTTS</option>
|
<option>MQTTS</option>
|
||||||
</select>
|
</select>
|
||||||
<span style="">://</span>
|
<span style="">://</span>
|
||||||
<input name="mqtt-host" type="text" length=32 placeholder="Host" data-bind="mqtt.hostname" style="width:calc(100% - 137px);">
|
<input name="mqtt-host" type="text" length=64 placeholder="Host" data-bind="mqtt.hostname" style="width:calc(100% - 137px);">
|
||||||
<span>:</span>
|
<span>:</span>
|
||||||
<input name="mqtt-port" type="text" length=5 placeholder="Port" data-bind="mqtt.port" data-datatype="int" style="width:50px;">
|
<input name="mqtt-port" type="text" length=5 placeholder="Port" data-bind="mqtt.port" data-datatype="int" style="width:50px;">
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -291,6 +291,7 @@
|
||||||
<select id="selShadeProto" name="proto" data-bind="proto" data-datatype="int" style="width:100%;">
|
<select id="selShadeProto" name="proto" data-bind="proto" data-datatype="int" style="width:100%;">
|
||||||
<option value="0">RTS</option>
|
<option value="0">RTS</option>
|
||||||
<option value="1">RTW</option>
|
<option value="1">RTW</option>
|
||||||
|
<option value="2">RTV</option>
|
||||||
</select>
|
</select>
|
||||||
<label for="selShadeProto">Protocol</label>
|
<label for="selShadeProto">Protocol</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1186,7 +1186,7 @@ var security = new Security();
|
||||||
|
|
||||||
class General {
|
class General {
|
||||||
initialized = false;
|
initialized = false;
|
||||||
appVersion = 'v2.0.0';
|
appVersion = 'v2.0.1';
|
||||||
reloadApp = false;
|
reloadApp = false;
|
||||||
init() {
|
init() {
|
||||||
if (this.initialized) return;
|
if (this.initialized) return;
|
||||||
|
|
@ -1380,15 +1380,19 @@ class General {
|
||||||
let pnl = document.getElementById('divSystemSettings');
|
let pnl = document.getElementById('divSystemSettings');
|
||||||
let obj = ui.fromElement(pnl).general;
|
let obj = ui.fromElement(pnl).general;
|
||||||
if (typeof obj.hostname === 'undefined' || !obj.hostname || obj.hostname === '') {
|
if (typeof obj.hostname === 'undefined' || !obj.hostname || obj.hostname === '') {
|
||||||
ui.errorMessage(pnl, 'You must supply a valid host name.');
|
ui.errorMessage('Invalid Host Name').querySelector('.sub-message').innerHTML = 'You must supply a valid Host Name.';
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
if (valid && !/^[a-zA-Z0-9-]+$/.test(obj.hostname)) {
|
if (valid && !/^[a-zA-Z0-9-]+$/.test(obj.hostname)) {
|
||||||
ui.errorMessage(pnl, 'The host name must only include numbers, letters, or dash.');
|
ui.errorMessage('Invalid Host Name').querySelector('.sub-message').innerHTML = 'The host name must only include numbers, letters, or dash.';
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
if (valid && obj.hostname.length > 32) {
|
if (valid && obj.hostname.length > 32) {
|
||||||
ui.errorMessage(pnl, 'The host name can only be up to 32 characters long.');
|
ui.errorMessage('Invalid Host Name').querySelector('.sub-message').innerHTML = 'The maximum Host Name length is 32 characters.';
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
if (valid && typeof obj.ntpServer === 'string' && obj.ntpServer.length > 64) {
|
||||||
|
ui.errorMessage('Invalid NTP Server').querySelector('.sub-message').innerHTML = 'The maximum NTP Server length is 64 characters.';
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
if (valid) {
|
if (valid) {
|
||||||
|
|
@ -1471,10 +1475,20 @@ class General {
|
||||||
ui.errorMessage('No Username Provided').querySelector('.sub-message').innerHTML = 'You must provide a username for password security.';
|
ui.errorMessage('No Username Provided').querySelector('.sub-message').innerHTML = 'You must provide a username for password security.';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (sec.username.length > 32) {
|
||||||
|
ui.errorMessage('Invalid Username').querySelector('.sub-message').innerHTML = 'The maximum username length is 32 characters.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (sec.password.length === 0) {
|
if (sec.password.length === 0) {
|
||||||
ui.errorMessage('No Password Provided').querySelector('.sub-message').innerHTML = 'You must provide a password for password security.';
|
ui.errorMessage('No Password Provided').querySelector('.sub-message').innerHTML = 'You must provide a password for password security.';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (sec.password.length > 32) {
|
||||||
|
ui.errorMessage('Invalid Password').querySelector('.sub-message').innerHTML = 'The maximum password length is 32 characters.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (security.repeatpassword.length === 0) {
|
if (security.repeatpassword.length === 0) {
|
||||||
ui.errorMessage('Re-enter Password').querySelector('.sub-message').innerHTML = 'You must re-enter the password in the Re-enter Password field.';
|
ui.errorMessage('Re-enter Password').querySelector('.sub-message').innerHTML = 'You must re-enter the password in the Re-enter Password field.';
|
||||||
return;
|
return;
|
||||||
|
|
@ -1647,7 +1661,7 @@ class Wifi {
|
||||||
encryption: el.getAttribute('data-encryption'),
|
encryption: el.getAttribute('data-encryption'),
|
||||||
strength: parseInt(el.getAttribute('data-strength'), 10),
|
strength: parseInt(el.getAttribute('data-strength'), 10),
|
||||||
channel: parseInt(el.getAttribute('data-channel'), 10)
|
channel: parseInt(el.getAttribute('data-channel'), 10)
|
||||||
}
|
};
|
||||||
console.log(obj);
|
console.log(obj);
|
||||||
document.getElementsByName('ssid')[0].value = obj.name;
|
document.getElementsByName('ssid')[0].value = obj.name;
|
||||||
}
|
}
|
||||||
|
|
@ -1762,7 +1776,17 @@ class Wifi {
|
||||||
let obj = {
|
let obj = {
|
||||||
ssid: document.getElementsByName('ssid')[0].value,
|
ssid: document.getElementsByName('ssid')[0].value,
|
||||||
passphrase: document.getElementsByName('passphrase')[0].value
|
passphrase: document.getElementsByName('passphrase')[0].value
|
||||||
|
};
|
||||||
|
if (obj.ssid.length > 64) {
|
||||||
|
ui.errorMessage('Invalid SSID').querySelector('.sub-message').innerHTML = 'The maximum length of the SSID is 64 characters.';
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
if (obj.passphrase.length > 64) {
|
||||||
|
ui.errorMessage('Invalid Passphrase').querySelector('.sub-message').innerHTML = 'The maximum length of the passphrase is 64 characters.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
let overlay = ui.waitMessage(document.getElementById('divNetAdapter'));
|
let overlay = ui.waitMessage(document.getElementById('divNetAdapter'));
|
||||||
putJSON('/connectwifi', obj, (err, response) => {
|
putJSON('/connectwifi', obj, (err, response) => {
|
||||||
overlay.remove();
|
overlay.remove();
|
||||||
|
|
@ -2280,12 +2304,21 @@ class Somfy {
|
||||||
// the browser.
|
// the browser.
|
||||||
let fnFmtDate = (dt) => {
|
let fnFmtDate = (dt) => {
|
||||||
return `${(dt.getMonth() + 1).fmt('00')}/${dt.getDate().fmt('00')} ${dt.getHours().fmt('00')}:${dt.getMinutes().fmt('00')}:${dt.getSeconds().fmt('00')}.${dt.getMilliseconds().fmt('000')}`;
|
return `${(dt.getMonth() + 1).fmt('00')}/${dt.getDate().fmt('00')} ${dt.getHours().fmt('00')}:${dt.getMinutes().fmt('00')}:${dt.getSeconds().fmt('00')}.${dt.getMilliseconds().fmt('000')}`;
|
||||||
}
|
};
|
||||||
let fnFmtTime = (dt) => {
|
let fnFmtTime = (dt) => {
|
||||||
return `${dt.getHours().fmt('00')}:${dt.getMinutes().fmt('00')}:${dt.getSeconds().fmt('00')}.${dt.getMilliseconds().fmt('000')}`;
|
return `${dt.getHours().fmt('00')}:${dt.getMinutes().fmt('00')}:${dt.getSeconds().fmt('00')}.${dt.getMilliseconds().fmt('000')}`;
|
||||||
}
|
};
|
||||||
frame.time = new Date();
|
frame.time = new Date();
|
||||||
let html = `<span>${frame.encKey}</span><span>${frame.address}</span><span>${frame.command}</span><span>${frame.rcode}</span><span>${frame.rssi}dBm</span><span>${frame.bits}</span><span>${fnFmtTime(frame.time)}</span><div class="frame-pulses">`;
|
let proto = '-S';
|
||||||
|
switch (frame.proto) {
|
||||||
|
case 1:
|
||||||
|
proto = '-W';
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
proto = '-V';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let html = `<span>${frame.encKey}</span><span>${frame.address}</span><span>${frame.command}</span><span>${frame.rcode}</span><span>${frame.rssi}dBm</span><span>${frame.bits}${proto}</span><span>${fnFmtTime(frame.time)}</span><div class="frame-pulses">`;
|
||||||
for (let i = 0; i < frame.pulses.length; i++) {
|
for (let i = 0; i < frame.pulses.length; i++) {
|
||||||
if (i !== 0) html += ',';
|
if (i !== 0) html += ',';
|
||||||
html += `${frame.pulses[i]}`;
|
html += `${frame.pulses[i]}`;
|
||||||
|
|
@ -3204,16 +3237,30 @@ class MQTT {
|
||||||
console.log(obj);
|
console.log(obj);
|
||||||
if (obj.mqtt.enabled) {
|
if (obj.mqtt.enabled) {
|
||||||
if (typeof obj.mqtt.hostname !== 'string' || obj.mqtt.hostname.length === 0) {
|
if (typeof obj.mqtt.hostname !== 'string' || obj.mqtt.hostname.length === 0) {
|
||||||
ui.errorMessage('Invalid host name.').querySelector('.sub-message').innerHTML = 'You must supply a host name to connect to MQTT.';
|
ui.errorMessage('Invalid host name').querySelector('.sub-message').innerHTML = 'You must supply a host name to connect to MQTT.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (obj.mqtt.hostname.length > 64) {
|
||||||
|
ui.errorMessage('Invalid host name').querySelector('.sub-message').innerHTML = 'The maximum length of the host name is 64 characters.';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isNaN(obj.mqtt.port) || obj.mqtt.port < 0) {
|
if (isNaN(obj.mqtt.port) || obj.mqtt.port < 0) {
|
||||||
ui.errorMessage('Invalid port number.').querySelector('.sub-message').innerHTML = 'Likely ports are 1183, 8883 for MQTT/S or 80,443 for HTTP/S';
|
ui.errorMessage('Invalid port number').querySelector('.sub-message').innerHTML = 'Likely ports are 1183, 8883 for MQTT/S or 80,443 for HTTP/S';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof obj.mqtt.username === 'string' && obj.mqtt.username.length > 32) {
|
||||||
|
ui.errorMessage('Invalid Username').querySelector('.sub-message').innerHTML = 'The maximum length of the username is 32 characters.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof obj.mqtt.password === 'string' && obj.mqtt.password.length > 32) {
|
||||||
|
ui.errorMessage('Invalid Password').querySelector('.sub-message').innerHTML = 'The maximum length of the password is 32 characters.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof obj.mqtt.rootTopic === 'string' && obj.mqtt.rootTopic.length > 64) {
|
||||||
|
ui.errorMessage('Invalid Root Topic').querySelector('.sub-message').innerHTML = 'The maximum length of the root topic is 64 characters.';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
putJSONSync('/connectmqtt', obj.mqtt, (err, response) => {
|
putJSONSync('/connectmqtt', obj.mqtt, (err, response) => {
|
||||||
if (err) ui.serviceError(err);
|
if (err) ui.serviceError(err);
|
||||||
console.log(response);
|
console.log(response);
|
||||||
|
|
|
||||||
|
|
@ -769,20 +769,20 @@ div.frame-header > span {
|
||||||
}
|
}
|
||||||
div.frame-row > span:nth-child(1),
|
div.frame-row > span:nth-child(1),
|
||||||
div.frame-header > span:nth-child(1) {
|
div.frame-header > span:nth-child(1) {
|
||||||
width:30px;
|
width:25px;
|
||||||
}
|
}
|
||||||
div.frame-row > span:nth-child(2),
|
div.frame-row > span:nth-child(2),
|
||||||
div.frame-header > span:nth-child(2) {
|
div.frame-header > span:nth-child(2) {
|
||||||
width: 67px;
|
width: 64px;
|
||||||
}
|
}
|
||||||
div.frame-row > span:nth-child(3),
|
div.frame-row > span:nth-child(3),
|
||||||
div.frame-header > span:nth-child(3) {
|
div.frame-header > span:nth-child(3) {
|
||||||
width: 77px;
|
width: 80px;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
}
|
}
|
||||||
div.frame-row > span:nth-child(4),
|
div.frame-row > span:nth-child(4),
|
||||||
div.frame-header > span:nth-child(4) {
|
div.frame-header > span:nth-child(4) {
|
||||||
width: 50px;
|
width: 43px;
|
||||||
}
|
}
|
||||||
div.frame-row > span:nth-child(5),
|
div.frame-row > span:nth-child(5),
|
||||||
div.frame-header > span:nth-child(5) {
|
div.frame-header > span:nth-child(5) {
|
||||||
|
|
@ -790,7 +790,7 @@ div.frame-header > span {
|
||||||
}
|
}
|
||||||
div.frame-row > span:nth-child(6),
|
div.frame-row > span:nth-child(6),
|
||||||
div.frame-header > span:nth-child(6) {
|
div.frame-header > span:nth-child(6) {
|
||||||
width: 27px;
|
width: 42px;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
}
|
}
|
||||||
div.frame-row > span:nth-child(7),
|
div.frame-row > span:nth-child(7),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue