Browse Source

fixes, added about page

master
Ondřej Hruška 6 years ago
parent
commit
46d861e0dc
  1. 6
      _web-build_do.sh
  2. 7
      esp_meas.pro
  3. 2
      esp_meas.pro.user
  4. 2
      html/css/app.css
  5. 5
      html/img/cvut.svg
  6. 5
      html/js/all.js
  7. 66
      html/pages/about.tpl
  8. 85
      html/pages/fft.html
  9. 6
      html/pages/status.tpl
  10. 12
      html/pages/wfm.html
  11. 4
      html/pages/wifi.tpl
  12. 6
      html_src/_start.php
  13. 2
      html_src/css/app.css
  14. 2
      html_src/css/app.css.map
  15. 1
      html_src/gulpfile.js
  16. 5
      html_src/img/cvut.svg
  17. 2
      html_src/js-src/lib/chartist.axis-title.js
  18. 71
      html_src/js-src/lib/chartist.js
  19. 547
      html_src/js-src/lib/chartist.zoom.js
  20. 3344
      html_src/js-src/lib/lodash.custom.js
  21. 226
      html_src/js-src/page_waveform.js
  22. 4
      html_src/js-src/page_wifi.js
  23. 32
      html_src/js-src/utils.js
  24. 5
      html_src/js/all.js
  25. 2
      html_src/js/all.js.map
  26. 44
      html_src/page_about.php
  27. 63
      html_src/page_fft.php
  28. 2
      html_src/page_status.php
  29. 11
      html_src/page_waveform.php
  30. 19
      html_src/sass/app.scss
  31. 2
      html_src/sass/form/_buttons.scss
  32. 2
      html_src/sass/layout/_box.scss
  33. 2
      html_src/sass/lib/chartist/_chartist-settings.scss
  34. 4
      html_src/sass/lib/chartist/_chartist.scss
  35. 17
      html_src/sass/pages/_about.scss
  36. 23
      html_src/sass/pages/_wfm.scss
  37. 0
      html_src/x_page_layout.php
  38. 2
      libesphttpd/include/httpd.h
  39. 2
      libesphttpd/include/logging.h
  40. 6
      user/fw_version.h
  41. 35
      user/page_about.c
  42. 8
      user/page_about.h
  43. 8
      user/routes.c
  44. 8
      user/user_main.c

6
_web-build_do.sh

@ -9,6 +9,7 @@ BLDDIR=html
rm -rf "$BLDDIR/pages"
rm -rf "$BLDDIR/js"
rm -rf "$BLDDIR/css"
rm -rf "$BLDDIR/img"
cd "$SRCDIR"
gulp --production
@ -16,11 +17,14 @@ cd ..
cp -R "$SRCDIR/css" "$BLDDIR"
cp -R "$SRCDIR/js" "$BLDDIR"
cp -R "$SRCDIR/img" "$BLDDIR"
find "$BLDDIR" -name "*.map" -delete
mkdir -p "$BLDDIR/pages"
php "$SRCDIR/page_status.php" > "$BLDDIR/pages/status.tpl"
php "$SRCDIR/page_about.php" > "$BLDDIR/pages/about.tpl"
php "$SRCDIR/page_wifi.php" > "$BLDDIR/pages/wifi.tpl"
php "$SRCDIR/page_waveform.php" > "$BLDDIR/pages/waveform.html" # no substitutions, .html allows to gzip it.
php "$SRCDIR/page_waveform.php" > "$BLDDIR/pages/wfm.html" # no substitutions, .html allows to gzip it.
php "$SRCDIR/page_fft.php" > "$BLDDIR/pages/fft.html" # same

7
esp_meas.pro

@ -60,7 +60,8 @@ SOURCES += \
user/page_status.c \
user/page_waveform.c \
user/utils.c \
sbmp/library/payload_builder.c
sbmp/library/payload_builder.c \
user/page_about.c
HEADERS += \
include/uart_hw.h \
@ -142,7 +143,9 @@ HEADERS += \
user/page_waveform.h \
libesphttpd/include/espmissingprotos.h \
user/utils.h \
sbmp/library/payload_builder.h
sbmp/library/payload_builder.h \
user/page_about.h \
user/fw_version.h
DISTFILES += \
style.astylerc \

2
esp_meas.pro.user

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 3.6.1, 2016-03-29T21:53:03. -->
<!-- Written by QtCreator 3.6.1, 2016-03-31T02:51:43. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>

2
html/css/app.css

File diff suppressed because one or more lines are too long

5
html/img/cvut.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.8 KiB

5
html/js/all.js

File diff suppressed because one or more lines are too long

66
html/pages/about.tpl

@ -0,0 +1,66 @@
<!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>About - Current analyzer</title>
<link href="/css/app.css" rel="stylesheet">
<script src="/js/all.js"></script>
</head>
<body class="page-about">
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/about" class="selected">About</a></nav>
<div id="content">
<h1>About</h1>
<div class="Box">
<img src="/img/cvut.svg" id="logo" class="mq-tablet-min">
<h2>Current Analyser</h2>
<img src="/img/cvut.svg" id="logo2" class="mq-phone">
<p>&copy; Ondřej Hruška, 2016 &lt;<a href="mailto:ondra@ondrovo.com" target="blank">ondra@ondrovo.com</a>&gt;</p>
<p><a href="http://measure.feld.cvut.cz/" target="blank">Katedra měření FEL ČVUT</a><br>Department of Measurement, FEE CTU</p>
</div>
<div class="Box">
<h2>Firmware</h2>
<p>
The ESP8266 firmware is based on the amazing <a href="https://github.com/Spritetm/esphttpd" target="blank">esp-httpd</a>
library by Jeroen Domburg.
</p>
<table>
<tr>
<th>Firmware</th>
<td>v%vers_fw%, build <i>%date%</i> at <i>%time%</i></td>
</tr>
<tr>
<th>HTTPD</th>
<td>v%vers_httpd%</td>
</tr>
<tr>
<th>SBMP</th>
<td>v%vers_sbmp%</td>
</tr>
<tr>
<th>IoT SDK</th>
<td>v%vers_sdk%</td>
</tr>
</table>
</div>
</div>
</div>
</body>
</html>

85
html/pages/fft.html

@ -0,0 +1,85 @@
<!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>FFT - Current analyzer</title>
<link href="/css/app.css" rel="stylesheet">
<script src="/js/all.js"></script>
</head>
<body class="page-fft">
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft" class="selected">FFT</a><a href="/about">About</a></nav>
<div id="content">
<h1>FFT</h1>
<div class="Box center" id="samp-ctrl">
<div>
<label for="count">Bins</label>
<label for="count" class="select-wrap">
<select name="count" id="count">
<!-- <option value="16">8-->
<!-- <option value="32">16-->
<!-- <option value="64">32-->
<!-- <option value="128">64-->
<!-- <option value="256">128-->
<option value="512">256
<option value="1024">512
<option value="2048" selected>1024
</select>
</label>
</div>
<div>
<label for="freq">Rate <span class="mq-tablet-max" style="font-weight:normal;">(Hz)</span></label>
<input id="freq" type="number" value="4096">
<span class="mq-normal-min">Hz</span>
</div>
<div>
<a id="load" class="button btn-green">Load</a>
</div>
</div>
<div class="Box medium chartbox">
<div id="chart" class="ct-chart ct-wide ct-with-area"></div>
<div class="stats invis">
<table>
<tr>
<th>Samples</th>
<td id="stat-count"></td>
</tr>
<tr>
<th>f<sub>s</sub></th>
<td id="stat-f-s"></td>
</tr>
<tr>
<th>I<sub>peak</sub></th>
<td id="stat-i-peak"></td>
</tr>
<tr>
<th>I<sub>RMS</sub></th>
<td id="stat-i-rms"></td>
</tr>
</table>
<div class="ar"><!-- auto reload -->
<input type="number" id="ar-time" step="0.5" value="1" min="0">&nbsp;s
<input type="button" id="ar-btn" class="btn-blue narrow" value="Auto">
</div>
</div>
</div>
<script>
$().ready(page_waveform.init('fft'));
</script>
</div>
</div>
</body>
</html>

6
html/pages/status.tpl

@ -15,8 +15,8 @@
<body class="page-home">
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current analyzer</div>
<a href="/" class="selected">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a></nav>
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/" class="selected">Home</a><a href="/wifi">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/about">About</a></nav>
<div id="content">
<h1>System Status</h1>
@ -24,7 +24,6 @@
<div class="Box">
<h2>Runtime</h2>
<table>
<tbody>
<tr>
<th>Uptime:</th>
<td id="uptime">%uptime%</td>
@ -33,7 +32,6 @@
<th>Free heap:</th>
<td id="heap">%heap%</td>
</tr>
</tbody>
</table>
</div>

12
html/pages/waveform.html → html/pages/wfm.html

@ -15,8 +15,8 @@
<body class="page-waveform">
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current analyzer</div>
<a href="/">Home</a><a href="/wifi">WiFi config</a><a href="/waveform" class="selected">Waveform</a></nav>
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/">Home</a><a href="/wifi">WiFi config</a><a href="/waveform" class="selected">Waveform</a><a href="/fft">FFT</a><a href="/about">About</a></nav>
<div id="content">
<h1>Waveform</h1>
@ -41,7 +41,7 @@
<div class="stats invis">
<table>
<tr>
<th>#</th>
<th>Samples</th>
<td id="stat-count"></td>
</tr>
<tr>
@ -57,11 +57,15 @@
<td id="stat-i-rms"></td>
</tr>
</table>
<div class="ar"><!-- auto reload -->
<input type="number" id="ar-time" step="0.5" value="1" min="0">&nbsp;s
<input type="button" id="ar-btn" class="btn-blue narrow" value="Auto">
</div>
</div>
</div>
<script>
$().ready(page_waveform.init());
$().ready(page_waveform.init('raw'));
</script>
</div>

4
html/pages/wifi.tpl

@ -15,8 +15,8 @@
<body class="page-wifi">
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current analyzer</div>
<a href="/">Home</a><a href="/wifi" class="selected">WiFi config</a><a href="/waveform">Waveform</a></nav>
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<a href="/">Home</a><a href="/wifi" class="selected">WiFi config</a><a href="/waveform">Waveform</a><a href="/fft">FFT</a><a href="/about">About</a></nav>
<div id="content">
<h1>Wireless Setup</h1>

6
html_src/_start.php

@ -4,10 +4,10 @@ $menu = [
'home' => [ '/', 'Home' ],
'wifi' => [ '/wifi', 'WiFi config' ],
'waveform' => [ '/waveform', 'Waveform' ],
// 'fft' => [ '/fft', 'FFT' ],
'fft' => [ '/fft', 'FFT' ],
// 'spectrogram' => [ '/spectrogram', 'Spectrogram' ],
// 'transient' => [ '/transient', 'Power-on transient' ],
// 'about' => [ '/about', 'Credits & About' ],
'about' => [ '/about', 'About' ],
];
$appname = 'Current analyzer';
@ -37,7 +37,7 @@ $appname = 'Current analyzer';
<body class="page-<?=$page?>">
<div id="outer">
<nav id="menu">
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current analyzer</div>
<div id="brand" onclick="$('#menu').toggleClass('expanded')">Current Analyser</div>
<?php
// generate the menu
foreach($menu as $k => $m) {

2
html_src/css/app.css

File diff suppressed because one or more lines are too long

2
html_src/css/app.css.map

File diff suppressed because one or more lines are too long

1
html_src/gulpfile.js

@ -24,6 +24,7 @@ elixir(function (mix) {
'js-src/lib/chartist.js',
'js-src/lib/chartist.axis-title.js',
'js-src/lib/chartist.zoom.js',
'js-src/lib/lodash.custom.js',
'js-src/utils.js',
'js-src/modal.js',
'js-src/app.js',

5
html_src/img/cvut.svg

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.8 KiB

2
html_src/js-src/lib/chartist.axis-title.js

@ -42,8 +42,6 @@
options = Chartist.extend({}, defaultOptions, options);
console.log(options);
return function ctAxisTitle(chart) {
chart.on('created', function (data) {

71
html_src/js-src/lib/chartist.js

@ -253,13 +253,13 @@ var Chartist = {
* @memberof Chartist.Core
* @type {Object}
*/
Chartist.escapingMap = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
'\'': '&#039;'
};
// Chartist.escapingMap = {
// '&': '&amp;',
// '<': '&lt;',
// '>': '&gt;',
// '"': '&quot;',
// '\'': '&#039;'
// };
/**
* This function serializes arbitrary data to a string. In case of data that can't be easily converted to a string, this function will create a wrapper object and serialize the data using JSON.stringify. The outcoming string will always be escaped using Chartist.escapingMap.
@ -278,9 +278,11 @@ var Chartist = {
data = JSON.stringify({data: data});
}
return Object.keys(Chartist.escapingMap).reduce(function(result, key) {
return Chartist.replaceAll(result, key, Chartist.escapingMap[key]);
}, data);
return _.escape(data);
// return Object.keys(Chartist.escapingMap).reduce(function(result, key) {
// return Chartist.replaceAll(result, key, Chartist.escapingMap[key]);
// }, data);
};
/**
@ -295,9 +297,10 @@ var Chartist = {
return data;
}
data = Object.keys(Chartist.escapingMap).reduce(function(result, key) {
return Chartist.replaceAll(result, Chartist.escapingMap[key], key);
}, data);
// data = Object.keys(Chartist.escapingMap).reduce(function(result, key) {
// return Chartist.replaceAll(result, Chartist.escapingMap[key], key);
// }, data);
data = _.unescape(data);
try {
data = JSON.parse(data);
@ -770,24 +773,24 @@ var Chartist = {
return bounds;
};
/**
* Calculate cartesian coordinates of polar coordinates
*
* @memberof Chartist.Core
* @param {Number} centerX X-axis coordinates of center point of circle segment
* @param {Number} centerY X-axis coordinates of center point of circle segment
* @param {Number} radius Radius of circle segment
* @param {Number} angleInDegrees Angle of circle segment in degrees
* @return {{x:Number, y:Number}} Coordinates of point on circumference
*/
Chartist.polarToCartesian = function (centerX, centerY, radius, angleInDegrees) {
var angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;
return {
x: centerX + (radius * Math.cos(angleInRadians)),
y: centerY + (radius * Math.sin(angleInRadians))
};
};
// /**
// * Calculate cartesian coordinates of polar coordinates
// *
// * @memberof Chartist.Core
// * @param {Number} centerX X-axis coordinates of center point of circle segment
// * @param {Number} centerY X-axis coordinates of center point of circle segment
// * @param {Number} radius Radius of circle segment
// * @param {Number} angleInDegrees Angle of circle segment in degrees
// * @return {{x:Number, y:Number}} Coordinates of point on circumference
// */
// Chartist.polarToCartesian = function (centerX, centerY, radius, angleInDegrees) {
// var angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;
//
// return {
// x: centerX + (radius * Math.cos(angleInRadians)),
// y: centerY + (radius * Math.sin(angleInRadians))
// };
// };
/**
* Initialize chart drawing rectangle (area where chart is drawn) x1,y1 = bottom left / x2,y2 = top right
@ -907,8 +910,10 @@ var Chartist = {
positionalData[axis.counterUnits.len] = axisOffset - 10;
var lblText = labels[index];
//round (!! will break for non-numeric)
lblText = Math.round(+lblText*100)/100;
if (_.isNumber(lblText)) {
lblText = Chartist.roundWithPrecision(lblText, 2);
}
if(useForeignObject) {
// We need to set width and height explicitly to px as span will not expand with width and height being

547
html_src/js-src/lib/chartist.zoom.js

@ -1,272 +1,285 @@
(function (root, factory) {
// if (typeof define === 'function' && define.amd) {
// // AMD. Register as an anonymous module.
// define([], function () {
// return (root.returnExportsGlobal = factory());
// });
// } else if (typeof exports === 'object') {
// // Node. Does not work with strict CommonJS, but
// // only CommonJS-like enviroments that support module.exports,
// // like Node.
// module.exports = factory();
// } else {
root['Chartist.plugins.zoom'] = factory();
// }
// if (typeof define === 'function' && define.amd) {
// // AMD. Register as an anonymous module.
// define([], function () {
// return (root.returnExportsGlobal = factory());
// });
// } else if (typeof exports === 'object') {
// // Node. Does not work with strict CommonJS, but
// // only CommonJS-like enviroments that support module.exports,
// // like Node.
// module.exports = factory();
// } else {
root['Chartist.plugins.zoom'] = factory();
// }
}(this, function () {
/**
* Chartist.js zoom plugin.
*
*/
(function (window, document, Chartist) {
'use strict';
var defaultOptions = {
// onZoom
// resetOnRightMouseBtn
};
Chartist.plugins = Chartist.plugins || {};
Chartist.plugins.zoom = function (options) {
options = Chartist.extend({}, defaultOptions, options);
return function zoom(chart) {
if (!(chart instanceof Chartist.Line)) {
return;
}
var rect, svg, axisX, axisY, chartRect;
var downPosition;
var onZoom = options.onZoom;
var ongoingTouches = [];
chart.on('draw', function (data) {
var type = data.type;
if (type === 'line' || type === 'bar' || type === 'area' || type === 'point') {
data.element.attr({
'clip-path': 'url(#zoom-mask)'
});
}
});
chart.on('created', function (data) {
axisX = data.axisX;
axisY = data.axisY;
chartRect = data.chartRect;
svg = data.svg._node;
rect = data.svg.elem('rect', {
x: 10,
y: 10,
width: 100,
height: 100,
}, 'ct-zoom-rect');
hide(rect);
var defs = data.svg.querySelector('defs') || data.svg.elem('defs');
var width = chartRect.width();
var height = chartRect.height();
defs
.elem('clipPath', {
id: 'zoom-mask'
})
.elem('rect', {
x: chartRect.x1,
y: chartRect.y2,
width: width,
height: height,
fill: 'white'
});
svg.addEventListener('mousedown', onMouseDown);
svg.addEventListener('mouseup', onMouseUp);
svg.addEventListener('mousemove', onMouseMove);
svg.addEventListener('touchstart', onTouchStart);
svg.addEventListener('touchmove', onTouchMove);
svg.addEventListener('touchend', onTouchEnd);
svg.addEventListener('touchcancel', onTouchCancel);
});
function copyTouch(touch) {
var p = position(touch, svg);
p.id = touch.identifier;
return p;
}
function ongoingTouchIndexById(idToFind) {
for (var i = 0; i < ongoingTouches.length; i++) {
var id = ongoingTouches[i].id;
if (id === idToFind) {
return i;
}
}
return -1;
}
function onTouchStart(event) {
var touches = event.changedTouches;
for (var i = 0; i < touches.length; i++) {
ongoingTouches.push(copyTouch(touches[i]));
}
if (ongoingTouches.length > 1) {
rect.attr(getRect(ongoingTouches[0], ongoingTouches[1]));
show(rect);
}
}
function onTouchMove(event) {
var touches = event.changedTouches;
for (var i = 0; i < touches.length; i++) {
var idx = ongoingTouchIndexById(touches[i].identifier);
ongoingTouches.splice(idx, 1, copyTouch(touches[i]));
}
if (ongoingTouches.length > 1) {
rect.attr(getRect(ongoingTouches[0], ongoingTouches[1]));
show(rect);
event.preventDefault();
}
}
function onTouchCancel(event) {
removeTouches(event.changedTouches);
}
function removeTouches(touches) {
for (var i = 0; i < touches.length; i++) {
var idx = ongoingTouchIndexById(touches[i].identifier);
if (idx >= 0) {
ongoingTouches.splice(idx, 1);
}
}
}
function onTouchEnd(event) {
if (ongoingTouches.length > 1) {
zoomIn(getRect(ongoingTouches[0], ongoingTouches[1]));
}
removeTouches(event.changedTouches);
hide(rect);
}
function onMouseDown(event) {
if (event.button === 0) {
downPosition = position(event, svg);
rect.attr(getRect(downPosition, downPosition));
show(rect);
event.preventDefault();
}
}
var reset = function () {
chart.options.axisX.highLow = null;
chart.options.axisY.highLow = null;
chart.update(chart.data, chart.options);
};
function onMouseUp(event) {
if (event.button === 0) {
var box = getRect(downPosition, position(event, svg));
zoomIn(box);
downPosition = null;
hide(rect);
event.preventDefault();
}
else if (options.resetOnRightMouseBtn && event.button === 2) {
reset();
event.preventDefault();
}
}
function zoomIn(rect) {
if (rect.width > 5 && rect.height > 5) {
var x1 = rect.x - chartRect.x1;
var x2 = x1 + rect.width;
var y2 = chartRect.y1 - rect.y;
var y1 = y2 - rect.height;
chart.options.axisX.highLow = { low: project(x1, axisX), high: project(x2, axisX) };
chart.options.axisY.highLow = { low: project(y1, axisY), high: project(y2, axisY) };
chart.update(chart.data, chart.options);
onZoom && onZoom(chart, reset);
}
}
function onMouseMove(event) {
if (downPosition) {
var point = position(event, svg);
rect.attr(getRect(downPosition, point));
event.preventDefault();
}
}
};
};
function hide(rect) {
rect.attr({ style: 'display:none' });
}
function show(rect) {
rect.attr({ style: 'display:block' });
}
function getRect(firstPoint, secondPoint) {
var x = firstPoint.x;
var y = firstPoint.y;
var width = secondPoint.x - x;
var height = secondPoint.y - y;
if (width < 0) {
width = -width;
x = secondPoint.x;
}
if (height < 0) {
height = -height;
y = secondPoint.y;
}
return {
x: x,
y: y,
width: width,
height: height
};
}
function position(event, svg) {
return transform(event.clientX, event.clientY, svg);
}
function transform(x, y, svgElement) {
var svg = svgElement.tagName === 'svg' ? svgElement : svgElement.ownerSVGElement;
var matrix = svg.getScreenCTM();
var point = svg.createSVGPoint();
point.x = x;
point.y = y;
point = point.matrixTransform(matrix.inverse());
return point || { x: 0, y: 0 };
}
function project(value, axis) {
var max = axis.bounds.max;
var min = axis.bounds.min;
if (axis.scale && axis.scale.type === 'log') {
var base = axis.scale.base;
return Math.pow(base,
value * baseLog(max / min, base) / axis.axisLength) * min;
}
return (value * axis.bounds.range / axis.axisLength) + min;
}
function baseLog(val, base) {
return Math.log(val) / Math.log(base);
}
} (window, document, Chartist));
return Chartist.plugins.zoom;
/**
* Chartist.js zoom plugin.
*
*/
(function (window, document, Chartist) {
'use strict';
var defaultOptions = {
// onZoom
// resetOnRightMouseBtn
};
Chartist.plugins = Chartist.plugins || {};
Chartist.plugins.zoom = function (options) {
options = Chartist.extend({}, defaultOptions, options);
return function zoom(chart) {
if (!(chart instanceof Chartist.Line)) {
return;
}
var rect, svg, axisX, axisY, chartRect;
var downPosition;
var onZoom = options.onZoom;
var ongoingTouches = [];
chart.on('draw', function (data) {
var type = data.type;
if (type === 'line' || type === 'bar' || type === 'area' || type === 'point') {
data.element.attr({
'clip-path': 'url(#zoom-mask)'
});
}
});
chart.on('created', function (data) {
axisX = data.axisX;
axisY = data.axisY;
chartRect = data.chartRect;
svg = data.svg._node;
rect = data.svg.elem('rect', {
x: 10,
y: 10,
width: 100,
height: 100,
}, 'ct-zoom-rect');
hide(rect);
var defs = data.svg.querySelector('defs') || data.svg.elem('defs');
var width = chartRect.width();
var height = chartRect.height();
defs
.elem('clipPath', {
id: 'zoom-mask'
})
.elem('rect', {
x: chartRect.x1,
y: chartRect.y2,
width: width,
height: height,
fill: 'white'
});
svg.addEventListener('mousedown', onMouseDown);
svg.addEventListener('mouseup', onMouseUp);
svg.addEventListener('mousemove', onMouseMove);
svg.addEventListener('touchstart', onTouchStart);
svg.addEventListener('touchmove', onTouchMove);
svg.addEventListener('touchend', onTouchEnd);
svg.addEventListener('touchcancel', onTouchCancel);
});
function copyTouch(touch) {
var p = position(touch, svg);
p.id = touch.identifier;
return p;
}
function ongoingTouchIndexById(idToFind) {
for (var i = 0; i < ongoingTouches.length; i++) {
var id = ongoingTouches[i].id;
if (id === idToFind) {
return i;
}
}
return -1;
}
function onTouchStart(event) {
var touches = event.changedTouches;
for (var i = 0; i < touches.length; i++) {
ongoingTouches.push(copyTouch(touches[i]));
}
if (ongoingTouches.length > 1) {
rect.attr(getRect(ongoingTouches[0], ongoingTouches[1]));
show(rect);
}
}
function onTouchMove(event) {
var touches = event.changedTouches;
for (var i = 0; i < touches.length; i++) {
var idx = ongoingTouchIndexById(touches[i].identifier);
ongoingTouches.splice(idx, 1, copyTouch(touches[i]));
}
if (ongoingTouches.length > 1) {
rect.attr(getRect(ongoingTouches[0], ongoingTouches[1]));
show(rect);
event.preventDefault();
}
}
function onTouchCancel(event) {
removeTouches(event.changedTouches);
}
function removeTouches(touches) {
for (var i = 0; i < touches.length; i++) {
var idx = ongoingTouchIndexById(touches[i].identifier);
if (idx >= 0) {
ongoingTouches.splice(idx, 1);
}
}
}
function onTouchEnd(event) {
if (ongoingTouches.length > 1) {
zoomIn(getRect(ongoingTouches[0], ongoingTouches[1]));
}
removeTouches(event.changedTouches);
hide(rect);
}
function onMouseDown(event) {
if (event.button === 0) {
downPosition = position(event, svg);
rect.attr(getRect(downPosition, downPosition));
show(rect);
event.preventDefault();
}
}
var reset = function () {
chart.options.axisX.highLow = null;
chart.options.axisY.highLow = null;
chart.update(chart.data, chart.options);
};
function onMouseUp(event) {
if (event.button === 0) {
var box = getRect(downPosition, position(event, svg));
zoomIn(box);
downPosition = null;
hide(rect);
event.preventDefault();
}
else if (options.resetOnRightMouseBtn && event.button === 2) {
reset();
event.preventDefault();
}
}
function zoomIn(rect) {
if (rect.width > 5 && rect.height > 5) {
var x1 = rect.x - chartRect.x1;
var x2 = x1 + rect.width;
var y2 = chartRect.y1 - rect.y;
var y1 = y2 - rect.height;
var xLow = project(x1, axisX);
var xHigh = project(x2, axisX);
var yLow = project(y1, axisY);
var yHigh = project(y2, axisY);
var explb = chart.options.explicitBounds;
if (!_.isUndefined(explb)) {
if (!_.isUndefined(explb.xLow)) xLow = Math.max(explb.xLow, xLow);
if (!_.isUndefined(explb.xHigh)) xHigh = Math.min(explb.xHigh, xHigh);
if (!_.isUndefined(explb.yLow)) yLow = Math.max(explb.yLow, yLow);
if (!_.isUndefined(explb.yHigh)) yHigh = Math.min(explb.yHigh, yHigh);
}
chart.options.axisX.highLow = {low: xLow, high: xHigh};
chart.options.axisY.highLow = {low: yLow, high: yHigh};
chart.update(chart.data, chart.options);
onZoom && onZoom(chart, reset);
}
}
function onMouseMove(event) {
if (downPosition) {
var point = position(event, svg);
rect.attr(getRect(downPosition, point));
event.preventDefault();
}
}
};
};
function hide(rect) {
rect.attr({style: 'display:none'});
}
function show(rect) {
rect.attr({style: 'display:block'});
}
function getRect(firstPoint, secondPoint) {
var x = firstPoint.x;
var y = firstPoint.y;
var width = secondPoint.x - x;
var height = secondPoint.y - y;
if (width < 0) {
width = -width;
x = secondPoint.x;
}
if (height < 0) {
height = -height;
y = secondPoint.y;
}
return {
x: x,
y: y,
width: width,
height: height
};
}
function position(event, svg) {
return transform(event.clientX, event.clientY, svg);
}
function transform(x, y, svgElement) {
var svg = svgElement.tagName === 'svg' ? svgElement : svgElement.ownerSVGElement;
var matrix = svg.getScreenCTM();
var point = svg.createSVGPoint();
point.x = x;
point.y = y;
point = point.matrixTransform(matrix.inverse());
return point || {x: 0, y: 0};
}
function project(value, axis) {
var max = axis.bounds.max;
var min = axis.bounds.min;
if (axis.scale && axis.scale.type === 'log') {
var base = axis.scale.base;
return Math.pow(base,
value * baseLog(max / min, base) / axis.axisLength) * min;
}
return (value * axis.bounds.range / axis.axisLength) + min;
}
function baseLog(val, base) {
return Math.log(val) / Math.log(base);
}
}(window, document, Chartist));
return Chartist.plugins.zoom;
}));

3344
html_src/js-src/lib/lodash.custom.js

File diff suppressed because it is too large Load Diff

226
html_src/js-src/page_waveform.js

@ -2,50 +2,98 @@ var page_waveform = (function () {
var wfm = {};
var zoomResetFn;
var dataFormat;
function buildChart(obj, xlabel, ylabel) {
var points = [];
obj.samples.forEach(function (a, i) {
points.push({x: i, y: a});
});
var readoutPending = false;
var autoReload = false;
var autoReloadTime = 1;
var arTimeout = -1;
var zoomSavedX, zoomSavedY;
function buildChart(j) {
// Build the chart
var plugins = [];
var mql = window.matchMedia('screen and (min-width: 544px)');
var isPhone = !mql.matches;
if (!isPhone) {
// larger than phone
plugins.push(
Chartist.plugins.ctAxisTitle({
axisX: {
axisTitle: xlabel,
offset: {
x: 0,
y: 55
}
},
axisY: {
axisTitle: ylabel,
flipText: true,
offset: {
x: 0,
y: 15
}
}
})
);
var fft = (j.stats.format == 'FFT');
var xLabel, yLabel;
if (fft) {
xLabel = 'Frequency - [ Hz ]';
yLabel = 'Magnitude - [ mA ]';
} else {
xLabel = 'Sample time - [ ms ]';
yLabel = 'Current - [ mA ]';
}
// zoom
plugins.push(Chartist.plugins.zoom({
resetOnRightMouseBtn:true,
onZoom: function(chart, reset) {
zoomResetFn = reset;
}
}));
var peak = Math.max(-j.stats.min, j.stats.max);
var displayPeak = Math.max(peak, 10);
// Sidebar
var peak = obj.stats.peak;
$('#stat-count').html(j.stats.count);
$('#stat-f-s').html(j.stats.freq);
$('#stat-i-peak').html(peak);
$('#stat-i-rms').html(j.stats.rms);
$('.stats').removeClass('invis');
// --- chart ---
// Generate point entries
// add synthetic properties
var step = fft ? (j.stats.freq/j.stats.count) : (1000/j.stats.freq);
var points = _.map(j.samples, function (a, i) {
return {
x: i * step,
y: a
};
});
var plugins = [
Chartist.plugins.zoom({
resetOnRightMouseBtn: true,
onZoom: function (chart, reset) {
zoomResetFn = reset;
zoomSavedX = chart.options.axisX.highLow;
zoomSavedY = chart.options.axisY.highLow;
}
})
];
if (!isPhone) plugins.push( // larger than phone
Chartist.plugins.ctAxisTitle({
axisX: {
axisTitle: xLabel,
offset: {
x: 0,
y: 55
}
},
axisY: {
axisTitle: yLabel,
flipText: true,
offset: {
x: 0,
y: 15
}
}
})
);
var xHigh, xLow, yHigh, yLow;
if (zoomSavedX) {
// we have saved coords of the zoom rect, restore the zoom.
xHigh = zoomSavedX.high;
xLow = zoomSavedX.low;
yHigh = zoomSavedY.high;
yLow = zoomSavedY.low;
} else {
yHigh = fft ? undefined : displayPeak;
yLow = fft ? 0 : -displayPeak;
}
new Chartist.Line('#chart', {
series: [
@ -56,7 +104,7 @@ var page_waveform = (function () {
]
}, {
showPoint: false,
// showArea: true,
showArea: fft,
fullWidth: true,
chartPadding: (isPhone ? {right: 20, bottom: 5, left: 0} : {right: 25, bottom: 30, left: 25}),
series: {
@ -66,22 +114,32 @@ var page_waveform = (function () {
},
axisX: {
type: Chartist.AutoScaleAxis,
onlyInteger: true
//onlyInteger: !fft // only for raw
high: xHigh,
low: xLow,
},
axisY: {
type: Chartist.AutoScaleAxis,
//onlyInteger: true
high: peak,
low: -peak,
high: yHigh,
low: yLow,
},
explicitBounds: {
xLow: 0,
yLow: fft ? 0 : undefined,
xHigh: points[points.length-1].x
},
plugins: plugins
});
}
function onRxData(resp, status) {
readoutPending = false;
if (status != 200) {
// bad response
alert("Request failed.");
if (status != 0) { // 0 = aborted
alert("Request failed.");
}
return;
}
@ -91,46 +149,84 @@ var page_waveform = (function () {
return;
}
j.stats.peak = Math.max(-j.stats.min, j.stats.max);
buildChart(j, 'Sample number', 'Current - mA');
if (autoReload)
arTimeout = setTimeout(requestReload, autoReloadTime);
$('#stat-count').html(j.stats.count);
$('#stat-f-s').html(j.stats.freq);
$('#stat-i-peak').html(j.stats.peak);
$('#stat-i-rms').html(j.stats.rms);
$('.stats').removeClass('invis');
buildChart(j);
}
wfm.init = function() {
// var resp = {
// "samples": [1878, 1883, 1887, 1897, 1906, 1915, 1926, 1940, 1955, 1970, 1982, 1996, 2012, 2026, 2038, 2049],
// "success": true
// };
function requestReload() {
if (readoutPending) return false;
function loadBtnClick() {
var samples = $('#count').val();
var freq = $('#freq').val();
readoutPending = true;
//http://192.168.1.13
$().get('/api/raw.json?n='+samples+'&fs='+freq, onRxData, true, true);
}
//http://192.168.1.13
var url = '/api/{fmt}.json?n={n}&fs={fs}'.format({
fmt: dataFormat,
n: $('#count').val(),
fs: $('#freq').val()
});
$().get(url, onRxData, true, true);
return true;
}
wfm.init = function (format) {
// --- Load data ---
dataFormat = format;
$('#load').on('click', loadBtnClick);
// initial
// requestReload();
$('#count,#freq').on('keyup', function(e) {
$('#load').on('click', requestReload);
$('#count,#freq').on('keyup', function (e) {
if (e.which == 13) {
loadBtnClick();
requestReload();
}
});
// chart zooming
$('#chart').on('contextmenu', function(e) {
// --- zooming ---
$('#chart').on('contextmenu', function (e) { // right click on the chart -> reset
zoomResetFn && zoomResetFn();
zoomResetFn = null;
zoomSavedX = null;
zoomSavedY = null;
e.preventDefault();
return false;
});
// --- scroll the input box ---
$('input[type=number]').on('mousewheel', function(e) {
var val = +$(this).val();
var step = +($(this).attr('step') || 1);
if(e.wheelDelta > 0) {
val += step;
} else {
val -= step;
}
$(this).val(val);
});
// auto-reload button
$('#ar-btn').on('click', function() {
autoReloadTime = +$('#ar-time').val() * 1000; // ms
autoReload = !autoReload;
if (autoReload) {
requestReload();
} else {
clearTimeout(arTimeout);
}
$('#ar-btn')
.toggleClass('btn-blue')
.toggleClass('btn-red')
.val(autoReload ? 'Stop' : 'Auto');
});
};
return wfm;

4
html_src/js-src/page_wifi.js

@ -50,7 +50,7 @@ var page_wifi = (function () {
var inner = document.createElement('div');
var $inner = $(inner).addClass('inner')
.htmlAppend('<div class="rssi">{0}</div>'.format(ap.rssi_perc))
.htmlAppend('<div class="essid" title="{0}">{0}</div>'.format(e(ap.essid)))
.htmlAppend('<div class="essid" title="{0}">{0}</div>'.format(_.escape(ap.essid)))
.htmlAppend('<div class="auth">{0}</div>'.format(authStr[ap.enc]));
$item.on('click', function () {